diff --git a/include/cli.hpp b/include/cli.hpp index 7b3343a5..6a5c30d2 100644 --- a/include/cli.hpp +++ b/include/cli.hpp @@ -1,21 +1,21 @@ -// SPDX-License-Identifier: MIT - -#ifndef RGBDS_CLI_HPP -#define RGBDS_CLI_HPP - -#include -#include - -#include "extern/getopt.hpp" // option -#include "usage.hpp" - -void cli_ParseArgs( - int argc, - char *argv[], - char const *shortOpts, - option const *longOpts, - void (*parseArg)(int, char *), - Usage usage -); - -#endif // RGBDS_CLI_HPP +// SPDX-License-Identifier: MIT + +#ifndef RGBDS_CLI_HPP +#define RGBDS_CLI_HPP + +#include +#include + +#include "extern/getopt.hpp" // option +#include "usage.hpp" + +void cli_ParseArgs( + int argc, + char *argv[], + char const *shortOpts, + option const *longOpts, + void (*parseArg)(int, char *), + Usage usage +); + +#endif // RGBDS_CLI_HPP diff --git a/include/gfx/flip.hpp b/include/gfx/flip.hpp index 5ede0fb7..3ccfb1fd 100644 --- a/include/gfx/flip.hpp +++ b/include/gfx/flip.hpp @@ -1,23 +1,23 @@ -// SPDX-License-Identifier: MIT - -#ifndef RGBDS_GFX_FLIP_HPP -#define RGBDS_GFX_FLIP_HPP - -#include -#include - -// Flipping tends to happen fairly often, so take a bite out of dcache to speed it up -static std::array flipTable = ([]() constexpr { - std::array table{}; - for (uint16_t i = 0; i < table.size(); ++i) { - // To flip all the bits, we'll flip both nibbles, then each nibble half, etc. - uint16_t byte = i; - byte = (byte & 0b0000'1111) << 4 | (byte & 0b1111'0000) >> 4; - byte = (byte & 0b0011'0011) << 2 | (byte & 0b1100'1100) >> 2; - byte = (byte & 0b0101'0101) << 1 | (byte & 0b1010'1010) >> 1; - table[i] = byte; - } - return table; -})(); - -#endif // RGBDS_GFX_FLIP_HPP +// SPDX-License-Identifier: MIT + +#ifndef RGBDS_GFX_FLIP_HPP +#define RGBDS_GFX_FLIP_HPP + +#include +#include + +// Flipping tends to happen fairly often, so take a bite out of dcache to speed it up +static std::array flipTable = ([]() constexpr { + std::array table{}; + for (uint16_t i = 0; i < table.size(); ++i) { + // To flip all the bits, we'll flip both nibbles, then each nibble half, etc. + uint16_t byte = i; + byte = (byte & 0b0000'1111) << 4 | (byte & 0b1111'0000) >> 4; + byte = (byte & 0b0011'0011) << 2 | (byte & 0b1100'1100) >> 2; + byte = (byte & 0b0101'0101) << 1 | (byte & 0b1010'1010) >> 1; + table[i] = byte; + } + return table; +})(); + +#endif // RGBDS_GFX_FLIP_HPP diff --git a/include/gfx/palette.hpp b/include/gfx/palette.hpp index 2b31db70..4b7e883e 100644 --- a/include/gfx/palette.hpp +++ b/include/gfx/palette.hpp @@ -1,27 +1,27 @@ -// SPDX-License-Identifier: MIT - -#ifndef RGBDS_GFX_PALETTE_HPP -#define RGBDS_GFX_PALETTE_HPP - -#include -#include -#include - -struct Palette { - // An array of 4 GBC-native (RGB555) colors - std::array colors{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX}; - - void addColor(uint16_t color); - uint8_t indexOf(uint16_t color) const; - uint16_t &operator[](size_t index) { return colors[index]; } - uint16_t const &operator[](size_t index) const { return colors[index]; } - - decltype(colors)::iterator begin(); - decltype(colors)::iterator end(); - decltype(colors)::const_iterator begin() const; - decltype(colors)::const_iterator end() const; - - uint8_t size() const; -}; - -#endif // RGBDS_GFX_PALETTE_HPP +// SPDX-License-Identifier: MIT + +#ifndef RGBDS_GFX_PALETTE_HPP +#define RGBDS_GFX_PALETTE_HPP + +#include +#include +#include + +struct Palette { + // An array of 4 GBC-native (RGB555) colors + std::array colors{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX}; + + void addColor(uint16_t color); + uint8_t indexOf(uint16_t color) const; + uint16_t &operator[](size_t index) { return colors[index]; } + uint16_t const &operator[](size_t index) const { return colors[index]; } + + decltype(colors)::iterator begin(); + decltype(colors)::iterator end(); + decltype(colors)::const_iterator begin() const; + decltype(colors)::const_iterator end() const; + + uint8_t size() const; +}; + +#endif // RGBDS_GFX_PALETTE_HPP diff --git a/src/cli.cpp b/src/cli.cpp index 61634e13..238ad70d 100644 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -1,159 +1,159 @@ -// SPDX-License-Identifier: MIT - -#include "cli.hpp" - -#include -#include -#include -#include -#include -#include - -#include "extern/getopt.hpp" -#include "style.hpp" -#include "usage.hpp" -#include "util.hpp" // isBlankSpace - -using namespace std::literals; - -// Turn an at-file's contents into an argv that `getopt` can handle, appending them to `argPool`. -static std::vector - readAtFile(std::string const &path, std::vector &argPool, Usage usage) { - std::vector argvOfs; - - std::filebuf file; - if (!file.open(path, std::ios_base::in)) { - int errnum = errno; - style_Set(stderr, STYLE_RED, true); - fputs("FATAL: ", stderr); - style_Reset(stderr); - fprintf(stderr, "Failed to open at-file \"%s\": %s\n", path.c_str(), strerror(errnum)); - usage.printAndExit(1); - } - - for (;;) { - int c = file.sbumpc(); - - // First, discard any leading blank space - while (isBlankSpace(c)) { - c = file.sbumpc(); - } - - // If it's a comment, discard everything until EOL - if (c == '#') { - c = file.sbumpc(); - while (c != EOF && !isNewline(c)) { - c = file.sbumpc(); - } - } - - if (c == EOF) { - return argvOfs; - } else if (isNewline(c)) { - continue; // Start processing the next line - } - - // Alright, now we can parse the line - do { - argvOfs.push_back(argPool.size()); - - // Read one argument (until the next whitespace char). - // We know there is one because we already have its first character in `c`. - for (; c != EOF && !isWhitespace(c); c = file.sbumpc()) { - argPool.push_back(c); - } - argPool.push_back('\0'); - - // Discard blank space until the next argument (candidate) - while (isBlankSpace(c)) { - c = file.sbumpc(); - } - } while (c != EOF && !isNewline(c)); // End if we reached EOL - } -} - -void cli_ParseArgs( - int argc, - char *argv[], - char const *shortOpts, - option const *longOpts, - void (*parseArg)(int, char *), - Usage usage -) { - struct AtFileStackEntry { - int parentInd; // Saved offset into parent argv - std::vector argv; // This context's arg pointer vec - - AtFileStackEntry(int parentInd_, std::vector argv_) - : parentInd(parentInd_), argv(argv_) {} - }; - std::vector atFileStack; - - int curArgc = argc; - char **curArgv = argv; - std::string optString = "-"s + shortOpts; // Request position arguments with a leading '-' - std::vector> argPools; - - for (;;) { - char *atFileName = nullptr; - for (int ch; - (ch = musl_getopt_long_only(curArgc, curArgv, optString.c_str(), longOpts)) != -1;) { - if (ch == 1 && musl_optarg[0] == '@') { - atFileName = &musl_optarg[1]; - break; - } else { - parseArg(ch, musl_optarg); - } - } - - if (atFileName) { - // We need to allocate a new arg pool for each at-file, so as not to invalidate pointers - // previous at-files may have generated to their own arg pools. - // But for the same reason, the arg pool must also outlive the at-file's stack entry! - std::vector &argPool = argPools.emplace_back(); - - // Copy `argv[0]` for error reporting, and because option parsing skips it - AtFileStackEntry &stackEntry = - atFileStack.emplace_back(musl_optind, std::vector{atFileName}); - - // It would be nice to compute the char pointers on the fly, but reallocs don't allow - // that; so we must compute the offsets after the pool is fixed - std::vector offsets = readAtFile(&musl_optarg[1], argPool, usage); - stackEntry.argv.reserve(offsets.size() + 2); // Avoid a bunch of reallocs - for (size_t ofs : offsets) { - stackEntry.argv.push_back(&argPool.data()[ofs]); - } - stackEntry.argv.push_back(nullptr); // Don't forget the arg vector terminator! - - curArgc = stackEntry.argv.size() - 1; - curArgv = stackEntry.argv.data(); - musl_optind = 1; // Don't use 0 because we're not scanning a different argv per se - } else { - if (musl_optind != curArgc) { - // This happens if `--` is passed, process the remaining arg(s) as positional - assume(musl_optind < curArgc); - for (int i = musl_optind; i < curArgc; ++i) { - parseArg(1, argv[i]); // Positional argument - } - } - - // Pop off the top stack entry, or end parsing if none - if (atFileStack.empty()) { - break; - } - - // OK to restore `optind` directly, because `optpos` must be 0 right now. - // (Providing 0 would be a "proper" reset, but we want to resume parsing) - musl_optind = atFileStack.back().parentInd; - atFileStack.pop_back(); - if (atFileStack.empty()) { - curArgc = argc; - curArgv = argv; - } else { - std::vector &vec = atFileStack.back().argv; - curArgc = vec.size(); - curArgv = vec.data(); - } - } - } -} +// SPDX-License-Identifier: MIT + +#include "cli.hpp" + +#include +#include +#include +#include +#include +#include + +#include "extern/getopt.hpp" +#include "style.hpp" +#include "usage.hpp" +#include "util.hpp" // isBlankSpace + +using namespace std::literals; + +// Turn an at-file's contents into an argv that `getopt` can handle, appending them to `argPool`. +static std::vector + readAtFile(std::string const &path, std::vector &argPool, Usage usage) { + std::vector argvOfs; + + std::filebuf file; + if (!file.open(path, std::ios_base::in)) { + int errnum = errno; + style_Set(stderr, STYLE_RED, true); + fputs("FATAL: ", stderr); + style_Reset(stderr); + fprintf(stderr, "Failed to open at-file \"%s\": %s\n", path.c_str(), strerror(errnum)); + usage.printAndExit(1); + } + + for (;;) { + int c = file.sbumpc(); + + // First, discard any leading blank space + while (isBlankSpace(c)) { + c = file.sbumpc(); + } + + // If it's a comment, discard everything until EOL + if (c == '#') { + c = file.sbumpc(); + while (c != EOF && !isNewline(c)) { + c = file.sbumpc(); + } + } + + if (c == EOF) { + return argvOfs; + } else if (isNewline(c)) { + continue; // Start processing the next line + } + + // Alright, now we can parse the line + do { + argvOfs.push_back(argPool.size()); + + // Read one argument (until the next whitespace char). + // We know there is one because we already have its first character in `c`. + for (; c != EOF && !isWhitespace(c); c = file.sbumpc()) { + argPool.push_back(c); + } + argPool.push_back('\0'); + + // Discard blank space until the next argument (candidate) + while (isBlankSpace(c)) { + c = file.sbumpc(); + } + } while (c != EOF && !isNewline(c)); // End if we reached EOL + } +} + +void cli_ParseArgs( + int argc, + char *argv[], + char const *shortOpts, + option const *longOpts, + void (*parseArg)(int, char *), + Usage usage +) { + struct AtFileStackEntry { + int parentInd; // Saved offset into parent argv + std::vector argv; // This context's arg pointer vec + + AtFileStackEntry(int parentInd_, std::vector argv_) + : parentInd(parentInd_), argv(argv_) {} + }; + std::vector atFileStack; + + int curArgc = argc; + char **curArgv = argv; + std::string optString = "-"s + shortOpts; // Request position arguments with a leading '-' + std::vector> argPools; + + for (;;) { + char *atFileName = nullptr; + for (int ch; + (ch = musl_getopt_long_only(curArgc, curArgv, optString.c_str(), longOpts)) != -1;) { + if (ch == 1 && musl_optarg[0] == '@') { + atFileName = &musl_optarg[1]; + break; + } else { + parseArg(ch, musl_optarg); + } + } + + if (atFileName) { + // We need to allocate a new arg pool for each at-file, so as not to invalidate pointers + // previous at-files may have generated to their own arg pools. + // But for the same reason, the arg pool must also outlive the at-file's stack entry! + std::vector &argPool = argPools.emplace_back(); + + // Copy `argv[0]` for error reporting, and because option parsing skips it + AtFileStackEntry &stackEntry = + atFileStack.emplace_back(musl_optind, std::vector{atFileName}); + + // It would be nice to compute the char pointers on the fly, but reallocs don't allow + // that; so we must compute the offsets after the pool is fixed + std::vector offsets = readAtFile(&musl_optarg[1], argPool, usage); + stackEntry.argv.reserve(offsets.size() + 2); // Avoid a bunch of reallocs + for (size_t ofs : offsets) { + stackEntry.argv.push_back(&argPool.data()[ofs]); + } + stackEntry.argv.push_back(nullptr); // Don't forget the arg vector terminator! + + curArgc = stackEntry.argv.size() - 1; + curArgv = stackEntry.argv.data(); + musl_optind = 1; // Don't use 0 because we're not scanning a different argv per se + } else { + if (musl_optind != curArgc) { + // This happens if `--` is passed, process the remaining arg(s) as positional + assume(musl_optind < curArgc); + for (int i = musl_optind; i < curArgc; ++i) { + parseArg(1, argv[i]); // Positional argument + } + } + + // Pop off the top stack entry, or end parsing if none + if (atFileStack.empty()) { + break; + } + + // OK to restore `optind` directly, because `optpos` must be 0 right now. + // (Providing 0 would be a "proper" reset, but we want to resume parsing) + musl_optind = atFileStack.back().parentInd; + atFileStack.pop_back(); + if (atFileStack.empty()) { + curArgc = argc; + curArgv = argv; + } else { + std::vector &vec = atFileStack.back().argv; + curArgc = vec.size(); + curArgv = vec.data(); + } + } + } +} diff --git a/src/gfx/palette.cpp b/src/gfx/palette.cpp index dd6e9c67..deb1b809 100644 --- a/src/gfx/palette.cpp +++ b/src/gfx/palette.cpp @@ -1,55 +1,55 @@ -// SPDX-License-Identifier: MIT - -#include "gfx/palette.hpp" - -#include -#include - -#include "helpers.hpp" - -#include "gfx/main.hpp" -#include "gfx/rgba.hpp" - -void Palette::addColor(uint16_t color) { - for (size_t i = 0; true; ++i) { - assume(i < colors.size()); // The packing should guarantee this - if (colors[i] == color) { // The color is already present - break; - } else if (colors[i] == UINT16_MAX) { // Empty slot - colors[i] = color; - break; - } - } -} - -// Returns the ID of the color in the palette, or `size()` if the color is not in -uint8_t Palette::indexOf(uint16_t color) const { - return color == Rgba::transparent - ? 0 - : std::find(begin(), colors.end(), color) - begin() + options.hasTransparentPixels; -} - -auto Palette::begin() -> decltype(colors)::iterator { - // Skip the first slot if reserved for transparency - return colors.begin() + options.hasTransparentPixels; -} - -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(RRANGE(colors), [](uint16_t c) { return c != UINT16_MAX; }).base(); -} - -auto Palette::begin() const -> decltype(colors)::const_iterator { - // Same as the non-const begin(). - return colors.begin() + options.hasTransparentPixels; -} - -auto Palette::end() const -> decltype(colors)::const_iterator { - // Same as the non-const end(). - return std::find_if(RRANGE(colors), [](uint16_t c) { return c != UINT16_MAX; }).base(); -} - -uint8_t Palette::size() const { - return end() - colors.begin(); -} +// SPDX-License-Identifier: MIT + +#include "gfx/palette.hpp" + +#include +#include + +#include "helpers.hpp" + +#include "gfx/main.hpp" +#include "gfx/rgba.hpp" + +void Palette::addColor(uint16_t color) { + for (size_t i = 0; true; ++i) { + assume(i < colors.size()); // The packing should guarantee this + if (colors[i] == color) { // The color is already present + break; + } else if (colors[i] == UINT16_MAX) { // Empty slot + colors[i] = color; + break; + } + } +} + +// Returns the ID of the color in the palette, or `size()` if the color is not in +uint8_t Palette::indexOf(uint16_t color) const { + return color == Rgba::transparent + ? 0 + : std::find(begin(), colors.end(), color) - begin() + options.hasTransparentPixels; +} + +auto Palette::begin() -> decltype(colors)::iterator { + // Skip the first slot if reserved for transparency + return colors.begin() + options.hasTransparentPixels; +} + +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(RRANGE(colors), [](uint16_t c) { return c != UINT16_MAX; }).base(); +} + +auto Palette::begin() const -> decltype(colors)::const_iterator { + // Same as the non-const begin(). + return colors.begin() + options.hasTransparentPixels; +} + +auto Palette::end() const -> decltype(colors)::const_iterator { + // Same as the non-const end(). + return std::find_if(RRANGE(colors), [](uint16_t c) { return c != UINT16_MAX; }).base(); +} + +uint8_t Palette::size() const { + return end() - colors.begin(); +} diff --git a/test/asm/.gitattributes b/test/asm/.gitattributes new file mode 100644 index 00000000..39d6b733 --- /dev/null +++ b/test/asm/.gitattributes @@ -0,0 +1,4 @@ +# Some files test CRLF line endings +character-escapes.out -text diff +crlf.asm text eol=crlf +string-literal-macro-arg.out -text diff diff --git a/test/asm/crlf.asm b/test/asm/crlf.asm index ea07f197..55aa9f39 100644 --- a/test/asm/crlf.asm +++ b/test/asm/crlf.asm @@ -8,6 +8,15 @@ assert !strcmp("{s}", "Hello, world!") * block comment */ -DEF t EQUS """Hello, +REPT 2 + REDEF t EQUS """Hello, world!""" -assert !strcmp("{t}", "Hello,\nworld!") + assert !strcmp("{t}", "Hello,\nworld!") +ENDR + +MACRO m + assert "\1" === "Hello, world!" +ENDM + +m Hello\, \ +world! diff --git a/test/asm/fixed-point-specific.asm b/test/asm/fixed-point-specific.asm index 88b24bf9..e49a2551 100644 --- a/test/asm/fixed-point-specific.asm +++ b/test/asm/fixed-point-specific.asm @@ -1,24 +1,24 @@ -MACRO compare - print "\3: " - if _NARG == 4 - def v1 = \3(\4q\1, \1) - def v2 = \3(\4q\2, \2) - elif _NARG == 5 - def v1 = \3(\4q\1, \5q\1, \1) - def v2 = \3(\4q\2, \5q\2, \2) - endc - println "{.4q\1f:v1} == {.4q\2f:v2}" -ENDM - - compare 8, 16, mul, 6.0, 7.0 - compare 12, 24, div, 115.625, 9.25 - compare 7, 14, pow, 3.5, 5.5 - - compare 4, 8, sin, 0.25 - compare 5, 9, cos, 0.75 - compare 6, 10, asin, 1.0 - compare 7, 11, acos, 0.0 - - compare 3, 6, round, 1.75 - compare 10, 20, ceil, 123.4 - compare 13, 17, floor, 567.8 +MACRO compare + print "\3: " + if _NARG == 4 + def v1 = \3(\4q\1, \1) + def v2 = \3(\4q\2, \2) + elif _NARG == 5 + def v1 = \3(\4q\1, \5q\1, \1) + def v2 = \3(\4q\2, \5q\2, \2) + endc + println "{.4q\1f:v1} == {.4q\2f:v2}" +ENDM + + compare 8, 16, mul, 6.0, 7.0 + compare 12, 24, div, 115.625, 9.25 + compare 7, 14, pow, 3.5, 5.5 + + compare 4, 8, sin, 0.25 + compare 5, 9, cos, 0.75 + compare 6, 10, asin, 1.0 + compare 7, 11, acos, 0.0 + + compare 3, 6, round, 1.75 + compare 10, 20, ceil, 123.4 + compare 13, 17, floor, 567.8 diff --git a/test/asm/section-union-data.asm b/test/asm/section-union-data.asm index 5baf3ea7..cca31aeb 100644 --- a/test/asm/section-union-data.asm +++ b/test/asm/section-union-data.asm @@ -1,2 +1,2 @@ -SECTION UNION "wat", ROM0 -db 42 +SECTION UNION "wat", ROM0 +db 42 diff --git a/test/link/.gitattributes b/test/link/.gitattributes new file mode 100644 index 00000000..425c4f96 --- /dev/null +++ b/test/link/.gitattributes @@ -0,0 +1,2 @@ +# Some files test CRLF line endings +linkerscript-escapes-test.link -text diff