Run clang-format on everything (#1332)

This commit is contained in:
Sylvie
2024-03-04 14:22:49 -05:00
committed by GitHub
parent b004648a13
commit e74073e480
66 changed files with 6091 additions and 4957 deletions

View File

@@ -1,13 +1,13 @@
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignAfterOpenBracket: BlockIndent
AlignArrayOfStructures: Left
AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: Consecutive
AlignConsecutiveDeclarations: None
AlignConsecutiveMacros: Consecutive
AlignEscapedNewlines: Left
AlignEscapedNewlines: DontAlign
AlignOperands: Align
AlignTrailingComments: false
AlignTrailingComments: true
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: true
@@ -21,8 +21,8 @@ AlwaysBreakTemplateDeclarations: Yes
AttributeMacros:
- format_
- attr_
BinPackArguments: true
BinPackParameters: true
BinPackArguments: false
BinPackParameters: false
BitFieldColonSpacing: Both
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Attach
@@ -60,8 +60,8 @@ IndentPPDirectives: BeforeHash
IndentRequires: true
IndentWidth: 4
IndentWrappedFunctionNames: true
# Only support for Javascript as of clang-format 13...
# InsertTrailingCommas: true
# Only support for Javascript as of clang-format 14...
# InsertTrailingCommas: Wrapped
KeepEmptyLinesAtTheStartOfBlocks: false
LambdaBodyIndentation: Signature
Language: Cpp

View File

@@ -3,6 +3,7 @@
#ifndef RGBDS_FORMAT_SPEC_H
#define RGBDS_FORMAT_SPEC_H
#include <stddef.h>
#include <stdint.h>
enum FormatState {

View File

@@ -11,10 +11,10 @@
#include <variant>
#include <vector>
#include "asm/lexer.hpp"
#include "linkdefs.hpp"
#include "asm/lexer.hpp"
struct FileStackNode {
FileStackNode *parent; // Pointer to parent node, for error reporting
// Line at which the parent context was exited; meaningless for the root level
@@ -28,7 +28,8 @@ struct FileStackNode {
std::monostate, // Default constructed; `.type` and `.data` must be set manually
std::vector<uint32_t>, // NODE_REPT
std::string // NODE_FILE, NODE_MACRO
> data;
>
data;
// REPT iteration counts since last named node, in reverse depth order
std::vector<uint32_t> &iters();
@@ -62,8 +63,15 @@ bool yywrap();
void fstk_RunInclude(char const *path);
void fstk_RunMacro(char const *macroName, MacroArgs &args);
void fstk_RunRept(uint32_t count, 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);
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_StopRept();
bool fstk_Break();

View File

@@ -85,38 +85,29 @@ struct LexerState {
bool expandStrings;
std::deque<Expansion> expansions; // Front is the innermost current expansion
std::variant<
std::monostate,
MmappedLexerState,
ViewedLexerState,
BufferedLexerState
> content;
std::variant<std::monostate, MmappedLexerState, ViewedLexerState, BufferedLexerState> content;
};
extern LexerState *lexerState;
extern LexerState *lexerStateEOL;
static inline void lexer_SetState(LexerState *state)
{
static inline void lexer_SetState(LexerState *state) {
lexerState = state;
}
static inline void lexer_SetStateAtEOL(LexerState *state)
{
static inline void lexer_SetStateAtEOL(LexerState *state) {
lexerStateEOL = state;
}
extern char binDigits[2];
extern char gfxDigits[4];
static inline void lexer_SetBinDigits(char const digits[2])
{
static inline void lexer_SetBinDigits(char const digits[2]) {
binDigits[0] = digits[0];
binDigits[1] = digits[1];
}
static inline void lexer_SetGfxDigits(char const digits[4])
{
static inline void lexer_SetGfxDigits(char const digits[4]) {
gfxDigits[0] = digits[0];
gfxDigits[1] = digits[1];
gfxDigits[2] = digits[2];
@@ -125,8 +116,9 @@ static inline void lexer_SetGfxDigits(char const digits[4])
// `path` is referenced, but not held onto..!
bool lexer_OpenFile(LexerState &state, char const *path);
void lexer_OpenFileView(LexerState &state, char const *path, char const *buf, size_t size,
uint32_t lineNo);
void lexer_OpenFileView(
LexerState &state, char const *path, char const *buf, size_t size, uint32_t lineNo
);
void lexer_RestartRept(uint32_t lineNo);
void lexer_CleanupState(LexerState &state);
void lexer_Init();

View File

@@ -8,10 +8,10 @@
#include <string>
#include <vector>
#include "asm/warning.hpp"
#include "helpers.hpp"
#include "asm/warning.hpp"
struct MacroArgs {
unsigned int shift;
std::vector<std::string> args;

View File

@@ -16,7 +16,9 @@ void out_RegisterNode(FileStackNode *node);
void out_ReplaceNode(FileStackNode *node);
void out_SetFileName(char *s);
void out_CreatePatch(uint32_t type, Expression const &expr, uint32_t ofs, uint32_t pcShift);
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
);
void out_WriteObject();
#endif // RGBDS_ASM_OUTPUT_H

View File

@@ -5,6 +5,7 @@
#include <stdint.h>
#include <string>
#include <vector>
#include "linkdefs.hpp"
@@ -26,7 +27,9 @@ struct Expression {
void rpn_Number(Expression &expr, uint32_t val);
void rpn_Symbol(Expression &expr, char const *symName);
void rpn_LOGNOT(Expression &expr, const Expression &src);
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
);
void rpn_HIGH(Expression &expr, const Expression &src);
void rpn_LOW(Expression &expr, const Expression &src);
void rpn_ISCONST(Expression &expr, const Expression &src);

View File

@@ -52,10 +52,20 @@ extern std::deque<Section> sectionList;
extern Section *currentSection;
Section *sect_FindSectionByName(char const *name);
void sect_NewSection(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);
void sect_NewSection(
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
);
void sect_EndLoadSection();
Section *sect_GetSymbolSection();

View File

@@ -4,8 +4,8 @@
#define RGBDS_SYMBOL_H
#include <stdint.h>
#include <string>
#include <string.h>
#include <string>
#include <string_view>
#include <time.h>
#include <variant>
@@ -40,7 +40,8 @@ struct Symbol {
int32_t (*)(), // If isNumeric() and has a callback
std::string_view *, // For SYM_MACRO
std::string * // For SYM_EQUS
> data;
>
data;
uint32_t ID; // ID of the symbol in the object file (-1 if none)

View File

@@ -7,12 +7,7 @@
extern unsigned int nbErrors, maxErrors;
enum WarningState {
WARNING_DEFAULT,
WARNING_DISABLED,
WARNING_ENABLED,
WARNING_ERROR
};
enum WarningState { WARNING_DEFAULT, WARNING_DISABLED, WARNING_ENABLED, WARNING_ERROR };
enum WarningID {
WARNING_ASSERT, // Assertions

View File

@@ -13,7 +13,6 @@ void warnx(char const *fmt, ...) format_(printf, 1, 2);
[[noreturn]] void err(char const *fmt, ...) format_(printf, 1, 2);
[[noreturn]] void errx(char const *fmt, ...) format_(printf, 1, 2);
}
#endif // RGBDS_ERROR_H

View File

@@ -17,8 +17,9 @@ struct option {
int val;
};
int musl_getopt_long_only(int argc, char **argv, char const *optstring,
const option *longopts, int *idx);
int musl_getopt_long_only(
int argc, char **argv, char const *optstring, const option *longopts, int *idx
);
#define no_argument 0
#define required_argument 1

View File

@@ -11,8 +11,8 @@
#include <ios>
#include <iostream>
#include <streambuf>
#include <string>
#include <string.h>
#include <string>
#include <string_view>
#include <variant>
@@ -40,8 +40,11 @@ public:
assert(!(mode & std::ios_base::out));
_file.emplace<std::streambuf *>(std::cin.rdbuf());
if (setmode(STDIN_FILENO, (mode & std::ios_base::binary) ? O_BINARY : O_TEXT) == -1) {
fatal("Failed to set stdin to %s mode: %s",
mode & std::ios_base::binary ? "binary" : "text", strerror(errno));
fatal(
"Failed to set stdin to %s mode: %s",
mode & std::ios_base::binary ? "binary" : "text",
strerror(errno)
);
}
} else {
assert(mode & std::ios_base::out);
@@ -50,9 +53,12 @@ public:
return this;
}
std::streambuf &operator*() {
return std::visit(Visitor{[](std::filebuf &file) -> std::streambuf & { return file; },
return std::visit(
Visitor{
[](std::filebuf &file) -> std::streambuf & { return file; },
[](std::streambuf *buf) -> std::streambuf & { return *buf; }},
_file);
_file
);
}
std::streambuf const &operator*() const {
// The non-`const` version does not perform any modifications, so it's okay.
@@ -65,25 +71,32 @@ public:
}
File *close() {
return std::visit(Visitor{[this](std::filebuf &file) {
return std::visit(
Visitor{
[this](std::filebuf &file) {
// This is called by the destructor, and an explicit `close`
// shouldn't close twice.
_file.emplace<std::streambuf *>(nullptr);
return file.close() != nullptr;
},
[](std::streambuf *buf) { return buf != nullptr; }},
_file)
[](std::streambuf *buf) { return buf != nullptr; },
},
_file
)
? this
: nullptr;
}
char const *c_str(std::string const &path) const {
return std::visit(Visitor{[&path](std::filebuf const &) { return path.c_str(); },
return std::visit(
Visitor{
[&path](std::filebuf const &) { return path.c_str(); },
[](std::streambuf const *buf) {
return buf == std::cin.rdbuf()
? "<stdin>" : "<stdout>";
}},
_file);
return buf == std::cin.rdbuf() ? "<stdin>" : "<stdout>";
},
},
_file
);
}
};

View File

@@ -15,10 +15,16 @@ struct Palette;
namespace sorting {
void indexed(std::vector<Palette> &palettes, int palSize, png_color const *palRGB,
int palAlphaSize, png_byte *palAlpha);
void grayscale(std::vector<Palette> &palettes,
std::array<std::optional<Rgba>, 0x8001> const &colors);
void indexed(
std::vector<Palette> &palettes,
int palSize,
png_color const *palRGB,
int palAlphaSize,
png_byte *palAlpha
);
void grayscale(
std::vector<Palette> &palettes, std::array<std::optional<Rgba>, 0x8001> const &colors
);
void rgb(std::vector<Palette> &palettes);
} // namespace sorting

View File

@@ -25,8 +25,12 @@ struct Rgba {
fiveBpp &= 0b11111; // For caller's convenience
return fiveBpp << 3 | fiveBpp >> 2;
};
return {_5to8(cgbColor), _5to8(cgbColor >> 5), _5to8(cgbColor >> 10),
(uint8_t)(cgbColor & 0x8000 ? 0x00 : 0xFF)};
return {
_5to8(cgbColor),
_5to8(cgbColor >> 5),
_5to8(cgbColor >> 10),
(uint8_t)(cgbColor & 0x8000 ? 0x00 : 0xFF),
};
}
/*

View File

@@ -20,7 +20,8 @@
#define attr_(...)
// This seems to generate similar code to __builtin_unreachable, despite different semantics
// Note that executing this is undefined behavior (declared [[noreturn]], but does return)
[[noreturn]] static inline void unreachable_() {}
[[noreturn]] static inline void unreachable_() {
}
#endif
// Use builtins whenever possible, and shim them otherwise
@@ -32,16 +33,14 @@
#include <assert.h>
#include <intrin.h>
#pragma intrinsic(_BitScanReverse, _BitScanForward)
static inline int ctz(unsigned int x)
{
static inline int ctz(unsigned int x) {
unsigned long cnt;
assert(x != 0);
_BitScanForward(&cnt, x);
return cnt;
}
static inline int clz(unsigned int x)
{
static inline int clz(unsigned int x) {
unsigned long cnt;
assert(x != 0);
@@ -51,8 +50,7 @@
#else
#include <limits.h>
static inline int ctz(unsigned int x)
{
static inline int ctz(unsigned int x) {
int cnt = 0;
while (!(x & 1)) {
@@ -62,8 +60,7 @@
return cnt;
}
static inline int clz(unsigned int x)
{
static inline int clz(unsigned int x) {
int cnt = 0;
while (x <= UINT_MAX / 2) {

View File

@@ -64,8 +64,9 @@ public:
}
auto operator*() const {
return std::apply([](auto &&...it) { return std::tuple<decltype(*it)...>(*it...); },
_iters);
return std::apply(
[](auto &&...it) { return std::tuple<decltype(*it)...>(*it...); }, _iters
);
}
friend auto operator==(Zip const &lhs, Zip const &rhs) {
@@ -92,7 +93,8 @@ public:
using std::begin;
return std::make_tuple(begin(containers)...);
},
_containers));
_containers
));
}
auto end() {
@@ -101,14 +103,15 @@ public:
using std::end;
return std::make_tuple(end(containers)...);
},
_containers));
_containers
));
}
};
// Take ownership of objects and rvalue refs passed to us, but not lvalue refs
template<typename T>
using Holder = std::conditional_t<std::is_lvalue_reference_v<T>, T,
std::remove_cv_t<std::remove_reference_t<T>>>;
using Holder = std::
conditional_t<std::is_lvalue_reference_v<T>, T, std::remove_cv_t<std::remove_reference_t<T>>>;
} // namespace detail
// Does the same number of iterations as the first container's iterator!

View File

@@ -31,7 +31,8 @@ extern bool isWRAM0Mode;
extern bool disablePadding;
// Helper macro for printing verbose-mode messages
#define verbosePrint(...) do { \
#define verbosePrint(...) \
do { \
if (beVerbose) \
fprintf(stderr, __VA_ARGS__); \
} while (0)
@@ -46,7 +47,8 @@ struct FileStackNode {
std::monostate, // Default constructed; `.type` and `.data` must be set manually
std::vector<uint32_t>, // NODE_REPT
std::string // NODE_FILE, NODE_MACRO
> data;
>
data;
// REPT iteration counts since last named node, in reverse depth order
std::vector<uint32_t> &iters();
@@ -58,8 +60,10 @@ struct FileStackNode {
std::string const *dumpFileStack() const;
};
void warning(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) format_(printf, 3, 4);
void warning(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
format_(printf, 3, 4);
void error(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) format_(printf, 3, 4);
[[noreturn]] void fatal(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) format_(printf, 3, 4);
[[noreturn]] void fatal(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
format_(printf, 3, 4);
#endif // RGBDS_LINK_MAIN_H

View File

@@ -8,10 +8,10 @@
#include <stdint.h>
#include <vector>
#include "link/section.hpp"
#include "linkdefs.hpp"
#include "link/section.hpp"
struct Assertion {
Patch patch; // Also used for its `.type`
std::string message;

View File

@@ -10,10 +10,10 @@
#include <string>
#include <vector>
#include "link/main.hpp"
#include "linkdefs.hpp"
#include "link/main.hpp"
struct FileStackNode;
struct Section;
struct Symbol;

View File

@@ -32,7 +32,8 @@ struct Symbol {
std::variant<
int32_t, // Constants just have a numeric value
Label // Label values refer to an offset within a specific section
> data;
>
data;
Label &label();
Label const &label() const;

View File

@@ -10,11 +10,7 @@
#define RGBDS_OBJECT_VERSION_STRING "RGBA"
#define RGBDS_OBJECT_REV 10U
enum AssertionType {
ASSERT_WARN,
ASSERT_ERROR,
ASSERT_FATAL
};
enum AssertionType { ASSERT_WARN, ASSERT_ERROR, ASSERT_FATAL };
enum RPNCommand {
RPN_ADD = 0x00,
@@ -96,8 +92,7 @@ extern struct SectionTypeInfo {
* @param type The section's type
* @return `true` if the section's definition includes data
*/
static inline bool sect_HasData(enum SectionType type)
{
static inline bool sect_HasData(enum SectionType type) {
assert(type != SECTTYPE_INVALID);
return type == SECTTYPE_ROM0 || type == SECTTYPE_ROMX;
}
@@ -106,8 +101,7 @@ static inline bool sect_HasData(enum SectionType type)
* Computes a memory region's end address (last byte), eg. 0x7FFF
* @return The address of the last byte in that memory region
*/
static inline uint16_t endaddr(enum SectionType type)
{
static inline uint16_t endaddr(enum SectionType type) {
return sectionTypeInfo[type].startAddr + sectionTypeInfo[type].size - 1;
}
@@ -115,24 +109,15 @@ static inline uint16_t endaddr(enum SectionType type)
* Computes a memory region's number of banks
* @return The number of banks, 1 for regions without banking
*/
static inline uint32_t nbbanks(enum SectionType type)
{
static inline uint32_t nbbanks(enum SectionType type) {
return sectionTypeInfo[type].lastBank - sectionTypeInfo[type].firstBank + 1;
}
enum SectionModifier {
SECTION_NORMAL,
SECTION_UNION,
SECTION_FRAGMENT
};
enum SectionModifier { SECTION_NORMAL, SECTION_UNION, SECTION_FRAGMENT };
extern char const * const sectionModNames[];
enum ExportLevel {
SYMTYPE_LOCAL,
SYMTYPE_IMPORT,
SYMTYPE_EXPORT
};
enum ExportLevel { SYMTYPE_LOCAL, SYMTYPE_IMPORT, SYMTYPE_EXPORT };
enum PatchType {
PATCHTYPE_BYTE,

View File

@@ -10,7 +10,6 @@ extern "C" {
#define PACKAGE_VERSION_PATCH 0
char const *get_package_version_string();
}
#endif // EXTERN_VERSION_H

View File

@@ -1,23 +1,24 @@
/* 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,
@@ -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;

View File

@@ -2,11 +2,12 @@
// 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"
@@ -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);
}

View File

@@ -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';
@@ -215,8 +212,9 @@ 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

View File

@@ -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;
@@ -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) {
@@ -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,8 +159,7 @@ static bool isPathValid(char const *path)
return !S_ISDIR(statbuf.st_mode);
}
std::string *fstk_FindFile(char const *path)
{
std::string *fstk_FindFile(char const *path) {
std::string *fullPath = new (std::nothrow) std::string();
if (!fullPath) {
@@ -189,13 +180,15 @@ 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
@@ -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));
@@ -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;
@@ -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) {
@@ -387,8 +375,7 @@ void fstk_RunMacro(char const *macroName, MacroArgs &args)
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,15 +385,15 @@ 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;
@@ -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,15 +489,13 @@ 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))

View File

@@ -1,7 +1,8 @@
/* SPDX-License-Identifier: MIT */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
@@ -14,8 +15,8 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <string>
#include <unordered_map>
#include <variant>
#include <vector>
@@ -26,10 +27,10 @@
#include "platform.hpp" // For `ssize_t` and `AT`
#include "util.hpp"
#include "asm/lexer.hpp"
#include "asm/fixpoint.hpp"
#include "asm/format.hpp"
#include "asm/fstack.hpp"
#include "asm/lexer.hpp"
#include "asm/macro.hpp"
#include "asm/main.hpp"
#include "asm/rpn.hpp"
@@ -40,19 +41,29 @@
// Neither MSVC nor MinGW provide `mmap`
#if defined(_MSC_VER) || defined(__MINGW32__)
// clang-format off
// (we need these `include`s in this order)
#define WIN32_LEAN_AND_MEAN // include less from windows.h
#include <windows.h> // target architecture
#include <fileapi.h> // CreateFileA
#include <winbase.h> // CreateFileMappingA
#include <memoryapi.h> // MapViewOfFile
#include <handleapi.h> // CloseHandle
// clang-format on
#define MAP_FAILED nullptr
# define mapFile(ptr, fd, path, size) do { \
#define mapFile(ptr, fd, path, size) \
do { \
(ptr) = MAP_FAILED; \
HANDLE file = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, \
FILE_FLAG_POSIX_SEMANTICS | FILE_FLAG_RANDOM_ACCESS, nullptr); \
HANDLE file = CreateFileA( \
path, \
GENERIC_READ, \
FILE_SHARE_READ, \
nullptr, \
OPEN_EXISTING, \
FILE_FLAG_POSIX_SEMANTICS | FILE_FLAG_RANDOM_ACCESS, \
nullptr \
); \
HANDLE mappingObj; \
\
if (file == INVALID_HANDLE_VALUE) \
break; \
mappingObj = CreateFileMappingA(file, nullptr, PAGE_READONLY, 0, 0, nullptr); \
@@ -66,14 +77,15 @@
#else // defined(_MSC_VER) || defined(__MINGW32__)
#include <sys/mman.h>
# define mapFile(ptr, fd, path, size) do { \
#define mapFile(ptr, fd, path, size) \
do { \
(ptr) = mmap(nullptr, (size), PROT_READ, MAP_PRIVATE, (fd), 0); \
\
if ((ptr) == MAP_FAILED && errno == ENOTSUP) { \
/*
* The implementation may not support MAP_PRIVATE; try again with MAP_SHARED
* instead, offering, I believe, weaker guarantees about external modifications to
* the file while reading it. That's still better than not opening it at all, though
/* \
* The implementation may not support MAP_PRIVATE; try again with MAP_SHARED \
* instead, offering, I believe, weaker guarantees about external modifications to \
* the file while reading it. That's still better than not opening it at all, \
* though \
*/ \
if (verbose) \
printf("mmap(%s, MAP_PRIVATE) failed, retrying with MAP_SHARED\n", path); \
@@ -297,16 +309,14 @@ static std::unordered_map<std::string, int, CaseInsensitive, CaseInsensitive> ke
{".", T_PERIOD },
};
static bool isWhitespace(int c)
{
static bool isWhitespace(int c) {
return c == ' ' || c == '\t';
}
LexerState *lexerState = nullptr;
LexerState *lexerStateEOL = nullptr;
static void initState(LexerState &state)
{
static void initState(LexerState &state) {
state.mode = LEXER_NORMAL;
state.atLineStart = true; // yylex() will init colNo due to this
state.lastToken = T_EOF;
@@ -324,60 +334,46 @@ static void initState(LexerState &state)
state.expansions.clear();
}
static void nextLine()
{
static void nextLine() {
lexerState->lineNo++;
lexerState->colNo = 1;
}
uint32_t lexer_GetIFDepth()
{
uint32_t lexer_GetIFDepth() {
return lexerState->ifStack.size();
}
void lexer_IncIFDepth()
{
void lexer_IncIFDepth() {
lexerState->ifStack.push_front({.ranIfBlock = false, .reachedElseBlock = false});
}
void lexer_DecIFDepth()
{
void lexer_DecIFDepth() {
if (lexerState->ifStack.empty())
fatalerror("Found ENDC outside an IF construct\n");
lexerState->ifStack.pop_front();
}
bool lexer_RanIFBlock()
{
bool lexer_RanIFBlock() {
return lexerState->ifStack.front().ranIfBlock;
}
bool lexer_ReachedELSEBlock()
{
bool lexer_ReachedELSEBlock() {
return lexerState->ifStack.front().reachedElseBlock;
}
void lexer_RunIFBlock()
{
void lexer_RunIFBlock() {
lexerState->ifStack.front().ranIfBlock = true;
}
void lexer_ReachELSEBlock()
{
void lexer_ReachELSEBlock() {
lexerState->ifStack.front().reachedElseBlock = true;
}
bool lexer_OpenFile(LexerState &state, char const *path)
{
bool lexer_OpenFile(LexerState &state, char const *path) {
if (!strcmp(path, "-")) {
state.path = "<stdin>";
state.content = BufferedLexerState{
.fd = STDIN_FILENO,
.index = 0,
.buf = {},
.nbChars = 0
};
state.content = BufferedLexerState{.fd = STDIN_FILENO, .index = 0, .buf = {}, .nbChars = 0};
if (verbose)
printf("Opening stdin\n");
} else {
@@ -407,7 +403,7 @@ bool lexer_OpenFile(LexerState &state, char const *path)
.ptr = (char *)mappingAddr,
.size = (size_t)fileInfo.st_size,
.offset = 0,
.isReferenced = false
.isReferenced = false,
};
if (verbose)
printf("File \"%s\" is mmap()ped\n", path);
@@ -417,18 +413,12 @@ bool lexer_OpenFile(LexerState &state, char const *path)
if (!isMmapped) {
// Sometimes mmap() fails or isn't available, so have a fallback
state.content = BufferedLexerState{
.fd = fd,
.index = 0,
.buf = {},
.nbChars = 0
};
state.content = BufferedLexerState{.fd = fd, .index = 0, .buf = {}, .nbChars = 0};
if (verbose) {
if (fileInfo.st_size == 0) {
printf("File \"%s\" is empty\n", path);
} else {
printf("File \"%s\" is opened; errno reports: %s\n",
path, strerror(errno));
printf("File \"%s\" is opened; errno reports: %s\n", path, strerror(errno));
}
}
}
@@ -439,32 +429,29 @@ bool lexer_OpenFile(LexerState &state, char const *path)
return true;
}
void lexer_OpenFileView(LexerState &state, char const *path, char const *buf, size_t size,
uint32_t lineNo)
{
void lexer_OpenFileView(
LexerState &state, char const *path, char const *buf, size_t size, uint32_t lineNo
) {
state.path = path; // Used to report read errors in `peekInternal`
state.content = ViewedLexerState{
.ptr = buf,
.size = size,
.offset = 0
};
state.content = ViewedLexerState{.ptr = buf, .size = size, .offset = 0};
initState(state);
state.lineNo = lineNo; // Will be incremented at first line start
}
void lexer_RestartRept(uint32_t lineNo)
{
std::visit(Visitor{
void lexer_RestartRept(uint32_t lineNo) {
std::visit(
Visitor{
[](MmappedLexerState &mmap) { mmap.offset = 0; },
[](ViewedLexerState &view) { view.offset = 0; },
[](auto &) {},
}, lexerState->content);
},
lexerState->content
);
initState(*lexerState);
lexerState->lineNo = lineNo;
}
void lexer_CleanupState(LexerState &state)
{
void lexer_CleanupState(LexerState &state) {
// A big chunk of the lexer state soundness is the file stack ("fstack").
// Each context in the fstack has its own *unique* lexer state; thus, we always guarantee
// that lexer states lifetimes are always properly managed, since they're handled solely
@@ -478,32 +465,30 @@ void lexer_CleanupState(LexerState &state)
// `lexerStateEOL`, but there's currently no situation in which this should happen.
assert(&state != lexerStateEOL);
std::visit(Visitor{
std::visit(
Visitor{
[](MmappedLexerState &mmap) {
if (!mmap.isReferenced)
munmap(mmap.ptr, mmap.size);
},
[](BufferedLexerState &cbuf) {
close(cbuf.fd);
},
[](BufferedLexerState &cbuf) { close(cbuf.fd); },
[](auto &) {},
}, state.content);
},
state.content
);
}
void lexer_SetMode(enum LexerMode mode)
{
void lexer_SetMode(enum LexerMode mode) {
lexerState->mode = mode;
}
void lexer_ToggleStringExpansion(bool enable)
{
void lexer_ToggleStringExpansion(bool enable) {
lexerState->expandStrings = enable;
}
// Functions for the actual lexer to obtain characters
static void beginExpansion(char const *str, bool owned, char const *name)
{
static void beginExpansion(char const *str, bool owned, char const *name) {
size_t size = strlen(str);
// Do not expand empty strings
@@ -522,20 +507,17 @@ static void beginExpansion(char const *str, bool owned, char const *name)
});
}
void lexer_CheckRecursionDepth()
{
void lexer_CheckRecursionDepth() {
if (lexerState->expansions.size() > maxRecursionDepth + 1)
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
}
static void freeExpansion(Expansion &expansion)
{
static void freeExpansion(Expansion &expansion) {
if (expansion.owned)
delete[] expansion.contents.owned;
}
static bool isMacroChar(char c)
{
static bool isMacroChar(char c) {
return c == '@' || c == '#' || c == '<' || (c >= '0' && c <= '9');
}
@@ -546,8 +528,7 @@ static uint32_t readNumber(int radix, uint32_t baseValue);
static bool startsIdentifier(int c);
static bool continuesIdentifier(int c);
static uint32_t readBracketedMacroArgNum()
{
static uint32_t readBracketedMacroArgNum() {
bool disableMacroArgs = lexerState->disableMacroArgs;
bool disableInterpolation = lexerState->disableInterpolation;
@@ -612,8 +593,7 @@ static uint32_t readBracketedMacroArgNum()
return num;
}
static char const *readMacroArg(char name)
{
static char const *readMacroArg(char name) {
char const *str = nullptr;
if (name == '@') {
@@ -642,8 +622,7 @@ static char const *readMacroArg(char name)
return str;
}
static size_t readInternal(BufferedLexerState &cbuf, size_t bufIndex, size_t nbChars)
{
static size_t readInternal(BufferedLexerState &cbuf, size_t bufIndex, size_t nbChars) {
// This buffer overflow made me lose WEEKS of my life. Never again.
assert(bufIndex + nbChars <= LEXER_BUF_SIZE);
ssize_t nbReadChars = read(cbuf.fd, &cbuf.buf[bufIndex], nbChars);
@@ -656,8 +635,7 @@ static size_t readInternal(BufferedLexerState &cbuf, size_t bufIndex, size_t nbC
}
// We only need one character of lookahead, for macro arguments
static int peekInternal(uint8_t distance)
{
static int peekInternal(uint8_t distance) {
for (Expansion &exp : lexerState->expansions) {
// An expansion that has reached its end will have `exp->offset` == `exp->size`,
// and `peekInternal` will continue with its parent
@@ -668,10 +646,14 @@ static int peekInternal(uint8_t distance)
}
if (distance >= LEXER_BUF_SIZE)
fatalerror("Internal lexer error: buffer has insufficient size for peeking (%"
PRIu8 " >= %u)\n", distance, LEXER_BUF_SIZE);
fatalerror(
"Internal lexer error: buffer has insufficient size for peeking (%" PRIu8 " >= %u)\n",
distance,
LEXER_BUF_SIZE
);
return std::visit(Visitor{
return std::visit(
Visitor{
[&distance](MmappedLexerState &mmap) -> int {
if (size_t idx = mmap.offset + distance; idx < mmap.size)
return (uint8_t)mmap.ptr[idx];
@@ -717,18 +699,17 @@ static int peekInternal(uint8_t distance)
// If there aren't enough chars even after refilling, give up
return EOF;
},
[](std::monostate) -> int {
return EOF;
}
}, lexerState->content);
[](std::monostate) -> int { return EOF; },
},
lexerState->content
);
}
// forward declarations for peek
static void shiftChar();
static char const *readInterpolation(size_t depth);
static int peek()
{
static int peek() {
int c = peekInternal(0);
if (lexerState->macroArgScanDistance > 0)
@@ -773,8 +754,7 @@ static int peek()
return c;
}
static void shiftChar()
{
static void shiftChar() {
if (lexerState->capturing) {
if (lexerState->captureBuf)
lexerState->captureBuf->push_back(peek());
@@ -800,13 +780,10 @@ restart:
} else {
// Advance within the file contents
lexerState->colNo++;
std::visit(Visitor{
[](MmappedLexerState &mmap) {
mmap.offset++;
},
[](ViewedLexerState &view) {
view.offset++;
},
std::visit(
Visitor{
[](MmappedLexerState &mmap) { mmap.offset++; },
[](ViewedLexerState &view) { view.offset++; },
[](BufferedLexerState &cbuf) {
assert(cbuf.index < LEXER_BUF_SIZE);
cbuf.index++;
@@ -815,13 +792,14 @@ restart:
assert(cbuf.nbChars > 0);
cbuf.nbChars--;
},
[](std::monostate) {}
}, lexerState->content);
[](std::monostate) {},
},
lexerState->content
);
}
}
static int nextChar()
{
static int nextChar() {
int c = peek();
// If not at EOF, advance read position
@@ -830,31 +808,26 @@ static int nextChar()
return c;
}
static void handleCRLF(int c)
{
static void handleCRLF(int c) {
if (c == '\r' && peek() == '\n')
shiftChar();
}
// "Services" provided by the lexer to the rest of the program
char const *lexer_GetFileName()
{
char const *lexer_GetFileName() {
return lexerState ? lexerState->path : nullptr;
}
uint32_t lexer_GetLineNo()
{
uint32_t lexer_GetLineNo() {
return lexerState->lineNo;
}
uint32_t lexer_GetColNo()
{
uint32_t lexer_GetColNo() {
return lexerState->colNo;
}
void lexer_DumpStringExpansions()
{
void lexer_DumpStringExpansions() {
if (!lexerState)
return;
@@ -866,8 +839,7 @@ void lexer_DumpStringExpansions()
}
// Discards a block comment
static void discardBlockComment()
{
static void discardBlockComment() {
lexerState->disableMacroArgs = true;
lexerState->disableInterpolation = true;
for (;;) {
@@ -887,8 +859,7 @@ static void discardBlockComment()
continue;
case '/':
if (peek() == '*') {
warning(WARNING_NESTED_COMMENT,
"/* in block comment\n");
warning(WARNING_NESTED_COMMENT, "/* in block comment\n");
}
continue;
case '*':
@@ -908,8 +879,7 @@ finish:
// Function to discard all of a line's comments
static void discardComment()
{
static void discardComment() {
lexerState->disableMacroArgs = true;
lexerState->disableInterpolation = true;
for (;; shiftChar()) {
@@ -924,8 +894,7 @@ static void discardComment()
// Function to read a line continuation
static void readLineContinuation()
{
static void readLineContinuation() {
for (;;) {
int c = peek();
@@ -941,8 +910,7 @@ static void readLineContinuation()
} else if (c == ';') {
discardComment();
} else {
error("Begun line continuation, but encountered character %s\n",
printChar(c));
error("Begun line continuation, but encountered character %s\n", printChar(c));
break;
}
}
@@ -950,8 +918,7 @@ static void readLineContinuation()
// Function to read an anonymous label ref
static void readAnonLabelRef(char c)
{
static void readAnonLabelRef(char c) {
uint32_t n = 0;
// We come here having already peeked at one char, so no need to do it again
@@ -965,8 +932,7 @@ static void readAnonLabelRef(char c)
// Functions to lex numbers of various radixes
static uint32_t readNumber(int radix, uint32_t baseValue)
{
static uint32_t readNumber(int radix, uint32_t baseValue) {
uint32_t value = baseValue;
for (;; shiftChar()) {
@@ -984,8 +950,7 @@ static uint32_t readNumber(int radix, uint32_t baseValue)
return value;
}
static uint32_t readFractionalPart(uint32_t integer)
{
static uint32_t readFractionalPart(uint32_t integer) {
uint32_t value = 0, divisor = 1;
uint8_t precision = 0;
enum {
@@ -1007,8 +972,7 @@ static uint32_t readFractionalPart(uint32_t integer)
break;
}
if (divisor > (UINT32_MAX - (c - '0')) / 10) {
warning(WARNING_LARGE_CONSTANT,
"Precision of fixed-point constant is too large\n");
warning(WARNING_LARGE_CONSTANT, "Precision of fixed-point constant is too large\n");
// Discard any additional digits
shiftChar();
while (c = peek(), (c >= '0' && c <= '9') || c == '_')
@@ -1048,8 +1012,7 @@ static uint32_t readFractionalPart(uint32_t integer)
char binDigits[2];
static uint32_t readBinaryNumber()
{
static uint32_t readBinaryNumber() {
uint32_t value = 0;
for (;; shiftChar()) {
@@ -1073,8 +1036,7 @@ static uint32_t readBinaryNumber()
return value;
}
static uint32_t readHexNumber()
{
static uint32_t readHexNumber() {
uint32_t value = 0;
bool empty = true;
@@ -1107,8 +1069,7 @@ static uint32_t readHexNumber()
char gfxDigits[4];
static uint32_t readGfxConstant()
{
static uint32_t readGfxConstant() {
uint32_t bitPlaneLower = 0, bitPlaneUpper = 0;
uint8_t width = 0;
@@ -1141,27 +1102,26 @@ static uint32_t readGfxConstant()
if (width == 0)
error("Invalid graphics constant, no digits after '`'\n");
else if (width == 9)
warning(WARNING_LARGE_CONSTANT,
"Graphics constant is too long, only first 8 pixels considered\n");
warning(
WARNING_LARGE_CONSTANT,
"Graphics constant is too long, only first 8 pixels considered\n"
);
return bitPlaneUpper << 8 | bitPlaneLower;
}
// Functions to read identifiers & keywords
static bool startsIdentifier(int c)
{
static bool startsIdentifier(int c) {
// Anonymous labels internally start with '!'
return (c <= 'Z' && c >= 'A') || (c <= 'z' && c >= 'a') || c == '.' || c == '_';
}
static bool continuesIdentifier(int c)
{
static bool continuesIdentifier(int c) {
return startsIdentifier(c) || (c <= '9' && c >= '0') || c == '#' || c == '@';
}
static int readIdentifier(char firstChar)
{
static int readIdentifier(char firstChar) {
// Lex while checking for a keyword
yylval.symName[0] = firstChar;
int tokenType = firstChar == '.' ? T_LOCAL_ID : T_ID;
@@ -1195,8 +1155,7 @@ static int readIdentifier(char firstChar)
// Functions to read strings
static char const *readInterpolation(size_t depth)
{
static char const *readInterpolation(size_t depth) {
if (depth > maxRecursionDepth)
fatalerror("Recursion limit (%zu) exceeded\n", maxRecursionDepth);
@@ -1273,14 +1232,14 @@ static char const *readInterpolation(size_t depth)
return nullptr;
}
#define append_yylval_string(c) do { \
#define append_yylval_string(c) \
do { \
/* Evaluate c exactly once in case it has side effects */ \
if (char v = (c); i < sizeof(yylval.string)) \
yylval.string[i++] = v; \
} while (0)
static size_t appendEscapedSubstring(char const *str, size_t i)
{
static size_t appendEscapedSubstring(char const *str, size_t i) {
// Copy one extra to flag overflow
while (*str) {
char c = *str++;
@@ -1312,8 +1271,7 @@ static size_t appendEscapedSubstring(char const *str, size_t i)
return i;
}
static void readString(bool raw)
{
static void readString(bool raw) {
lexerState->disableMacroArgs = true;
lexerState->disableInterpolation = true;
@@ -1466,8 +1424,7 @@ finish:
lexerState->disableInterpolation = false;
}
static size_t appendStringLiteral(size_t i, bool raw)
{
static size_t appendStringLiteral(size_t i, bool raw) {
lexerState->disableMacroArgs = true;
lexerState->disableInterpolation = true;
@@ -1618,8 +1575,7 @@ finish:
static int yylex_SKIP_TO_ENDC(); // forward declaration for yylex_NORMAL
static int yylex_NORMAL()
{
static int yylex_NORMAL() {
for (;;) {
int c = nextChar();
@@ -1880,8 +1836,7 @@ static int yylex_NORMAL()
// An ELIF after a taken IF needs to not evaluate its condition
if (tokenType == T_POP_ELIF && lexerState->lastToken == T_NEWLINE
&& lexer_GetIFDepth() > 0 && lexer_RanIFBlock()
&& !lexer_ReachedELSEBlock())
&& lexer_GetIFDepth() > 0 && lexer_RanIFBlock() && !lexer_ReachedELSEBlock())
return yylex_SKIP_TO_ENDC();
// If a keyword, don't try to expand
@@ -1919,8 +1874,7 @@ static int yylex_NORMAL()
}
}
static int yylex_RAW()
{
static int yylex_RAW() {
// This is essentially a modified `appendStringLiteral`
size_t parenDepth = 0;
size_t i = 0;
@@ -2094,8 +2048,7 @@ finish:
// "meaningful" (= at line start) vs. "meaningless" (everything else) tokens.
// It's especially important due to macro args not being handled in this
// state, and lexing them in "normal" mode potentially producing such tokens.
static int skipIfBlock(bool toEndc)
{
static int skipIfBlock(bool toEndc) {
lexer_SetMode(LEXER_NORMAL);
uint32_t startingDepth = lexer_GetIFDepth();
int token;
@@ -2178,18 +2131,15 @@ finish:
return token;
}
static int yylex_SKIP_TO_ELIF()
{
static int yylex_SKIP_TO_ELIF() {
return skipIfBlock(false);
}
static int yylex_SKIP_TO_ENDC()
{
static int yylex_SKIP_TO_ENDC() {
return skipIfBlock(true);
}
static int yylex_SKIP_TO_ENDR()
{
static int yylex_SKIP_TO_ENDR() {
lexer_SetMode(LEXER_NORMAL);
int depth = 1;
bool atLineStart = lexerState->atLineStart;
@@ -2265,8 +2215,7 @@ finish:
return T_EOF;
}
int yylex()
{
int yylex() {
if (lexerState->atLineStart && lexerStateEOL) {
lexer_SetState(lexerStateEOL);
lexerStateEOL = nullptr;
@@ -2296,8 +2245,7 @@ int yylex()
return token;
}
static void startCapture(CaptureBody &capture)
{
static void startCapture(CaptureBody &capture) {
assert(!lexerState->capturing);
lexerState->capturing = true;
lexerState->captureSize = 0;
@@ -2305,17 +2253,18 @@ static void startCapture(CaptureBody &capture)
lexerState->disableInterpolation = true;
capture.lineNo = lexer_GetLineNo();
capture.body = std::visit(Visitor{
capture.body = std::visit(
Visitor{
[](MmappedLexerState &mmap) -> char const * {
return lexerState->expansions.empty() ? &mmap.ptr[mmap.offset] : nullptr;
},
[](ViewedLexerState &view) -> char const * {
return lexerState->expansions.empty() ? &view.ptr[view.offset] : nullptr;
},
[](auto &) -> char const * {
return nullptr;
}
}, lexerState->content);
[](auto &) -> char const * { return nullptr; },
},
lexerState->content
);
if (capture.body == nullptr) {
// Indicates to retrieve the capture buffer when done capturing
@@ -2326,8 +2275,7 @@ static void startCapture(CaptureBody &capture)
}
}
static void endCapture(CaptureBody &capture)
{
static void endCapture(CaptureBody &capture) {
// This being `nullptr` means we're capturing from the capture buf, which is reallocated
// during the whole capture process, and so MUST be retrieved at the end
if (!capture.body)
@@ -2340,8 +2288,7 @@ static void endCapture(CaptureBody &capture)
lexerState->disableInterpolation = false;
}
bool lexer_CaptureRept(CaptureBody &capture)
{
bool lexer_CaptureRept(CaptureBody &capture) {
startCapture(capture);
size_t depth = 0;
@@ -2398,8 +2345,7 @@ finish:
return c != EOF;
}
bool lexer_CaptureMacroBody(CaptureBody &capture)
{
bool lexer_CaptureMacroBody(CaptureBody &capture) {
startCapture(capture);
// If the file is `mmap`ed, we need not to unmap it to keep access to the macro

View File

@@ -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,22 +105,19 @@ 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();
@@ -142,7 +132,6 @@ void macro_ShiftCurrentArgs(int32_t count)
}
}
uint32_t macro_NbArgs()
{
uint32_t macro_NbArgs() {
return macroArgs->args.size() - macroArgs->shift;
}

View File

@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: MIT */
#include "asm/main.hpp"
#include <ctype.h>
#include <errno.h>
#include <float.h>
@@ -7,31 +9,29 @@
#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__)
@@ -43,7 +43,9 @@
// 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;
@@ -125,8 +126,7 @@ static option const longopts[] = {
{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"
@@ -141,11 +141,11 @@ static void printUsage()
" -W, --warning <warning> enable or disable warnings\n"
"\n"
"For help, use `man rgbasm' or go to https://rgbds.gbdev.io/docs/\n",
stderr);
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);
}

View File

@@ -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;

View File

@@ -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 &sect, FILE *f)
{
static void writesection(Section const &sect, FILE *f) {
putstring(sect.name.c_str(), f);
putlong(sect.size, f);
@@ -138,8 +132,7 @@ static void writesection(Section const &sect, 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,15 +155,13 @@ 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;
@@ -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);
@@ -335,8 +322,7 @@ static void writeFileStackNode(FileStackNode const &node, FILE *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
fatalerror(
"Internal error: fstack node #%" PRIu32 " follows #%" PRIu32
". Please report this to the developers!\n",
it[1]->ID, node->ID);
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;

File diff suppressed because it is too large Load Diff

View File

@@ -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,8 +41,7 @@ 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>();
if (!expr.rpn)
@@ -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,8 +250,7 @@ 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)
{
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
@@ -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,
warning(
WARNING_SHIFT_AMOUNT,
"Shifting right by negative amount %" PRId32 "\n",
src2.val);
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,
warning(
WARNING_SHIFT_AMOUNT,
"Shifting right by negative amount %" PRId32 "\n",
src2.val);
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,
warning(
WARNING_DIV,
"Division of %" PRId32 " by -1 yields %" PRId32 "\n",
INT32_MIN, INT32_MIN);
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;

View File

@@ -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 &sect, uint32_t size)
{
attr_(warn_unused_result) static bool checkSectionSize(Section const &sect, 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.
@@ -109,8 +112,7 @@ attr_(warn_unused_result) static bool reserveSpace(uint32_t delta_size)
&& (!currentLoadSection || currentLoadSection->size != UINT32_MAX);
}
Section *sect_FindSectionByName(char const *name)
{
Section *sect_FindSectionByName(char const *name) {
for (Section &sect : sectionList) {
if (sect.name == name)
return &sect;
@@ -119,14 +121,15 @@ Section *sect_FindSectionByName(char const *name)
}
#define mask(align) ((1U << (align)) - 1)
#define fail(...) do { \
#define fail(...) \
do { \
error(__VA_ARGS__); \
nbSectErrors++; \
} while (0)
static unsigned int mergeSectUnion(Section &sect, enum SectionType type, uint32_t org,
uint8_t alignment, uint16_t alignOffset)
{
static unsigned int mergeSectUnion(
Section &sect, 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 &sect, 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 &sect, 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);
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"
fail(
"Section already declared with incompatible %u"
"-byte alignment (offset %" PRIu16 ")\n",
1U << sect.align, sect.alignOfs);
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 &sect, enum SectionType type, uint32_
return nbSectErrors;
}
static unsigned int mergeFragments(Section &sect, uint32_t org, uint8_t alignment, uint16_t alignOffset)
{
static unsigned int
mergeFragments(Section &sect, 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 &sect, 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 &sect, 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);
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"
fail(
"Section already declared with incompatible %u"
"-byte alignment (offset %" PRIu16 ")\n",
1U << sect.align, sect.alignOfs);
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 &sect, uint32_t org, uint8_t alignmen
return nbSectErrors;
}
static void mergeSections(Section &sect, enum SectionType type, uint32_t org, uint32_t bank,
uint8_t alignment, uint16_t alignOffset, enum SectionModifier mod)
{
static void mergeSections(
Section &sect,
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 &sect, 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 &sect, 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 &sect, 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 &sect = 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;
@@ -495,8 +551,7 @@ uint32_t sect_GetAlignBytes(uint8_t alignment, uint16_t offset)
% (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
@@ -579,8 +632,7 @@ void sect_StartUnion()
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,8 +976,7 @@ cleanup:
}
// Section stack routines
void sect_PushSection()
{
void sect_PushSection() {
sectionStack.push_front({
.section = currentSection,
.loadSection = currentLoadSection,
@@ -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");

View File

@@ -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 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);
[](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)
{
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,8 +146,7 @@ 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)
{
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));
@@ -164,18 +154,15 @@ static void assignStringSymbol(Symbol &sym, char const *value)
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
error(
"Reference to anonymous label %" PRIu32 " before, when only %" PRIu32
" ha%s been created so far\n",
ofs, anonLabelID, anonLabelID == 1 ? "s" : "ve");
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,8 +527,7 @@ 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)
@@ -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,7 +596,8 @@ void sym_Init(time_t now)
sym_AddVar("_RS", 0)->isBuiltin = true;
#define addSym(fn, name, val) do { \
#define addSym(fn, name, val) \
do { \
Symbol *sym = fn(name, val); \
assert(sym); \
sym->isBuiltin = true; \
@@ -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);

View File

@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: MIT */
#include "asm/warning.hpp"
#include <inttypes.h>
#include <limits.h>
#include <stdarg.h>
@@ -8,13 +10,12 @@
#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;
@@ -48,8 +49,7 @@ 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;
@@ -109,8 +109,7 @@ static const struct {
{"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,9 +147,7 @@ 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[] = {
@@ -163,7 +162,7 @@ static uint8_t const _wallCommands[] = {
WARNING_OBSOLETE,
WARNING_NUMERIC_STRING_1,
WARNING_UNMAPPED_CHAR_1,
META_WARNING_DONE
META_WARNING_DONE,
};
// Warnings that are less likely to indicate an error
@@ -177,7 +176,7 @@ static uint8_t const _wextraCommands[] = {
WARNING_TRUNCATION_2,
WARNING_UNMAPPED_CHAR_1,
WARNING_UNMAPPED_CHAR_2,
META_WARNING_DONE
META_WARNING_DONE,
};
// Literally everything. Notably useful for testing
@@ -202,17 +201,16 @@ static uint8_t const _weverythingCommands[] = {
WARNING_UNMAPPED_CHAR_1,
WARNING_UNMAPPED_CHAR_2,
// WARNING_USER,
META_WARNING_DONE
META_WARNING_DONE,
};
static uint8_t const *metaWarningCommands[NB_META_WARNINGS] = {
_wallCommands,
_wextraCommands,
_weverythingCommands
_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;
@@ -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;

View File

@@ -1,15 +1,14 @@
/* SPDX-License-Identifier: MIT */
#include "error.hpp"
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "error.hpp"
static void vwarn(char const *fmt, va_list ap)
{
static void vwarn(char const *fmt, va_list ap) {
const char *error = strerror(errno);
fprintf(stderr, "warning: ");
@@ -17,15 +16,13 @@ static void vwarn(char const *fmt, va_list ap)
fprintf(stderr, ": %s\n", error);
}
static void vwarnx(char const *fmt, va_list ap)
{
static void vwarnx(char const *fmt, va_list ap) {
fprintf(stderr, "warning: ");
vfprintf(stderr, fmt, ap);
putc('\n', stderr);
}
[[noreturn]] static void verr(char const *fmt, va_list ap)
{
[[noreturn]] static void verr(char const *fmt, va_list ap) {
const char *error = strerror(errno);
fprintf(stderr, "error: ");
@@ -35,8 +32,7 @@ static void vwarnx(char const *fmt, va_list ap)
exit(1);
}
[[noreturn]] static void verrx(char const *fmt, va_list ap)
{
[[noreturn]] static void verrx(char const *fmt, va_list ap) {
fprintf(stderr, "error: ");
vfprintf(stderr, fmt, ap);
putc('\n', stderr);
@@ -44,8 +40,7 @@ static void vwarnx(char const *fmt, va_list ap)
exit(1);
}
void warn(char const *fmt, ...)
{
void warn(char const *fmt, ...) {
va_list ap;
va_start(ap, fmt);
@@ -53,8 +48,7 @@ void warn(char const *fmt, ...)
va_end(ap);
}
void warnx(char const *fmt, ...)
{
void warnx(char const *fmt, ...) {
va_list ap;
va_start(ap, fmt);
@@ -62,16 +56,14 @@ void warnx(char const *fmt, ...)
va_end(ap);
}
[[noreturn]] void err(char const *fmt, ...)
{
[[noreturn]] void err(char const *fmt, ...) {
va_list ap;
va_start(ap, fmt);
verr(fmt, ap);
}
[[noreturn]] void errx(char const *fmt, ...)
{
[[noreturn]] void errx(char const *fmt, ...) {
va_list ap;
va_start(ap, fmt);

74
src/extern/getopt.cpp vendored
View File

@@ -2,32 +2,28 @@
/* This implementation was taken from musl and modified for RGBDS */
#include <stddef.h>
#include <stdlib.h>
#include "extern/getopt.hpp"
#include <limits.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include "extern/getopt.hpp"
char *musl_optarg;
int musl_optind = 1, musl_opterr = 1, musl_optopt;
int musl_optreset = 0;
static int musl_optpos;
static void musl_getopt_msg(char const *a, char const *b, char const *c, size_t l)
{
static void musl_getopt_msg(char const *a, char const *b, char const *c, size_t l) {
FILE *f = stderr;
if (fputs(a, f) >= 0 &&
fwrite(b, strlen(b), 1, f) &&
fwrite(c, 1, l, f) == l)
if (fputs(a, f) >= 0 && fwrite(b, strlen(b), 1, f) && fwrite(c, 1, l, f) == l)
putc('\n', f);
}
static int getopt(int argc, char *argv[], char const *optstring)
{
static int getopt(int argc, char *argv[], char const *optstring) {
int i;
wchar_t c, d;
int k, l;
@@ -101,16 +97,14 @@ static int getopt(int argc, char *argv[], char const *optstring)
if (optstring[0] == ':')
return ':';
if (musl_opterr)
musl_getopt_msg(argv[0], ": option requires an argument: ",
optchar, k);
musl_getopt_msg(argv[0], ": option requires an argument: ", optchar, k);
return '?';
}
}
return c;
}
static void permute(char **argv, int dest, int src)
{
static void permute(char **argv, int dest, int src) {
char *tmp = argv[src];
int i;
@@ -119,12 +113,13 @@ static void permute(char **argv, int dest, int src)
argv[dest] = tmp;
}
static int musl_getopt_long_core(int argc, char **argv, char const *optstring,
const option *longopts, int *idx, int longonly);
static int musl_getopt_long_core(
int argc, char **argv, char const *optstring, const option *longopts, int *idx, int longonly
);
static int musl_getopt_long(int argc, char **argv, char const *optstring,
const option *longopts, int *idx, int longonly)
{
static int musl_getopt_long(
int argc, char **argv, char const *optstring, const option *longopts, int *idx, int longonly
) {
int ret, skipped, resumed;
if (!musl_optind || musl_optreset) {
@@ -159,13 +154,13 @@ static int musl_getopt_long(int argc, char **argv, char const *optstring,
return ret;
}
static int musl_getopt_long_core(int argc, char **argv, char const *optstring,
const option *longopts, int *idx, int longonly)
{
static int musl_getopt_long_core(
int argc, char **argv, char const *optstring, const option *longopts, int *idx, int longonly
) {
musl_optarg = 0;
if (longopts && argv[musl_optind][0] == '-' &&
((longonly && argv[musl_optind][1] && argv[musl_optind][1] != '-') ||
(argv[musl_optind][1] == '-' && argv[musl_optind][2]))) {
if (longopts && argv[musl_optind][0] == '-'
&& ((longonly && argv[musl_optind][1] && argv[musl_optind][1] != '-')
|| (argv[musl_optind][1] == '-' && argv[musl_optind][2]))) {
int colon = optstring[optstring[0] == '+' || optstring[0] == '-'] == ':';
int i, cnt, match = 0;
char *arg = 0, *opt, *start = argv[musl_optind] + 1;
@@ -213,10 +208,12 @@ static int musl_getopt_long_core(int argc, char **argv, char const *optstring,
musl_optopt = longopts[i].val;
if (colon || !musl_opterr)
return '?';
musl_getopt_msg(argv[0],
musl_getopt_msg(
argv[0],
": option does not take an argument: ",
longopts[i].name,
strlen(longopts[i].name));
strlen(longopts[i].name)
);
return '?';
}
musl_optarg = opt + 1;
@@ -228,10 +225,12 @@ static int musl_getopt_long_core(int argc, char **argv, char const *optstring,
return ':';
if (!musl_opterr)
return '?';
musl_getopt_msg(argv[0],
musl_getopt_msg(
argv[0],
": option requires an argument: ",
longopts[i].name,
strlen(longopts[i].name));
strlen(longopts[i].name)
);
return '?';
}
musl_optind++;
@@ -247,11 +246,12 @@ static int musl_getopt_long_core(int argc, char **argv, char const *optstring,
if (argv[musl_optind][1] == '-') {
musl_optopt = 0;
if (!colon && musl_opterr)
musl_getopt_msg(argv[0], cnt ?
": option is ambiguous: " :
": unrecognized option: ",
musl_getopt_msg(
argv[0],
cnt ? ": option is ambiguous: " : ": unrecognized option: ",
argv[musl_optind] + 2,
strlen(argv[musl_optind] + 2));
strlen(argv[musl_optind] + 2)
);
musl_optind++;
return '?';
}
@@ -259,8 +259,8 @@ static int musl_getopt_long_core(int argc, char **argv, char const *optstring,
return getopt(argc, argv, optstring);
}
int musl_getopt_long_only(int argc, char **argv, char const *optstring,
const option *longopts, int *idx)
{
int musl_getopt_long_only(
int argc, char **argv, char const *optstring, const option *longopts, int *idx
) {
return musl_getopt_long(argc, argv, optstring, longopts, idx, 1);
}

View File

@@ -32,13 +32,10 @@ static const uint8_t utf8d[] = {
1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* s8 */
};
uint32_t decode(uint32_t *state, uint32_t *codep, uint8_t byte)
{
uint32_t decode(uint32_t *state, uint32_t *codep, uint8_t byte) {
uint32_t type = utf8d[byte];
*codep = (*state != 0) ?
(byte & 0x3FU) | (*codep << 6) :
(0xFF >> type) & (byte);
*codep = (*state != 0) ? (byte & 0x3FU) | (*codep << 6) : (0xFF >> type) & (byte);
*state = utf8d[256 + *state * 16 + type];
return *state;

View File

@@ -1,7 +1,8 @@
/* SPDX-License-Identifier: MIT */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
@@ -14,7 +15,6 @@
#include <vector>
#include "extern/getopt.hpp"
#include "helpers.hpp"
#include "platform.hpp"
#include "version.hpp"
@@ -56,8 +56,7 @@ static option const longopts[] = {
{nullptr, no_argument, nullptr, 0 }
};
static void printUsage()
{
static void printUsage() {
fputs(
"Usage: rgbfix [-jOsVv] [-C | -c] [-f <fix_spec>] [-i <game_id>] [-k <licensee>]\n"
" [-l <licensee_byte>] [-m <mbc_type>] [-n <rom_version>]\n"
@@ -71,13 +70,13 @@ static void printUsage()
" -v, --validate fix the header logo and both checksums (-f lhg)\n"
"\n"
"For help, use `man rgbfix' or go to https://rgbds.gbdev.io/docs/\n",
stderr);
stderr
);
}
static uint8_t nbErrors;
static format_(printf, 1, 2) void report(char const *fmt, ...)
{
static format_(printf, 1, 2) void report(char const *fmt, ...) {
va_list ap;
va_start(ap, fmt);
@@ -158,8 +157,7 @@ enum MbcType {
MBC_BAD_RANGE, // MBC number out of range
};
static void printAcceptedMBCNames()
{
static void printAcceptedMBCNames() {
fputs("\tROM ($00) [aka ROM_ONLY]\n", stderr);
fputs("\tMBC1 ($01), MBC1+RAM ($02), MBC1+RAM+BATTERY ($03)\n", stderr);
fputs("\tMBC2 ($05), MBC2+BATTERY ($06)\n", stderr);
@@ -188,8 +186,7 @@ static uint8_t tpp1Rev[2];
/*
* @return False on failure
*/
static bool readMBCSlice(char const *&name, char const *expected)
{
static bool readMBCSlice(char const *&name, char const *expected) {
while (*expected) {
char c = *name++;
@@ -207,8 +204,7 @@ static bool readMBCSlice(char const *&name, char const *expected)
return true;
}
static enum MbcType parseMBC(char const *name)
{
static enum MbcType parseMBC(char const *name) {
if (!strcasecmp(name, "help")) {
fputs("Accepted MBC names:\n", stderr);
printAcceptedMBCNames();
@@ -498,8 +494,9 @@ do { \
}
static_assert(MBC3 + 1 == MBC3_RAM, "Enum sanity check failed!");
static_assert(MBC3 + 2 == MBC3_RAM_BATTERY, "Enum sanity check failed!");
static_assert(MBC3_TIMER_BATTERY + 1 == MBC3_TIMER_RAM_BATTERY,
"Enum sanity check failed!");
static_assert(
MBC3_TIMER_BATTERY + 1 == MBC3_TIMER_RAM_BATTERY, "Enum sanity check failed!"
);
if (features == RAM)
mbc++;
else if (features == (RAM | BATTERY))
@@ -516,8 +513,7 @@ do { \
static_assert(MBC5 + 1 == MBC5_RAM, "Enum sanity check failed!");
static_assert(MBC5 + 2 == MBC5_RAM_BATTERY, "Enum sanity check failed!");
static_assert(MBC5_RUMBLE + 1 == MBC5_RUMBLE_RAM, "Enum sanity check failed!");
static_assert(MBC5_RUMBLE + 2 == MBC5_RUMBLE_RAM_BATTERY,
"Enum sanity check failed!");
static_assert(MBC5_RUMBLE + 2 == MBC5_RUMBLE_RAM_BATTERY, "Enum sanity check failed!");
if (features == RAM)
mbc++;
else if (features == (RAM | BATTERY))
@@ -547,8 +543,9 @@ do { \
case TPP1:
if (features & RAM)
fprintf(stderr,
"warning: TPP1 requests RAM implicitly if given a non-zero RAM size");
fprintf(
stderr, "warning: TPP1 requests RAM implicitly if given a non-zero RAM size"
);
if (features & BATTERY)
mbc |= 0x08;
if (features & TIMER)
@@ -574,8 +571,7 @@ do { \
}
}
static char const *mbcName(enum MbcType type)
{
static char const *mbcName(enum MbcType type) {
switch (type) {
case ROM:
return "ROM";
@@ -673,8 +669,7 @@ static char const *mbcName(enum MbcType type)
unreachable_();
}
static bool hasRAM(enum MbcType type)
{
static bool hasRAM(enum MbcType type) {
switch (type) {
case ROM:
case MBC1:
@@ -736,21 +731,19 @@ static bool hasRAM(enum MbcType type)
}
static const uint8_t ninLogo[] = {
0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B,
0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D,
0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E,
0xDC, 0xCC, 0x6E, 0xE6, 0xDD, 0xDD, 0xD9, 0x99,
0xBB, 0xBB, 0x67, 0x63, 0x6E, 0x0E, 0xEC, 0xCC,
0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E
0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B, 0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D,
0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E, 0xDC, 0xCC, 0x6E, 0xE6, 0xDD, 0xDD, 0xD9, 0x99,
0xBB, 0xBB, 0x67, 0x63, 0x6E, 0x0E, 0xEC, 0xCC, 0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E,
};
static const uint8_t trashLogo[] = {
0xFF^0xCE, 0xFF^0xED, 0xFF^0x66, 0xFF^0x66, 0xFF^0xCC, 0xFF^0x0D, 0xFF^0x00, 0xFF^0x0B,
0xFF^0x03, 0xFF^0x73, 0xFF^0x00, 0xFF^0x83, 0xFF^0x00, 0xFF^0x0C, 0xFF^0x00, 0xFF^0x0D,
0xFF^0x00, 0xFF^0x08, 0xFF^0x11, 0xFF^0x1F, 0xFF^0x88, 0xFF^0x89, 0xFF^0x00, 0xFF^0x0E,
0xFF^0xDC, 0xFF^0xCC, 0xFF^0x6E, 0xFF^0xE6, 0xFF^0xDD, 0xFF^0xDD, 0xFF^0xD9, 0xFF^0x99,
0xFF^0xBB, 0xFF^0xBB, 0xFF^0x67, 0xFF^0x63, 0xFF^0x6E, 0xFF^0x0E, 0xFF^0xEC, 0xFF^0xCC,
0xFF^0xDD, 0xFF^0xDC, 0xFF^0x99, 0xFF^0x9F, 0xFF^0xBB, 0xFF^0xB9, 0xFF^0x33, 0xFF^0x3E
0xFF ^ 0xCE, 0xFF ^ 0xED, 0xFF ^ 0x66, 0xFF ^ 0x66, 0xFF ^ 0xCC, 0xFF ^ 0x0D, 0xFF ^ 0x00,
0xFF ^ 0x0B, 0xFF ^ 0x03, 0xFF ^ 0x73, 0xFF ^ 0x00, 0xFF ^ 0x83, 0xFF ^ 0x00, 0xFF ^ 0x0C,
0xFF ^ 0x00, 0xFF ^ 0x0D, 0xFF ^ 0x00, 0xFF ^ 0x08, 0xFF ^ 0x11, 0xFF ^ 0x1F, 0xFF ^ 0x88,
0xFF ^ 0x89, 0xFF ^ 0x00, 0xFF ^ 0x0E, 0xFF ^ 0xDC, 0xFF ^ 0xCC, 0xFF ^ 0x6E, 0xFF ^ 0xE6,
0xFF ^ 0xDD, 0xFF ^ 0xDD, 0xFF ^ 0xD9, 0xFF ^ 0x99, 0xFF ^ 0xBB, 0xFF ^ 0xBB, 0xFF ^ 0x67,
0xFF ^ 0x63, 0xFF ^ 0x6E, 0xFF ^ 0x0E, 0xFF ^ 0xEC, 0xFF ^ 0xCC, 0xFF ^ 0xDD, 0xFF ^ 0xDC,
0xFF ^ 0x99, 0xFF ^ 0x9F, 0xFF ^ 0xBB, 0xFF ^ 0xB9, 0xFF ^ 0x33, 0xFF ^ 0x3E,
};
static enum { DMG, BOTH, CGB } model = DMG; // If DMG, byte is left alone
@@ -776,13 +769,11 @@ static bool sgb = false; // If false, SGB flags are left alone
static const char *title = nullptr;
static uint8_t titleLen;
static uint8_t maxTitleLen()
{
static uint8_t maxTitleLen() {
return gameID ? 11 : model != DMG ? 15 : 16;
}
static ssize_t readBytes(int fd, uint8_t *buf, size_t len)
{
static ssize_t readBytes(int fd, uint8_t *buf, size_t len) {
// POSIX specifies that lengths greater than SSIZE_MAX yield implementation-defined results
assert(len <= SSIZE_MAX);
@@ -807,8 +798,7 @@ static ssize_t readBytes(int fd, uint8_t *buf, size_t len)
return total;
}
static ssize_t writeBytes(int fd, uint8_t *buf, size_t len)
{
static ssize_t writeBytes(int fd, uint8_t *buf, size_t len) {
// POSIX specifies that lengths greater than SSIZE_MAX yield implementation-defined results
assert(len <= SSIZE_MAX);
@@ -838,8 +828,7 @@ static ssize_t writeBytes(int fd, uint8_t *buf, size_t len)
* @param fixedByte The fixed byte at the address
* @param areaName Name to be displayed in the warning message
*/
static void overwriteByte(uint8_t *rom0, uint16_t addr, uint8_t fixedByte, char const *areaName)
{
static void overwriteByte(uint8_t *rom0, uint16_t addr, uint8_t fixedByte, char const *areaName) {
uint8_t origByte = rom0[addr];
if (!overwriteRom && origByte != 0 && origByte != fixedByte)
@@ -855,16 +844,15 @@ static void overwriteByte(uint8_t *rom0, uint16_t addr, uint8_t fixedByte, char
* @param size How many bytes to check
* @param areaName Name to be displayed in the warning message
*/
static void overwriteBytes(uint8_t *rom0, uint16_t startAddr, uint8_t const *fixed, uint8_t size,
char const *areaName)
{
static void overwriteBytes(
uint8_t *rom0, uint16_t startAddr, uint8_t const *fixed, uint8_t size, char const *areaName
) {
if (!overwriteRom) {
for (uint8_t i = 0; i < size; i++) {
uint8_t origByte = rom0[i + startAddr];
if (origByte != 0 && origByte != fixed[i]) {
fprintf(stderr, "warning: Overwrote a non-zero byte in the %s\n",
areaName);
fprintf(stderr, "warning: Overwrote a non-zero byte in the %s\n", areaName);
break;
}
}
@@ -879,8 +867,7 @@ static void overwriteBytes(uint8_t *rom0, uint16_t startAddr, uint8_t const *fix
* @param name The file's name, to be displayed for error output
* @param fileSize The file's size if known, 0 if not.
*/
static void processFile(int input, int output, char const *name, off_t fileSize)
{
static void processFile(int input, int output, char const *name, off_t fileSize) {
// Both of these should be true for seekable files, and neither otherwise
if (input == output)
assert(fileSize != 0);
@@ -896,8 +883,13 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
report("FATAL: Failed to read \"%s\"'s header: %s\n", name, strerror(errno));
return;
} else if (rom0Len < headerSize) {
report("FATAL: \"%s\" too short, expected at least %jd ($%jx) bytes, got only %jd\n",
name, (intmax_t)headerSize, (intmax_t)headerSize, (intmax_t)rom0Len);
report(
"FATAL: \"%s\" too short, expected at least %jd ($%jx) bytes, got only %jd\n",
name,
(intmax_t)headerSize,
(intmax_t)headerSize,
(intmax_t)rom0Len
);
return;
}
// Accept partial reads if the file contains at least the header
@@ -919,8 +911,9 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
overwriteByte(rom0, 0x143, model == BOTH ? 0x80 : 0xC0, "CGB flag");
if (newLicensee)
overwriteBytes(rom0, 0x144, (uint8_t const *)newLicensee, newLicenseeLen,
"new licensee code");
overwriteBytes(
rom0, 0x144, (uint8_t const *)newLicensee, newLicenseeLen, "new licensee code"
);
if (sgb)
overwriteByte(rom0, 0x146, 0x03, "SGB flag");
@@ -963,9 +956,11 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
if (oldLicensee != UNSPECIFIED)
overwriteByte(rom0, 0x14B, oldLicensee, "old licensee code");
else if (sgb && rom0[0x14B] != 0x33)
fprintf(stderr,
fprintf(
stderr,
"warning: SGB compatibility enabled, but old licensee was 0x%02x, not 0x33\n",
rom0[0x14B]);
rom0[0x14B]
);
if (romVersion != UNSPECIFIED)
overwriteByte(rom0, 0x14C, romVersion, "mask ROM version number");
@@ -1006,7 +1001,9 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
// Update bank count, ONLY IF at least one byte was read
if (bankLen) {
// We're gonna read another bank, check that it won't be too much
static_assert(0x10000 * BANK_SIZE <= SSIZE_MAX, "Max input file size too large for OS");
static_assert(
0x10000 * BANK_SIZE <= SSIZE_MAX, "Max input file size too large for OS"
);
if (nbBanks == 0x10000) {
report("FATAL: \"%s\" has more than 65536 banks\n", name);
return;
@@ -1062,8 +1059,7 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
for (uint16_t i = 0x134; i < 0x14D; i++)
sum -= rom0[i] + 1;
overwriteByte(rom0, 0x14D, fixSpec & TRASH_HEADER_SUM ? ~sum : sum,
"header checksum");
overwriteByte(rom0, 0x14D, fixSpec & TRASH_HEADER_SUM ? ~sum : sum, "header checksum");
}
if (fixSpec & (FIX_GLOBAL_SUM | TRASH_GLOBAL_SUM)) {
@@ -1113,8 +1109,12 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
report("FATAL: Failed to write \"%s\"'s ROM0: %s\n", name, strerror(errno));
return;
} else if (writeLen < rom0Len) {
report("FATAL: Could only write %jd of \"%s\"'s %jd ROM0 bytes\n",
(intmax_t)writeLen, name, (intmax_t)rom0Len);
report(
"FATAL: Could only write %jd of \"%s\"'s %jd ROM0 bytes\n",
(intmax_t)writeLen,
name,
(intmax_t)rom0Len
);
return;
}
@@ -1127,8 +1127,12 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
report("FATAL: Failed to write \"%s\"'s ROMX: %s\n", name, strerror(errno));
return;
} else if ((size_t)writeLen < totalRomxLen) {
report("FATAL: Could only write %jd of \"%s\"'s %zu ROMX bytes\n",
(intmax_t)writeLen, name, totalRomxLen);
report(
"FATAL: Could only write %jd of \"%s\"'s %zu ROMX bytes\n",
(intmax_t)writeLen,
name,
totalRomxLen
);
return;
}
}
@@ -1137,8 +1141,7 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
if (padValue != UNSPECIFIED) {
if (input == output) {
if (lseek(output, 0, SEEK_END) == (off_t)-1) {
report("FATAL: Failed to seek to end of \"%s\": %s\n",
name, strerror(errno));
report("FATAL: Failed to seek to end of \"%s\": %s\n", name, strerror(errno));
return;
}
}
@@ -1153,8 +1156,7 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
// The return value is either -1, or at most `thisLen`,
// so it's fine to cast to `size_t`
if ((size_t)ret != thisLen) {
report("FATAL: Failed to write \"%s\"'s padding: %s\n",
name, strerror(errno));
report("FATAL: Failed to write \"%s\"'s padding: %s\n", name, strerror(errno));
break;
}
len -= thisLen;
@@ -1162,8 +1164,7 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
}
}
static bool processFilename(char const *name)
{
static bool processFilename(char const *name) {
nbErrors = 0;
if (!strcmp(name, "-")) {
(void)setmode(STDIN_FILENO, O_BINARY);
@@ -1181,21 +1182,24 @@ static bool processFilename(char const *name)
struct stat stat;
if (input == -1) {
report("FATAL: Failed to open \"%s\" for reading+writing: %s\n",
name, strerror(errno));
report("FATAL: Failed to open \"%s\" for reading+writing: %s\n", name, strerror(errno));
goto finish;
}
if (fstat(input, &stat) == -1) {
report("FATAL: Failed to stat \"%s\": %s\n", name, strerror(errno));
} else if (!S_ISREG(stat.st_mode)) { // TODO: Do we want to support other types?
report("FATAL: \"%s\" is not a regular file, and thus cannot be modified in-place\n",
name);
report(
"FATAL: \"%s\" is not a regular file, and thus cannot be modified in-place\n", name
);
} else if (stat.st_size < 0x150) {
// This check is in theory redundant with the one in `processFile`, but it
// prevents passing a file size of 0, which usually indicates pipes
report("FATAL: \"%s\" too short, expected at least 336 ($150) bytes, got only %jd\n",
name, (intmax_t)stat.st_size);
report(
"FATAL: \"%s\" too short, expected at least 336 ($150) bytes, got only %jd\n",
name,
(intmax_t)stat.st_size
);
} else {
processFile(input, input, name, stat.st_size);
}
@@ -1204,13 +1208,17 @@ static bool processFilename(char const *name)
}
finish:
if (nbErrors)
fprintf(stderr, "Fixing \"%s\" failed with %u error%s\n",
name, nbErrors, nbErrors == 1 ? "" : "s");
fprintf(
stderr,
"Fixing \"%s\" failed with %u error%s\n",
name,
nbErrors,
nbErrors == 1 ? "" : "s"
);
return nbErrors;
}
int main(int argc, char *argv[])
{
int main(int argc, char *argv[]) {
nbErrors = 0;
for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) {
@@ -1230,8 +1238,10 @@ do { \
tmp = strtoul(musl_optarg, &endptr, 0); \
} \
if (*endptr) \
report("error: Expected number as argument to option '" name "', got %s\n", \
musl_optarg); \
report( \
"error: Expected number as argument to option '" name "', got %s\n", \
musl_optarg \
); \
else if (tmp > 0xFF) \
report("error: Argument to option '" name "' is larger than 255: %lu\n", tmp); \
else \
@@ -1244,8 +1254,7 @@ do { \
model = ch == 'c' ? BOTH : CGB;
if (titleLen > 15) {
titleLen = 15;
fprintf(stderr, "warning: Truncating title \"%s\" to 15 chars\n",
title);
fprintf(stderr, "warning: Truncating title \"%s\" to 15 chars\n", title);
}
break;
@@ -1262,8 +1271,7 @@ do { \
#define overrideSpec(cur, bad) \
do { \
if (fixSpec & SPEC_##bad) \
fprintf(stderr, \
"warning: '" #cur "' overriding '" #bad "' in fix spec\n"); \
fprintf(stderr, "warning: '" #cur "' overriding '" #bad "' in fix spec\n"); \
fixSpec = (fixSpec & ~SPEC_##bad) | SPEC_##cur; \
} while (0)
case 'l':
@@ -1288,8 +1296,7 @@ do { \
break;
default:
fprintf(stderr, "warning: Ignoring '%c' in fix spec\n",
*musl_optarg);
fprintf(stderr, "warning: Ignoring '%c' in fix spec\n", *musl_optarg);
#undef overrideSpec
}
musl_optarg++;
@@ -1301,14 +1308,12 @@ do { \
len = strlen(gameID);
if (len > 4) {
len = 4;
fprintf(stderr, "warning: Truncating game ID \"%s\" to 4 chars\n",
gameID);
fprintf(stderr, "warning: Truncating game ID \"%s\" to 4 chars\n", gameID);
}
gameIDLen = len;
if (titleLen > 11) {
titleLen = 11;
fprintf(stderr, "warning: Truncating title \"%s\" to 11 chars\n",
title);
fprintf(stderr, "warning: Truncating title \"%s\" to 11 chars\n", title);
}
break;
@@ -1321,9 +1326,9 @@ do { \
len = strlen(newLicensee);
if (len > 2) {
len = 2;
fprintf(stderr,
"warning: Truncating new licensee \"%s\" to 2 chars\n",
newLicensee);
fprintf(
stderr, "warning: Truncating new licensee \"%s\" to 2 chars\n", newLicensee
);
}
newLicenseeLen = len;
break;
@@ -1335,18 +1340,22 @@ do { \
case 'm':
cartridgeType = parseMBC(musl_optarg);
if (cartridgeType == MBC_BAD) {
report("error: Unknown MBC \"%s\"\nAccepted MBC names:\n",
musl_optarg);
report("error: Unknown MBC \"%s\"\nAccepted MBC names:\n", musl_optarg);
printAcceptedMBCNames();
} else if (cartridgeType == MBC_WRONG_FEATURES) {
report("error: Features incompatible with MBC (\"%s\")\nAccepted combinations:\n",
musl_optarg);
report(
"error: Features incompatible with MBC (\"%s\")\nAccepted combinations:\n",
musl_optarg
);
printAcceptedMBCNames();
} else if (cartridgeType == MBC_BAD_RANGE) {
report("error: Specified MBC ID out of range 0-255: %s\n",
musl_optarg);
report("error: Specified MBC ID out of range 0-255: %s\n", musl_optarg);
} else if (cartridgeType == ROM_RAM || cartridgeType == ROM_RAM_BATTERY) {
fprintf(stderr, "warning: ROM+RAM / ROM+RAM+BATTERY are under-specified and poorly supported\n");
fprintf(
stderr,
"warning: ROM+RAM / ROM+RAM+BATTERY are under-specified and poorly "
"supported\n"
);
}
break;
@@ -1377,8 +1386,7 @@ do { \
if (len > maxLen) {
len = maxLen;
fprintf(stderr, "warning: Truncating title \"%s\" to %u chars\n",
title, maxLen);
fprintf(stderr, "warning: Truncating title \"%s\" to %u chars\n", title, maxLen);
}
titleLen = len;
break;
@@ -1401,42 +1409,58 @@ do { \
}
if ((cartridgeType & 0xFF00) == TPP1 && !japanese)
fprintf(stderr, "warning: TPP1 overwrites region flag for its identification code, ignoring `-j`\n");
fprintf(
stderr,
"warning: TPP1 overwrites region flag for its identification code, ignoring `-j`\n"
);
// Check that RAM size is correct for "standard" mappers
if (ramSize != UNSPECIFIED && (cartridgeType & 0xFF00) == 0) {
if (cartridgeType == ROM_RAM || cartridgeType == ROM_RAM_BATTERY) {
if (ramSize != 1)
fprintf(stderr, "warning: MBC \"%s\" should have 2 KiB of RAM (-r 1)\n",
mbcName(cartridgeType));
fprintf(
stderr,
"warning: MBC \"%s\" should have 2 KiB of RAM (-r 1)\n",
mbcName(cartridgeType)
);
} else if (hasRAM(cartridgeType)) {
if (!ramSize) {
fprintf(stderr,
fprintf(
stderr,
"warning: MBC \"%s\" has RAM, but RAM size was set to 0\n",
mbcName(cartridgeType));
mbcName(cartridgeType)
);
} else if (ramSize == 1) {
fprintf(stderr,
fprintf(
stderr,
"warning: RAM size 1 (2 KiB) was specified for MBC \"%s\"\n",
mbcName(cartridgeType));
mbcName(cartridgeType)
);
} // TODO: check possible values?
} else if (ramSize) {
fprintf(stderr,
fprintf(
stderr,
"warning: MBC \"%s\" has no RAM, but RAM size was set to %u\n",
mbcName(cartridgeType), ramSize);
mbcName(cartridgeType),
ramSize
);
}
}
if (sgb && oldLicensee != UNSPECIFIED && oldLicensee != 0x33)
fprintf(stderr,
fprintf(
stderr,
"warning: SGB compatibility enabled, but old licensee is 0x%02x, not 0x33\n",
oldLicensee);
oldLicensee
);
argv += musl_optind;
bool failed = nbErrors;
if (!*argv) {
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);
}

View File

@@ -151,7 +151,8 @@ static option const longopts[] = {
};
static void printUsage() {
fputs("Usage: rgbgfx [-r stride] [-CmOuVZ] [-v [-v ...]] [-a <attr_map> | -A]\n"
fputs(
"Usage: rgbgfx [-r stride] [-CmOuVZ] [-v [-v ...]] [-a <attr_map> | -A]\n"
" [-b <base_ids>] [-c <colors>] [-d <depth>] [-L <slice>] [-N <nb_tiles>]\n"
" [-n <nb_pals>] [-o <out_file>] [-p <pal_file> | -P] [-q <pal_map> | -Q]\n"
" [-s <nb_colors>] [-t <tile_map> | -T] [-x <nb_tiles>] <file>\n"
@@ -163,7 +164,8 @@ static void printUsage() {
" -V, --version print RGBGFX version and exit\n"
"\n"
"For help, use `man rgbgfx' or go to https://rgbds.gbdev.io/docs/\n",
stderr);
stderr
);
}
/*
@@ -215,8 +217,9 @@ static uint16_t parseNumber(char *&string, char const *errPrefix, uint16_t errVa
};
if (charIndex(*string) == 255) {
error("%s: expected digit%s, but found nothing", errPrefix,
base != 10 ? " after base" : "");
error(
"%s: expected digit%s, but found nothing", errPrefix, base != 10 ? " after base" : ""
);
return errVal;
}
uint16_t number = 0;
@@ -246,10 +249,13 @@ static void skipWhitespace(char *&arg) {
static void registerInput(char const *arg) {
if (!options.input.empty()) {
fprintf(stderr,
fprintf(
stderr,
"FATAL: input image specified more than once! (first \"%s\", then "
"\"%s\")\n",
options.input.c_str(), arg);
options.input.c_str(),
arg
);
printUsage();
exit(1);
} else if (arg[0] == '\0') { // Empty input path
@@ -272,8 +278,10 @@ static std::vector<size_t> readAtFile(std::string const &path, std::vector<char>
}
// We only filter out `EOF`, but calling `isblank()` on anything else is UB!
static_assert(std::remove_reference_t<decltype(*file)>::traits_type::eof() == EOF,
"isblank(char_traits<...>::eof()) is UB!");
static_assert(
std::remove_reference_t<decltype(*file)>::traits_type::eof() == EOF,
"isblank(char_traits<...>::eof()) is UB!"
);
std::vector<size_t> argvOfs;
for (;;) {
@@ -371,8 +379,10 @@ static char *parseArgv(int argc, char *argv[]) {
}
skipWhitespace(arg);
if (*arg != ',') {
error("Base tile IDs must be one or two comma-separated numbers, not \"%s\"",
musl_optarg);
error(
"Base tile IDs must be one or two comma-separated numbers, not \"%s\"",
musl_optarg
);
break;
}
++arg; // Skip comma
@@ -384,8 +394,10 @@ static char *parseArgv(int argc, char *argv[]) {
options.baseTileIDs[1] = number;
}
if (*arg != '\0') {
error("Base tile IDs must be one or two comma-separated numbers, not \"%s\"",
musl_optarg);
error(
"Base tile IDs must be one or two comma-separated numbers, not \"%s\"",
musl_optarg
);
break;
}
break;
@@ -474,8 +486,10 @@ static char *parseArgv(int argc, char *argv[]) {
}
skipWhitespace(arg);
if (*arg != ',') {
error("Bank capacity must be one or two comma-separated numbers, not \"%s\"",
musl_optarg);
error(
"Bank capacity must be one or two comma-separated numbers, not \"%s\"",
musl_optarg
);
break;
}
++arg; // Skip comma
@@ -485,8 +499,10 @@ static char *parseArgv(int argc, char *argv[]) {
error("Bank 1 cannot contain more than 256 tiles");
}
if (*arg != '\0') {
error("Bank capacity must be one or two comma-separated numbers, not \"%s\"",
musl_optarg);
error(
"Bank capacity must be one or two comma-separated numbers, not \"%s\"",
musl_optarg
);
break;
}
break;
@@ -665,16 +681,23 @@ int main(int argc, char *argv[]) {
if (options.nbColorsPerPal == 0) {
options.nbColorsPerPal = 1u << options.bitDepth;
} else if (options.nbColorsPerPal > 1u << options.bitDepth) {
error("%" PRIu8 "bpp palettes can only contain %u colors, not %" PRIu8, options.bitDepth,
1u << options.bitDepth, options.nbColorsPerPal);
error(
"%" PRIu8 "bpp palettes can only contain %u colors, not %" PRIu8,
options.bitDepth,
1u << options.bitDepth,
options.nbColorsPerPal
);
}
auto autoOutPath = [](bool autoOptEnabled, std::string &path, char const *extension) {
if (autoOptEnabled) {
auto &image = localOptions.groupOutputs ? options.output : options.input;
if (image.empty()) {
fprintf(stderr, "FATAL: No %s specified\n", localOptions.groupOutputs
? "output tile data file" : "input image");
fprintf(
stderr,
"FATAL: No %s specified\n",
localOptions.groupOutputs ? "output tile data file" : "input image"
);
printUsage();
exit(1);
}
@@ -723,7 +746,8 @@ int main(int argc, char *argv[]) {
static std::array<char const *, 3> textbox{
" ,----------------------------------------.",
" | Augh, dimensional interference again?! |",
" `----------------------------------------'"};
" `----------------------------------------'",
};
for (size_t i = 0; i < gfx.size(); ++i) {
uint16_t row = gfx[i];
for (uint8_t _ = 0; _ < 10; ++_) {
@@ -781,15 +805,27 @@ int main(int argc, char *argv[]) {
}
fputs("\t]\n", stderr);
}
fprintf(stderr,
"\tInput image slice: %" PRIu32 "x%" PRIu32 " pixels starting at (%" PRIi32
", %" PRIi32 ")\n",
options.inputSlice.width, options.inputSlice.height, options.inputSlice.left,
options.inputSlice.top);
fprintf(stderr, "\tBase tile IDs: [%" PRIu8 ", %" PRIu8 "]\n", options.baseTileIDs[0],
options.baseTileIDs[1]);
fprintf(stderr, "\tMaximum %" PRIu16 " tiles in bank 0, %" PRIu16 " in bank 1\n",
options.maxNbTiles[0], options.maxNbTiles[1]);
fprintf(
stderr,
"\tInput image slice: %" PRIu32 "x%" PRIu32 " pixels starting at (%" PRIi32 ", %" PRIi32
")\n",
options.inputSlice.width,
options.inputSlice.height,
options.inputSlice.left,
options.inputSlice.top
);
fprintf(
stderr,
"\tBase tile IDs: [%" PRIu8 ", %" PRIu8 "]\n",
options.baseTileIDs[0],
options.baseTileIDs[1]
);
fprintf(
stderr,
"\tMaximum %" PRIu16 " tiles in bank 0, %" PRIu16 " in bank 1\n",
options.maxNbTiles[0],
options.maxNbTiles[1]
);
auto printPath = [](char const *name, std::string const &path) {
if (!path.empty()) {
fprintf(stderr, "\t%s: %s\n", name, path.c_str());
@@ -814,8 +850,7 @@ int main(int argc, char *argv[]) {
} else {
process();
}
} else if (!options.palettes.empty() && options.palSpecType == Options::EXPLICIT
&& !options.reverse()) {
} else if (!options.palettes.empty() && options.palSpecType == Options::EXPLICIT && !options.reverse()) {
processPalettes();
} else {
fputs("FATAL: No input image specified\n", stderr);
@@ -858,9 +893,9 @@ auto Palette::begin() -> decltype(colors)::iterator {
auto Palette::end() -> decltype(colors)::iterator {
// Return an iterator pointing past the last non-empty element.
// Since the palette may contain gaps, we must scan from the end.
return std::find_if(colors.rbegin(), colors.rend(),
[](uint16_t c) { return c != UINT16_MAX; })
.base();
return std::find_if(
colors.rbegin(), colors.rend(), [](uint16_t c) { return c != UINT16_MAX; }
).base();
}
auto Palette::begin() const -> decltype(colors)::const_iterator {
@@ -870,9 +905,9 @@ auto Palette::begin() const -> decltype(colors)::const_iterator {
auto Palette::end() const -> decltype(colors)::const_iterator {
// Same as the non-const end().
return std::find_if(colors.rbegin(), colors.rend(),
[](uint16_t c) { return c != UINT16_MAX; })
.base();
return std::find_if(
colors.rbegin(), colors.rend(), [](uint16_t c) { return c != UINT16_MAX; }
).base();
}
uint8_t Palette::size() const {

View File

@@ -140,9 +140,10 @@ public:
*/
template<typename... Ts>
void assign(Ts &&...args) {
auto freeSlot = std::find_if_not(
RANGE(_assigned),
[](std::optional<ProtoPalAttrs> const &slot) { return slot.has_value(); });
auto freeSlot =
std::find_if_not(RANGE(_assigned), [](std::optional<ProtoPalAttrs> const &slot) {
return slot.has_value();
});
if (freeSlot == _assigned.end()) { // We are full, use a new slot
_assigned.emplace_back(std::forward<Ts>(args)...);
@@ -158,15 +159,20 @@ public:
bool empty() const {
return std::find_if(
RANGE(_assigned),
[](std::optional<ProtoPalAttrs> const &slot) { return slot.has_value(); })
[](std::optional<ProtoPalAttrs> const &slot) { return slot.has_value(); }
)
== _assigned.end();
}
size_t nbProtoPals() const { return std::distance(RANGE(*this)); }
private:
template<typename Iter>
static void addUniqueColors(std::unordered_set<uint16_t> &colors, Iter iter, Iter const &end,
std::vector<ProtoPalette> const &protoPals) {
static void addUniqueColors(
std::unordered_set<uint16_t> &colors,
Iter iter,
Iter const &end,
std::vector<ProtoPalette> const &protoPals
) {
for (; iter != end; ++iter) {
ProtoPalette const &protoPal = protoPals[iter->protoPalIndex];
colors.insert(RANGE(protoPal));
@@ -226,8 +232,8 @@ public:
* Computes the "relative size" of a set of proto-palettes on this palette
*/
template<typename Iter>
auto combinedVolume(Iter &&begin, Iter const &end,
std::vector<ProtoPalette> const &protoPals) const {
auto combinedVolume(Iter &&begin, Iter const &end, std::vector<ProtoPalette> const &protoPals)
const {
auto &colors = uniqueColors();
addUniqueColors(colors, std::forward<Iter>(begin), end, protoPals);
return colors.size();
@@ -243,8 +249,9 @@ public:
}
};
static void decant(std::vector<AssignedProtos> &assignments,
std::vector<ProtoPalette> const &protoPalettes) {
static void decant(
std::vector<AssignedProtos> &assignments, std::vector<ProtoPalette> const &protoPalettes
) {
// "Decanting" is the process of moving all *things* that can fit in a lower index there
auto decantOn = [&assignments](auto const &tryDecanting) {
// No need to attempt decanting on palette #0, as there are no palettes to decant to
@@ -268,8 +275,9 @@ static void decant(std::vector<AssignedProtos> &assignments,
}
};
options.verbosePrint(Options::VERB_DEBUG, "%zu palettes before decanting\n",
assignments.size());
options.verbosePrint(
Options::VERB_DEBUG, "%zu palettes before decanting\n", assignments.size()
);
// Decant on palettes
decantOn([&protoPalettes](AssignedProtos &to, AssignedProtos &from) {
@@ -281,8 +289,9 @@ static void decant(std::vector<AssignedProtos> &assignments,
from.clear();
}
});
options.verbosePrint(Options::VERB_DEBUG, "%zu palettes after decanting on palettes\n",
assignments.size());
options.verbosePrint(
Options::VERB_DEBUG, "%zu palettes after decanting on palettes\n", assignments.size()
);
// Decant on "components" (= proto-pals sharing colors)
decantOn([&protoPalettes](AssignedProtos &to, AssignedProtos &from) {
@@ -331,8 +340,9 @@ static void decant(std::vector<AssignedProtos> &assignments,
}
}
});
options.verbosePrint(Options::VERB_DEBUG, "%zu palettes after decanting on \"components\"\n",
assignments.size());
options.verbosePrint(
Options::VERB_DEBUG, "%zu palettes after decanting on \"components\"\n", assignments.size()
);
// Decant on individual proto-palettes
decantOn([&protoPalettes](AssignedProtos &to, AssignedProtos &from) {
@@ -343,14 +353,16 @@ static void decant(std::vector<AssignedProtos> &assignments,
}
}
});
options.verbosePrint(Options::VERB_DEBUG, "%zu palettes after decanting on proto-palettes\n",
assignments.size());
options.verbosePrint(
Options::VERB_DEBUG, "%zu palettes after decanting on proto-palettes\n", assignments.size()
);
}
std::tuple<DefaultInitVec<size_t>, size_t>
overloadAndRemove(std::vector<ProtoPalette> const &protoPalettes) {
options.verbosePrint(Options::VERB_LOG_ACT,
"Paginating palettes using \"overload-and-remove\" strategy...\n");
options.verbosePrint(
Options::VERB_LOG_ACT, "Paginating palettes using \"overload-and-remove\" strategy...\n"
);
// Sort the proto-palettes by size, which improves the packing algorithm's efficiency
DefaultInitVec<size_t> sortedProtoPalIDs(protoPalettes.size());
@@ -379,9 +391,14 @@ std::tuple<DefaultInitVec<size_t>, size_t>
continue;
}
options.verbosePrint(Options::VERB_DEBUG, "%zu/%zu: Rel size: %f (size = %zu)\n", i + 1,
assignments.size(), assignments[i].relSizeOf(protoPal),
protoPal.size());
options.verbosePrint(
Options::VERB_DEBUG,
"%zu/%zu: Rel size: %f (size = %zu)\n",
i + 1,
assignments.size(),
assignments[i].relSizeOf(protoPal),
protoPal.size()
);
if (assignments[i].relSizeOf(protoPal) < bestRelSize) {
bestPalIndex = i;
}
@@ -397,21 +414,26 @@ std::tuple<DefaultInitVec<size_t>, size_t>
// If this overloads the palette, get it back to normal (if possible)
while (bestPal.volume() > options.maxOpaqueColors()) {
options.verbosePrint(Options::VERB_DEBUG,
options.verbosePrint(
Options::VERB_DEBUG,
"Palette %zu is overloaded! (%zu > %" PRIu8 ")\n",
bestPalIndex, bestPal.volume(), options.maxOpaqueColors());
bestPalIndex,
bestPal.volume(),
options.maxOpaqueColors()
);
// Look for a proto-pal minimizing "efficiency" (size / rel_size)
auto efficiency = [&bestPal](ProtoPalette const &pal) {
return pal.size() / bestPal.relSizeOf(pal);
};
auto [minEfficiencyIter, maxEfficiencyIter] =
std::minmax_element(RANGE(bestPal),
[&efficiency, &protoPalettes](ProtoPalAttrs const &lhs,
ProtoPalAttrs const &rhs) {
auto [minEfficiencyIter, maxEfficiencyIter] = std::minmax_element(
RANGE(bestPal),
[&efficiency,
&protoPalettes](ProtoPalAttrs const &lhs, ProtoPalAttrs const &rhs) {
return efficiency(protoPalettes[lhs.protoPalIndex])
< efficiency(protoPalettes[rhs.protoPalIndex]);
});
}
);
// All efficiencies are identical iff min equals max
// TODO: maybe not ideal to re-compute these two?
@@ -443,18 +465,24 @@ std::tuple<DefaultInitVec<size_t>, size_t>
while (!queue.empty()) {
ProtoPalAttrs const &attrs = queue.front();
ProtoPalette const &protoPal = protoPalettes[attrs.protoPalIndex];
auto iter =
std::find_if(RANGE(assignments),
[&protoPal](AssignedProtos const &pal) { return pal.canFit(protoPal); });
auto iter = std::find_if(RANGE(assignments), [&protoPal](AssignedProtos const &pal) {
return pal.canFit(protoPal);
});
if (iter == assignments.end()) { // No such page, create a new one
options.verbosePrint(Options::VERB_DEBUG,
options.verbosePrint(
Options::VERB_DEBUG,
"Adding new palette (%zu) for overflowing proto-pal %zu\n",
assignments.size(), attrs.protoPalIndex);
assignments.size(),
attrs.protoPalIndex
);
assignments.emplace_back(protoPalettes, std::move(attrs));
} else {
options.verbosePrint(Options::VERB_DEBUG,
options.verbosePrint(
Options::VERB_DEBUG,
"Assigning overflowing proto-pal %zu to palette %zu\n",
attrs.protoPalIndex, iter - assignments.begin());
attrs.protoPalIndex,
iter - assignments.begin()
);
iter->assign(std::move(attrs));
}
queue.pop();

View File

@@ -13,13 +13,20 @@
namespace sorting {
void indexed(std::vector<Palette> &palettes, int palSize, png_color const *palRGB,
int palAlphaSize, png_byte *palAlpha) {
void indexed(
std::vector<Palette> &palettes,
int palSize,
png_color const *palRGB,
int palAlphaSize,
png_byte *palAlpha
) {
options.verbosePrint(Options::VERB_LOG_ACT, "Sorting palettes using embedded palette...\n");
auto pngToRgb = [&palRGB, &palAlphaSize, &palAlpha](int index) {
auto const &c = palRGB[index];
return Rgba(c.red, c.green, c.blue, palAlpha && index < palAlphaSize ? palAlpha[index] : 0xFF);
return Rgba(
c.red, c.green, c.blue, palAlpha && index < palAlphaSize ? palAlpha[index] : 0xFF
);
};
for (Palette &pal : palettes) {
@@ -43,8 +50,9 @@ void indexed(std::vector<Palette> &palettes, int palSize, png_color const *palRG
}
}
void grayscale(std::vector<Palette> &palettes,
std::array<std::optional<Rgba>, 0x8001> const &colors) {
void grayscale(
std::vector<Palette> &palettes, std::array<std::optional<Rgba>, 0x8001> const &colors
) {
options.verbosePrint(Options::VERB_LOG_ACT, "Sorting grayscale-only palette...\n");
// This method is only applicable if there are at most as many colors as colors per palette, so

View File

@@ -66,10 +66,12 @@ void parseInlinePalSpec(char const * const rawArg) {
assert(len <= arg.length());
errorMessage(msg);
fprintf(stderr,
fprintf(
stderr,
"In inline palette spec: %s\n"
" ",
rawArg);
rawArg
);
for (auto i = ofs; i; --i) {
putc(' ', stderr);
}
@@ -97,12 +99,17 @@ void parseInlinePalSpec(char const * const rawArg) {
auto pos = std::min(arg.find_first_not_of("0123456789ABCDEFabcdef"sv, n), arg.length());
switch (pos - n) {
case 3:
color = Rgba(singleToHex(arg[n + 0]), singleToHex(arg[n + 1]), singleToHex(arg[n + 2]),
0xFF);
color = Rgba(
singleToHex(arg[n + 0]), singleToHex(arg[n + 1]), singleToHex(arg[n + 2]), 0xFF
);
break;
case 6:
color = Rgba(toHex(arg[n + 0], arg[n + 1]), toHex(arg[n + 2], arg[n + 3]),
toHex(arg[n + 4], arg[n + 5]), 0xFF);
color = Rgba(
toHex(arg[n + 0], arg[n + 1]),
toHex(arg[n + 2], arg[n + 3]),
toHex(arg[n + 4], arg[n + 5]),
0xFF
);
break;
case 0:
parseError(n - 1, 1, "Missing color after '#'");
@@ -239,36 +246,31 @@ static std::optional<U> parseDec(std::string const &str, std::string::size_type
return std::optional<U>{value};
}
static std::optional<Rgba> parseColor(std::string const &str, std::string::size_type &n,
uint16_t i) {
static std::optional<Rgba>
parseColor(std::string const &str, std::string::size_type &n, uint16_t i) {
std::optional<uint8_t> r = parseDec<uint8_t>(str, n);
if (!r) {
error("Failed to parse color #%d (\"%s\"): invalid red component", i + 1,
str.c_str());
error("Failed to parse color #%d (\"%s\"): invalid red component", i + 1, str.c_str());
return std::nullopt;
}
skipWhitespace(str, n);
if (n == str.length()) {
error("Failed to parse color #%d (\"%s\"): missing green component", i + 1,
str.c_str());
error("Failed to parse color #%d (\"%s\"): missing green component", i + 1, str.c_str());
return std::nullopt;
}
std::optional<uint8_t> g = parseDec<uint8_t>(str, n);
if (!g) {
error("Failed to parse color #%d (\"%s\"): invalid green component", i + 1,
str.c_str());
error("Failed to parse color #%d (\"%s\"): invalid green component", i + 1, str.c_str());
return std::nullopt;
}
skipWhitespace(str, n);
if (n == str.length()) {
error("Failed to parse color #%d (\"%s\"): missing blue component", i + 1,
str.c_str());
error("Failed to parse color #%d (\"%s\"): missing blue component", i + 1, str.c_str());
return std::nullopt;
}
std::optional<uint8_t> b = parseDec<uint8_t>(str, n);
if (!b) {
error("Failed to parse color #%d (\"%s\"): invalid blue component", i + 1,
str.c_str());
error("Failed to parse color #%d (\"%s\"): invalid blue component", i + 1, str.c_str());
return std::nullopt;
}
@@ -301,10 +303,14 @@ static void parsePSPFile(std::filebuf &file) {
return;
}
if (uint16_t nbPalColors = options.nbColorsPerPal * options.nbPalettes; *nbColors > nbPalColors) {
warning("PSP file contains %" PRIu16 " colors, but there can only be %" PRIu16
if (uint16_t nbPalColors = options.nbColorsPerPal * options.nbPalettes;
*nbColors > nbPalColors) {
warning(
"PSP file contains %" PRIu16 " colors, but there can only be %" PRIu16
"; ignoring extra",
*nbColors, nbPalColors);
*nbColors,
nbPalColors
);
nbColors = nbPalColors;
}
@@ -320,8 +326,11 @@ static void parsePSPFile(std::filebuf &file) {
return;
}
if (n != line.length()) {
error("Failed to parse color #%d (\"%s\"): trailing characters after blue component",
i + 1, line.c_str());
error(
"Failed to parse color #%d (\"%s\"): trailing characters after blue component",
i + 1,
line.c_str()
);
return;
}
@@ -372,9 +381,12 @@ static void parseGPLFile(std::filebuf &file) {
}
if (nbColors > maxNbColors) {
warning("GPL file contains %" PRIu16 " colors, but there can only be %" PRIu16
warning(
"GPL file contains %" PRIu16 " colors, but there can only be %" PRIu16
"; ignoring extra",
nbColors, maxNbColors);
nbColors,
maxNbColors
);
}
}
@@ -393,8 +405,11 @@ static void parseHEXFile(std::filebuf &file) {
if (line.length() != 6
|| line.find_first_not_of("0123456789ABCDEFabcdef"sv) != std::string::npos) {
error("Failed to parse color #%d (\"%s\"): invalid \"rrggbb\" line",
nbColors + 1, line.c_str());
error(
"Failed to parse color #%d (\"%s\"): invalid \"rrggbb\" line",
nbColors + 1,
line.c_str()
);
return;
}
@@ -411,9 +426,12 @@ static void parseHEXFile(std::filebuf &file) {
}
if (nbColors > maxNbColors) {
warning("HEX file contains %" PRIu16 " colors, but there can only be %" PRIu16
warning(
"HEX file contains %" PRIu16 " colors, but there can only be %" PRIu16
"; ignoring extra",
nbColors, maxNbColors);
nbColors,
maxNbColors
);
}
}
@@ -436,10 +454,14 @@ static void parseACTFile(std::filebuf &file) {
return;
}
if (uint16_t nbPalColors = options.nbColorsPerPal * options.nbPalettes; nbColors > nbPalColors) {
warning("ACT file contains %" PRIu16 " colors, but there can only be %" PRIu16
if (uint16_t nbPalColors = options.nbColorsPerPal * options.nbPalettes;
nbColors > nbPalColors) {
warning(
"ACT file contains %" PRIu16 " colors, but there can only be %" PRIu16
"; ignoring extra",
nbColors, nbPalColors);
nbColors,
nbPalColors
);
nbColors = nbPalColors;
}
@@ -486,10 +508,14 @@ static void parseACOFile(std::filebuf &file) {
}
uint16_t nbColors = readBE<uint16_t>(buf);
if (uint16_t nbPalColors = options.nbColorsPerPal * options.nbPalettes; nbColors > nbPalColors) {
warning("ACO file contains %" PRIu16 " colors, but there can only be %" PRIu16
if (uint16_t nbPalColors = options.nbColorsPerPal * options.nbPalettes;
nbColors > nbPalColors) {
warning(
"ACO file contains %" PRIu16 " colors, but there can only be %" PRIu16
"; ignoring extra",
nbColors, nbPalColors);
nbColors,
nbPalColors
);
nbColors = nbPalColors;
}
@@ -543,16 +569,22 @@ static void parseGBCFile(std::filebuf &file) {
if (len == 0) {
break;
} else if (len != sizeof(buf)) {
error("GBC palette dump contains %zu 8-byte palette%s, plus %zu byte%s",
options.palSpec.size(), options.palSpec.size() == 1 ? "" : "s", len,
len == 1 ? "" : "s");
error(
"GBC palette dump contains %zu 8-byte palette%s, plus %zu byte%s",
options.palSpec.size(),
options.palSpec.size() == 1 ? "" : "s",
len,
len == 1 ? "" : "s"
);
break;
}
options.palSpec.push_back({Rgba::fromCGBColor(readLE<uint16_t>(&buf[0])),
options.palSpec.push_back(
{Rgba::fromCGBColor(readLE<uint16_t>(&buf[0])),
Rgba::fromCGBColor(readLE<uint16_t>(&buf[2])),
Rgba::fromCGBColor(readLE<uint16_t>(&buf[4])),
Rgba::fromCGBColor(readLE<uint16_t>(&buf[6]))});
Rgba::fromCGBColor(readLE<uint16_t>(&buf[6]))}
);
}
}
@@ -576,14 +608,16 @@ void parseExternalPalSpec(char const *arg) {
std::tuple{"GBC", &parseGBCFile, std::ios::binary},
};
auto iter = std::find_if(RANGE(parsers),
[&arg, &ptr](decltype(parsers)::value_type const &parser) {
auto iter =
std::find_if(RANGE(parsers), [&arg, &ptr](decltype(parsers)::value_type const &parser) {
return strncasecmp(arg, std::get<0>(parser), ptr - arg) == 0;
});
if (iter == parsers.end()) {
error("Unknown external palette format \"%.*s\"",
error(
"Unknown external palette format \"%.*s\"",
static_cast<int>(std::min(ptr - arg, static_cast<decltype(ptr - arg)>(INT_MAX))),
arg);
arg
);
return;
}

View File

@@ -59,8 +59,7 @@ public:
}
size_t size() const {
return std::count_if(RANGE(_colors),
[](decltype(_colors)::value_type const &slot) {
return std::count_if(RANGE(_colors), [](decltype(_colors)::value_type const &slot) {
return slot.has_value() && !slot->isTransparent();
});
}
@@ -105,10 +104,13 @@ class Png {
self->file->sgetn(reinterpret_cast<char *>(data), expectedLen);
if (nbBytesRead != expectedLen) {
fatal("Error reading input image (\"%s\"): file too short (expected at least %zd more "
fatal(
"Error reading input image (\"%s\"): file too short (expected at least %zd more "
"bytes after reading %zu)",
self->c_str(), length - nbBytesRead,
(size_t)self->file->pubseekoff(0, std::ios_base::cur));
self->c_str(),
length - nbBytesRead,
(size_t)self->file->pubseekoff(0, std::ios_base::cur)
);
}
}
@@ -134,9 +136,12 @@ public:
bool isSuitableForGrayscale() const {
// Check that all of the grays don't fall into the same "bin"
if (colors.size() > options.maxOpaqueColors()) { // Apply the Pigeonhole Principle
options.verbosePrint(Options::VERB_DEBUG,
options.verbosePrint(
Options::VERB_DEBUG,
"Too many colors for grayscale sorting (%zu > %" PRIu8 ")\n",
colors.size(), options.maxOpaqueColors());
colors.size(),
options.maxOpaqueColors()
);
return false;
}
uint8_t bins = 0;
@@ -145,9 +150,11 @@ public:
continue;
}
if (!color->isGray()) {
options.verbosePrint(Options::VERB_DEBUG,
options.verbosePrint(
Options::VERB_DEBUG,
"Found non-gray color #%08x, not using grayscale sorting\n",
color->toCSS());
color->toCSS()
);
return false;
}
uint8_t mask = 1 << color->grayIndex();
@@ -155,7 +162,8 @@ public:
options.verbosePrint(
Options::VERB_DEBUG,
"Color #%08x conflicts with another one, not using grayscale sorting\n",
color->toCSS());
color->toCSS()
);
return false;
}
bins |= mask;
@@ -174,8 +182,7 @@ public:
*/
explicit Png(std::string const &filePath) : path(filePath), colors() {
if (file.open(path, std::ios_base::in | std::ios_base::binary) == nullptr) {
fatal("Failed to open input image (\"%s\"): %s", file.c_str(path),
strerror(errno));
fatal("Failed to open input image (\"%s\"): %s", file.c_str(path), strerror(errno));
}
options.verbosePrint(Options::VERB_LOG_ACT, "Opened input file\n");
@@ -190,8 +197,9 @@ public:
options.verbosePrint(Options::VERB_INTERM, "PNG header signature is OK\n");
png = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)this, handleError,
handleWarning);
png = png_create_read_struct(
PNG_LIBPNG_VER_STRING, (png_voidp)this, handleError, handleWarning
);
if (!png) {
fatal("Failed to allocate PNG structure: %s", strerror(errno));
}
@@ -215,8 +223,9 @@ public:
int bitDepth, interlaceType; //, compressionType, filterMethod;
png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, &interlaceType, nullptr,
nullptr);
png_get_IHDR(
png, info, &width, &height, &bitDepth, &colorType, &interlaceType, nullptr, nullptr
);
if (options.inputSlice.width == 0 && width % 8 != 0) {
fatal("Image width (%" PRIu32 " pixels) is not a multiple of 8!", width);
@@ -253,23 +262,35 @@ public:
fatal("Unknown interlace type %d", interlaceType);
}
};
options.verbosePrint(Options::VERB_INTERM,
"Input image: %" PRIu32 "x%" PRIu32 " pixels, %dbpp %s, %s\n", width,
height, bitDepth, colorTypeName(), interlaceTypeName());
options.verbosePrint(
Options::VERB_INTERM,
"Input image: %" PRIu32 "x%" PRIu32 " pixels, %dbpp %s, %s\n",
width,
height,
bitDepth,
colorTypeName(),
interlaceTypeName()
);
if (png_get_PLTE(png, info, &embeddedPal, &nbColors) != 0) {
if (png_get_tRNS(png, info, &transparencyPal, &nbTransparentEntries, nullptr)) {
assert(nbTransparentEntries <= nbColors);
}
options.verbosePrint(Options::VERB_INTERM, "Embedded palette has %d colors: [",
nbColors);
options.verbosePrint(
Options::VERB_INTERM, "Embedded palette has %d colors: [", nbColors
);
for (int i = 0; i < nbColors; ++i) {
auto const &color = embeddedPal[i];
options.verbosePrint(
Options::VERB_INTERM, "#%02x%02x%02x%02x%s", color.red, color.green, color.blue,
Options::VERB_INTERM,
"#%02x%02x%02x%02x%s",
color.red,
color.green,
color.blue,
transparencyPal && i < nbTransparentEntries ? transparencyPal[i] : 0xFF,
i != nbColors - 1 ? ", " : "]\n");
i != nbColors - 1 ? ", " : "]\n"
);
}
} else {
options.verbosePrint(Options::VERB_INTERM, "No embedded palette\n");
@@ -329,25 +350,35 @@ public:
std::vector<uint32_t> indeterminates;
// Assign a color to the given position, and register it in the image palette as well
auto assignColor = [this, &conflicts, &indeterminates](png_uint_32 x, png_uint_32 y,
Rgba &&color) {
auto assignColor =
[this, &conflicts, &indeterminates](png_uint_32 x, png_uint_32 y, Rgba &&color) {
if (!color.isTransparent() && !color.isOpaque()) {
uint32_t css = color.toCSS();
if (std::find(RANGE(indeterminates), css)
== indeterminates.end()) {
error("Color #%08x is neither transparent (alpha < %u) nor opaque (alpha >= "
if (std::find(RANGE(indeterminates), css) == indeterminates.end()) {
error(
"Color #%08x is neither transparent (alpha < %u) nor opaque (alpha >= "
"%u) [first seen at x: %" PRIu32 ", y: %" PRIu32 "]",
css, Rgba::transparency_threshold, Rgba::opacity_threshold, x, y);
css,
Rgba::transparency_threshold,
Rgba::opacity_threshold,
x,
y
);
indeterminates.push_back(css);
}
} else if (Rgba const *other = colors.registerColor(color); other) {
std::tuple conflicting{color.toCSS(), other->toCSS()};
// Do not report combinations twice
if (std::find(RANGE(conflicts), conflicting) == conflicts.end()) {
warning("Fusing colors #%08x and #%08x into Game Boy color $%04x [first seen "
warning(
"Fusing colors #%08x and #%08x into Game Boy color $%04x [first seen "
"at x: %" PRIu32 ", y: %" PRIu32 "]",
std::get<0>(conflicting), std::get<1>(conflicting), color.cgbColor(), x,
y);
std::get<0>(conflicting),
std::get<1>(conflicting),
color.cgbColor(),
x,
y
);
// Do not report this combination again
conflicts.emplace_back(conflicting);
}
@@ -361,8 +392,9 @@ public:
png_read_row(png, row.data(), nullptr);
for (png_uint_32 x = 0; x < width; ++x) {
assignColor(x, y,
Rgba(row[x * 4], row[x * 4 + 1], row[x * 4 + 2], row[x * 4 + 3]));
assignColor(
x, y, Rgba(row[x * 4], row[x * 4 + 1], row[x * 4 + 2], row[x * 4 + 3])
);
}
}
} else {
@@ -459,9 +491,12 @@ public:
};
public:
TilesVisitor visitAsTiles() const {
return {*this, options.columnMajor,
return {
*this,
options.columnMajor,
options.inputSlice.width ? options.inputSlice.width * 8 : width,
options.inputSlice.height ? options.inputSlice.height * 8 : height};
options.inputSlice.height ? options.inputSlice.height * 8 : height,
};
}
};
@@ -523,8 +558,12 @@ static void generatePalSpec(Png const &png) {
embPalSize = options.maxOpaqueColors();
}
for (int i = 0; i < embPalSize; ++i) {
options.palSpec[0][i] = Rgba(embPalRGB[i].red, embPalRGB[i].green, embPalRGB[i].blue,
embPalAlpha && i < embPalAlphaSize ? embPalAlpha[i] : 0xFF);
options.palSpec[0][i] = Rgba(
embPalRGB[i].red,
embPalRGB[i].green,
embPalRGB[i].blue,
embPalAlpha && i < embPalAlphaSize ? embPalAlpha[i] : 0xFF
);
}
}
@@ -536,8 +575,12 @@ static std::tuple<DefaultInitVec<size_t>, std::vector<Palette>>
assert(mappings.size() == protoPalettes.size());
if (options.verbosity >= Options::VERB_INTERM) {
fprintf(stderr, "Proto-palette mappings: (%zu palette%s)\n", nbPalettes,
nbPalettes != 1 ? "s" : "");
fprintf(
stderr,
"Proto-palette mappings: (%zu palette%s)\n",
nbPalettes,
nbPalettes != 1 ? "s" : ""
);
for (size_t i = 0; i < mappings.size(); ++i) {
fprintf(stderr, "%zu -> %zu\n", i, mappings[i]);
}
@@ -615,8 +658,11 @@ static std::tuple<DefaultInitVec<size_t>, std::vector<Palette>>
mappings[i] = iter - palettes.begin(); // Bogus value, but whatever
}
if (bad) {
fprintf(stderr, "note: The following palette%s specified:\n",
palettes.size() == 1 ? " was" : "s were");
fprintf(
stderr,
"note: The following palette%s specified:\n",
palettes.size() == 1 ? " was" : "s were"
);
for (Palette const &pal : palettes) {
fprintf(stderr, " [%s]\n", listColors(pal));
}
@@ -640,15 +686,17 @@ static void outputPalettes(std::vector<Palette> const &palettes) {
if (palettes.size() > options.nbPalettes) {
// If the palette generation is wrong, other (dependee) operations are likely to be
// nonsensical, so fatal-error outright
fatal("Generated %zu palettes, over the maximum of %" PRIu8, palettes.size(),
options.nbPalettes);
fatal(
"Generated %zu palettes, over the maximum of %" PRIu8,
palettes.size(),
options.nbPalettes
);
}
if (!options.palettes.empty()) {
File output;
if (!output.open(options.palettes, std::ios_base::out | std::ios_base::binary)) {
fatal("Failed to open \"%s\": %s", output.c_str(options.palettes),
strerror(errno));
fatal("Failed to open \"%s\": %s", output.c_str(options.palettes), strerror(errno));
}
for (Palette const &palette : palettes) {
@@ -675,8 +723,8 @@ public:
// of altering the element's hash, but the tile ID is not part of it.
mutable uint16_t tileID;
static uint16_t rowBitplanes(Png::TilesVisitor::Tile const &tile, Palette const &palette,
uint32_t y) {
static uint16_t
rowBitplanes(Png::TilesVisitor::Tile const &tile, Palette const &palette, uint32_t y) {
uint16_t row = 0;
for (uint32_t x = 0; x < 8; ++x) {
row <<= 1;
@@ -734,8 +782,9 @@ public:
}
// Check if we have horizontal mirroring, which scans the array forward again
if (std::equal(RANGE(_data), other._data.begin(),
[](uint8_t lhs, uint8_t rhs) { return lhs == flipTable[rhs]; })) {
if (std::equal(RANGE(_data), other._data.begin(), [](uint8_t lhs, uint8_t rhs) {
return lhs == flipTable[rhs];
})) {
return MatchType::HFLIP;
}
@@ -773,16 +822,20 @@ struct std::hash<TileData> {
namespace unoptimized {
static void outputTileData(Png const &png, DefaultInitVec<AttrmapEntry> const &attrmap,
static void outputTileData(
Png const &png,
DefaultInitVec<AttrmapEntry> const &attrmap,
std::vector<Palette> const &palettes,
DefaultInitVec<size_t> const &mappings) {
DefaultInitVec<size_t> const &mappings
) {
File output;
if (!output.open(options.output, std::ios_base::out | std::ios_base::binary)) {
fatal("Failed to open \"%s\": %s", output.c_str(options.output), strerror(errno));
}
uint16_t widthTiles = options.inputSlice.width ? options.inputSlice.width : png.getWidth() / 8;
uint16_t heightTiles = options.inputSlice.height ? options.inputSlice.height : png.getHeight() / 8;
uint16_t heightTiles =
options.inputSlice.height ? options.inputSlice.height : png.getHeight() / 8;
uint64_t remainingTiles = widthTiles * heightTiles;
if (remainingTiles <= options.trim) {
return;
@@ -808,15 +861,15 @@ static void outputTileData(Png const &png, DefaultInitVec<AttrmapEntry> const &a
assert(remainingTiles == 0);
}
static void outputMaps(DefaultInitVec<AttrmapEntry> const &attrmap,
DefaultInitVec<size_t> const &mappings) {
static void outputMaps(
DefaultInitVec<AttrmapEntry> const &attrmap, DefaultInitVec<size_t> const &mappings
) {
std::optional<File> tilemapOutput, attrmapOutput, palmapOutput;
auto autoOpenPath = [](std::string const &path, std::optional<File> &file) {
if (!path.empty()) {
file.emplace();
if (!file->open(path, std::ios_base::out | std::ios_base::binary)) {
fatal("Failed to open \"%s\": %s", file->c_str(options.tilemap),
strerror(errno));
fatal("Failed to open \"%s\": %s", file->c_str(options.tilemap), strerror(errno));
}
}
};
@@ -864,8 +917,8 @@ struct UniqueTiles {
/*
* Adds a tile to the collection, and returns its ID
*/
std::tuple<uint16_t, TileData::MatchType> addTile(Png::TilesVisitor::Tile const &tile,
Palette const &palette) {
std::tuple<uint16_t, TileData::MatchType>
addTile(Png::TilesVisitor::Tile const &tile, Palette const &palette) {
TileData newTile(tile, palette);
auto [tileData, inserted] = tileset.insert(newTile);
@@ -893,9 +946,12 @@ struct UniqueTiles {
* 8-bit tile IDs + the bank bit; this will save the work when we output the data later (potentially
* twice)
*/
static UniqueTiles dedupTiles(Png const &png, DefaultInitVec<AttrmapEntry> &attrmap,
static UniqueTiles dedupTiles(
Png const &png,
DefaultInitVec<AttrmapEntry> &attrmap,
std::vector<Palette> const &palettes,
DefaultInitVec<size_t> const &mappings) {
DefaultInitVec<size_t> const &mappings
) {
// Iterate throughout the image, generating tile data as we go
// (We don't need the full tile data to be able to dedup tiles, but we don't lose anything
// by caching the full tile data anyway, so we might as well.)
@@ -941,8 +997,9 @@ static void outputTilemap(DefaultInitVec<AttrmapEntry> const &attrmap) {
}
}
static void outputAttrmap(DefaultInitVec<AttrmapEntry> const &attrmap,
DefaultInitVec<size_t> const &mappings) {
static void outputAttrmap(
DefaultInitVec<AttrmapEntry> const &attrmap, DefaultInitVec<size_t> const &mappings
) {
File output;
if (!output.open(options.attrmap, std::ios_base::out | std::ios_base::binary)) {
fatal("Failed to create \"%s\": %s", output.c_str(options.attrmap), strerror(errno));
@@ -956,8 +1013,9 @@ static void outputAttrmap(DefaultInitVec<AttrmapEntry> const &attrmap,
}
}
static void outputPalmap(DefaultInitVec<AttrmapEntry> const &attrmap,
DefaultInitVec<size_t> const &mappings) {
static void outputPalmap(
DefaultInitVec<AttrmapEntry> const &attrmap, DefaultInitVec<size_t> const &mappings
) {
File output;
if (!output.open(options.palmap, std::ios_base::out | std::ios_base::binary)) {
fatal("Failed to create \"%s\": %s", output.c_str(options.palmap), strerror(errno));
@@ -1063,21 +1121,33 @@ void process() {
}
if (nbColorsInTile > options.maxOpaqueColors()) {
fatal("Tile at (%" PRIu32 ", %" PRIu32 ") has %" PRIu8 " opaque colors, more than %" PRIu8 "!",
tile.x, tile.y, nbColorsInTile, options.maxOpaqueColors());
fatal(
"Tile at (%" PRIu32 ", %" PRIu32 ") has %" PRIu8 " opaque colors, more than %" PRIu8
"!",
tile.x,
tile.y,
nbColorsInTile,
options.maxOpaqueColors()
);
}
attrs.protoPaletteID = protoPalettes.size();
if (protoPalettes.size() == AttrmapEntry::transparent) { // Check for overflow
fatal("Reached %zu proto-palettes... sorry, this image is too much for me to handle :(",
AttrmapEntry::transparent);
fatal(
"Reached %zu proto-palettes... sorry, this image is too much for me to handle :(",
AttrmapEntry::transparent
);
}
protoPalettes.push_back(tileColors);
contained:;
}
options.verbosePrint(Options::VERB_INTERM, "Image contains %zu proto-palette%s\n",
protoPalettes.size(), protoPalettes.size() != 1 ? "s" : "");
options.verbosePrint(
Options::VERB_INTERM,
"Image contains %zu proto-palette%s\n",
protoPalettes.size(),
protoPalettes.size() != 1 ? "s" : ""
);
if (options.verbosity >= Options::VERB_INTERM) {
for (auto const &protoPal : protoPalettes) {
fputs("[ ", stderr);
@@ -1102,8 +1172,12 @@ contained:;
// Check the tile count
if (nbTilesW * nbTilesH > options.maxNbTiles[0] + options.maxNbTiles[1]) {
fatal("Image contains %" PRIu32 " tiles, exceeding the limit of %" PRIu16 " + %" PRIu16,
nbTilesW * nbTilesH, options.maxNbTiles[0], options.maxNbTiles[1]);
fatal(
"Image contains %" PRIu32 " tiles, exceeding the limit of %" PRIu16 " + %" PRIu16,
nbTilesW * nbTilesH,
options.maxNbTiles[0],
options.maxNbTiles[1]
);
}
if (!options.output.empty()) {
@@ -1114,7 +1188,8 @@ contained:;
if (!options.tilemap.empty() || !options.attrmap.empty() || !options.palmap.empty()) {
options.verbosePrint(
Options::VERB_LOG_ACT,
"Generating unoptimized tilemap and/or attrmap and/or palmap...\n");
"Generating unoptimized tilemap and/or attrmap and/or palmap...\n"
);
unoptimized::outputMaps(attrmap, mappings);
}
} else {
@@ -1123,8 +1198,12 @@ contained:;
optimized::UniqueTiles tiles = optimized::dedupTiles(png, attrmap, palettes, mappings);
if (tiles.size() > options.maxNbTiles[0] + options.maxNbTiles[1]) {
fatal("Image contains %zu tiles, exceeding the limit of %" PRIu16 " + %" PRIu16,
tiles.size(), options.maxNbTiles[0], options.maxNbTiles[1]);
fatal(
"Image contains %zu tiles, exceeding the limit of %" PRIu16 " + %" PRIu16,
tiles.size(),
options.maxNbTiles[0],
options.maxNbTiles[1]
);
}
if (!options.output.empty()) {

View File

@@ -1,7 +1,5 @@
/* SPDX-License-Identifier: MIT */
#include "helpers.hpp"
#include "gfx/proto_palette.hpp"
#include <algorithm>
@@ -10,6 +8,8 @@
#include <stddef.h>
#include <stdint.h>
#include "helpers.hpp"
bool ProtoPalette::add(uint16_t color) {
size_t i = 0;

View File

@@ -53,13 +53,19 @@ static DefaultInitVec<uint8_t> readInto(const std::string &path) {
}
[[noreturn]] static void pngError(png_structp png, char const *msg) {
fatal("Error writing reversed image (\"%s\"): %s",
static_cast<char const *>(png_get_error_ptr(png)), msg);
fatal(
"Error writing reversed image (\"%s\"): %s",
static_cast<char const *>(png_get_error_ptr(png)),
msg
);
}
static void pngWarning(png_structp png, char const *msg) {
warning("While writing reversed image (\"%s\"): %s",
static_cast<char const *>(png_get_error_ptr(png)), msg);
warning(
"While writing reversed image (\"%s\"): %s",
static_cast<char const *>(png_get_error_ptr(png)),
msg
);
}
void writePng(png_structp png, png_bytep data, size_t length) {
@@ -94,17 +100,23 @@ void reverse() {
warning("\"Sliced-off\" pixels are ignored in reverse mode");
}
if (options.inputSlice.width != 0 && options.inputSlice.width != options.reversedWidth * 8) {
warning("Specified input slice width (%" PRIu16
warning(
"Specified input slice width (%" PRIu16
") doesn't match provided reversing width (%" PRIu16 " * 8)",
options.inputSlice.width, options.reversedWidth);
options.inputSlice.width,
options.reversedWidth
);
}
options.verbosePrint(Options::VERB_LOG_ACT, "Reading tiles...\n");
auto const tiles = readInto(options.output);
uint8_t tileSize = 8 * options.bitDepth;
if (tiles.size() % tileSize != 0) {
fatal("Tile data size (%zu bytes) is not a multiple of %" PRIu8 " bytes",
tiles.size(), tileSize);
fatal(
"Tile data size (%zu bytes) is not a multiple of %" PRIu8 " bytes",
tiles.size(),
tileSize
);
}
// By default, assume tiles are not deduplicated, and add the (allegedly) trimmed tiles
@@ -121,19 +133,27 @@ void reverse() {
fatal("Cannot generate empty image");
}
if (nbTileInstances > options.maxNbTiles[0] + options.maxNbTiles[1]) {
warning("Read %zu tiles, more than the limit of %" PRIu16 " + %" PRIu16,
nbTileInstances, options.maxNbTiles[0], options.maxNbTiles[1]);
warning(
"Read %zu tiles, more than the limit of %" PRIu16 " + %" PRIu16,
nbTileInstances,
options.maxNbTiles[0],
options.maxNbTiles[1]
);
}
size_t width = options.reversedWidth, height; // In tiles
if (nbTileInstances % width != 0) {
fatal("Total number of tiles read (%zu) cannot be divided by image width (%zu tiles)",
nbTileInstances, width);
fatal(
"Total number of tiles read (%zu) cannot be divided by image width (%zu tiles)",
nbTileInstances,
width
);
}
height = nbTileInstances / width;
options.verbosePrint(Options::VERB_INTERM, "Reversed image dimensions: %zux%zu tiles\n", width,
height);
options.verbosePrint(
Options::VERB_INTERM, "Reversed image dimensions: %zux%zu tiles\n", width, height
);
// TODO: `-U` to configure tile size beyond 8x8px ("deduplication units")
@@ -155,27 +175,37 @@ void reverse() {
if (nbRead == buf.size()) {
// Expand the colors
auto &palette = palettes.emplace_back();
std::generate(palette.begin(), palette.begin() + options.nbColorsPerPal,
std::generate(
palette.begin(),
palette.begin() + options.nbColorsPerPal,
[&buf, i = 0]() mutable {
i += 2;
return Rgba::fromCGBColor(buf[i - 2] + (buf[i - 1] << 8));
});
}
);
} else if (nbRead != 0) {
fatal("Palette data size (%zu) is not a multiple of %zu bytes!\n",
palettes.size() * buf.size() + nbRead, buf.size());
fatal(
"Palette data size (%zu) is not a multiple of %zu bytes!\n",
palettes.size() * buf.size() + nbRead,
buf.size()
);
}
} while (nbRead != 0);
if (palettes.size() > options.nbPalettes) {
warning("Read %zu palettes, more than the specified limit of %" PRIu8,
palettes.size(), options.nbPalettes);
warning(
"Read %zu palettes, more than the specified limit of %" PRIu8,
palettes.size(),
options.nbPalettes
);
}
if (options.palSpecType == Options::EXPLICIT && palettes != options.palSpec) {
warning("Colors in the palette file do not match those specified with `-c`!");
}
} else if (options.palSpecType == Options::EMBEDDED) {
warning("An embedded palette was requested, but no palette file was specified; ignoring request.");
warning("An embedded palette was requested, but no palette file was specified; ignoring "
"request.");
} else if (options.palSpecType == Options::EXPLICIT) {
palettes = std::move(options.palSpec); // We won't be using it again.
}
@@ -184,8 +214,11 @@ void reverse() {
if (!options.attrmap.empty()) {
attrmap = readInto(options.attrmap);
if (attrmap->size() != nbTileInstances) {
fatal("Attribute map size (%zu tiles) doesn't match image's (%zu)", attrmap->size(),
nbTileInstances);
fatal(
"Attribute map size (%zu tiles) doesn't match image's (%zu)",
attrmap->size(),
nbTileInstances
);
}
// Scan through the attributes for inconsistencies
@@ -195,8 +228,9 @@ void reverse() {
bool bad = false;
for (auto attr : *attrmap) {
if ((attr & 0b111) > palettes.size()) {
error("Referencing palette %u, but there are only %zu!",
attr & 0b111, palettes.size());
error(
"Referencing palette %u, but there are only %zu!", attr & 0b111, palettes.size()
);
bad = true;
}
if (attr & 0x08 && !tilemap) {
@@ -213,16 +247,22 @@ void reverse() {
for (auto [id, attr] : zip(*tilemap, *attrmap)) {
bool bank = attr & 1 << 3;
if (id >= options.maxNbTiles[bank]) {
warning("Tile #%" PRIu8
" was referenced, but the limit for bank %u is %" PRIu16,
id, bank, options.maxNbTiles[bank]);
warning(
"Tile #%" PRIu8 " was referenced, but the limit for bank %u is %" PRIu16,
id,
bank,
options.maxNbTiles[bank]
);
}
}
} else {
for (auto id : *tilemap) {
if (id >= options.maxNbTiles[0]) {
warning("Tile #%" PRIu8 " was referenced, but the limit is %" PRIu16, id,
options.maxNbTiles[0]);
warning(
"Tile #%" PRIu8 " was referenced, but the limit is %" PRIu16,
id,
options.maxNbTiles[0]
);
}
}
}
@@ -232,8 +272,11 @@ void reverse() {
if (!options.palmap.empty()) {
palmap = readInto(options.palmap);
if (palmap->size() != nbTileInstances) {
fatal("Palette map size (%zu tiles) doesn't match image's (%zu)", palmap->size(),
nbTileInstances);
fatal(
"Palette map size (%zu tiles) doesn't match image's (%zu)",
palmap->size(),
nbTileInstances
);
}
}
@@ -244,8 +287,10 @@ void reverse() {
}
png_structp png = png_create_write_struct(
PNG_LIBPNG_VER_STRING,
const_cast<png_voidp>(static_cast<void const *>(pngFile.c_str(options.input))), pngError,
pngWarning);
const_cast<png_voidp>(static_cast<void const *>(pngFile.c_str(options.input))),
pngError,
pngWarning
);
if (!png) {
fatal("Failed to create PNG write struct: %s", strerror(errno));
}
@@ -255,8 +300,17 @@ void reverse() {
}
png_set_write_fn(png, &pngFile, writePng, flushPng);
png_set_IHDR(png, pngInfo, options.reversedWidth * 8, height * 8, 8, PNG_COLOR_TYPE_RGB_ALPHA,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_set_IHDR(
png,
pngInfo,
options.reversedWidth * 8,
height * 8,
8,
PNG_COLOR_TYPE_RGB_ALPHA,
PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT
);
png_write_info(png, pngInfo);
png_color_8 sbitChunk;
@@ -270,10 +324,14 @@ void reverse() {
size_t const SIZEOF_ROW = options.reversedWidth * 8 * SIZEOF_PIXEL;
std::vector<uint8_t> tileRow(8 * SIZEOF_ROW, 0xFF); // Data for 8 rows of pixels
uint8_t * const rowPtrs[8] = {
&tileRow.data()[0 * SIZEOF_ROW], &tileRow.data()[1 * SIZEOF_ROW],
&tileRow.data()[2 * SIZEOF_ROW], &tileRow.data()[3 * SIZEOF_ROW],
&tileRow.data()[4 * SIZEOF_ROW], &tileRow.data()[5 * SIZEOF_ROW],
&tileRow.data()[6 * SIZEOF_ROW], &tileRow.data()[7 * SIZEOF_ROW],
&tileRow.data()[0 * SIZEOF_ROW],
&tileRow.data()[1 * SIZEOF_ROW],
&tileRow.data()[2 * SIZEOF_ROW],
&tileRow.data()[3 * SIZEOF_ROW],
&tileRow.data()[4 * SIZEOF_ROW],
&tileRow.data()[5 * SIZEOF_ROW],
&tileRow.data()[6 * SIZEOF_ROW],
&tileRow.data()[7 * SIZEOF_ROW],
};
for (size_t ty = 0; ty < height; ++ty) {
@@ -294,8 +352,22 @@ void reverse() {
// We do not have data for tiles trimmed with `-x`, so assume they are "blank"
static std::array<uint8_t, 16> const trimmedTile{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
};
uint8_t const *tileData = tileID > nbTileInstances - options.trim
? trimmedTile.data()

View File

@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: MIT */
#include "link/assign.hpp"
#include <algorithm>
#include <deque>
#include <inttypes.h>
@@ -8,18 +10,17 @@
#include <string.h>
#include <vector>
#include "link/assign.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
#include "link/object.hpp"
#include "link/main.hpp"
#include "link/output.hpp"
#include "error.hpp"
#include "helpers.hpp"
#include "itertools.hpp"
#include "linkdefs.hpp"
#include "link/main.hpp"
#include "link/object.hpp"
#include "link/output.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
struct MemoryLocation {
uint16_t address;
uint32_t bank;
@@ -36,15 +37,13 @@ std::vector<std::deque<FreeSpace>> memory[SECTTYPE_INVALID];
uint64_t nbSectionsToAssign;
// Init the free space-modelling structs
static void initFreeSpace()
{
static void initFreeSpace() {
for (enum SectionType type : EnumSeq(SECTTYPE_INVALID)) {
memory[type].resize(nbbanks(type));
for (std::deque<FreeSpace> &bankMem : memory[type]) {
bankMem.push_back({
.address = sectionTypeInfo[type].startAddr,
.size = sectionTypeInfo[type].size
});
bankMem.push_back(
{.address = sectionTypeInfo[type].startAddr, .size = sectionTypeInfo[type].size}
);
}
}
}
@@ -54,8 +53,7 @@ static void initFreeSpace()
* @param section The section to assign
* @param location The location to assign the section to
*/
static void assignSection(Section &section, MemoryLocation const &location)
{
static void assignSection(Section &section, MemoryLocation const &location) {
// Propagate the assigned location to all UNIONs/FRAGMENTs
// so `jr` patches in them will have the correct offset
for (Section *next = &section; next != nullptr; next = next->nextu) {
@@ -77,9 +75,9 @@ static void assignSection(Section &section, MemoryLocation const &location)
* @param location The location to attempt placing the section at
* @return True if the location is suitable, false otherwise.
*/
static bool isLocationSuitable(Section const &section, FreeSpace const &freeSpace,
MemoryLocation const &location)
{
static bool isLocationSuitable(
Section const &section, FreeSpace const &freeSpace, MemoryLocation const &location
) {
if (section.isAddressFixed && section.org != location.address)
return false;
@@ -99,8 +97,7 @@ static bool isLocationSuitable(Section const &section, FreeSpace const &freeSpac
* @return The index into `memory[section->type]` of the free space encompassing the location,
* or -1 if none was found
*/
static ssize_t getPlacement(Section const &section, MemoryLocation &location)
{
static ssize_t getPlacement(Section const &section, MemoryLocation &location) {
SectionTypeInfo const &typeInfo = sectionTypeInfo[section.type];
static uint16_t curScrambleROM = 0;
@@ -166,16 +163,16 @@ static ssize_t getPlacement(Section const &section, MemoryLocation &location)
// If that location is past the current block's end,
// go forwards until that is no longer the case.
while (spaceIdx < bankMem.size() && location.address >=
bankMem[spaceIdx].address + bankMem[spaceIdx].size)
while (spaceIdx < bankMem.size()
&& location.address >= bankMem[spaceIdx].address + bankMem[spaceIdx].size)
spaceIdx++;
// Try again with the new location/free space combo
}
// Try again in the next bank, if one is available.
// Try scrambled banks in descending order until no bank in the scrambled range is available.
// Otherwise, try in ascending order.
// Try scrambled banks in descending order until no bank in the scrambled range is
// available. Otherwise, try in ascending order.
if (section.isBankFixed) {
return -1;
} else if (scrambleROMX && section.type == SECTTYPE_ROMX && location.bank <= scrambleROMX) {
@@ -213,20 +210,17 @@ static ssize_t getPlacement(Section const &section, MemoryLocation &location)
* sections of decreasing size.
* @param section The section to place
*/
static void placeSection(Section &section)
{
static void placeSection(Section &section) {
MemoryLocation location;
// Specially handle 0-byte SECTIONs, as they can't overlap anything
if (section.size == 0) {
// Unless the SECTION's address was fixed, the starting address
// is fine for any alignment, as checked in sect_DoSanityChecks.
location.address = section.isAddressFixed
? section.org
: sectionTypeInfo[section.type].startAddr;
location.bank = section.isBankFixed
? section.bank
: sectionTypeInfo[section.type].firstBank;
location.address =
section.isAddressFixed ? section.org : sectionTypeInfo[section.type].startAddr;
location.bank =
section.isBankFixed ? section.bank : sectionTypeInfo[section.type].firstBank;
assignSection(section, location);
return;
}
@@ -234,8 +228,8 @@ static void placeSection(Section &section)
// Place section using first-fit decreasing algorithm
// https://en.wikipedia.org/wiki/Bin_packing_problem#First-fit_algorithm
if (ssize_t spaceIdx = getPlacement(section, location); spaceIdx != -1) {
std::deque<FreeSpace> &bankMem = memory[section.type][location.bank -
sectionTypeInfo[section.type].firstBank];
std::deque<FreeSpace> &bankMem =
memory[section.type][location.bank - sectionTypeInfo[section.type].firstBank];
FreeSpace &freeSpace = bankMem[spaceIdx];
assignSection(section, location);
@@ -249,11 +243,12 @@ static void placeSection(Section &section)
} else if (!noLeftSpace && !noRightSpace) {
// The free space is split in two
// Append the new space after the original one
bankMem.insert(bankMem.begin() + spaceIdx + 1, {
.address = (uint16_t)(section.org + section.size),
.size = (uint16_t)(freeSpace.address + freeSpace.size -
section.org - section.size)
});
bankMem.insert(
bankMem.begin() + spaceIdx + 1,
{.address = (uint16_t)(section.org + section.size),
.size =
(uint16_t)(freeSpace.address + freeSpace.size - section.org - section.size)}
);
// Resize the original space (address is unmodified)
freeSpace.size = section.org - freeSpace.address;
} else {
@@ -271,37 +266,62 @@ static void placeSection(Section &section)
if (section.isBankFixed && nbbanks(section.type) != 1) {
if (section.isAddressFixed)
snprintf(where, sizeof(where), "at $%02" PRIx32 ":%04" PRIx16,
section.bank, section.org);
snprintf(
where, sizeof(where), "at $%02" PRIx32 ":%04" PRIx16, section.bank, section.org
);
else if (section.isAlignFixed)
snprintf(where, sizeof(where), "in bank $%02" PRIx32 " with align mask %" PRIx16,
section.bank, (uint16_t)~section.alignMask);
snprintf(
where,
sizeof(where),
"in bank $%02" PRIx32 " with align mask %" PRIx16,
section.bank,
(uint16_t)~section.alignMask
);
else
snprintf(where, sizeof(where), "in bank $%02" PRIx32, section.bank);
} else {
if (section.isAddressFixed)
snprintf(where, sizeof(where), "at address $%04" PRIx16, section.org);
else if (section.isAlignFixed)
snprintf(where, sizeof(where), "with align mask %" PRIx16 " and offset %" PRIx16,
(uint16_t)~section.alignMask, section.alignOfs);
snprintf(
where,
sizeof(where),
"with align mask %" PRIx16 " and offset %" PRIx16,
(uint16_t)~section.alignMask,
section.alignOfs
);
else
strcpy(where, "anywhere");
}
// If a section failed to go to several places, nothing we can report
if (!section.isBankFixed || !section.isAddressFixed)
errx("Unable to place \"%s\" (%s section) %s",
section.name.c_str(), sectionTypeInfo[section.type].name.c_str(), where);
errx(
"Unable to place \"%s\" (%s section) %s",
section.name.c_str(),
sectionTypeInfo[section.type].name.c_str(),
where
);
// If the section just can't fit the bank, report that
else if (section.org + section.size > endaddr(section.type) + 1)
errx("Unable to place \"%s\" (%s section) %s: section runs past end of region ($%04x > $%04x)",
section.name.c_str(), sectionTypeInfo[section.type].name.c_str(), where,
section.org + section.size, endaddr(section.type) + 1);
errx(
"Unable to place \"%s\" (%s section) %s: section runs past end of region ($%04x > "
"$%04x)",
section.name.c_str(),
sectionTypeInfo[section.type].name.c_str(),
where,
section.org + section.size,
endaddr(section.type) + 1
);
// Otherwise there is overlap with another section
else
errx("Unable to place \"%s\" (%s section) %s: section overlaps with \"%s\"",
section.name.c_str(), sectionTypeInfo[section.type].name.c_str(), where,
out_OverlappingSection(section)->name.c_str());
errx(
"Unable to place \"%s\" (%s section) %s: section overlaps with \"%s\"",
section.name.c_str(),
sectionTypeInfo[section.type].name.c_str(),
where,
out_OverlappingSection(section)->name.c_str()
);
}
#define BANK_CONSTRAINED (1 << 2)
@@ -314,8 +334,7 @@ static std::deque<Section *> unassignedSections[1 << 3];
* This is so the most-constrained sections are placed first
* @param section The section to categorize
*/
static void categorizeSection(Section &section)
{
static void categorizeSection(Section &section) {
uint8_t constraints = 0;
if (section.isBankFixed)
@@ -337,8 +356,7 @@ static void categorizeSection(Section &section)
nbSectionsToAssign++;
}
void assign_AssignSections()
{
void assign_AssignSections() {
verbosePrint("Beginning assignment...\n");
// Initialize assignment
@@ -368,8 +386,7 @@ void assign_AssignSections()
for (int8_t constraints = BANK_CONSTRAINED | ALIGN_CONSTRAINED; constraints >= 0;
constraints--) {
for (Section *section : unassignedSections[constraints]) {
fprintf(stderr, "%c \"%s\"", nbSections == 0 ? ';': ',',
section->name.c_str());
fprintf(stderr, "%c \"%s\"", nbSections == 0 ? ';' : ',', section->name.c_str());
nbSections++;
if (nbSections == 10)
goto max_out;
@@ -384,7 +401,8 @@ max_out:
}
// Assign all remaining sections by decreasing constraint order
for (int8_t constraints = BANK_CONSTRAINED | ALIGN_CONSTRAINED; constraints >= 0; constraints--) {
for (int8_t constraints = BANK_CONSTRAINED | ALIGN_CONSTRAINED; constraints >= 0;
constraints--) {
for (Section *section : unassignedSections[constraints])
placeSection(*section);

View File

@@ -1,35 +1,35 @@
/* SPDX-License-Identifier: MIT */
#include <sys/stat.h>
#include <sys/types.h>
#include <assert.h>
#include <inttypes.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string>
#include <variant>
#include <vector>
#include "error.hpp"
#include "extern/getopt.hpp"
#include "itertools.hpp"
#include "linkdefs.hpp"
#include "platform.hpp"
#include "script.hpp"
#include "version.hpp"
#include "link/assign.hpp"
#include "link/object.hpp"
#include "link/output.hpp"
#include "link/patch.hpp"
#include "link/section.hpp"
#include "script.hpp"
#include "link/symbol.hpp"
#include "extern/getopt.hpp"
#include "error.hpp"
#include "itertools.hpp"
#include "linkdefs.hpp"
#include "platform.hpp"
#include "version.hpp"
bool isDmgMode; // -d
char *linkerScriptName; // -l
char const *mapFileName; // -m
@@ -72,8 +72,7 @@ std::string const &FileStackNode::name() const {
}
// Helper function to dump a file stack to stderr
std::string const *FileStackNode::dumpFileStack() const
{
std::string const *FileStackNode::dumpFileStack() const {
std::string const *lastName;
if (parent) {
@@ -95,9 +94,9 @@ std::string const *FileStackNode::dumpFileStack() const
return lastName;
}
void printDiag(char const *fmt, va_list args, char const *type,
FileStackNode const *where, uint32_t lineNo)
{
void printDiag(
char const *fmt, va_list args, char const *type, FileStackNode const *where, uint32_t lineNo
) {
fputs(type, stderr);
fputs(": ", stderr);
if (where) {
@@ -108,8 +107,7 @@ void printDiag(char const *fmt, va_list args, char const *type,
putc('\n', stderr);
}
void warning(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
{
void warning(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
va_list args;
va_start(args, fmt);
@@ -117,8 +115,7 @@ void warning(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
va_end(args);
}
void error(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
{
void error(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
va_list args;
va_start(args, fmt);
@@ -129,8 +126,7 @@ void error(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
nbErrors++;
}
void argErr(char flag, char const *fmt, ...)
{
void argErr(char flag, char const *fmt, ...) {
va_list args;
fprintf(stderr, "error: Invalid argument for option '%c': ", flag);
@@ -143,8 +139,7 @@ void argErr(char flag, char const *fmt, ...)
nbErrors++;
}
[[noreturn]] void fatal(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
{
[[noreturn]] void fatal(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...) {
va_list args;
va_start(args, fmt);
@@ -154,8 +149,9 @@ void argErr(char flag, char const *fmt, ...)
if (nbErrors != UINT32_MAX)
nbErrors++;
fprintf(stderr, "Linking aborted after %" PRIu32 " error%s\n", nbErrors,
nbErrors == 1 ? "" : "s");
fprintf(
stderr, "Linking aborted after %" PRIu32 " error%s\n", nbErrors, nbErrors == 1 ? "" : "s"
);
exit(1);
}
@@ -191,8 +187,7 @@ static option const longopts[] = {
{nullptr, no_argument, nullptr, 0 }
};
static void printUsage()
{
static void printUsage() {
fputs(
"Usage: rgblink [-dMtVvwx] [-l script] [-m map_file] [-n sym_file]\n"
" [-O overlay_file] [-o out_file] [-p pad_value]\n"
@@ -207,7 +202,8 @@ static void printUsage()
" -V, --version print RGBLINK version and exits\n"
"\n"
"For help, use `man rgblink' or go to https://rgbds.gbdev.io/docs/\n",
stderr);
stderr
);
}
enum ScrambledRegion {
@@ -227,8 +223,7 @@ struct {
{"wramx", 7 }, // SCRAMBLE_WRAMX
};
static void parseScrambleSpec(char const *spec)
{
static void parseScrambleSpec(char const *spec) {
// Skip any leading whitespace
spec += strspn(spec, " \t");
@@ -259,8 +254,9 @@ static void parseScrambleSpec(char const *spec)
// Find the next non-blank char after the region name's end
spec += regionNameLen + strspn(&spec[regionNameLen], " \t");
if (*spec != '\0' && *spec != ',' && *spec != '=') {
argErr('S', "Unexpected '%c' after region name \"%.*s\"",
regionNamePrintLen, regionName);
argErr(
'S', "Unexpected '%c' after region name \"%.*s\"", regionNamePrintLen, regionName
);
// Skip to next ',' or '=' (or NUL) and keep parsing
spec += 1 + strcspn(&spec[1], ",=");
}
@@ -285,22 +281,30 @@ static void parseScrambleSpec(char const *spec)
char *endptr;
if (*spec == '\0' || *spec == ',') {
argErr('S', "Empty limit for region \"%.*s\"",
regionNamePrintLen, regionName);
argErr('S', "Empty limit for region \"%.*s\"", regionNamePrintLen, regionName);
goto next;
}
limit = strtoul(spec, &endptr, 10);
endptr += strspn(endptr, " \t");
if (*endptr != '\0' && *endptr != ',') {
argErr('S', "Invalid non-numeric limit for region \"%.*s\"",
regionNamePrintLen, regionName);
argErr(
'S',
"Invalid non-numeric limit for region \"%.*s\"",
regionNamePrintLen,
regionName
);
endptr = strchr(endptr, ',');
}
spec = endptr;
if (region != SCRAMBLE_UNK && limit > scrambleSpecs[region].max) {
argErr('S', "Limit for region \"%.*s\" may not exceed %" PRIu16,
regionNamePrintLen, regionName, scrambleSpecs[region].max);
argErr(
'S',
"Limit for region \"%.*s\" may not exceed %" PRIu16,
regionNamePrintLen,
regionName,
scrambleSpecs[region].max
);
limit = scrambleSpecs[region].max;
}
@@ -321,8 +325,7 @@ static void parseScrambleSpec(char const *spec)
// Only WRAMX can be implied, since ROMX and SRAM size may vary
scrambleWRAMX = 7;
} else {
argErr('S', "Cannot imply limit for region \"%.*s\"",
regionNamePrintLen, regionName);
argErr('S', "Cannot imply limit for region \"%.*s\"", regionNamePrintLen, regionName);
}
next:
@@ -337,13 +340,13 @@ next:
}
[[noreturn]] void reportErrors() {
fprintf(stderr, "Linking failed with %" PRIu32 " error%s\n",
nbErrors, nbErrors == 1 ? "" : "s");
fprintf(
stderr, "Linking failed with %" PRIu32 " error%s\n", nbErrors, nbErrors == 1 ? "" : "s"
);
exit(1);
}
static void freeSection(Section &section)
{
static void freeSection(Section &section) {
Section *next = &section;
for (Section *nextu; next; next = nextu) {
@@ -352,13 +355,11 @@ static void freeSection(Section &section)
};
}
static void freeSections()
{
static void freeSections() {
sect_ForEach(freeSection);
}
int main(int argc, char *argv[])
{
int main(int argc, char *argv[]) {
// Parse options
for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) {
switch (ch) {
@@ -444,8 +445,9 @@ int main(int argc, char *argv[])
// If no input files were specified, the user must have screwed up
if (curArgIndex == argc) {
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);
}

View File

@@ -1,30 +1,31 @@
/* SPDX-License-Identifier: MIT */
#include "link/object.hpp"
#include <algorithm>
#include <deque>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <new>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
#include "link/assign.hpp"
#include "link/main.hpp"
#include "link/object.hpp"
#include "link/patch.hpp"
#include "link/sdas_obj.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
#include "error.hpp"
#include "helpers.hpp"
#include "linkdefs.hpp"
#include "version.hpp"
#include "link/assign.hpp"
#include "link/main.hpp"
#include "link/patch.hpp"
#include "link/sdas_obj.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
static std::deque<std::vector<Symbol>> symbolLists;
static std::vector<std::vector<FileStackNode>> nodes;
static std::deque<Assertion> assertions;
@@ -33,14 +34,13 @@ static std::deque<Assertion> assertions;
// Internal, DO NOT USE.
// For helper wrapper macros defined below, such as `tryReadlong`
#define tryRead(func, type, errval, vartype, var, file, ...) do { \
#define tryRead(func, type, errval, vartype, var, file, ...) \
do { \
FILE *tmpFile = file; \
type tmpVal = func(tmpFile); \
/* TODO: maybe mark the condition as `unlikely`; how to do that portably? */ \
if (tmpVal == (errval)) { \
errx(__VA_ARGS__, feof(tmpFile) \
? "Unexpected end of file" \
: strerror(errno)); \
errx(__VA_ARGS__, feof(tmpFile) ? "Unexpected end of file" : strerror(errno)); \
} \
var = (vartype)tmpVal; \
} while (0)
@@ -50,8 +50,7 @@ static std::deque<Assertion> assertions;
* @param file The file to read from. This will read 4 bytes from the file.
* @return The value read, cast to a int64_t, or -1 on failure.
*/
static int64_t readlong(FILE *file)
{
static int64_t readlong(FILE *file) {
uint32_t value = 0;
// Read the little-endian value byte by byte
@@ -92,8 +91,7 @@ static int64_t readlong(FILE *file)
* @param ... A format string and related arguments; note that an extra string
* argument is provided, the reason for failure
*/
#define tryGetc(type, var, file, ...) \
tryRead(getc, int, EOF, type, var, file, __VA_ARGS__)
#define tryGetc(type, var, file, ...) tryRead(getc, int, EOF, type, var, file, __VA_ARGS__)
/*
* Helper macro for readings '\0'-terminated strings from a file, and errors out if it fails to.
@@ -103,14 +101,13 @@ static int64_t readlong(FILE *file)
* @param ... A format string and related arguments; note that an extra string
* argument is provided, the reason for failure
*/
#define tryReadstring(var, file, ...) do { \
#define tryReadstring(var, file, ...) \
do { \
FILE *tmpFile = file; \
std::string &tmpVal = var; \
for (int tmpByte = getc(tmpFile); tmpByte != '\0'; tmpByte = getc(tmpFile)) { \
if (tmpByte == EOF) { \
errx(__VA_ARGS__, feof(tmpFile) \
? "Unexpected end of file" \
: strerror(errno)); \
errx(__VA_ARGS__, feof(tmpFile) ? "Unexpected end of file" : strerror(errno)); \
} else { \
tmpVal.push_back(tmpByte); \
} \
@@ -126,39 +123,55 @@ static int64_t readlong(FILE *file)
* @param i The ID of the node in the array
* @param fileName The filename to report in errors
*/
static void readFileStackNode(FILE *file, std::vector<FileStackNode> &fileNodes, uint32_t i,
char const *fileName)
{
static void readFileStackNode(
FILE *file, std::vector<FileStackNode> &fileNodes, uint32_t i, char const *fileName
) {
FileStackNode &node = fileNodes[i];
uint32_t parentID;
tryReadlong(parentID, file,
"%s: Cannot read node #%" PRIu32 "'s parent ID: %s", fileName, i);
tryReadlong(parentID, file, "%s: Cannot read node #%" PRIu32 "'s parent ID: %s", fileName, i);
node.parent = parentID != (uint32_t)-1 ? &fileNodes[parentID] : nullptr;
tryReadlong(node.lineNo, file,
"%s: Cannot read node #%" PRIu32 "'s line number: %s", fileName, i);
tryGetc(enum FileStackNodeType, node.type, file,
"%s: Cannot read node #%" PRIu32 "'s type: %s", fileName, i);
tryReadlong(
node.lineNo, file, "%s: Cannot read node #%" PRIu32 "'s line number: %s", fileName, i
);
tryGetc(
enum FileStackNodeType,
node.type,
file,
"%s: Cannot read node #%" PRIu32 "'s type: %s",
fileName,
i
);
switch (node.type) {
case NODE_FILE:
case NODE_MACRO:
node.data = "";
tryReadstring(node.name(), file,
"%s: Cannot read node #%" PRIu32 "'s file name: %s", fileName, i);
tryReadstring(
node.name(), file, "%s: Cannot read node #%" PRIu32 "'s file name: %s", fileName, i
);
break;
uint32_t depth;
case NODE_REPT:
tryReadlong(depth, file,
"%s: Cannot read node #%" PRIu32 "'s rept depth: %s", fileName, i);
tryReadlong(depth, file, "%s: Cannot read node #%" PRIu32 "'s rept depth: %s", fileName, i);
node.data = std::vector<uint32_t>(depth);
for (uint32_t k = 0; k < depth; k++)
tryReadlong(node.iters()[k], file,
tryReadlong(
node.iters()[k],
file,
"%s: Cannot read node #%" PRIu32 "'s iter #%" PRIu32 ": %s",
fileName, i, k);
fileName,
i,
k
);
if (!node.parent)
fatal(nullptr, 0, "%s is not a valid object file: root node (#%"
PRIu32 ") may not be REPT", fileName, i);
fatal(
nullptr,
0,
"%s is not a valid object file: root node (#%" PRIu32 ") may not be REPT",
fileName,
i
);
}
}
@@ -168,26 +181,44 @@ static void readFileStackNode(FILE *file, std::vector<FileStackNode> &fileNodes,
* @param symbol The symbol to fill
* @param fileName The filename to report in errors
*/
static void readSymbol(FILE *file, Symbol &symbol, char const *fileName,
std::vector<FileStackNode> const &fileNodes)
{
static void readSymbol(
FILE *file, Symbol &symbol, char const *fileName, std::vector<FileStackNode> const &fileNodes
) {
tryReadstring(symbol.name, file, "%s: Cannot read symbol name: %s", fileName);
tryGetc(enum ExportLevel, symbol.type, file, "%s: Cannot read \"%s\"'s type: %s",
fileName, symbol.name.c_str());
tryGetc(
enum ExportLevel,
symbol.type,
file,
"%s: Cannot read \"%s\"'s type: %s",
fileName,
symbol.name.c_str()
);
// If the symbol is defined in this file, read its definition
if (symbol.type != SYMTYPE_IMPORT) {
symbol.objFileName = fileName;
uint32_t nodeID;
tryReadlong(nodeID, file, "%s: Cannot read \"%s\"'s node ID: %s",
fileName, symbol.name.c_str());
tryReadlong(
nodeID, file, "%s: Cannot read \"%s\"'s node ID: %s", fileName, symbol.name.c_str()
);
symbol.src = &fileNodes[nodeID];
tryReadlong(symbol.lineNo, file, "%s: Cannot read \"%s\"'s line number: %s",
fileName, symbol.name.c_str());
tryReadlong(
symbol.lineNo,
file,
"%s: Cannot read \"%s\"'s line number: %s",
fileName,
symbol.name.c_str()
);
int32_t sectionID, value;
tryReadlong(sectionID, file, "%s: Cannot read \"%s\"'s section ID: %s",
fileName, symbol.name.c_str());
tryReadlong(value, file, "%s: Cannot read \"%s\"'s value: %s",
fileName, symbol.name.c_str());
tryReadlong(
sectionID,
file,
"%s: Cannot read \"%s\"'s section ID: %s",
fileName,
symbol.name.c_str()
);
tryReadlong(
value, file, "%s: Cannot read \"%s\"'s value: %s", fileName, symbol.name.c_str()
);
if (sectionID == -1) {
symbol.data = value;
} else {
@@ -210,53 +241,96 @@ static void readSymbol(FILE *file, Symbol &symbol, char const *fileName,
* @param fileName The filename to report in errors
* @param i The number of the patch to report in errors
*/
static void readPatch(FILE *file, Patch &patch, char const *fileName, std::string const &sectName,
uint32_t i, std::vector<FileStackNode> const &fileNodes)
{
static void readPatch(
FILE *file,
Patch &patch,
char const *fileName,
std::string const &sectName,
uint32_t i,
std::vector<FileStackNode> const &fileNodes
) {
uint32_t nodeID, rpnSize;
enum PatchType type;
tryReadlong(nodeID, file,
tryReadlong(
nodeID,
file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s node ID: %s",
fileName, sectName.c_str(), i);
fileName,
sectName.c_str(),
i
);
patch.src = &fileNodes[nodeID];
tryReadlong(patch.lineNo, file,
tryReadlong(
patch.lineNo,
file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s line number: %s",
fileName, sectName.c_str(), i);
tryReadlong(patch.offset, file,
fileName,
sectName.c_str(),
i
);
tryReadlong(
patch.offset,
file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s offset: %s",
fileName, sectName.c_str(), i);
tryReadlong(patch.pcSectionID, file,
fileName,
sectName.c_str(),
i
);
tryReadlong(
patch.pcSectionID,
file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
fileName, sectName.c_str(), i);
tryReadlong(patch.pcOffset, file,
fileName,
sectName.c_str(),
i
);
tryReadlong(
patch.pcOffset,
file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
fileName, sectName.c_str(), i);
tryGetc(enum PatchType, type, file,
fileName,
sectName.c_str(),
i
);
tryGetc(
enum PatchType,
type,
file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s type: %s",
fileName, sectName.c_str(), i);
fileName,
sectName.c_str(),
i
);
patch.type = type;
tryReadlong(rpnSize, file,
tryReadlong(
rpnSize,
file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s RPN size: %s",
fileName, sectName.c_str(), i);
fileName,
sectName.c_str(),
i
);
patch.rpnExpression.resize(rpnSize);
size_t nbElementsRead = fread(patch.rpnExpression.data(), 1, rpnSize, file);
if (nbElementsRead != rpnSize)
errx("%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s",
fileName, sectName.c_str(), i,
feof(file) ? "Unexpected end of file" : strerror(errno));
errx(
"%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s",
fileName,
sectName.c_str(),
i,
feof(file) ? "Unexpected end of file" : strerror(errno)
);
}
/*
* Sets a patch's pcSection from its pcSectionID.
* @param patch The patch to fix
*/
static void linkPatchToPCSect(Patch &patch, std::vector<Section *> const &fileSections)
{
patch.pcSection = patch.pcSectionID != (uint32_t)-1 ? fileSections[patch.pcSectionID]
: nullptr;
static void linkPatchToPCSect(Patch &patch, std::vector<Section *> const &fileSections) {
patch.pcSection = patch.pcSectionID != (uint32_t)-1 ? fileSections[patch.pcSectionID] : nullptr;
}
/*
@@ -265,9 +339,9 @@ static void linkPatchToPCSect(Patch &patch, std::vector<Section *> const &fileSe
* @param section The section to fill
* @param fileName The filename to report in errors
*/
static void readSection(FILE *file, Section &section, char const *fileName,
std::vector<FileStackNode> const &fileNodes)
{
static void readSection(
FILE *file, Section &section, char const *fileName, std::vector<FileStackNode> const &fileNodes
) {
int32_t tmp;
uint8_t byte;
@@ -277,8 +351,9 @@ static void readSection(FILE *file, Section &section, char const *fileName,
errx("\"%s\"'s section size (%" PRId32 ") is invalid", section.name.c_str(), tmp);
section.size = tmp;
section.offset = 0;
tryGetc(uint8_t, byte, file, "%s: Cannot read \"%s\"'s type: %s", fileName,
section.name.c_str());
tryGetc(
uint8_t, byte, file, "%s: Cannot read \"%s\"'s type: %s", fileName, section.name.c_str()
);
section.type = (enum SectionType)(byte & 0x3F);
if (byte >> 7)
section.modifier = SECTION_UNION;
@@ -296,17 +371,29 @@ static void readSection(FILE *file, Section &section, char const *fileName,
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s bank: %s", fileName, section.name.c_str());
section.isBankFixed = tmp >= 0;
section.bank = tmp;
tryGetc(uint8_t, byte, file, "%s: Cannot read \"%s\"'s alignment: %s", fileName,
section.name.c_str());
tryGetc(
uint8_t,
byte,
file,
"%s: Cannot read \"%s\"'s alignment: %s",
fileName,
section.name.c_str()
);
if (byte > 16)
byte = 16;
section.isAlignFixed = byte != 0;
section.alignMask = (1 << byte) - 1;
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s alignment offset: %s", fileName,
section.name.c_str());
tryReadlong(
tmp, file, "%s: Cannot read \"%s\"'s alignment offset: %s", fileName, section.name.c_str()
);
if (tmp > UINT16_MAX) {
error(nullptr, 0, "\"%s\"'s alignment offset is too large (%" PRId32 ")",
section.name.c_str(), tmp);
error(
nullptr,
0,
"\"%s\"'s alignment offset is too large (%" PRId32 ")",
section.name.c_str(),
tmp
);
tmp = UINT16_MAX;
}
section.alignOfs = tmp;
@@ -316,15 +403,23 @@ static void readSection(FILE *file, Section &section, char const *fileName,
section.data.resize(section.size);
if (size_t nbRead = fread(section.data.data(), 1, section.size, file);
nbRead != section.size)
errx("%s: Cannot read \"%s\"'s data: %s", fileName, section.name.c_str(),
feof(file) ? "Unexpected end of file" : strerror(errno));
errx(
"%s: Cannot read \"%s\"'s data: %s",
fileName,
section.name.c_str(),
feof(file) ? "Unexpected end of file" : strerror(errno)
);
}
uint32_t nbPatches;
tryReadlong(nbPatches, file,
"%s: Cannot read \"%s\"'s number of patches: %s", fileName,
section.name.c_str());
tryReadlong(
nbPatches,
file,
"%s: Cannot read \"%s\"'s number of patches: %s",
fileName,
section.name.c_str()
);
section.patches.resize(nbPatches);
for (uint32_t i = 0; i < nbPatches; i++)
@@ -337,8 +432,7 @@ static void readSection(FILE *file, Section &section, char const *fileName,
* @param symbol The symbol to link
* @param section The section to link
*/
static void linkSymToSect(Symbol &symbol, Section &section)
{
static void linkSymToSect(Symbol &symbol, Section &section) {
uint32_t a = 0, b = section.symbols.size();
int32_t symbolOffset = symbol.label().offset;
@@ -361,9 +455,13 @@ static void linkSymToSect(Symbol &symbol, Section &section)
* @param assert The assertion to fill
* @param fileName The filename to report in errors
*/
static void readAssertion(FILE *file, Assertion &assert, char const *fileName, uint32_t i,
std::vector<FileStackNode> const &fileNodes)
{
static void readAssertion(
FILE *file,
Assertion &assert,
char const *fileName,
uint32_t i,
std::vector<FileStackNode> const &fileNodes
) {
char assertName[sizeof("Assertion #4294967295")]; // UINT32_MAX
snprintf(assertName, sizeof(assertName), "Assertion #%" PRIu32, i);
@@ -372,13 +470,11 @@ static void readAssertion(FILE *file, Assertion &assert, char const *fileName, u
tryReadstring(assert.message, file, "%s: Cannot read assertion's message: %s", fileName);
}
static Section *getMainSection(Section &section)
{
static Section *getMainSection(Section &section) {
return section.modifier != SECTION_NORMAL ? sect_GetSection(section.name) : &section;
}
void obj_ReadFile(char const *fileName, unsigned int fileID)
{
void obj_ReadFile(char const *fileName, unsigned int fileID) {
FILE *file;
if (strcmp(fileName, "-")) {
@@ -405,12 +501,9 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
default: // This is (probably) a SDCC object file, defer the rest of detection to it
// Since SDCC does not provide line info, everything will be reported as coming from the
// object file. It's better than nothing.
nodes[fileID].push_back({
.parent = nullptr,
.lineNo = 0,
.type = NODE_FILE,
.data = fileName
});
nodes[fileID].push_back(
{.parent = nullptr, .lineNo = 0, .type = NODE_FILE, .data = fileName}
);
std::vector<Symbol> &fileSymbols = symbolLists.emplace_front();
@@ -431,10 +524,16 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
tryReadlong(revNum, file, "%s: Cannot read revision number: %s", fileName);
if (revNum != RGBDS_OBJECT_REV)
errx("%s: Unsupported object file for rgblink %s; try rebuilding \"%s\"%s"
" (expected revision %d, got %d)", fileName, get_package_version_string(),
fileName, revNum > RGBDS_OBJECT_REV ? " or updating rgblink" : "",
RGBDS_OBJECT_REV, revNum);
errx(
"%s: Unsupported object file for rgblink %s; try rebuilding \"%s\"%s"
" (expected revision %d, got %d)",
fileName,
get_package_version_string(),
fileName,
revNum > RGBDS_OBJECT_REV ? " or updating rgblink" : "",
RGBDS_OBJECT_REV,
revNum
);
uint32_t nbNodes;
uint32_t nbSymbols;
@@ -527,12 +626,10 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
fclose(file);
}
void obj_CheckAssertions()
{
void obj_CheckAssertions() {
patch_CheckAssertions(assertions);
}
void obj_Setup(unsigned int nbFiles)
{
void obj_Setup(unsigned int nbFiles) {
nodes.resize(nbFiles);
}

View File

@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: MIT */
#include "link/output.hpp"
#include <algorithm>
#include <assert.h>
#include <deque>
@@ -10,17 +12,15 @@
#include <string.h>
#include <vector>
#include "link/output.hpp"
#include "error.hpp"
#include "extern/utf8decoder.hpp"
#include "itertools.hpp"
#include "linkdefs.hpp"
#include "link/main.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
#include "extern/utf8decoder.hpp"
#include "error.hpp"
#include "itertools.hpp"
#include "linkdefs.hpp"
#define BANK_SIZE 0x4000
FILE *outputFile;
@@ -49,11 +49,10 @@ static enum SectionType typeMap[SECTTYPE_INVALID] = {
SECTTYPE_WRAM0,
SECTTYPE_WRAMX,
SECTTYPE_OAM,
SECTTYPE_HRAM
SECTTYPE_HRAM,
};
void out_AddSection(Section const &section)
{
void out_AddSection(Section const &section) {
static const uint32_t maxNbBanks[SECTTYPE_INVALID] = {
1, // SECTTYPE_WRAM0
2, // SECTTYPE_VRAM
@@ -69,14 +68,18 @@ void out_AddSection(Section const &section)
uint32_t minNbBanks = targetBank + 1;
if (minNbBanks > maxNbBanks[section.type])
errx("Section \"%s\" has an invalid bank range (%" PRIu32 " > %" PRIu32 ")",
section.name.c_str(), section.bank, maxNbBanks[section.type] - 1);
errx(
"Section \"%s\" has an invalid bank range (%" PRIu32 " > %" PRIu32 ")",
section.name.c_str(),
section.bank,
maxNbBanks[section.type] - 1
);
for (uint32_t i = sections[section.type].size(); i < minNbBanks; i++)
sections[section.type].emplace_back();
std::deque<Section const *> &bankSections = section.size
? sections[section.type][targetBank].sections
std::deque<Section const *> &bankSections =
section.size ? sections[section.type][targetBank].sections
: sections[section.type][targetBank].zeroLenSections;
auto pos = bankSections.begin();
@@ -86,8 +89,7 @@ void out_AddSection(Section const &section)
bankSections.insert(pos, &section);
}
Section const *out_OverlappingSection(Section const &section)
{
Section const *out_OverlappingSection(Section const &section) {
uint32_t bank = section.bank - sectionTypeInfo[section.type].firstBank;
for (Section const *ptr : sections[section.type][bank].sections) {
@@ -101,8 +103,7 @@ Section const *out_OverlappingSection(Section const &section)
* Performs sanity checks on the overlay file.
* @return The number of ROM banks in the overlay file
*/
static uint32_t checkOverlaySize()
{
static uint32_t checkOverlaySize() {
if (!overlayFile)
return 0;
@@ -136,8 +137,7 @@ static uint32_t checkOverlaySize()
* covered by any sections.
* @param nbOverlayBanks The number of banks in the overlay file
*/
static void coverOverlayBanks(uint32_t nbOverlayBanks)
{
static void coverOverlayBanks(uint32_t nbOverlayBanks) {
// 2 if is32kMode, 1 otherwise
uint32_t nbRom0Banks = sectionTypeInfo[SECTTYPE_ROM0].size / BANK_SIZE;
// Discount ROM0 banks to avoid outputting too much
@@ -157,8 +157,8 @@ static void coverOverlayBanks(uint32_t nbOverlayBanks)
* @param baseOffset The address of the bank's first byte in GB address space
* @param size The size of the bank
*/
static void writeBank(std::deque<Section const *> *bankSections, uint16_t baseOffset, uint16_t size)
{
static void
writeBank(std::deque<Section const *> *bankSections, uint16_t baseOffset, uint16_t size) {
uint16_t offset = 0;
if (bankSections) {
@@ -190,8 +190,7 @@ static void writeBank(std::deque<Section const *> *bankSections, uint16_t baseOf
}
// Writes a ROM file to the output.
static void writeROM()
{
static void writeROM() {
if (outputFileName) {
if (strcmp(outputFileName, "-")) {
outputFile = fopen(outputFileName, "wb");
@@ -220,12 +219,18 @@ static void writeROM()
coverOverlayBanks(nbOverlayBanks);
if (outputFile) {
writeBank(!sections[SECTTYPE_ROM0].empty() ? &sections[SECTTYPE_ROM0][0].sections : nullptr,
sectionTypeInfo[SECTTYPE_ROM0].startAddr, sectionTypeInfo[SECTTYPE_ROM0].size);
writeBank(
!sections[SECTTYPE_ROM0].empty() ? &sections[SECTTYPE_ROM0][0].sections : nullptr,
sectionTypeInfo[SECTTYPE_ROM0].startAddr,
sectionTypeInfo[SECTTYPE_ROM0].size
);
for (uint32_t i = 0; i < sections[SECTTYPE_ROMX].size(); i++)
writeBank(&sections[SECTTYPE_ROMX][i].sections,
sectionTypeInfo[SECTTYPE_ROMX].startAddr, sectionTypeInfo[SECTTYPE_ROMX].size);
writeBank(
&sections[SECTTYPE_ROMX][i].sections,
sectionTypeInfo[SECTTYPE_ROMX].startAddr,
sectionTypeInfo[SECTTYPE_ROMX].size
);
}
if (outputFile)
@@ -235,22 +240,19 @@ static void writeROM()
}
// Checks whether this character is legal as the first character of a symbol's name in a sym file
static bool canStartSymName(char c)
{
static bool canStartSymName(char c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_';
}
// Checks whether this character is legal in a symbol's name in a sym file
static bool isLegalForSymName(char c)
{
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
c == '_' || c == '@' || c == '#' || c == '$' || c == '.';
static bool isLegalForSymName(char c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_'
|| c == '@' || c == '#' || c == '$' || c == '.';
}
// Prints a symbol's name to `symFile`, assuming that the first character is legal.
// Illegal characters are UTF-8-decoded (errors are replaced by U+FFFD) and emitted as `\u`/`\U`.
static void printSymName(char const *name)
{
static void printSymName(char const *name) {
for (char const *ptr = name; *ptr != '\0';) {
char c = *ptr;
@@ -277,16 +279,14 @@ static void printSymName(char const *name)
++ptr;
} while (state != 0);
fprintf(symFile, codepoint <= 0xFFFF ? "\\u%04" PRIx32 : "\\U%08" PRIx32,
codepoint);
fprintf(symFile, codepoint <= 0xFFFF ? "\\u%04" PRIx32 : "\\U%08" PRIx32, codepoint);
}
}
}
// Comparator function for `std::stable_sort` to sort symbols
// Symbols are ordered by address, then by parentage
static int compareSymbols(SortedSymbol const &sym1, SortedSymbol const &sym2)
{
static int compareSymbols(SortedSymbol const &sym1, SortedSymbol const &sym2) {
if (sym1.addr != sym2.addr)
return sym1.addr < sym2.addr ? -1 : 1;
@@ -315,10 +315,12 @@ static int compareSymbols(SortedSymbol const &sym1, SortedSymbol const &sym2)
* Write a bank's contents to the sym file
* @param bankSections The bank's sections
*/
static void writeSymBank(SortedSections const &bankSections, enum SectionType type, uint32_t bank)
{
#define forEachSortedSection(sect, ...) do { \
for (auto it = bankSections.zeroLenSections.begin(); it != bankSections.zeroLenSections.end(); it++) { \
static void writeSymBank(SortedSections const &bankSections, enum SectionType type, uint32_t bank) {
#define forEachSortedSection(sect, ...) \
do { \
for (auto it = bankSections.zeroLenSections.begin(); \
it != bankSections.zeroLenSections.end(); \
it++) { \
for (Section const *sect = *it; sect; sect = sect->nextu) \
__VA_ARGS__ \
} \
@@ -330,9 +332,7 @@ static void writeSymBank(SortedSections const &bankSections, enum SectionType ty
uint32_t nbSymbols = 0;
forEachSortedSection(sect, {
nbSymbols += sect->symbols.size();
});
forEachSortedSection(sect, { nbSymbols += sect->symbols.size(); });
if (!nbSymbols)
return;
@@ -345,10 +345,8 @@ static void writeSymBank(SortedSections const &bankSections, enum SectionType ty
for (Symbol const *sym : sect->symbols) {
// Don't output symbols that begin with an illegal character
if (!sym->name.empty() && canStartSymName(sym->name[0]))
symList.push_back({
.sym = sym,
.addr = (uint16_t)(sym->label().offset + sect->org)
});
symList.push_back({.sym = sym, .addr = (uint16_t)(sym->label().offset + sect->org)}
);
}
});
@@ -365,23 +363,31 @@ static void writeSymBank(SortedSections const &bankSections, enum SectionType ty
}
}
static void writeEmptySpace(uint16_t begin, uint16_t end)
{
static void writeEmptySpace(uint16_t begin, uint16_t end) {
if (begin < end) {
uint16_t len = end - begin;
fprintf(mapFile, "\tEMPTY: $%04x-$%04x ($%04" PRIx16 " byte%s)\n",
begin, end - 1, len, len == 1 ? "" : "s");
fprintf(
mapFile,
"\tEMPTY: $%04x-$%04x ($%04" PRIx16 " byte%s)\n",
begin,
end - 1,
len,
len == 1 ? "" : "s"
);
}
}
/*
* Write a bank's contents to the map file
*/
static void writeMapBank(SortedSections const &sectList, enum SectionType type, uint32_t bank)
{
fprintf(mapFile, "\n%s bank #%" PRIu32 ":\n", sectionTypeInfo[type].name.c_str(),
bank + sectionTypeInfo[type].firstBank);
static void writeMapBank(SortedSections const &sectList, enum SectionType type, uint32_t bank) {
fprintf(
mapFile,
"\n%s bank #%" PRIu32 ":\n",
sectionTypeInfo[type].name.c_str(),
bank + sectionTypeInfo[type].firstBank
);
uint16_t used = 0;
auto section = sectList.sections.begin();
@@ -392,7 +398,8 @@ static void writeMapBank(SortedSections const &sectList, enum SectionType type,
// Pick the lowest section by address out of the two
auto &pickedSection = section == sectList.sections.end() ? zeroLenSection
: zeroLenSection == sectList.zeroLenSections.end() ? section
: (*section)->org < (*zeroLenSection)->org ? section : zeroLenSection;
: (*section)->org < (*zeroLenSection)->org ? section
: zeroLenSection;
Section const *sect = *pickedSection;
used += sect->size;
@@ -403,31 +410,41 @@ static void writeMapBank(SortedSections const &sectList, enum SectionType type,
prevEndAddr = sect->org + sect->size;
if (sect->size != 0)
fprintf(mapFile, "\tSECTION: $%04" PRIx16 "-$%04x ($%04" PRIx16
" byte%s) [\"%s\"]\n",
sect->org, prevEndAddr - 1, sect->size, sect->size == 1 ? "" : "s",
sect->name.c_str());
fprintf(
mapFile,
"\tSECTION: $%04" PRIx16 "-$%04x ($%04" PRIx16 " byte%s) [\"%s\"]\n",
sect->org,
prevEndAddr - 1,
sect->size,
sect->size == 1 ? "" : "s",
sect->name.c_str()
);
else
fprintf(mapFile, "\tSECTION: $%04" PRIx16 " (0 bytes) [\"%s\"]\n",
sect->org, sect->name.c_str());
fprintf(
mapFile,
"\tSECTION: $%04" PRIx16 " (0 bytes) [\"%s\"]\n",
sect->org,
sect->name.c_str()
);
if (!noSymInMap) {
// Also print symbols in the following "pieces"
for (uint16_t org = sect->org; sect; sect = sect->nextu) {
for (Symbol *sym : sect->symbols)
// Space matches "\tSECTION: $xxxx ..."
fprintf(mapFile, "\t $%04" PRIx32 " = %s\n",
fprintf(
mapFile,
"\t $%04" PRIx32 " = %s\n",
sym->label().offset + org,
sym->name.c_str());
sym->name.c_str()
);
if (sect->nextu) {
// Announce the following "piece"
if (sect->nextu->modifier == SECTION_UNION)
fprintf(mapFile,
"\t ; Next union\n");
fprintf(mapFile, "\t ; Next union\n");
else if (sect->nextu->modifier == SECTION_FRAGMENT)
fprintf(mapFile,
"\t ; Next fragment\n");
fprintf(mapFile, "\t ; Next fragment\n");
}
}
}
@@ -444,16 +461,14 @@ static void writeMapBank(SortedSections const &sectList, enum SectionType type,
uint16_t slack = sectionTypeInfo[type].size - used;
fprintf(mapFile, "\tTOTAL EMPTY: $%04" PRIx16 " byte%s\n", slack,
slack == 1 ? "" : "s");
fprintf(mapFile, "\tTOTAL EMPTY: $%04" PRIx16 " byte%s\n", slack, slack == 1 ? "" : "s");
}
}
/*
* Write the total used and free space by section type to the map file
*/
static void writeMapSummary()
{
static void writeMapSummary() {
fputs("SUMMARY:\n", mapFile);
for (uint8_t i = 0; i < SECTTYPE_INVALID; i++) {
@@ -491,19 +506,22 @@ static void writeMapSummary()
usedTotal += used;
}
fprintf(mapFile, "\t%s: %" PRId32 " byte%s used / %" PRId32 " free",
sectionTypeInfo[type].name.c_str(), usedTotal, usedTotal == 1 ? "" : "s",
nbBanks * sectionTypeInfo[type].size - usedTotal);
if (sectionTypeInfo[type].firstBank != sectionTypeInfo[type].lastBank
|| nbBanks > 1)
fprintf(
mapFile,
"\t%s: %" PRId32 " byte%s used / %" PRId32 " free",
sectionTypeInfo[type].name.c_str(),
usedTotal,
usedTotal == 1 ? "" : "s",
nbBanks * sectionTypeInfo[type].size - usedTotal
);
if (sectionTypeInfo[type].firstBank != sectionTypeInfo[type].lastBank || nbBanks > 1)
fprintf(mapFile, " in %u bank%s", nbBanks, nbBanks == 1 ? "" : "s");
putc('\n', mapFile);
}
}
// Writes the sym file, if applicable.
static void writeSym()
{
static void writeSym() {
if (!symFileName)
return;
@@ -529,8 +547,7 @@ static void writeSym()
}
// Writes the map file, if applicable.
static void writeMap()
{
static void writeMap() {
if (!mapFileName)
return;
@@ -555,8 +572,7 @@ static void writeMap()
fclose(mapFile);
}
void out_WriteFiles()
{
void out_WriteFiles() {
writeROM();
writeSym();
writeMap();

View File

@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: MIT */
#include "link/patch.hpp"
#include <assert.h>
#include <deque>
#include <inttypes.h>
@@ -8,17 +10,16 @@
#include <string.h>
#include <variant>
#include "link/object.hpp"
#include "link/patch.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
#include "error.hpp"
#include "helpers.hpp"
#include "linkdefs.hpp"
#include "opmath.hpp"
#include "platform.hpp"
#include "link/object.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
struct RPNStackEntry {
int32_t value;
bool errorFlag; // Whether the value is a placeholder inserted for error recovery
@@ -26,8 +27,7 @@ struct RPNStackEntry {
std::deque<RPNStackEntry> rpnStack;
static void pushRPN(int32_t value, bool comesFromError)
{
static void pushRPN(int32_t value, bool comesFromError) {
rpnStack.push_front({.value = value, .errorFlag = comesFromError});
}
@@ -35,8 +35,7 @@ static void pushRPN(int32_t value, bool comesFromError)
// has popped any values with the error flag set.
static bool isError = false;
static int32_t popRPN(FileStackNode const *node, uint32_t lineNo)
{
static int32_t popRPN(FileStackNode const *node, uint32_t lineNo) {
if (rpnStack.empty())
fatal(node, lineNo, "Internal error, RPN stack empty");
@@ -49,17 +48,16 @@ static int32_t popRPN(FileStackNode const *node, uint32_t lineNo)
// RPN operators
static uint32_t getRPNByte(uint8_t const *&expression, int32_t &size,
FileStackNode const *node, uint32_t lineNo)
{
static uint32_t getRPNByte(
uint8_t const *&expression, int32_t &size, FileStackNode const *node, uint32_t lineNo
) {
if (!size--)
fatal(node, lineNo, "Internal error, RPN expression overread");
return *expression++;
}
static Symbol const *getSymbol(std::vector<Symbol> const &symbolList, uint32_t index)
{
static Symbol const *getSymbol(std::vector<Symbol> const &symbolList, uint32_t index) {
assert(index != (uint32_t)-1); // PC needs to be handled specially, not here
Symbol const &symbol = symbolList[index];
@@ -78,8 +76,7 @@ static Symbol const *getSymbol(std::vector<Symbol> const &symbolList, uint32_t i
* @return isError Set if an error occurred during evaluation, and further
* errors caused by the value should be suppressed.
*/
static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fileSymbols)
{
static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fileSymbols) {
// Small shortcut to avoid a lot of repetition
#define popRPN() popRPN(patch.src, patch.lineNo)
@@ -89,8 +86,8 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
rpnStack.clear();
while (size > 0) {
enum RPNCommand command = (enum RPNCommand)getRPNByte(expression, size,
patch.src, patch.lineNo);
enum RPNCommand command =
(enum RPNCommand)getRPNByte(expression, size, patch.src, patch.lineNo);
int32_t value;
isError = false;
@@ -218,22 +215,27 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
case RPN_BANK_SYM:
value = 0;
for (uint8_t shift = 0; shift < 32; shift += 8)
value |= getRPNByte(expression, size,
patch.src, patch.lineNo) << shift;
value |= getRPNByte(expression, size, patch.src, patch.lineNo) << shift;
symbol = getSymbol(fileSymbols, value);
if (!symbol) {
error(patch.src, patch.lineNo,
error(
patch.src,
patch.lineNo,
"Requested BANK() of symbol \"%s\", which was not found",
fileSymbols[value].name.c_str());
fileSymbols[value].name.c_str()
);
isError = true;
value = 1;
} else if (Label const *label = std::get_if<Label>(&symbol->data); label) {
value = label->section->bank;
} else {
error(patch.src, patch.lineNo,
error(
patch.src,
patch.lineNo,
"Requested BANK() of non-label symbol \"%s\"",
fileSymbols[value].name.c_str());
fileSymbols[value].name.c_str()
);
isError = true;
value = 1;
}
@@ -249,9 +251,12 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
sect = sect_GetSection(name);
if (!sect) {
error(patch.src, patch.lineNo,
error(
patch.src,
patch.lineNo,
"Requested BANK() of section \"%s\", which was not found",
name);
name
);
isError = true;
value = 1;
} else {
@@ -261,8 +266,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
case RPN_BANK_SELF:
if (!patch.pcSection) {
error(patch.src, patch.lineNo,
"PC has no bank outside a section");
error(patch.src, patch.lineNo, "PC has no bank outside a section");
isError = true;
value = 1;
} else {
@@ -279,9 +283,12 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
sect = sect_GetSection(name);
if (!sect) {
error(patch.src, patch.lineNo,
error(
patch.src,
patch.lineNo,
"Requested SIZEOF() of section \"%s\", which was not found",
name);
name
);
isError = true;
value = 1;
} else {
@@ -299,9 +306,12 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
assert(sect->offset == 0);
if (!sect) {
error(patch.src, patch.lineNo,
error(
patch.src,
patch.lineNo,
"Requested STARTOF() of section \"%s\", which was not found",
name);
name
);
isError = true;
value = 1;
} else {
@@ -312,8 +322,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
case RPN_SIZEOF_SECTTYPE:
value = getRPNByte(expression, size, patch.src, patch.lineNo);
if (value < 0 || value >= SECTTYPE_INVALID) {
error(patch.src, patch.lineNo,
"Requested SIZEOF() an invalid section type");
error(patch.src, patch.lineNo, "Requested SIZEOF() an invalid section type");
isError = true;
value = 0;
} else {
@@ -324,8 +333,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
case RPN_STARTOF_SECTTYPE:
value = getRPNByte(expression, size, patch.src, patch.lineNo);
if (value < 0 || value >= SECTTYPE_INVALID) {
error(patch.src, patch.lineNo,
"Requested STARTOF() an invalid section type");
error(patch.src, patch.lineNo, "Requested STARTOF() an invalid section type");
isError = true;
value = 0;
} else {
@@ -335,11 +343,8 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
case RPN_HRAM:
value = popRPN();
if (!isError && (value < 0
|| (value > 0xFF && value < 0xFF00)
|| value > 0xFFFF)) {
error(patch.src, patch.lineNo,
"Value %" PRId32 " is not in HRAM range", value);
if (!isError && (value < 0 || (value > 0xFF && value < 0xFF00) || value > 0xFFFF)) {
error(patch.src, patch.lineNo, "Value %" PRId32 " is not in HRAM range", value);
isError = true;
}
value &= 0xFF;
@@ -351,8 +356,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
// They can be easily checked with a bitmask
if (value & ~0x38) {
if (!isError)
error(patch.src, patch.lineNo,
"Value %" PRId32 " is not a RST vector", value);
error(patch.src, patch.lineNo, "Value %" PRId32 " is not a RST vector", value);
isError = true;
}
value |= 0xC7;
@@ -361,20 +365,17 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
case RPN_CONST:
value = 0;
for (uint8_t shift = 0; shift < 32; shift += 8)
value |= getRPNByte(expression, size,
patch.src, patch.lineNo) << shift;
value |= getRPNByte(expression, size, patch.src, patch.lineNo) << shift;
break;
case RPN_SYM:
value = 0;
for (uint8_t shift = 0; shift < 32; shift += 8)
value |= getRPNByte(expression, size,
patch.src, patch.lineNo) << shift;
value |= getRPNByte(expression, size, patch.src, patch.lineNo) << shift;
if (value == -1) { // PC
if (!patch.pcSection) {
error(patch.src, patch.lineNo,
"PC has no value outside a section");
error(patch.src, patch.lineNo, "PC has no value outside a section");
value = 0;
isError = true;
} else {
@@ -384,16 +385,23 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
symbol = getSymbol(fileSymbols, value);
if (!symbol) {
error(patch.src, patch.lineNo,
"Unknown symbol \"%s\"", fileSymbols[value].name.c_str());
error(
patch.src,
patch.lineNo,
"Unknown symbol \"%s\"",
fileSymbols[value].name.c_str()
);
isError = true;
} else {
value = std::visit(Visitor{
value = std::visit(
Visitor{
[](int32_t val) -> int32_t { return val; },
[](Label label) -> int32_t {
return label.section->org + label.offset;
}
}, symbol->data);
},
},
symbol->data
);
}
}
break;
@@ -403,8 +411,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
}
if (rpnStack.size() > 1)
error(patch.src, patch.lineNo,
"RPN stack has %zu entries on exit, not 1", rpnStack.size());
error(patch.src, patch.lineNo, "RPN stack has %zu entries on exit, not 1", rpnStack.size());
isError = false;
return popRPN();
@@ -412,8 +419,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
#undef popRPN
}
void patch_CheckAssertions(std::deque<Assertion> &assertions)
{
void patch_CheckAssertions(std::deque<Assertion> &assertions) {
verbosePrint("Checking assertions...\n");
for (Assertion &assert : assertions) {
@@ -423,24 +429,37 @@ void patch_CheckAssertions(std::deque<Assertion> &assertions)
if (!isError && !value) {
switch (type) {
case ASSERT_FATAL:
fatal(assert.patch.src, assert.patch.lineNo, "%s",
!assert.message.empty() ? assert.message.c_str()
: "assert failure");
fatal(
assert.patch.src,
assert.patch.lineNo,
"%s",
!assert.message.empty() ? assert.message.c_str() : "assert failure"
);
case ASSERT_ERROR:
error(assert.patch.src, assert.patch.lineNo, "%s",
!assert.message.empty() ? assert.message.c_str()
: "assert failure");
error(
assert.patch.src,
assert.patch.lineNo,
"%s",
!assert.message.empty() ? assert.message.c_str() : "assert failure"
);
break;
case ASSERT_WARN:
warning(assert.patch.src, assert.patch.lineNo, "%s",
!assert.message.empty() ? assert.message.c_str()
: "assert failure");
warning(
assert.patch.src,
assert.patch.lineNo,
"%s",
!assert.message.empty() ? assert.message.c_str() : "assert failure"
);
break;
}
} else if (isError && type == ASSERT_FATAL) {
fatal(assert.patch.src, assert.patch.lineNo,
fatal(
assert.patch.src,
assert.patch.lineNo,
"Failed to evaluate assertion%s%s",
!assert.message.empty() ? ": " : "", assert.message.c_str());
!assert.message.empty() ? ": " : "",
assert.message.c_str()
);
}
}
}
@@ -450,8 +469,7 @@ void patch_CheckAssertions(std::deque<Assertion> &assertions)
* @param section The section component to patch
* @param dataSection The section to patch
*/
static void applyFilePatches(Section &section, Section &dataSection)
{
static void applyFilePatches(Section &section, Section &dataSection) {
verbosePrint("Patching section \"%s\"...\n", section.name.c_str());
for (Patch &patch : section.patches) {
int32_t value = computeRPNExpr(patch, *section.fileSymbols);
@@ -465,9 +483,12 @@ static void applyFilePatches(Section &section, Section &dataSection)
int16_t jumpOffset = value - address;
if (!isError && (jumpOffset < -128 || jumpOffset > 127))
error(patch.src, patch.lineNo,
error(
patch.src,
patch.lineNo,
"jr target out of reach (expected -129 < %" PRId16 " < 128)",
jumpOffset);
jumpOffset
);
dataSection.data[offset] = jumpOffset & 0xFF;
} else {
// Patch a certain number of bytes
@@ -481,12 +502,15 @@ static void applyFilePatches(Section &section, Section &dataSection)
{4, INT32_MIN, INT32_MAX}, // PATCHTYPE_LONG
};
if (!isError && (value < types[patch.type].min
|| value > types[patch.type].max))
error(patch.src, patch.lineNo,
if (!isError && (value < types[patch.type].min || value > types[patch.type].max))
error(
patch.src,
patch.lineNo,
"Value %" PRId32 "%s is not %u-bit",
value, value < 0 ? " (maybe negative?)" : "",
types[patch.type].size * 8U);
value,
value < 0 ? " (maybe negative?)" : "",
types[patch.type].size * 8U
);
for (uint8_t i = 0; i < types[patch.type].size; i++) {
dataSection.data[offset + i] = value & 0xFF;
value >>= 8;
@@ -499,8 +523,7 @@ static void applyFilePatches(Section &section, Section &dataSection)
* Applies all of a section's patches, iterating over "components" of unionized sections
* @param section The section to patch
*/
static void applyPatches(Section &section)
{
static void applyPatches(Section &section) {
if (!sect_HasData(section.type))
return;
@@ -508,7 +531,6 @@ static void applyPatches(Section &section)
applyFilePatches(*component, section);
}
void patch_ApplyPatches()
{
void patch_ApplyPatches() {
sect_ForEach(applyPatches);
}

View File

@@ -79,35 +79,75 @@
%%
lines: %empty
lines:
%empty
| line lines
;
line: INCLUDE string newline { includeFile(std::move($2)); } // Note: this additionally increments the line number!
| directive newline { incLineNo(); }
| newline { incLineNo(); }
| error newline { yyerrok; incLineNo(); } // Error recovery.
line:
INCLUDE string newline {
includeFile(std::move($2)); // Note: this additionally increments the line number!
}
| directive newline {
incLineNo();
}
| newline {
incLineNo();
}
// Error recovery.
| error newline {
yyerrok;
incLineNo();
}
;
directive: section_type { setSectionType($1); }
| section_type number { setSectionType($1, $2); }
| FLOATING { makeAddrFloating(); }
| ORG number { setAddr($2); }
| ALIGN number { alignTo($2, 0); }
| ALIGN number COMMA number { alignTo($2, $4); }
| DS number { pad($2); }
| string optional { placeSection($1, $2); }
directive:
section_type {
setSectionType($1);
}
| section_type number {
setSectionType($1, $2);
}
| FLOATING {
makeAddrFloating();
}
| ORG number {
setAddr($2);
}
| ALIGN number {
alignTo($2, 0);
}
| ALIGN number COMMA number {
alignTo($2, $4);
}
| DS number {
pad($2);
}
| string optional {
placeSection($1, $2);
}
;
optional: %empty { $$ = false; }
| OPTIONAL { $$ = true; }
optional:
%empty {
$$ = false;
}
| OPTIONAL {
$$ = true;
}
;
%%
#define scriptError(context, fmt, ...) \
::error(nullptr, 0, "%s(%" PRIu32 "): " fmt, \
context.path.c_str(), context.lineNo __VA_OPT__(,) __VA_ARGS__)
::error( \
nullptr, \
0, \
"%s(%" PRIu32 "): " fmt, \
context.path.c_str(), \
context.lineNo __VA_OPT__(, ) \
__VA_ARGS__ \
)
// Lexer.
@@ -134,8 +174,9 @@ static void includeFile(std::string &&path) {
if (!newContext.file.open(newContext.path, std::ios_base::in)) {
// The order is important: report the error, increment the line number, modify the stack!
scriptError(prevContext, "Failed to open included linker script \"%s\"",
newContext.path.c_str());
scriptError(
prevContext, "Failed to open included linker script \"%s\"", newContext.path.c_str()
);
++prevContext.lineNo;
lexerStack.pop_back();
} else {
@@ -292,8 +333,7 @@ try_again: // Can't use a `do {} while(0)` loop, otherwise compilers (wrongly) t
// `locale::classic()` yields the "C" locale.
assert(!std::use_facet<std::ctype<char>>(std::locale::classic())
.is(std::ctype_base::lower, ref));
return std::use_facet<std::ctype<char>>(std::locale::classic())
.toupper(cmp) == ref;
return std::use_facet<std::ctype<char>>(std::locale::classic()).toupper(cmp) == ref;
};
ident.push_back(c);
@@ -351,8 +391,9 @@ static void setSectionType(SectionType type) {
auto const &context = lexerStack.back();
if (nbbanks(type) != 1) {
scriptError(context, "A bank number must be specified for %s",
sectionTypeInfo[type].name.c_str());
scriptError(
context, "A bank number must be specified for %s", sectionTypeInfo[type].name.c_str()
);
// Keep going with a default value for the bank index.
}
@@ -364,12 +405,16 @@ static void setSectionType(SectionType type, uint32_t bank) {
auto const &typeInfo = sectionTypeInfo[type];
if (bank < typeInfo.firstBank) {
scriptError(context, "%s bank %" PRIu32 " doesn't exist (the minimum is %" PRIu32 ")",
typeInfo.name.c_str(), bank, typeInfo.firstBank);
scriptError(
context, "%s bank %" PRIu32 " doesn't exist (the minimum is %" PRIu32 ")",
typeInfo.name.c_str(), bank, typeInfo.firstBank
);
bank = typeInfo.firstBank;
} else if (bank > typeInfo.lastBank) {
scriptError(context, "%s bank %" PRIu32 " doesn't exist (the maximum is %" PRIu32 ")",
typeInfo.name.c_str(), bank, typeInfo.lastBank);
scriptError(
context, "%s bank %" PRIu32 " doesn't exist (the maximum is %" PRIu32 ")",
typeInfo.name.c_str(), bank, typeInfo.lastBank
);
}
setActiveTypeAndIdx(type, bank - typeInfo.firstBank);
@@ -388,8 +433,10 @@ static void setAddr(uint32_t addr) {
if (addr < pc) {
scriptError(context, "Cannot decrease the current address (from $%04x to $%04x)", pc, addr);
} else if (addr > endaddr(activeType)) { // Allow "one past the end" sections.
scriptError(context, "Cannot set the current address to $%04" PRIx32 ": %s ends at $%04" PRIx16 "",
addr, typeInfo.name.c_str(), endaddr(activeType));
scriptError(
context, "Cannot set the current address to $%04" PRIx32 ": %s ends at $%04" PRIx16 "",
addr, typeInfo.name.c_str(), endaddr(activeType)
);
pc = endaddr(activeType);
} else {
pc = addr;
@@ -400,7 +447,9 @@ static void setAddr(uint32_t addr) {
static void makeAddrFloating() {
auto const &context = lexerStack.back();
if (activeType == SECTTYPE_INVALID) {
scriptError(context, "Cannot make the current address floating: no memory region is active");
scriptError(
context, "Cannot make the current address floating: no memory region is active"
);
return;
}
@@ -423,9 +472,12 @@ static void alignTo(uint32_t alignment, uint32_t alignOfs) {
uint32_t alignSize = 1u << alignment;
if (alignOfs >= alignSize) {
scriptError(context, "Cannot align: The alignment offset (%" PRIu32
scriptError(
context,
"Cannot align: The alignment offset (%" PRIu32
") must be less than alignment size (%" PRIu32 ")",
alignOfs, alignSize);
alignOfs, alignSize
);
return;
}
@@ -439,8 +491,9 @@ static void alignTo(uint32_t alignment, uint32_t alignOfs) {
auto &pc = curAddr[activeType][activeBankIdx];
if (alignment > 16) {
scriptError(context, "Cannot align: The alignment (%" PRIu32 ") must be less than 16",
alignment);
scriptError(
context, "Cannot align: The alignment (%" PRIu32 ") must be less than 16", alignment
);
return;
}
@@ -451,9 +504,12 @@ static void alignTo(uint32_t alignment, uint32_t alignOfs) {
uint32_t alignSize = 1u << alignment;
if (alignOfs >= alignSize) {
scriptError(context, "Cannot align: The alignment offset (%" PRIu32
scriptError(
context,
"Cannot align: The alignment offset (%" PRIu32
") must be less than alignment size (%" PRIu32 ")",
alignOfs, alignSize);
alignOfs, alignSize
);
return;
}
@@ -462,9 +518,12 @@ static void alignTo(uint32_t alignment, uint32_t alignOfs) {
}
if (uint16_t offset = pc - typeInfo.startAddr; length > typeInfo.size - offset) {
scriptError(context, "Cannot align: the next suitable address after $%04"
PRIx16 " is $%04" PRIx16 ", past $%04" PRIx16,
pc, (uint16_t)(pc + length), (uint16_t)(endaddr(activeType) + 1));
scriptError(
context,
"Cannot align: the next suitable address after $%04" PRIx16 " is $%04" PRIx16
", past $%04" PRIx16,
pc, (uint16_t)(pc + length), (uint16_t)(endaddr(activeType) + 1)
);
return;
}
@@ -488,8 +547,11 @@ static void pad(uint32_t length) {
assert(pc >= typeInfo.startAddr);
if (uint16_t offset = pc - typeInfo.startAddr; length + offset > typeInfo.size) {
scriptError(context, "Cannot increase the current address by %u bytes: only %u bytes to $%04" PRIx16,
length, typeInfo.size - offset, (uint16_t)(endaddr(activeType) + 1));
scriptError(
context,
"Cannot increase the current address by %u bytes: only %u bytes to $%04" PRIx16, length,
typeInfo.size - offset, (uint16_t)(endaddr(activeType) + 1)
);
} else {
pc += length;
}
@@ -498,8 +560,9 @@ static void pad(uint32_t length) {
static void placeSection(std::string const &name, bool isOptional) {
auto const &context = lexerStack.back();
if (activeType == SECTTYPE_INVALID) {
scriptError(context, "No memory region has been specified to place section \"%s\" in",
name.c_str());
scriptError(
context, "No memory region has been specified to place section \"%s\" in", name.c_str()
);
return;
}
@@ -520,14 +583,20 @@ static void placeSection(std::string const &name, bool isOptional) {
fragment->type = activeType;
}
} else if (section->type != activeType) {
scriptError(context, "\"%s\" is specified to be a %s section, but it is already a %s section",
name.c_str(), typeInfo.name.c_str(), sectionTypeInfo[section->type].name.c_str());
scriptError(
context, "\"%s\" is specified to be a %s section, but it is already a %s section",
name.c_str(), typeInfo.name.c_str(), sectionTypeInfo[section->type].name.c_str()
);
}
uint32_t bank = activeBankIdx + typeInfo.firstBank;
if (section->isBankFixed && bank != section->bank) {
scriptError(context, "The linker script places section \"%s\" in %s bank %" PRIu32 ", but it was already defined in bank %" PRIu32,
name.c_str(), sectionTypeInfo[section->type].name.c_str(), bank, section->bank);
scriptError(
context,
"The linker script places section \"%s\" in %s bank %" PRIu32
", but it was already defined in bank %" PRIu32,
name.c_str(), sectionTypeInfo[section->type].name.c_str(), bank, section->bank
);
}
section->isBankFixed = true;
section->bank = bank;
@@ -535,12 +604,22 @@ static void placeSection(std::string const &name, bool isOptional) {
if (!isPcFloating) {
uint16_t &org = curAddr[activeType][activeBankIdx];
if (section->isAddressFixed && org != section->org) {
scriptError(context, "The linker script assigns section \"%s\" to address $%04" PRIx16 ", but it was already at $%04" PRIx16,
name.c_str(), org, section->org);
scriptError(
context,
"The linker script assigns section \"%s\" to address $%04" PRIx16
", but it was already at $%04" PRIx16,
name.c_str(), org, section->org
);
} else if (section->isAlignFixed && (org & section->alignMask) != section->alignOfs) {
uint8_t alignment = std::countr_one(section->alignMask);
scriptError(context, "The linker script assigns section \"%s\" to address $%04" PRIx16 ", but that would be ALIGN[%" PRIu8 ", %" PRIu16 "] instead of the requested ALIGN[%" PRIu8 ", %" PRIu16 "]",
name.c_str(), org, alignment, (uint16_t)(org & section->alignMask), alignment, section->alignOfs);
scriptError(
context,
"The linker script assigns section \"%s\" to address $%04" PRIx16
", but that would be ALIGN[%" PRIu8 ", %" PRIu16
"] instead of the requested ALIGN[%" PRIu8 ", %" PRIu16 "]",
name.c_str(), org, alignment, (uint16_t)(org & section->alignMask), alignment,
section->alignOfs
);
}
section->isAddressFixed = true;
section->isAlignFixed = false; // This can't be set when the above is.
@@ -549,9 +628,12 @@ static void placeSection(std::string const &name, bool isOptional) {
uint16_t curOfs = org - typeInfo.startAddr;
if (section->size > typeInfo.size - curOfs) {
uint16_t overflowSize = section->size - (typeInfo.size - curOfs);
scriptError(context, "The linker script assigns section \"%s\" to address $%04" PRIx16 ", but then it would overflow %s by %" PRIx16 " byte%s",
name.c_str(), org, typeInfo.name.c_str(),
overflowSize, overflowSize == 1 ? "" : "s");
scriptError(
context,
"The linker script assigns section \"%s\" to address $%04" PRIx16
", but then it would overflow %s by %" PRIx16 " byte%s",
name.c_str(), org, typeInfo.name.c_str(), overflowSize, overflowSize == 1 ? "" : "s"
);
// Fill as much as possible without going out of bounds.
org = typeInfo.startAddr + typeInfo.size;
} else {

View File

@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: MIT */
#include "link/sdas_obj.hpp"
#include <assert.h>
#include <ctype.h>
#include <errno.h>
@@ -12,13 +14,12 @@
#include <variant>
#include <vector>
#include "linkdefs.hpp"
#include "helpers.hpp"
#include "linkdefs.hpp"
#include "platform.hpp"
#include "link/assign.hpp"
#include "link/main.hpp"
#include "link/sdas_obj.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
@@ -35,8 +36,8 @@ static void consumeLF(FileStackNode const &where, uint32_t lineNo, FILE *file) {
static char const *delim = " \f\n\r\t\v"; // Whitespace according to the C and POSIX locales
static int nextLine(std::vector<char> &lineBuf, uint32_t &lineNo,
FileStackNode const &where, FILE *file) {
static int
nextLine(std::vector<char> &lineBuf, uint32_t &lineNo, FileStackNode const &where, FILE *file) {
retry:
++lineNo;
int firstChar = getc(file);
@@ -91,7 +92,9 @@ static uint32_t readNumber(char const *str, char const *&endptr, enum NumberType
}
}
static uint32_t parseNumber(FileStackNode const &where, uint32_t lineNo, char const *str, enum NumberType base) {
static uint32_t parseNumber(
FileStackNode const &where, uint32_t lineNo, char const *str, enum NumberType base
) {
if (str[0] == '\0')
fatal(&where, lineNo, "Expected number, got empty string");
@@ -103,7 +106,8 @@ static uint32_t parseNumber(FileStackNode const &where, uint32_t lineNo, char co
return res;
}
static uint8_t parseByte(FileStackNode const &where, uint32_t lineNo, char const *str, enum NumberType base) {
static uint8_t
parseByte(FileStackNode const &where, uint32_t lineNo, char const *str, enum NumberType base) {
uint32_t num = parseNumber(where, lineNo, str, base);
if (num > UINT8_MAX)
@@ -132,29 +136,38 @@ enum RelocFlags {
RELOC_BANKBYTE, // 8-bit size with 24-bit expr only; 0: follow RELOC_WHICHBYTE, 1: BANK()
RELOC_ALL_FLAGS = 1 << RELOC_SIZE | 1 << RELOC_ISSYM | 1 << RELOC_ISPCREL | 1 << RELOC_EXPR16
| 1 << RELOC_SIGNED | 1 << RELOC_ZPAGE | 1 << RELOC_NPAGE | 1 << RELOC_WHICHBYTE
| 1 << RELOC_EXPR24 | 1 << RELOC_BANKBYTE,
| 1 << RELOC_SIGNED | 1 << RELOC_ZPAGE | 1 << RELOC_NPAGE
| 1 << RELOC_WHICHBYTE | 1 << RELOC_EXPR24 | 1 << RELOC_BANKBYTE,
};
void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol> &fileSymbols) {
std::vector<char> line(256);
char const *token;
#define getToken(ptr, ...) do { \
#define getToken(ptr, ...) \
do { \
token = strtok((ptr), delim); \
if (!token) \
fatal(&where, lineNo, __VA_ARGS__); \
} while (0)
#define expectEol(...) do { \
#define expectEol(...) \
do { \
token = strtok(nullptr, delim); \
if (token) \
fatal(&where, lineNo, __VA_ARGS__); \
} while (0)
#define expectToken(expected, lineType) do { \
#define expectToken(expected, lineType) \
do { \
getToken(nullptr, "'%c' line is too short", (lineType)); \
if (strcasecmp(token, (expected)) != 0) \
fatal(&where, lineNo, "Malformed '%c' line: expected \"%s\", got \"%s\"", \
(lineType), (expected), token); \
fatal( \
&where, \
lineNo, \
"Malformed '%c' line: expected \"%s\", got \"%s\"", \
(lineType), \
(expected), \
token \
); \
} while (0)
uint32_t lineNo = 0;
@@ -175,8 +188,12 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
numberType = OCT;
break;
default:
fatal(&where, lineNo, "This does not look like a SDCC object file (unknown integer format '%c')",
lineType);
fatal(
&where,
lineNo,
"This does not look like a SDCC object file (unknown integer format '%c')",
lineType
);
}
switch (line[0]) {
@@ -237,8 +254,9 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
case 'A': {
if (fileSections.size() == expectedNbAreas)
warning(&where, lineNo, "Got more 'A' lines than the expected %" PRIu32,
expectedNbAreas);
warning(
&where, lineNo, "Got more 'A' lines than the expected %" PRIu32, expectedNbAreas
);
Section *curSection = new (std::nothrow) Section();
if (!curSection)
@@ -249,8 +267,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
// The following is required for fragment offsets to be reliably predicted
for (FileSection &entry : fileSections) {
if (!strcmp(token, entry.section->name.c_str()))
fatal(&where, lineNo, "Area \"%s\" already defined earlier",
token);
fatal(&where, lineNo, "Area \"%s\" already defined earlier", token);
}
char const *sectionName = token; // We'll deal with the section's name depending on type
@@ -263,8 +280,12 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
uint32_t tmp = parseNumber(where, lineNo, token, numberType);
if (tmp > UINT16_MAX)
fatal(&where, lineNo, "Area \"%s\" is larger than the GB address space!?",
curSection->name.c_str());
fatal(
&where,
lineNo,
"Area \"%s\" is larger than the GB address space!?",
curSection->name.c_str()
);
curSection->size = tmp;
expectToken("flags", 'A');
@@ -276,7 +297,8 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
curSection->isAddressFixed = tmp & (1 << AREA_ISABS);
curSection->isBankFixed = curSection->isAddressFixed;
curSection->modifier = curSection->isAddressFixed || (tmp & (1 << AREA_TYPE))
? SECTION_NORMAL : SECTION_FRAGMENT;
? SECTION_NORMAL
: SECTION_FRAGMENT;
// If the section is absolute, its name might not be unique; thus, mangle the name
if (curSection->modifier == SECTION_NORMAL) {
curSection->name.append(where.name());
@@ -328,8 +350,12 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
case 'S': {
if (fileSymbols.size() == expectedNbSymbols)
warning(&where, lineNo, "Got more 'S' lines than the expected %" PRIu32,
expectedNbSymbols);
warning(
&where,
lineNo,
"Got more 'S' lines than the expected %" PRIu32,
expectedNbSymbols
);
Symbol &symbol = fileSymbols.emplace_back();
// Init other members
@@ -347,16 +373,11 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
// Symbols in sections are labels; their value is an offset
Section *section = fileSections.back().section;
if (section->isAddressFixed) {
assert(value >= section->org &&
value <= section->org + section->size);
assert(value >= section->org && value <= section->org + section->size);
value -= section->org;
}
symbol.data = Label{
// No need to set the `sectionID`, since we set the pointer
.sectionID = 0,
.offset = value,
.section = section
};
symbol.data = Label{.sectionID = 0, .offset = value, .section = section};
} else {
// Symbols without sections are just constants
symbol.data = value;
@@ -382,7 +403,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
},
[](Label label) -> std::tuple<Section *, int32_t> {
return {label.section, label.offset};
}
},
};
auto [symbolSection, symbolValue] = std::visit(visitor, symbol.data);
auto [otherSection, otherValue] = std::visit(visitor, other->data);
@@ -391,9 +412,16 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|| (symbolSection && !symbolSection->isAddressFixed)) {
sym_AddSymbol(symbol); // This will error out
} else if (otherValue != symbolValue) {
error(&where, lineNo,
"Definition of \"%s\" conflicts with definition in %s (%" PRId32 " != %" PRId32 ")",
symbol.name.c_str(), other->objFileName, symbolValue, otherValue);
error(
&where,
lineNo,
"Definition of \"%s\" conflicts with definition in %s (%" PRId32
" != %" PRId32 ")",
symbol.name.c_str(),
other->objFileName,
symbolValue,
otherValue
);
}
} else {
// Add a new definition
@@ -443,8 +471,13 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
getToken(nullptr, "'R' line is too short");
areaIdx |= (uint16_t)parseByte(where, lineNo, token, numberType) << 8;
if (areaIdx >= fileSections.size())
fatal(&where, lineNo, "'R' line references area #%" PRIu16 ", but there are only %zu (so far)",
areaIdx, fileSections.size());
fatal(
&where,
lineNo,
"'R' line references area #%" PRIu16 ", but there are only %zu (so far)",
areaIdx,
fileSections.size()
);
assert(!fileSections.empty()); // There should be at least one, from the above check
Section *section = fileSections[areaIdx].section;
uint16_t *writeIndex = &fileSections[areaIdx].writeIndex;
@@ -453,16 +486,29 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
if (section->isAddressFixed) {
if (addr < section->org)
fatal(&where, lineNo, "'T' line reports address $%04" PRIx16 " in \"%s\", which starts at $%04" PRIx16,
addr, section->name.c_str(), section->org);
fatal(
&where,
lineNo,
"'T' line reports address $%04" PRIx16
" in \"%s\", which starts at $%04" PRIx16,
addr,
section->name.c_str(),
section->org
);
addr -= section->org;
}
// Lines are emitted that violate this check but contain no "payload";
// ignore those. "Empty" lines shouldn't trigger allocation, either.
if (data.size() != ADDR_SIZE) {
if (addr != *writeIndex)
fatal(&where, lineNo, "'T' lines which don't append to their section are not supported (%" PRIu16 " != %" PRIu16 ")",
addr, *writeIndex);
fatal(
&where,
lineNo,
"'T' lines which don't append to their section are not supported (%" PRIu16
" != %" PRIu16 ")",
addr,
*writeIndex
);
if (section->data.empty()) {
assert(section->size != 0);
section->data.resize(section->size);
@@ -487,18 +533,29 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
if ((flags & 0xF0) == 0xF0) {
getToken(nullptr, "Incomplete relocation");
flags = (flags & 0x0F) | (uint16_t)parseByte(where, lineNo, token, numberType) << 4;
flags =
(flags & 0x0F) | (uint16_t)parseByte(where, lineNo, token, numberType) << 4;
}
getToken(nullptr, "Incomplete relocation");
uint8_t offset = parseByte(where, lineNo, token, numberType);
if (offset < ADDR_SIZE)
fatal(&where, lineNo, "Relocation index cannot point to header (%" PRIu16 " < %u)",
offset, ADDR_SIZE);
fatal(
&where,
lineNo,
"Relocation index cannot point to header (%" PRIu16 " < %u)",
offset,
ADDR_SIZE
);
if (offset >= data.size())
fatal(&where, lineNo, "Relocation index is out of bounds (%" PRIu16 " >= %zu)",
offset, data.size());
fatal(
&where,
lineNo,
"Relocation index is out of bounds (%" PRIu16 " >= %zu)",
offset,
data.size()
);
getToken(nullptr, "Incomplete relocation");
uint16_t idx = parseByte(where, lineNo, token, numberType);
@@ -521,8 +578,14 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
if (section->patches.size() > 1) {
uint32_t prevOffset = section->patches[section->patches.size() - 2].offset;
if (prevOffset >= patch.offset)
fatal(&where, lineNo, "Relocs not sorted by offset are not supported (%" PRIu32 " >= %" PRIu32 ")",
prevOffset, patch.offset);
fatal(
&where,
lineNo,
"Relocs not sorted by offset are not supported (%" PRIu32 " >= %" PRIu32
")",
prevOffset,
patch.offset
);
}
patch.pcSection = section; // No need to fill `pcSectionID`, then
patch.pcOffset = patch.offset - 1; // For `jr`s
@@ -533,8 +596,13 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
assert(offset < data.size());
if (data.size() - offset < nbBaseBytes)
fatal(&where, lineNo, "Reloc would patch out of bounds (%" PRIu8 " > %zu)",
nbBaseBytes, data.size() - offset);
fatal(
&where,
lineNo,
"Reloc would patch out of bounds (%" PRIu8 " > %zu)",
nbBaseBytes,
data.size() - offset
);
for (uint8_t i = 0; i < nbBaseBytes; ++i)
baseValue = baseValue | data[offset + i] << (8 * i);
@@ -542,8 +610,13 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
// Generate a RPN expression from the info and flags
if (flags & 1 << RELOC_ISSYM) {
if (idx >= fileSymbols.size())
fatal(&where, lineNo, "Reloc refers to symbol #%" PRIu16 " out of %zu",
idx, fileSymbols.size());
fatal(
&where,
lineNo,
"Reloc refers to symbol #%" PRIu16 " out of %zu",
idx,
fileSymbols.size()
);
Symbol const &sym = fileSymbols[idx];
// SDCC has a bunch of "magic symbols" that start with a
@@ -552,13 +625,18 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
if (sym.name.starts_with("b_")) {
// Look for the symbol being referenced, and use its index instead
for (idx = 0; idx < fileSymbols.size(); ++idx) {
if (sym.name.ends_with(fileSymbols[idx].name) &&
1 + sym.name.length() == fileSymbols[idx].name.length())
if (sym.name.ends_with(fileSymbols[idx].name)
&& 1 + sym.name.length() == fileSymbols[idx].name.length())
break;
}
if (idx == fileSymbols.size())
fatal(&where, lineNo, "\"%s\" is missing a reference to \"%s\"",
sym.name.c_str(), &sym.name.c_str()[1]);
fatal(
&where,
lineNo,
"\"%s\" is missing a reference to \"%s\"",
sym.name.c_str(),
&sym.name.c_str()[1]
);
patch.rpnExpression.resize(5);
patch.rpnExpression[0] = RPN_BANK_SYM;
patch.rpnExpression[1] = idx;
@@ -568,11 +646,19 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
} else if (sym.name.starts_with("l_")) {
patch.rpnExpression.resize(1 + sym.name.length() - 2 + 1);
patch.rpnExpression[0] = RPN_SIZEOF_SECT;
memcpy((char *)&patch.rpnExpression[1], &sym.name.c_str()[2], sym.name.length() - 2 + 1);
memcpy(
(char *)&patch.rpnExpression[1],
&sym.name.c_str()[2],
sym.name.length() - 2 + 1
);
} else if (sym.name.starts_with("s_")) {
patch.rpnExpression.resize(1 + sym.name.length() - 2 + 1);
patch.rpnExpression[0] = RPN_STARTOF_SECT;
memcpy((char *)&patch.rpnExpression[1], &sym.name.c_str()[2], sym.name.length() - 2 + 1);
memcpy(
(char *)&patch.rpnExpression[1],
&sym.name.c_str()[2],
sym.name.length() - 2 + 1
);
} else {
patch.rpnExpression.resize(5);
patch.rpnExpression[0] = RPN_SYM;
@@ -583,8 +669,13 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
}
} else {
if (idx >= fileSections.size())
fatal(&where, lineNo, "Reloc refers to area #%" PRIu16 " out of %zu",
idx, fileSections.size());
fatal(
&where,
lineNo,
"Reloc refers to area #%" PRIu16 " out of %zu",
idx,
fileSections.size()
);
// It gets funky. If the area is absolute, *actually*, we
// must not add its base address, as the assembler will
// already have added it in `baseValue`.
@@ -625,10 +716,18 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
// are present, so we must skip two of them
if (flags & 1 << RELOC_EXPR16) {
if (*writeIndex + (offset - writtenOfs) > section->size)
fatal(&where, lineNo, "'T' line writes past \"%s\"'s end (%u > %" PRIu16 ")",
section->name.c_str(), *writeIndex + (offset - writtenOfs), section->size);
fatal(
&where,
lineNo,
"'T' line writes past \"%s\"'s end (%u > %" PRIu16 ")",
section->name.c_str(),
*writeIndex + (offset - writtenOfs),
section->size
);
// Copy all bytes up to those (plus the byte that we'll overwrite)
memcpy(&section->data[*writeIndex], &data[writtenOfs], offset - writtenOfs + 1);
memcpy(
&section->data[*writeIndex], &data[writtenOfs], offset - writtenOfs + 1
);
*writeIndex += offset - writtenOfs + 1;
writtenOfs = offset + 3; // Skip all three `baseValue` bytes, though
}
@@ -644,7 +743,9 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
patch.rpnExpression.push_back(16 >> 8);
patch.rpnExpression.push_back(16 >> 16);
patch.rpnExpression.push_back(16 >> 24);
patch.rpnExpression.push_back((flags & 1 << RELOC_SIGNED) ? RPN_SHR : RPN_USHR);
patch.rpnExpression.push_back(
(flags & 1 << RELOC_SIGNED) ? RPN_SHR : RPN_USHR
);
} else {
if (flags & 1 << RELOC_EXPR16 && flags & 1 << RELOC_WHICHBYTE) {
patch.rpnExpression.push_back(RPN_CONST);
@@ -652,7 +753,9 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
patch.rpnExpression.push_back(8 >> 8);
patch.rpnExpression.push_back(8 >> 16);
patch.rpnExpression.push_back(8 >> 24);
patch.rpnExpression.push_back((flags & 1 << RELOC_SIGNED) ? RPN_SHR : RPN_USHR);
patch.rpnExpression.push_back(
(flags & 1 << RELOC_SIGNED) ? RPN_SHR : RPN_USHR
);
}
patch.rpnExpression.push_back(RPN_CONST);
patch.rpnExpression.push_back(0xFF);
@@ -665,8 +768,12 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
assert(patch.type == PATCHTYPE_WORD);
fatal(&where, lineNo, "16-bit PC-relative relocations are not supported");
} else if (flags & (1 << RELOC_EXPR16 | 1 << RELOC_EXPR24)) {
fatal(&where, lineNo, "Flags 0x%x are not supported for 16-bit relocs",
flags & (1 << RELOC_EXPR16 | 1 << RELOC_EXPR24));
fatal(
&where,
lineNo,
"Flags 0x%x are not supported for 16-bit relocs",
flags & (1 << RELOC_EXPR16 | 1 << RELOC_EXPR24)
);
}
}
@@ -674,8 +781,14 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
if (writtenOfs != data.size()) {
assert(data.size() > writtenOfs);
if (*writeIndex + (data.size() - writtenOfs) > section->size)
fatal(&where, lineNo, "'T' line writes past \"%s\"'s end (%zu > %" PRIu16 ")",
section->name.c_str(), *writeIndex + (data.size() - writtenOfs), section->size);
fatal(
&where,
lineNo,
"'T' line writes past \"%s\"'s end (%zu > %" PRIu16 ")",
section->name.c_str(),
*writeIndex + (data.size() - writtenOfs),
section->size
);
memcpy(&section->data[*writeIndex], &data[writtenOfs], data.size() - writtenOfs);
*writeIndex += data.size() - writtenOfs;
}
@@ -694,11 +807,21 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
if (!data.empty())
warning(&where, lineNo, "Last 'T' line had no 'R' line (ignored)");
if (fileSections.size() < expectedNbAreas)
warning(&where, lineNo, "Expected %" PRIu32 " 'A' lines, got only %zu", expectedNbAreas,
fileSections.size());
warning(
&where,
lineNo,
"Expected %" PRIu32 " 'A' lines, got only %zu",
expectedNbAreas,
fileSections.size()
);
if (fileSymbols.size() < expectedNbSymbols)
warning(&where, lineNo, "Expected %" PRIu32 " 'S' lines, got only %zu", expectedNbSymbols,
fileSymbols.size());
warning(
&where,
lineNo,
"Expected %" PRIu32 " 'S' lines, got only %zu",
expectedNbSymbols,
fileSymbols.size()
);
nbSectionsToAssign += fileSections.size();
@@ -707,8 +830,14 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
// RAM sections can have a size, but don't get any data (they shouldn't have any)
if (entry.writeIndex != section->size && entry.writeIndex != 0)
fatal(&where, lineNo, "\"%s\" was not fully written (%" PRIu16 " < %" PRIu16 ")",
section->name.c_str(), entry.writeIndex, section->size);
fatal(
&where,
lineNo,
"\"%s\" was not fully written (%" PRIu16 " < %" PRIu16 ")",
section->name.c_str(),
entry.writeIndex,
section->size
);
sect_AddSection(*section);

View File

@@ -1,39 +1,47 @@
/* SPDX-License-Identifier: MIT */
#include "link/section.hpp"
#include <assert.h>
#include <inttypes.h>
#include <map>
#include <stdlib.h>
#include <string>
#include <string.h>
#include "link/main.hpp"
#include "link/section.hpp"
#include <string>
#include "error.hpp"
#include "linkdefs.hpp"
#include "link/main.hpp"
std::map<std::string, Section *> sections;
void sect_ForEach(void (*callback)(Section &))
{
void sect_ForEach(void (*callback)(Section &)) {
for (auto &it : sections)
callback(*it.second);
}
static void checkSectUnionCompat(Section &target, Section &other)
{
static void checkSectUnionCompat(Section &target, Section &other) {
if (other.isAddressFixed) {
if (target.isAddressFixed) {
if (target.org != other.org)
errx("Section \"%s\" is defined with conflicting addresses $%04"
PRIx16 " and $%04" PRIx16, other.name.c_str(), target.org,
other.org);
errx(
"Section \"%s\" is defined with conflicting addresses $%04" PRIx16
" and $%04" PRIx16,
other.name.c_str(),
target.org,
other.org
);
} else if (target.isAlignFixed) {
if ((other.org - target.alignOfs) & target.alignMask)
errx("Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
PRIu16 ") and address $%04" PRIx16, other.name.c_str(),
target.alignMask + 1, target.alignOfs, other.org);
errx(
"Section \"%s\" is defined with conflicting %d-byte alignment (offset %" PRIu16
") and address $%04" PRIx16,
other.name.c_str(),
target.alignMask + 1,
target.alignOfs,
other.org
);
}
target.isAddressFixed = true;
target.org = other.org;
@@ -41,17 +49,25 @@ static void checkSectUnionCompat(Section &target, Section &other)
} else if (other.isAlignFixed) {
if (target.isAddressFixed) {
if ((target.org - other.alignOfs) & other.alignMask)
errx("Section \"%s\" is defined with conflicting address $%04"
PRIx16 " and %d-byte alignment (offset %" PRIu16 ")",
other.name.c_str(), target.org, other.alignMask + 1,
other.alignOfs);
errx(
"Section \"%s\" is defined with conflicting address $%04" PRIx16
" and %d-byte alignment (offset %" PRIu16 ")",
other.name.c_str(),
target.org,
other.alignMask + 1,
other.alignOfs
);
} else if (target.isAlignFixed
&& (other.alignMask & target.alignOfs)
!= (target.alignMask & other.alignOfs)) {
errx("Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
PRIu16 ") and %d-byte alignment (offset %" PRIu16 ")",
other.name.c_str(), target.alignMask + 1, target.alignOfs,
other.alignMask + 1, other.alignOfs);
&& (other.alignMask & target.alignOfs) != (target.alignMask & other.alignOfs)) {
errx(
"Section \"%s\" is defined with conflicting %d-byte alignment (offset %" PRIu16
") and %d-byte alignment (offset %" PRIu16 ")",
other.name.c_str(),
target.alignMask + 1,
target.alignOfs,
other.alignMask + 1,
other.alignOfs
);
} else if (!target.isAlignFixed || (other.alignMask > target.alignMask)) {
target.isAlignFixed = true;
target.alignMask = other.alignMask;
@@ -59,22 +75,30 @@ static void checkSectUnionCompat(Section &target, Section &other)
}
}
static void checkFragmentCompat(Section &target, Section &other)
{
static void checkFragmentCompat(Section &target, Section &other) {
if (other.isAddressFixed) {
uint16_t org = other.org - target.size;
if (target.isAddressFixed) {
if (target.org != org)
errx("Section \"%s\" is defined with conflicting addresses $%04"
PRIx16 " and $%04" PRIx16, other.name.c_str(), target.org,
other.org);
errx(
"Section \"%s\" is defined with conflicting addresses $%04" PRIx16
" and $%04" PRIx16,
other.name.c_str(),
target.org,
other.org
);
} else if (target.isAlignFixed) {
if ((org - target.alignOfs) & target.alignMask)
errx("Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
PRIu16 ") and address $%04" PRIx16, other.name.c_str(),
target.alignMask + 1, target.alignOfs, other.org);
errx(
"Section \"%s\" is defined with conflicting %d-byte alignment (offset %" PRIu16
") and address $%04" PRIx16,
other.name.c_str(),
target.alignMask + 1,
target.alignOfs,
other.org
);
}
target.isAddressFixed = true;
target.org = org;
@@ -87,17 +111,25 @@ static void checkFragmentCompat(Section &target, Section &other)
if (target.isAddressFixed) {
if ((target.org - ofs) & other.alignMask)
errx("Section \"%s\" is defined with conflicting address $%04"
PRIx16 " and %d-byte alignment (offset %" PRIu16 ")",
other.name.c_str(), target.org, other.alignMask + 1,
other.alignOfs);
errx(
"Section \"%s\" is defined with conflicting address $%04" PRIx16
" and %d-byte alignment (offset %" PRIu16 ")",
other.name.c_str(),
target.org,
other.alignMask + 1,
other.alignOfs
);
} else if (target.isAlignFixed
&& (other.alignMask & target.alignOfs) != (target.alignMask & ofs)) {
errx("Section \"%s\" is defined with conflicting %d-byte alignment (offset %"
PRIu16 ") and %d-byte alignment (offset %" PRIu16 ")",
other.name.c_str(), target.alignMask + 1, target.alignOfs,
other.alignMask + 1, other.alignOfs);
} else if (target.isAlignFixed && (other.alignMask & target.alignOfs) != (target.alignMask & ofs)) {
errx(
"Section \"%s\" is defined with conflicting %d-byte alignment (offset %" PRIu16
") and %d-byte alignment (offset %" PRIu16 ")",
other.name.c_str(),
target.alignMask + 1,
target.alignOfs,
other.alignMask + 1,
other.alignOfs
);
} else if (!target.isAlignFixed || (other.alignMask > target.alignMask)) {
target.isAlignFixed = true;
@@ -107,22 +139,28 @@ static void checkFragmentCompat(Section &target, Section &other)
}
}
static void mergeSections(Section &target, Section &other, enum SectionModifier mod)
{
static void mergeSections(Section &target, Section &other, enum SectionModifier mod) {
// Common checks
if (target.type != other.type)
errx("Section \"%s\" is defined with conflicting types %s and %s",
other.name.c_str(), sectionTypeInfo[target.type].name.c_str(),
sectionTypeInfo[other.type].name.c_str());
errx(
"Section \"%s\" is defined with conflicting types %s and %s",
other.name.c_str(),
sectionTypeInfo[target.type].name.c_str(),
sectionTypeInfo[other.type].name.c_str()
);
if (other.isBankFixed) {
if (!target.isBankFixed) {
target.isBankFixed = true;
target.bank = other.bank;
} else if (target.bank != other.bank) {
errx("Section \"%s\" is defined with conflicting banks %" PRIu32 " and %"
PRIu32, other.name.c_str(), target.bank, other.bank);
errx(
"Section \"%s\" is defined with conflicting banks %" PRIu32 " and %" PRIu32,
other.name.c_str(),
target.bank,
other.bank
);
}
}
@@ -159,34 +197,38 @@ static void mergeSections(Section &target, Section &other, enum SectionModifier
target.nextu = &other;
}
void sect_AddSection(Section &section)
{
void sect_AddSection(Section &section) {
// Check if the section already exists
if (Section *other = sect_GetSection(section.name); other) {
if (section.modifier != other->modifier)
errx("Section \"%s\" defined as %s and %s", section.name.c_str(),
sectionModNames[section.modifier], sectionModNames[other->modifier]);
errx(
"Section \"%s\" defined as %s and %s",
section.name.c_str(),
sectionModNames[section.modifier],
sectionModNames[other->modifier]
);
else if (section.modifier == SECTION_NORMAL)
errx("Section name \"%s\" is already in use", section.name.c_str());
else
mergeSections(*other, section, section.modifier);
} else if (section.modifier == SECTION_UNION && sect_HasData(section.type)) {
errx("Section \"%s\" is of type %s, which cannot be unionized",
section.name.c_str(), sectionTypeInfo[section.type].name.c_str());
errx(
"Section \"%s\" is of type %s, which cannot be unionized",
section.name.c_str(),
sectionTypeInfo[section.type].name.c_str()
);
} else {
// If not, add it
sections[section.name] = &section;
}
}
Section *sect_GetSection(std::string const &name)
{
Section *sect_GetSection(std::string const &name) {
auto search = sections.find(name);
return search != sections.end() ? search->second : nullptr;
}
static void doSanityChecks(Section &section)
{
static void doSanityChecks(Section &section) {
// Sanity check the section's type
if (section.type < 0 || section.type >= SECTTYPE_INVALID) {
error(nullptr, 0, "Section \"%s\" has an invalid type", section.name.c_str());
@@ -195,21 +237,28 @@ static void doSanityChecks(Section &section)
if (is32kMode && section.type == SECTTYPE_ROMX) {
if (section.isBankFixed && section.bank != 1)
error(nullptr, 0, "%s: ROMX sections must be in bank 1 (if any) with option -t",
section.name.c_str());
error(
nullptr,
0,
"%s: ROMX sections must be in bank 1 (if any) with option -t",
section.name.c_str()
);
else
section.type = SECTTYPE_ROM0;
}
if (isWRAM0Mode && section.type == SECTTYPE_WRAMX) {
if (section.isBankFixed && section.bank != 1)
error(nullptr, 0, "%s: WRAMX sections must be in bank 1 with options -w or -d",
section.name.c_str());
error(
nullptr,
0,
"%s: WRAMX sections must be in bank 1 with options -w or -d",
section.name.c_str()
);
else
section.type = SECTTYPE_WRAM0;
}
if (isDmgMode && section.type == SECTTYPE_VRAM && section.bank == 1)
error(nullptr, 0, "%s: VRAM bank 1 can't be used with option -d",
section.name.c_str());
error(nullptr, 0, "%s: VRAM bank 1 can't be used with option -d", section.name.c_str());
// Check if alignment is reasonable, this is important to avoid UB
// An alignment of zero is equivalent to no alignment, basically
@@ -218,23 +267,42 @@ static void doSanityChecks(Section &section)
// Too large an alignment may not be satisfiable
if (section.isAlignFixed && (section.alignMask & sectionTypeInfo[section.type].startAddr))
error(nullptr, 0, "%s: %s sections cannot be aligned to $%04x bytes",
section.name.c_str(), sectionTypeInfo[section.type].name.c_str(),
section.alignMask + 1);
error(
nullptr,
0,
"%s: %s sections cannot be aligned to $%04x bytes",
section.name.c_str(),
sectionTypeInfo[section.type].name.c_str(),
section.alignMask + 1
);
uint32_t minbank = sectionTypeInfo[section.type].firstBank, maxbank = sectionTypeInfo[section.type].lastBank;
uint32_t minbank = sectionTypeInfo[section.type].firstBank,
maxbank = sectionTypeInfo[section.type].lastBank;
if (section.isBankFixed && section.bank < minbank && section.bank > maxbank)
error(nullptr, 0, minbank == maxbank
error(
nullptr,
0,
minbank == maxbank
? "Cannot place section \"%s\" in bank %" PRIu32 ", it must be %" PRIu32
: "Cannot place section \"%s\" in bank %" PRIu32 ", it must be between %" PRIu32 " and %" PRIu32,
section.name.c_str(), section.bank, minbank, maxbank);
: "Cannot place section \"%s\" in bank %" PRIu32 ", it must be between %" PRIu32
" and %" PRIu32,
section.name.c_str(),
section.bank,
minbank,
maxbank
);
// Check if section has a chance to be placed
if (section.size > sectionTypeInfo[section.type].size)
error(nullptr, 0, "Section \"%s\" is bigger than the max size for that type: $%"
PRIx16 " > $%" PRIx16,
section.name.c_str(), section.size, sectionTypeInfo[section.type].size);
error(
nullptr,
0,
"Section \"%s\" is bigger than the max size for that type: $%" PRIx16 " > $%" PRIx16,
section.name.c_str(),
section.size,
sectionTypeInfo[section.type].size
);
// Translate loose constraints to strong ones when they're equivalent
@@ -247,26 +315,41 @@ static void doSanityChecks(Section &section)
// It doesn't make sense to have both org and alignment set
if (section.isAlignFixed) {
if ((section.org & section.alignMask) != section.alignOfs)
error(nullptr, 0, "Section \"%s\"'s fixed address doesn't match its alignment",
section.name.c_str());
error(
nullptr,
0,
"Section \"%s\"'s fixed address doesn't match its alignment",
section.name.c_str()
);
section.isAlignFixed = false;
}
// Ensure the target address is valid
if (section.org < sectionTypeInfo[section.type].startAddr
|| section.org > endaddr(section.type))
error(nullptr, 0, "Section \"%s\"'s fixed address $%04" PRIx16 " is outside of range [$%04"
PRIx16 "; $%04" PRIx16 "]", section.name.c_str(), section.org,
sectionTypeInfo[section.type].startAddr, endaddr(section.type));
error(
nullptr,
0,
"Section \"%s\"'s fixed address $%04" PRIx16 " is outside of range [$%04" PRIx16
"; $%04" PRIx16 "]",
section.name.c_str(),
section.org,
sectionTypeInfo[section.type].startAddr,
endaddr(section.type)
);
if (section.org + section.size > endaddr(section.type) + 1)
error(nullptr, 0, "Section \"%s\"'s end address $%04x is greater than last address $%04x",
section.name.c_str(), section.org + section.size,
endaddr(section.type) + 1);
error(
nullptr,
0,
"Section \"%s\"'s end address $%04x is greater than last address $%04x",
section.name.c_str(),
section.org + section.size,
endaddr(section.type) + 1
);
}
}
void sect_DoSanityChecks()
{
void sect_DoSanityChecks() {
sect_ForEach(doSanityChecks);
}

View File

@@ -1,35 +1,33 @@
/* SPDX-License-Identifier: MIT */
#include "link/symbol.hpp"
#include <inttypes.h>
#include <map>
#include <stdlib.h>
#include <string>
#include <variant>
#include "link/object.hpp"
#include "link/section.hpp"
#include "link/symbol.hpp"
#include "link/main.hpp"
#include "error.hpp"
#include "helpers.hpp"
#include "link/main.hpp"
#include "link/object.hpp"
#include "link/section.hpp"
std::map<std::string, Symbol *> symbols;
Label &Symbol::label()
{
Label &Symbol::label() {
assert(std::holds_alternative<Label>(data));
return std::get<Label>(data);
}
Label const &Symbol::label() const
{
Label const &Symbol::label() const {
assert(std::holds_alternative<Label>(data));
return std::get<Label>(data);
}
void sym_AddSymbol(Symbol &symbol)
{
void sym_AddSymbol(Symbol &symbol) {
// Check if the symbol already exists
if (Symbol *other = sym_GetSymbol(symbol.name); other) {
fprintf(stderr, "error: \"%s\" both in %s from ", symbol.name.c_str(), symbol.objFileName);
@@ -44,8 +42,7 @@ void sym_AddSymbol(Symbol &symbol)
symbols[symbol.name] = &symbol;
}
Symbol *sym_GetSymbol(std::string const &name)
{
Symbol *sym_GetSymbol(std::string const &name) {
auto search = symbols.find(name);
return search != symbols.end() ? search->second : nullptr;
}

View File

@@ -1,63 +1,64 @@
/* SPDX-License-Identifier: MIT */
#include "linkdefs.hpp"
#include "platform.hpp"
using namespace std::literals;
// The default values are the most lax, as they are used as-is by RGBASM; only RGBLINK has the full info,
// so RGBASM's job is only to catch unconditional errors earlier.
// The default values are the most lax, as they are used as-is by RGBASM; only RGBLINK has the full
// info, so RGBASM's job is only to catch unconditional errors earlier.
SectionTypeInfo sectionTypeInfo[SECTTYPE_INVALID] = {
{ // SECTTYPE_WRAM0
{
.name = "WRAM0"s,
.startAddr = 0xC000,
.size = 0x2000, // Patched to 0x1000 if !isWRAM0Mode
.size = 0x2000 /* Patched to 0x1000 if !isWRAM0Mode */,
.firstBank = 0,
.lastBank = 0,
},
{ // SECTTYPE_VRAM
{
.name = "VRAM"s,
.startAddr = 0x8000,
.size = 0x2000,
.firstBank = 0,
.lastBank = 1, // Patched to 0 if isDmgMode
.lastBank = 1 /* Patched to 0 if isDmgMode */,
},
{ // SECTTYPE_ROMX
{
.name = "ROMX"s,
.startAddr = 0x4000,
.size = 0x4000,
.firstBank = 1,
.lastBank = 65535,
},
{ // SECTTYPE_ROM0
{
.name = "ROM0"s,
.startAddr = 0x0000,
.size = 0x8000, // Patched to 0x4000 if !is32kMode
.size = 0x8000 /* Patched to 0x4000 if !is32kMode */,
.firstBank = 0,
.lastBank = 0,
},
{ // SECTTYPE_HRAM
{
.name = "HRAM"s,
.startAddr = 0xFF80,
.size = 0x007F,
.firstBank = 0,
.lastBank = 0,
},
{ // SECTTYPE_WRAMX
{
.name = "WRAMX"s,
.startAddr = 0xD000,
.size = 0x1000,
.firstBank = 1,
.lastBank = 7,
},
{ // SECTTYPE_SRAM
{
.name = "SRAM"s,
.startAddr = 0xA000,
.size = 0x2000,
.firstBank = 0,
.lastBank = 255,
},
{ // SECTTYPE_OAM
{
.name = "OAM"s,
.startAddr = 0xFE00,
.size = 0x00A0,

View File

@@ -2,19 +2,17 @@
// Mathematical operators that don't reuse C++'s behavior
#include <stdint.h>
#include "opmath.hpp"
int32_t op_divide(int32_t dividend, int32_t divisor)
{
#include <stdint.h>
int32_t op_divide(int32_t dividend, int32_t divisor) {
// Adjust division to floor toward negative infinity,
// not truncate toward zero
return dividend / divisor - ((dividend % divisor < 0) != (divisor < 0));
}
int32_t op_modulo(int32_t dividend, int32_t divisor)
{
int32_t op_modulo(int32_t dividend, int32_t divisor) {
int32_t remainder = dividend % divisor;
// Adjust modulo to have the sign of the divisor,
@@ -22,8 +20,7 @@ int32_t op_modulo(int32_t dividend, int32_t divisor)
return remainder + divisor * ((remainder < 0) != (divisor < 0));
}
int32_t op_exponent(int32_t base, uint32_t power)
{
int32_t op_exponent(int32_t base, uint32_t power) {
int32_t result = 1;
for (;;) {
@@ -38,8 +35,7 @@ int32_t op_exponent(int32_t base, uint32_t power)
return result;
}
int32_t op_shift_left(int32_t value, int32_t amount)
{
int32_t op_shift_left(int32_t value, int32_t amount) {
// Get the easy cases out of the way
if (amount == 0)
return value;
@@ -55,8 +51,7 @@ int32_t op_shift_left(int32_t value, int32_t amount)
return (uint32_t)value << amount;
}
int32_t op_shift_right(int32_t value, int32_t amount)
{
int32_t op_shift_right(int32_t value, int32_t amount) {
// Repeat the easy cases here to avoid INT_MIN funny business
if (amount == 0)
return value;
@@ -79,8 +74,7 @@ int32_t op_shift_right(int32_t value, int32_t amount)
return ((uint32_t)value >> amount) | amount_high_bits;
}
int32_t op_shift_right_unsigned(int32_t value, int32_t amount)
{
int32_t op_shift_right_unsigned(int32_t value, int32_t amount) {
// Repeat the easy cases here to avoid INT_MIN funny business
if (amount == 0)
return value;

View File

@@ -1,16 +1,15 @@
/* SPDX-License-Identifier: MIT */
#include "util.hpp"
#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <vector>
#include "util.hpp"
#include "extern/utf8decoder.hpp"
char const *printChar(int c)
{
char const *printChar(int c) {
// "'A'" + '\0': 4 bytes
// "'\\n'" + '\0': 5 bytes
// "0xFF" + '\0': 5 bytes
@@ -51,8 +50,7 @@ char const *printChar(int c)
return buf;
}
size_t readUTF8Char(std::vector<uint8_t> *dest, char const *src)
{
size_t readUTF8Char(std::vector<uint8_t> *dest, char const *src) {
uint32_t state = 0;
uint32_t codep;
size_t i = 0;

View File

@@ -1,10 +1,11 @@
/* SPDX-License-Identifier: MIT */
#include "version.hpp"
#include <stdio.h>
#include <string.h>
#include "helpers.hpp"
#include "version.hpp"
// This variable is passed via `-D` from the Makefile, but not from CMake
// (in which `configure_file()` is used on this file to replace some syntax)
@@ -13,20 +14,16 @@
#define BUILD_VERSION_STRING "@GIT_REV@"
#endif
char const *get_package_version_string()
{
char const *get_package_version_string() {
// The following conditional should be simplified by the compiler.
if (strlen(BUILD_VERSION_STRING) == 0) {
// Fallback if version string can't be obtained from Git
#ifndef PACKAGE_VERSION_RC
return "v" EXPAND_AND_STR(PACKAGE_VERSION_MAJOR)
"." EXPAND_AND_STR(PACKAGE_VERSION_MINOR)
"." EXPAND_AND_STR(PACKAGE_VERSION_PATCH);
return "v" EXPAND_AND_STR(PACKAGE_VERSION_MAJOR) "." EXPAND_AND_STR(PACKAGE_VERSION_MINOR
) "." EXPAND_AND_STR(PACKAGE_VERSION_PATCH);
#else
return "v" EXPAND_AND_STR(PACKAGE_VERSION_MAJOR)
"." EXPAND_AND_STR(PACKAGE_VERSION_MINOR)
"." EXPAND_AND_STR(PACKAGE_VERSION_PATCH)
"-rc" EXPAND_AND_STR(PACKAGE_VERSION_RC);
return "v" EXPAND_AND_STR(PACKAGE_VERSION_MAJOR) "." EXPAND_AND_STR(PACKAGE_VERSION_MINOR
) "." EXPAND_AND_STR(PACKAGE_VERSION_PATCH) "-rc" EXPAND_AND_STR(PACKAGE_VERSION_RC);
#endif
} else {
return BUILD_VERSION_STRING;

View File

@@ -15,8 +15,8 @@
#include <png.h>
#include <stdint.h>
#include <stdio.h>
#include <string>
#include <string.h>
#include <string>
#include <vector>
struct Attributes {
@@ -70,8 +70,9 @@ static void generate_tile_attributes(Attributes *attributes) {
// Use an array to look up the number of colors in the palette; this is faster (and simpler)
// than doing a population count over the bits
static char const popcount[] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3,
4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4};
static char const popcount[] = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4,
};
attributes->nbColors = popcount[pal];
}
@@ -104,8 +105,7 @@ static void generate_tile_data(unsigned char tiledata[8][8], unsigned colorcount
}
}
static void
copy_tile_data(unsigned char destination[8][8], unsigned char const source[8][8]) {
static void copy_tile_data(unsigned char destination[8][8], unsigned char const source[8][8]) {
// Apply a random rotation to the copy
// coord ^ 7 = inverted coordinate; coord ^ 0 = regular coordinate
unsigned xmask = getRandomBits(1) * 7;
@@ -148,9 +148,14 @@ static uint8_t _5to8(uint8_t five) {
}
// Can't mark as `const`, as the array type is otherwise not compatible (augh)
static void write_image(char const *filename, uint16_t /* const */ palettes[/* 60 */][4],
unsigned char /* const */ (*tileData)[8][8], Attributes const *attributes,
uint8_t width, uint8_t height) {
static void write_image(
char const *filename,
uint16_t /* const */ palettes[/* 60 */][4],
unsigned char /* const */ (*tileData)[8][8],
Attributes const *attributes,
uint8_t width,
uint8_t height
) {
uint8_t const nbTiles = width * height;
png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
png_infop pngInfo = png_create_info_struct(png);
@@ -167,9 +172,17 @@ static void write_image(char const *filename, uint16_t /* const */ palettes[/* 6
}
png_init_io(png, file);
png_set_IHDR(png, pngInfo, width * 8, height * 8, 8, PNG_COLOR_TYPE_RGB_ALPHA,
png_set_IHDR(
png,
pngInfo,
width * 8,
height * 8,
8,
PNG_COLOR_TYPE_RGB_ALPHA,
getRandomBits(1) ? PNG_INTERLACE_NONE : PNG_INTERLACE_ADAM7,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
PNG_COMPRESSION_TYPE_DEFAULT,
PNG_FILTER_TYPE_DEFAULT
);
// While it would be nice to write the image little by little, I really don't want to handle
// interlacing myself. (We're doing interlacing to test that RGBGFX correctly handles it.)

View File

@@ -111,10 +111,13 @@ class Png {
std::streamsize nbBytesRead = self->file.sgetn(reinterpret_cast<char *>(data), expectedLen);
if (nbBytesRead != expectedLen) {
fatal("Error reading input image (\"%s\"): file too short (expected at least %zd more "
fatal(
"Error reading input image (\"%s\"): file too short (expected at least %zd more "
"bytes after reading %zu)",
self->path.c_str(), length - nbBytesRead,
(size_t)self->file.pubseekoff(0, std::ios_base::cur));
self->path.c_str(),
length - nbBytesRead,
(size_t)self->file.pubseekoff(0, std::ios_base::cur)
);
}
}
@@ -149,8 +152,9 @@ public:
fatal("Input file (\"%s\") is not a PNG image!", path.c_str());
}
png = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)this, handleError,
handleWarning);
png = png_create_read_struct(
PNG_LIBPNG_VER_STRING, (png_voidp)this, handleError, handleWarning
);
if (!png) {
fatal("Failed to allocate PNG structure: %s", strerror(errno));
}
@@ -174,8 +178,9 @@ public:
int bitDepth, interlaceType; //, compressionType, filterMethod;
png_get_IHDR(png, info, &width, &height, &bitDepth, &colorType, &interlaceType, nullptr,
nullptr);
png_get_IHDR(
png, info, &width, &height, &bitDepth, &colorType, &interlaceType, nullptr, nullptr
);
if (width % 8 != 0) {
fatal("Image width (%" PRIu32 " pixels) is not a multiple of 8!", width);
@@ -308,19 +313,36 @@ static char *execProg(char const *name, char * const *argv) {
fatal("Error waiting for %s: %s", name, strerror(errno));
} else if (info.si_code != CLD_EXITED) {
assert(info.si_code == CLD_KILLED || info.si_code == CLD_DUMPED);
fatal("%s was terminated by signal %s%s\n\tThe command was: [%s]", name, strsignal(info.si_status),
info.si_code == CLD_DUMPED ? " (core dumped)" : "", formatArgv());
fatal(
"%s was terminated by signal %s%s\n\tThe command was: [%s]",
name,
strsignal(info.si_status),
info.si_code == CLD_DUMPED ? " (core dumped)" : "",
formatArgv()
);
} else if (info.si_status != 0) {
fatal("%s returned with status %d\n\tThe command was: [%s]", name, info.si_status, formatArgv());
fatal(
"%s returned with status %d\n\tThe command was: [%s]",
name,
info.si_status,
formatArgv()
);
}
#else // defined(_MSC_VER) || defined(__MINGW32__)
auto winStrerror = [](DWORD errnum) {
LPTSTR buf;
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
if (FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_MAX_WIDTH_MASK,
nullptr, errnum, 0, (LPTSTR)&buf, 0, nullptr)
nullptr,
errnum,
0,
(LPTSTR)&buf,
0,
nullptr
)
== 0) {
fatal("Failed to get error message for error 0x%x", errnum);
}
@@ -341,7 +363,8 @@ static char *execProg(char const *name, char * const *argv) {
STARTUPINFOA startupInfo;
GetStartupInfoA(&startupInfo);
STARTUPINFOA childStartupInfo{sizeof(startupInfo),
STARTUPINFOA childStartupInfo{
sizeof(startupInfo),
nullptr,
nullptr,
nullptr,
@@ -358,11 +381,13 @@ static char *execProg(char const *name, char * const *argv) {
nullptr,
0,
0,
0};
0,
};
PROCESS_INFORMATION child;
if (CreateProcessA(nullptr, cmdLine, nullptr, nullptr, true, 0, nullptr, nullptr,
&childStartupInfo, &child)
if (CreateProcessA(
nullptr, cmdLine, nullptr, nullptr, true, 0, nullptr, nullptr, &childStartupInfo, &child
)
== 0) {
return winStrerror(GetLastError());
}
@@ -395,8 +420,9 @@ int main(int argc, char *argv[]) {
char *args[] = {path, argv[1], file, nullptr};
if (auto ret = execProg("randtilegen", args); ret != nullptr) {
fatal("Failed to execute ./randtilegen (%s). Is it in the current working directory?",
ret);
fatal(
"Failed to execute ./randtilegen (%s). Is it in the current working directory?", ret
);
}
}
@@ -405,7 +431,8 @@ int main(int argc, char *argv[]) {
pal_opt[] = "-p", pal_file[] = "result.pal", attr_opt[] = "-a",
attr_file[] = "result.attrmap", in_file[] = "out0.png";
std::vector<char *> args(
{path, out_opt, out_file, pal_opt, pal_file, attr_opt, attr_file, in_file});
{path, out_opt, out_file, pal_opt, pal_file, attr_opt, attr_file, in_file}
);
// Also copy the trailing `nullptr`
std::copy_n(&argv[2], argc - 1, std::back_inserter(args));
@@ -422,8 +449,17 @@ int main(int argc, char *argv[]) {
attr_opt[] = "-a", attr_file[] = "result.attrmap", in_file[] = "result.png";
auto width_string = std::to_string(image0.getWidth() / 8);
std::vector<char *> args = {
path, reverse_opt, width_string.data(), out_opt, out_file, pal_opt,
pal_file, attr_opt, attr_file, in_file};
path,
reverse_opt,
width_string.data(),
out_opt,
out_file,
pal_opt,
pal_file,
attr_opt,
attr_file,
in_file,
};
// Also copy the trailing `nullptr`
std::copy_n(&argv[2], argc - 1, std::back_inserter(args));
@@ -456,10 +492,20 @@ int main(int argc, char *argv[]) {
};
if (cgbColor(px0) != cgbColor(px1)) {
error("Color mismatch at (%" PRIu32 ", %" PRIu32
error(
"Color mismatch at (%" PRIu32 ", %" PRIu32
"): (%u,%u,%u,%u) became (%u,%u,%u,%u) after round-tripping",
x, y, px0.red, px0.green, px0.blue, px0.alpha, px1.red, px1.green, px1.blue,
px1.alpha);
x,
y,
px0.red,
px0.green,
px0.blue,
px0.alpha,
px1.red,
px1.green,
px1.blue,
px1.alpha
);
}
}
}