mirror of
https://github.com/gbdev/rgbds.git
synced 2026-01-21 16:01:52 +00:00
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92bfe5d930 | ||
|
|
41fe1d8f25 | ||
|
|
63a911e657 | ||
|
|
a9ab248fed | ||
|
|
9b22ff3491 | ||
|
|
a4af81f250 | ||
|
|
e08de399db | ||
|
|
d4e0ca5f90 | ||
|
|
2666dcbc26 | ||
|
|
27dca5680c | ||
|
|
b0e0dfc56e | ||
|
|
33475e2c36 | ||
|
|
c8161be23a | ||
|
|
2c5c453ab8 | ||
|
|
c3e245c13e | ||
|
|
3631fab63c | ||
|
|
1c00123b33 | ||
|
|
131bb97ebc | ||
|
|
8d6c617875 | ||
|
|
752e2b3620 | ||
|
|
ad3188f038 | ||
|
|
a6eb6457d8 | ||
|
|
0d3276975e | ||
|
|
d961c697d7 | ||
|
|
1eb4eb3339 | ||
|
|
a3d3e1525a | ||
|
|
3553c9c4da | ||
|
|
5c2c893ced | ||
|
|
0f266d1c66 | ||
|
|
8ab4602ae5 | ||
|
|
04e3a904c2 | ||
|
|
395b03e88e |
4
.github/scripts/build_libpng.sh
vendored
4
.github/scripts/build_libpng.sh
vendored
@@ -1,13 +1,13 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
pngver=1.6.50
|
pngver=1.6.53
|
||||||
|
|
||||||
## Grab sources and check them
|
## Grab sources and check them
|
||||||
|
|
||||||
curl -LOJ "http://prdownloads.sourceforge.net/libpng/libpng-$pngver.tar.xz?download"
|
curl -LOJ "http://prdownloads.sourceforge.net/libpng/libpng-$pngver.tar.xz?download"
|
||||||
# Brew doesn't provide any sha256sum, so we're making do with `sha2` instead.
|
# Brew doesn't provide any sha256sum, so we're making do with `sha2` instead.
|
||||||
if [ "$(sha2 -q -256 libpng-$pngver.tar.xz)" != 4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 ]; then
|
if [ "$(sha2 -q -256 libpng-$pngver.tar.xz)" != 1d3fb8ccc2932d04aa3663e22ef5ef490244370f4e568d7850165068778d98d4 ]; then
|
||||||
sha2 -256 libpng-$pngver.tar.xz
|
sha2 -256 libpng-$pngver.tar.xz
|
||||||
echo Checksum mismatch! Aborting. >&2
|
echo Checksum mismatch! Aborting. >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
4
.github/scripts/get_win_deps.ps1
vendored
4
.github/scripts/get_win_deps.ps1
vendored
@@ -16,8 +16,8 @@ function getlibrary ([string] $URI, [string] $filename, [string] $hash, [string]
|
|||||||
}
|
}
|
||||||
|
|
||||||
getlibrary 'https://www.zlib.net/zlib131.zip' 'zlib.zip' '72af66d44fcc14c22013b46b814d5d2514673dda3d115e64b690c1ad636e7b17' .
|
getlibrary 'https://www.zlib.net/zlib131.zip' 'zlib.zip' '72af66d44fcc14c22013b46b814d5d2514673dda3d115e64b690c1ad636e7b17' .
|
||||||
getlibrary 'https://github.com/pnggroup/libpng/archive/refs/tags/v1.6.50.zip' 'libpng.zip' 'f6bb2544d2cf5465af3a695dee0b7eacff82f11a50aa4672ef0e19df6e16d455' .
|
getlibrary 'https://github.com/pnggroup/libpng/archive/refs/tags/v1.6.53.zip' 'libpng.zip' '9fb99118ec4523d9a9dab652ce7c2472ec76f6ccd69d1aba3ab873bb8cf84b98' .
|
||||||
getlibrary 'https://github.com/lexxmark/winflexbison/releases/download/v2.5.25/win_flex_bison-2.5.25.zip' 'winflexbison.zip' '8d324b62be33604b2c45ad1dd34ab93d722534448f55a16ca7292de32b6ac135' install_dir
|
getlibrary 'https://github.com/lexxmark/winflexbison/releases/download/v2.5.25/win_flex_bison-2.5.25.zip' 'winflexbison.zip' '8d324b62be33604b2c45ad1dd34ab93d722534448f55a16ca7292de32b6ac135' install_dir
|
||||||
|
|
||||||
Move-Item zlib-1.3.1 zlib
|
Move-Item zlib-1.3.1 zlib
|
||||||
Move-Item libpng-1.6.50 libpng
|
Move-Item libpng-1.6.53 libpng
|
||||||
|
|||||||
4
.github/scripts/mingw-w64-libpng-dev.sh
vendored
4
.github/scripts/mingw-w64-libpng-dev.sh
vendored
@@ -1,13 +1,13 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
pngver=1.6.50
|
pngver=1.6.53
|
||||||
arch="$1"
|
arch="$1"
|
||||||
|
|
||||||
## Grab sources and check them
|
## Grab sources and check them
|
||||||
|
|
||||||
wget http://downloads.sourceforge.net/project/libpng/libpng16/$pngver/libpng-$pngver.tar.xz
|
wget http://downloads.sourceforge.net/project/libpng/libpng16/$pngver/libpng-$pngver.tar.xz
|
||||||
echo 4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 libpng-$pngver.tar.xz | sha256sum -c -
|
echo 1d3fb8ccc2932d04aa3663e22ef5ef490244370f4e568d7850165068778d98d4 libpng-$pngver.tar.xz | sha256sum -c -
|
||||||
|
|
||||||
## Extract sources and patch them
|
## Extract sources and patch them
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ else()
|
|||||||
-fsanitize=float-divide-by-zero)
|
-fsanitize=float-divide-by-zero)
|
||||||
add_compile_options(${SAN_FLAGS})
|
add_compile_options(${SAN_FLAGS})
|
||||||
add_link_options(${SAN_FLAGS})
|
add_link_options(${SAN_FLAGS})
|
||||||
add_definitions(-D_GLIBCXX_ASSERTIONS)
|
add_definitions(-D_GLIBCXX_ASSERTIONS -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG)
|
||||||
# A non-zero optimization level is desired in debug mode, but allow overriding it nonetheless
|
# A non-zero optimization level is desired in debug mode, but allow overriding it nonetheless
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "-g -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls ${CMAKE_CXX_FLAGS_DEBUG}"
|
set(CMAKE_CXX_FLAGS_DEBUG "-g -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls ${CMAKE_CXX_FLAGS_DEBUG}"
|
||||||
CACHE STRING "" FORCE)
|
CACHE STRING "" FORCE)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
FROM debian:12-slim
|
FROM debian:12-slim
|
||||||
LABEL org.opencontainers.image.source=https://github.com/gbdev/rgbds
|
LABEL org.opencontainers.image.source=https://github.com/gbdev/rgbds
|
||||||
ARG version=1.0.0
|
ARG version=1.0.1
|
||||||
WORKDIR /rgbds
|
WORKDIR /rgbds
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|||||||
8
Makefile
8
Makefile
@@ -31,7 +31,7 @@ WARNFLAGS := -Wall -pedantic -Wno-unknown-warning-option -Wno-gnu-zero-variadic-
|
|||||||
# Overridable CXXFLAGS
|
# Overridable CXXFLAGS
|
||||||
CXXFLAGS ?= -O3 -flto -DNDEBUG
|
CXXFLAGS ?= -O3 -flto -DNDEBUG
|
||||||
# Non-overridable CXXFLAGS
|
# Non-overridable CXXFLAGS
|
||||||
REALCXXFLAGS := ${CXXFLAGS} ${WARNFLAGS} -std=c++2a -I include -fno-exceptions -fno-rtti
|
REALCXXFLAGS := ${CXXFLAGS} ${WARNFLAGS} -std=c++20 -I include -fno-exceptions -fno-rtti
|
||||||
# Overridable LDFLAGS
|
# Overridable LDFLAGS
|
||||||
LDFLAGS ?=
|
LDFLAGS ?=
|
||||||
# Non-overridable LDFLAGS
|
# Non-overridable LDFLAGS
|
||||||
@@ -222,8 +222,8 @@ develop:
|
|||||||
-Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1 \
|
-Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1 \
|
||||||
-Wno-format-nonliteral -Wno-strict-overflow -Wno-unused-but-set-variable \
|
-Wno-format-nonliteral -Wno-strict-overflow -Wno-unused-but-set-variable \
|
||||||
-Wno-type-limits -Wno-tautological-constant-out-of-range-compare -Wvla \
|
-Wno-type-limits -Wno-tautological-constant-out-of-range-compare -Wvla \
|
||||||
-D_GLIBCXX_ASSERTIONS -fsanitize=address -fsanitize=undefined \
|
-D_GLIBCXX_ASSERTIONS -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG \
|
||||||
-fsanitize=float-divide-by-zero" \
|
-fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero" \
|
||||||
CXXFLAGS="-ggdb3 -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls"
|
CXXFLAGS="-ggdb3 -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls"
|
||||||
|
|
||||||
# Target used in development to debug with gdb.
|
# Target used in development to debug with gdb.
|
||||||
@@ -254,7 +254,7 @@ tidy: src/asm/parser.hpp src/link/script.hpp
|
|||||||
iwyu:
|
iwyu:
|
||||||
$Qenv ${MAKE} \
|
$Qenv ${MAKE} \
|
||||||
CXX="include-what-you-use" \
|
CXX="include-what-you-use" \
|
||||||
REALCXXFLAGS="-std=c++2a -I include"
|
REALCXXFLAGS="-std=c++20 -I include"
|
||||||
|
|
||||||
# Targets for the project maintainer to easily create Windows exes.
|
# Targets for the project maintainer to easily create Windows exes.
|
||||||
# This is not for Windows users!
|
# This is not for Windows users!
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
-std=c++2a
|
-std=c++20
|
||||||
-I
|
-I
|
||||||
include
|
include
|
||||||
-fno-exceptions
|
-fno-exceptions
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ mkdir -p coverage
|
|||||||
COVERAGE_INFO=coverage/coverage.info
|
COVERAGE_INFO=coverage/coverage.info
|
||||||
lcov -c --no-external -d . -o "$COVERAGE_INFO"
|
lcov -c --no-external -d . -o "$COVERAGE_INFO"
|
||||||
lcov -r "$COVERAGE_INFO" src/asm/parser.{hpp,cpp} src/link/script.{hpp,cpp} -o "$COVERAGE_INFO"
|
lcov -r "$COVERAGE_INFO" src/asm/parser.{hpp,cpp} src/link/script.{hpp,cpp} -o "$COVERAGE_INFO"
|
||||||
genhtml --dark-mode -f -s -o coverage/ "$COVERAGE_INFO"
|
genhtml --dark-mode --num-spaces 4 -f -s -o coverage/ "$COVERAGE_INFO"
|
||||||
|
|
||||||
# Check whether running from coverage.yml workflow
|
# Check whether running from coverage.yml workflow
|
||||||
if [ "$1" != "ubuntu-ci" ]; then
|
if [ "$1" != "ubuntu-ci" ]; then
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ MacroArgs *fstk_GetCurrentMacroArgs();
|
|||||||
void fstk_AddIncludePath(std::string const &path);
|
void fstk_AddIncludePath(std::string const &path);
|
||||||
void fstk_AddPreIncludeFile(std::string const &path);
|
void fstk_AddPreIncludeFile(std::string const &path);
|
||||||
std::optional<std::string> fstk_FindFile(std::string const &path);
|
std::optional<std::string> fstk_FindFile(std::string const &path);
|
||||||
bool fstk_FileError(std::string const &path, char const *functionName);
|
bool fstk_FileError(std::string const &path, char const *description);
|
||||||
bool fstk_FailedOnMissingInclude();
|
bool fstk_FailedOnMissingInclude();
|
||||||
|
|
||||||
bool yywrap();
|
bool yywrap();
|
||||||
@@ -84,6 +84,6 @@ void fstk_RunFor(
|
|||||||
bool fstk_Break();
|
bool fstk_Break();
|
||||||
|
|
||||||
void fstk_NewRecursionDepth(size_t newDepth);
|
void fstk_NewRecursionDepth(size_t newDepth);
|
||||||
void fstk_Init(std::string const &mainPath);
|
bool fstk_Init(std::string const &mainPath);
|
||||||
|
|
||||||
#endif // RGBDS_ASM_FSTACK_HPP
|
#endif // RGBDS_ASM_FSTACK_HPP
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ struct Symbol {
|
|||||||
uint32_t getConstantValue() const;
|
uint32_t getConstantValue() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool sym_IsDotScope(std::string const &symName);
|
||||||
|
|
||||||
void sym_ForEach(void (*callback)(Symbol &));
|
void sym_ForEach(void (*callback)(Symbol &));
|
||||||
|
|
||||||
Symbol *sym_AddLocalLabel(std::string const &symName);
|
Symbol *sym_AddLocalLabel(std::string const &symName);
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ enum SectionModifier { SECTION_NORMAL, SECTION_UNION, SECTION_FRAGMENT };
|
|||||||
|
|
||||||
extern char const * const sectionModNames[];
|
extern char const * const sectionModNames[];
|
||||||
|
|
||||||
enum ExportLevel { SYMTYPE_LOCAL, SYMTYPE_IMPORT, SYMTYPE_EXPORT };
|
enum ExportLevel { SYMTYPE_LOCAL, SYMTYPE_IMPORT, SYMTYPE_EXPORT, SYMTYPE_INVALID };
|
||||||
|
|
||||||
enum PatchType {
|
enum PatchType {
|
||||||
PATCHTYPE_BYTE,
|
PATCHTYPE_BYTE,
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ struct Usage {
|
|||||||
std::vector<std::string> flags;
|
std::vector<std::string> flags;
|
||||||
std::vector<std::pair<std::vector<std::string>, std::vector<std::string>>> options;
|
std::vector<std::pair<std::vector<std::string>, std::vector<std::string>>> options;
|
||||||
|
|
||||||
|
void printVersion(bool error) const;
|
||||||
|
|
||||||
[[noreturn]]
|
[[noreturn]]
|
||||||
void printAndExit(int code) const;
|
void printAndExit(int code) const;
|
||||||
|
|
||||||
|
|||||||
@@ -3,17 +3,14 @@
|
|||||||
#ifndef RGBDS_VERBOSITY_HPP
|
#ifndef RGBDS_VERBOSITY_HPP
|
||||||
#define RGBDS_VERBOSITY_HPP
|
#define RGBDS_VERBOSITY_HPP
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "style.hpp"
|
|
||||||
|
|
||||||
// This macro does not evaluate its arguments unless the condition is true.
|
// This macro does not evaluate its arguments unless the condition is true.
|
||||||
#define verbosePrint(level, ...) \
|
#define verbosePrint(level, ...) \
|
||||||
do { \
|
do { \
|
||||||
if (checkVerbosity(level)) { \
|
if (checkVerbosity(level)) { \
|
||||||
style_Set(stderr, STYLE_MAGENTA, false); \
|
printVerbosely(__VA_ARGS__); \
|
||||||
fprintf(stderr, __VA_ARGS__); \
|
|
||||||
style_Reset(stderr); \
|
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
@@ -30,6 +27,9 @@ enum Verbosity {
|
|||||||
void incrementVerbosity();
|
void incrementVerbosity();
|
||||||
bool checkVerbosity(Verbosity level);
|
bool checkVerbosity(Verbosity level);
|
||||||
|
|
||||||
|
[[gnu::format(printf, 1, 2)]]
|
||||||
|
void printVerbosely(char const *fmt, ...);
|
||||||
|
|
||||||
void printVVVVVVerbosity();
|
void printVVVVVVerbosity();
|
||||||
|
|
||||||
#endif // RGBDS_VERBOSITY_HPP
|
#endif // RGBDS_VERBOSITY_HPP
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#define PACKAGE_VERSION_MAJOR 1
|
#define PACKAGE_VERSION_MAJOR 1
|
||||||
#define PACKAGE_VERSION_MINOR 0
|
#define PACKAGE_VERSION_MINOR 0
|
||||||
#define PACKAGE_VERSION_PATCH 0
|
#define PACKAGE_VERSION_PATCH 1
|
||||||
// #define PACKAGE_VERSION_RC 1
|
// #define PACKAGE_VERSION_RC 1
|
||||||
|
|
||||||
char const *get_package_version_string();
|
char const *get_package_version_string();
|
||||||
|
|||||||
51
man/gbz80.7
51
man/gbz80.7
@@ -1,6 +1,6 @@
|
|||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd October 31, 2025
|
.Dd January 1, 2026
|
||||||
.Dt GBZ80 7
|
.Dt GBZ80 7
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -11,6 +11,11 @@ This is the list of instructions supported by
|
|||||||
.Xr rgbasm 1 ,
|
.Xr rgbasm 1 ,
|
||||||
including a short description, the number of bytes needed to encode them and the number of CPU cycles at 1MHz (or 2MHz in GBC double speed mode) needed to complete them.
|
including a short description, the number of bytes needed to encode them and the number of CPU cycles at 1MHz (or 2MHz in GBC double speed mode) needed to complete them.
|
||||||
.Pp
|
.Pp
|
||||||
|
Instructons are documented according to the syntax accepted by
|
||||||
|
.Xr rgbasm 1 ,
|
||||||
|
which does not always match one-to-one with the way instructions are
|
||||||
|
.Lk https://gbdev.io/gb-opcodes/optables/ encoded .
|
||||||
|
.Pp
|
||||||
Note: All arithmetic and logic instructions that use register
|
Note: All arithmetic and logic instructions that use register
|
||||||
.Sy A
|
.Sy A
|
||||||
as a destination can omit the destination, since it is assumed to be register
|
as a destination can omit the destination, since it is assumed to be register
|
||||||
@@ -861,11 +866,13 @@ Flags: None affected.
|
|||||||
Relative Jump to address
|
Relative Jump to address
|
||||||
.Ar n16 .
|
.Ar n16 .
|
||||||
.Pp
|
.Pp
|
||||||
The address is encoded as a signed 8-bit offset from the address immediately following the
|
The target address
|
||||||
.Ic JR
|
|
||||||
instruction, so the target address
|
|
||||||
.Ar n16
|
.Ar n16
|
||||||
must be between
|
is
|
||||||
|
.Em encoded
|
||||||
|
as a signed 8-bit offset from the address immediately following the
|
||||||
|
.Ic JR
|
||||||
|
instruction, so it must be between
|
||||||
.Sy -128
|
.Sy -128
|
||||||
and
|
and
|
||||||
.Sy 127
|
.Sy 127
|
||||||
@@ -889,6 +896,18 @@ if condition
|
|||||||
.Ar cc
|
.Ar cc
|
||||||
is met.
|
is met.
|
||||||
.Pp
|
.Pp
|
||||||
|
The target address
|
||||||
|
.Ar n16
|
||||||
|
is
|
||||||
|
.Em encoded
|
||||||
|
as a signed 8-bit offset from the address immediately following the
|
||||||
|
.Ic JR
|
||||||
|
instruction, so it must be between
|
||||||
|
.Sy -128
|
||||||
|
and
|
||||||
|
.Sy 127
|
||||||
|
bytes away.
|
||||||
|
.Pp
|
||||||
Cycles: 3 taken / 2 untaken
|
Cycles: 3 taken / 2 untaken
|
||||||
.Pp
|
.Pp
|
||||||
Bytes: 2
|
Bytes: 2
|
||||||
@@ -992,8 +1011,15 @@ Flags: None affected.
|
|||||||
Copy the value in register
|
Copy the value in register
|
||||||
.Sy A
|
.Sy A
|
||||||
into the byte at address
|
into the byte at address
|
||||||
.Ar n16 ,
|
.Ar n16 .
|
||||||
provided the address is between
|
.Pp
|
||||||
|
The destination address
|
||||||
|
.Ar n16
|
||||||
|
is
|
||||||
|
.Em encoded
|
||||||
|
as its 8-bit low byte and assumes a high byte of
|
||||||
|
.Ad $FF ,
|
||||||
|
so it must be between
|
||||||
.Ad $FF00
|
.Ad $FF00
|
||||||
and
|
and
|
||||||
.Ad $FFFF .
|
.Ad $FFFF .
|
||||||
@@ -1043,8 +1069,15 @@ Flags: None affected.
|
|||||||
Copy the byte at address
|
Copy the byte at address
|
||||||
.Ar n16
|
.Ar n16
|
||||||
into register
|
into register
|
||||||
.Sy A ,
|
.Sy A .
|
||||||
provided the address is between
|
.Pp
|
||||||
|
The source address
|
||||||
|
.Ar n16
|
||||||
|
is
|
||||||
|
.Em encoded
|
||||||
|
as its 8-bit low byte and assumes a high byte of
|
||||||
|
.Ad $FF ,
|
||||||
|
so it must be between
|
||||||
.Ad $FF00
|
.Ad $FF00
|
||||||
and
|
and
|
||||||
.Ad $FFFF .
|
.Ad $FFFF .
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd October 31, 2025
|
.Dd January 1, 2026
|
||||||
.Dt RGBASM-OLD 5
|
.Dt RGBASM-OLD 5
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -370,7 +370,7 @@ or
|
|||||||
Deprecated in 1.0.0.
|
Deprecated in 1.0.0.
|
||||||
.Pp
|
.Pp
|
||||||
Instead, use
|
Instead, use
|
||||||
.Dl -Wno-overwrite .
|
.Ql -Wno-overwrite .
|
||||||
.Ss rgbgfx -h/--horizontal
|
.Ss rgbgfx -h/--horizontal
|
||||||
Removed in 0.6.0.
|
Removed in 0.6.0.
|
||||||
.Pp
|
.Pp
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd October 31, 2025
|
.Dd January 1, 2026
|
||||||
.Dt RGBASM 1
|
.Dt RGBASM 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd October 31, 2025
|
.Dd January 1, 2026
|
||||||
.Dt RGBASM 5
|
.Dt RGBASM 5
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -30,9 +30,9 @@ The syntax is line-based, just as in any other assembler.
|
|||||||
Each line may have components in either of these orders:
|
Each line may have components in either of these orders:
|
||||||
.Bl -bullet -offset indent
|
.Bl -bullet -offset indent
|
||||||
.It
|
.It
|
||||||
.Li Oo Ar label : Oc Oo Ar directive Oc Oo ;\ Ns Ar comment Oc
|
.Oo Ar label : Oc Oo Ar directive Oc Oo ;\ Ns Ar comment Oc
|
||||||
.It
|
.It
|
||||||
.Li Oo Ar label : Oc Oo Ar instruction Oo :: Ar instruction ... Oc Oc Oo ;\ Ns Ar comment Oc
|
.Oo Ar label : Oc Oo Ar instruction Oo :: Ar instruction ... Oc Oc Oo ;\ Ns Ar comment Oc
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
Directives are commands to the assembler itself, such as
|
Directives are commands to the assembler itself, such as
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd October 31, 2025
|
.Dd January 1, 2026
|
||||||
.Dt RGBDS 5
|
.Dt RGBDS 5
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -92,7 +92,7 @@ If the node is not a REPT node...
|
|||||||
.Pp
|
.Pp
|
||||||
.Bl -tag -width Ds -compact
|
.Bl -tag -width Ds -compact
|
||||||
.It Cm STRING Ar Name
|
.It Cm STRING Ar Name
|
||||||
The node's name: either a file name, or the macro's name prefixes by its definition's file name
|
The node's name: either a file name, or the macro's name prefixed by its definition's file name
|
||||||
.Pq e.g. Ql src/includes/defines.asm::error .
|
.Pq e.g. Ql src/includes/defines.asm::error .
|
||||||
.El
|
.El
|
||||||
.It Cm ELSE
|
.It Cm ELSE
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd October 31, 2025
|
.Dd January 1, 2026
|
||||||
.Dt RGBDS 7
|
.Dt RGBDS 7
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd October 31, 2025
|
.Dd January 1, 2026
|
||||||
.Dt RGBFIX 1
|
.Dt RGBFIX 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
|
|||||||
22
man/rgbgfx.1
22
man/rgbgfx.1
@@ -2,7 +2,7 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd October 31, 2025
|
.Dd January 1, 2026
|
||||||
.Dt RGBGFX 1
|
.Dt RGBGFX 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -143,9 +143,17 @@ Set the base IDs for tile map output.
|
|||||||
should be one or two numbers between 0 and 255, separated by a comma; they are for bank 0 and bank 1 respectively.
|
should be one or two numbers between 0 and 255, separated by a comma; they are for bank 0 and bank 1 respectively.
|
||||||
Both default to 0.
|
Both default to 0.
|
||||||
.It Fl C , Fl \-color-curve
|
.It Fl C , Fl \-color-curve
|
||||||
When generating palettes, use a color curve mimicking the Game Boy Color's screen.
|
Modifies the color palettes
|
||||||
The resulting colors may look closer to the input image's
|
.Pq whether they are generated from the input image or taken from an input palette specification
|
||||||
.Sy on hardware and accurate emulators .
|
with a color curve mimicking the Game Boy Color's screen.
|
||||||
|
This adjusts the
|
||||||
|
.Em absolute
|
||||||
|
RGB color values so that the
|
||||||
|
.Em perceived
|
||||||
|
colors, when displayed on Game Boy Color hardware
|
||||||
|
.Pq or an emulator with an accurate display filter ,
|
||||||
|
will look like the original colors as displayed on a backlit computer screen.
|
||||||
|
Note that GBC displays can look very different depending on the ambient light and their exact hardware model, so this color curve is only a "best effort".
|
||||||
.It Fl c Ar pal_spec , Fl \-colors Ar pal_spec
|
.It Fl c Ar pal_spec , Fl \-colors Ar pal_spec
|
||||||
Use the specified color palettes instead of having
|
Use the specified color palettes instead of having
|
||||||
.Nm
|
.Nm
|
||||||
@@ -305,8 +313,10 @@ This is useful for example if the input image is a sheet of some sort, and you w
|
|||||||
The default is to process the whole image as-is.
|
The default is to process the whole image as-is.
|
||||||
.Pp
|
.Pp
|
||||||
.Ar slice
|
.Ar slice
|
||||||
must be two number pairs, separated by a colon.
|
must be formatted as
|
||||||
The numbers must be separated by commas; space is allowed around all punctuation.
|
.Ql Ar X , Ns Ar Y : Ns Ar W , Ns Ar H :
|
||||||
|
two comma-separated number pairs, separated by a colon.
|
||||||
|
Whitespace is allowed around all punctuation.
|
||||||
The first number pair specifies the X and Y coordinates of the top-left pixel that will be processed (anything above it or to its left will be ignored).
|
The first number pair specifies the X and Y coordinates of the top-left pixel that will be processed (anything above it or to its left will be ignored).
|
||||||
The second number pair specifies how many tiles to process horizontally and vertically, respectively.
|
The second number pair specifies how many tiles to process horizontally and vertically, respectively.
|
||||||
.Pp
|
.Pp
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd October 31, 2025
|
.Dd January 1, 2026
|
||||||
.Dt RGBLINK 1
|
.Dt RGBLINK 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd October 31, 2025
|
.Dd January 1, 2026
|
||||||
.Dt RGBLINK 5
|
.Dt RGBLINK 5
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ std::optional<std::string> act_ReadFile(std::string const &name, uint32_t maxLen
|
|||||||
file = fopen(fullPath->c_str(), "rb");
|
file = fopen(fullPath->c_str(), "rb");
|
||||||
}
|
}
|
||||||
if (!file) {
|
if (!file) {
|
||||||
if (fstk_FileError(name, "READFILE")) {
|
if (fstk_FileError(name, "`READFILE`")) {
|
||||||
// If `fstk_FileError` returned true due to `-MG`, we should abort due to a
|
// If `fstk_FileError` returned true due to `-MG`, we should abort due to a
|
||||||
// missing file, so return `std::nullopt`, which tells the caller to `YYACCEPT`
|
// missing file, so return `std::nullopt`, which tells the caller to `YYACCEPT`
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|||||||
@@ -57,44 +57,47 @@ static std::vector<std::string> includePaths = {""}; // -I
|
|||||||
static std::deque<std::string> preIncludeNames; // -P
|
static std::deque<std::string> preIncludeNames; // -P
|
||||||
static bool failedOnMissingInclude = false;
|
static bool failedOnMissingInclude = false;
|
||||||
|
|
||||||
using TraceNode = std::pair<std::string, uint32_t>;
|
void FileStackNode::printBacktrace(uint32_t curLineNo) const {
|
||||||
|
using TraceItem = std::pair<FileStackNode const *, uint32_t>;
|
||||||
static std::vector<TraceNode> backtrace(FileStackNode const &node, uint32_t curLineNo) {
|
std::vector<TraceItem> items;
|
||||||
if (node.isQuiet && !tracing.loud) {
|
for (TraceItem item{this, curLineNo};;) {
|
||||||
if (node.parent) {
|
auto &[node, itemLineNo] = item;
|
||||||
|
bool loud = !node->isQuiet || tracing.loud;
|
||||||
|
if (loud) {
|
||||||
|
items.emplace_back(node, itemLineNo);
|
||||||
|
}
|
||||||
|
if (!node->parent) {
|
||||||
|
assume(node->type != NODE_REPT && std::holds_alternative<std::string>(node->data));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (loud || node->type != NODE_REPT) {
|
||||||
// Quiet REPT nodes will pass their interior line number up to their parent,
|
// Quiet REPT nodes will pass their interior line number up to their parent,
|
||||||
// which is more precise than the parent's own line number (since that will be
|
// which is more precise than the parent's own line number (since that will be
|
||||||
// the line number of the "REPT?" or "FOR?" itself).
|
// the line number of the "REPT?" or "FOR?" itself).
|
||||||
return backtrace(*node.parent, node.type == NODE_REPT ? curLineNo : node.lineNo);
|
itemLineNo = node->lineNo;
|
||||||
}
|
}
|
||||||
return {}; // LCOV_EXCL_LINE
|
node = &*node->parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!node.parent) {
|
using TraceNode = std::pair<std::string, uint32_t>;
|
||||||
assume(node.type != NODE_REPT && std::holds_alternative<std::string>(node.data));
|
std::vector<TraceNode> traceNodes;
|
||||||
return {
|
traceNodes.reserve(items.size());
|
||||||
{node.name(), curLineNo}
|
for (auto &[node, itemLineNo] : reversed(items)) {
|
||||||
};
|
if (std::holds_alternative<std::vector<uint32_t>>(node->data)) {
|
||||||
}
|
assume(!traceNodes.empty()); // REPT nodes use their parent's name
|
||||||
|
std::string reptName = traceNodes.back().first;
|
||||||
std::vector<TraceNode> traceNodes = backtrace(*node.parent, node.lineNo);
|
if (std::vector<uint32_t> const &nodeIters = node->iters(); !nodeIters.empty()) {
|
||||||
if (std::holds_alternative<std::vector<uint32_t>>(node.data)) {
|
reptName.append(NODE_SEPARATOR REPT_NODE_PREFIX);
|
||||||
assume(!traceNodes.empty()); // REPT nodes use their parent's name
|
reptName.append(std::to_string(nodeIters.front()));
|
||||||
std::string reptName = traceNodes.back().first;
|
}
|
||||||
if (std::vector<uint32_t> const &nodeIters = node.iters(); !nodeIters.empty()) {
|
traceNodes.emplace_back(reptName, itemLineNo);
|
||||||
reptName.append(NODE_SEPARATOR REPT_NODE_PREFIX);
|
} else {
|
||||||
reptName.append(std::to_string(nodeIters.front()));
|
traceNodes.emplace_back(node->name(), itemLineNo);
|
||||||
}
|
}
|
||||||
traceNodes.emplace_back(reptName, curLineNo);
|
|
||||||
} else {
|
|
||||||
traceNodes.emplace_back(node.name(), curLineNo);
|
|
||||||
}
|
}
|
||||||
return traceNodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileStackNode::printBacktrace(uint32_t curLineNo) const {
|
|
||||||
trace_PrintBacktrace(
|
trace_PrintBacktrace(
|
||||||
backtrace(*this, curLineNo),
|
traceNodes,
|
||||||
[](TraceNode const &node) { return node.first.c_str(); },
|
[](TraceNode const &node) { return node.first.c_str(); },
|
||||||
[](TraceNode const &node) { return node.second; }
|
[](TraceNode const &node) { return node.second; }
|
||||||
);
|
);
|
||||||
@@ -188,10 +191,14 @@ std::optional<std::string> fstk_FindFile(std::string const &path) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
errno = ENOENT;
|
|
||||||
if (options.missingIncludeState != INC_ERROR) {
|
if (options.missingIncludeState != INC_ERROR) {
|
||||||
printDep(path);
|
printDep(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set `errno` as if `fopen` had failed on a nonexistent file.
|
||||||
|
// This allows a subsequent `fstk_FileError` to report correctly with `strerror`.
|
||||||
|
errno = ENOENT;
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,17 +355,17 @@ static Context &
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fstk_FileError(std::string const &path, char const *functionName) {
|
bool fstk_FileError(std::string const &path, char const *description) {
|
||||||
if (options.missingIncludeState == INC_ERROR) {
|
if (options.missingIncludeState == INC_ERROR) {
|
||||||
error("Error opening `%s` file \"%s\": %s", functionName, path.c_str(), strerror(errno));
|
error("Error opening %s file \"%s\": %s", description, path.c_str(), strerror(errno));
|
||||||
} else {
|
} else {
|
||||||
failedOnMissingInclude = true;
|
failedOnMissingInclude = true;
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
if (options.missingIncludeState == GEN_EXIT) {
|
if (options.missingIncludeState == GEN_EXIT) {
|
||||||
verbosePrint(
|
verbosePrint(
|
||||||
VERB_NOTICE,
|
VERB_NOTICE,
|
||||||
"Aborting due to '-MG' on `%s` file \"%s\": %s\n",
|
"Aborting due to '-MG' on %s file \"%s\": %s\n",
|
||||||
functionName,
|
description,
|
||||||
path.c_str(),
|
path.c_str(),
|
||||||
strerror(errno)
|
strerror(errno)
|
||||||
);
|
);
|
||||||
@@ -379,7 +386,7 @@ bool fstk_RunInclude(std::string const &path, bool isQuiet) {
|
|||||||
newFileContext(*fullPath, isQuiet, false);
|
newFileContext(*fullPath, isQuiet, false);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return fstk_FileError(path, "INCLUDE");
|
return fstk_FileError(path, "`INCLUDE`");
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_RunMacro(
|
void fstk_RunMacro(
|
||||||
@@ -487,14 +494,16 @@ void fstk_NewRecursionDepth(size_t newDepth) {
|
|||||||
options.maxRecursionDepth = newDepth;
|
options.maxRecursionDepth = newDepth;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_Init(std::string const &mainPath) {
|
bool fstk_Init(std::string const &mainPath) {
|
||||||
newFileContext(mainPath, false, true);
|
newFileContext(mainPath, false, true);
|
||||||
|
|
||||||
for (std::string const &name : preIncludeNames) {
|
for (std::string const &name : preIncludeNames) {
|
||||||
if (std::optional<std::string> fullPath = fstk_FindFile(name); fullPath) {
|
if (std::optional<std::string> fullPath = fstk_FindFile(name); fullPath) {
|
||||||
newFileContext(*fullPath, false, false);
|
newFileContext(*fullPath, false, false);
|
||||||
} else {
|
} else if (fstk_FileError(name, "pre-included")) {
|
||||||
error("Error reading pre-included file \"%s\": %s", name.c_str(), strerror(errno));
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1295,7 +1295,7 @@ static Token readIdentifier(char firstChar, bool raw) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Label scopes `.` and `..` are the only nonlocal identifiers that start with a dot
|
// Label scopes `.` and `..` are the only nonlocal identifiers that start with a dot
|
||||||
if (identifier.find_first_not_of('.') == identifier.npos) {
|
if (sym_IsDotScope(identifier)) {
|
||||||
tokenType = T_(SYMBOL);
|
tokenType = T_(SYMBOL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1636,6 +1636,10 @@ static Token yylex_SKIP_TO_ENDC(); // Forward declaration for `yylex_NORMAL`
|
|||||||
|
|
||||||
// Must stay in sync with the `switch` in `yylex_NORMAL`!
|
// Must stay in sync with the `switch` in `yylex_NORMAL`!
|
||||||
static bool isGarbageCharacter(int c) {
|
static bool isGarbageCharacter(int c) {
|
||||||
|
// EOF is not garbage (it can't be reported anyway)
|
||||||
|
if (c == EOF) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// Whitespace characters are not garbage, even the non-"printable" ones
|
// Whitespace characters are not garbage, even the non-"printable" ones
|
||||||
if (isWhitespace(c)) {
|
if (isWhitespace(c)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1645,7 +1649,7 @@ static bool isGarbageCharacter(int c) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// All other printable characters are not garbage (i.e. `yylex_NORMAL` handles them), and
|
// All other printable characters are not garbage (i.e. `yylex_NORMAL` handles them), and
|
||||||
// all other nonprintable characters are garbage (including '\0' and EOF)
|
// all other nonprintable characters are garbage (including '\0')
|
||||||
return !isPrintable(c);
|
return !isPrintable(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1935,11 +1939,12 @@ static Token yylex_NORMAL() {
|
|||||||
|
|
||||||
// `token` is either a `SYMBOL` or a `LOCAL`, and both have a `std::string` value.
|
// `token` is either a `SYMBOL` or a `LOCAL`, and both have a `std::string` value.
|
||||||
assume(std::holds_alternative<std::string>(token.value));
|
assume(std::holds_alternative<std::string>(token.value));
|
||||||
|
std::string const &identifier = std::get<std::string>(token.value);
|
||||||
|
|
||||||
// Raw symbols and local symbols cannot be string expansions
|
// Raw symbols and local symbols cannot be string expansions
|
||||||
if (!raw && token.type == T_(SYMBOL) && lexerState->expandStrings) {
|
if (!raw && token.type == T_(SYMBOL) && lexerState->expandStrings) {
|
||||||
// Attempt string expansion
|
// Attempt string expansion
|
||||||
if (Symbol const *sym = sym_FindExactSymbol(std::get<std::string>(token.value));
|
if (Symbol const *sym = sym_FindExactSymbol(identifier);
|
||||||
sym && sym->type == SYM_EQUS) {
|
sym && sym->type == SYM_EQUS) {
|
||||||
beginExpansion(sym->getEqus(), sym->name);
|
beginExpansion(sym->getEqus(), sym->name);
|
||||||
continue; // Restart, reading from the new buffer
|
continue; // Restart, reading from the new buffer
|
||||||
@@ -1950,6 +1955,7 @@ static Token yylex_NORMAL() {
|
|||||||
// - label definitions (which are followed by a ':' and use the token `LABEL`)
|
// - label definitions (which are followed by a ':' and use the token `LABEL`)
|
||||||
// - quiet macro invocations (which are followed by a '?' and use the token `QMACRO`)
|
// - quiet macro invocations (which are followed by a '?' and use the token `QMACRO`)
|
||||||
// - regular macro invocations (which use the token `SYMBOL`)
|
// - regular macro invocations (which use the token `SYMBOL`)
|
||||||
|
// - label scopes "." and ".." (which use the token `SYMBOL` no matter what)
|
||||||
//
|
//
|
||||||
// If we had one `IDENTIFIER` token, the parser would need to perform "lookahead" to
|
// If we had one `IDENTIFIER` token, the parser would need to perform "lookahead" to
|
||||||
// determine which rule applies. But since macros need to enter "raw" mode to parse
|
// determine which rule applies. But since macros need to enter "raw" mode to parse
|
||||||
@@ -1960,7 +1966,7 @@ static Token yylex_NORMAL() {
|
|||||||
// one to lex depending on the character *immediately* following the identifier.
|
// one to lex depending on the character *immediately* following the identifier.
|
||||||
// Thus "name:" is a label definition, and "name?" is a quiet macro invocation, but
|
// Thus "name:" is a label definition, and "name?" is a quiet macro invocation, but
|
||||||
// "name :" and "name ?" and just "name" are all regular macro invocations.
|
// "name :" and "name ?" and just "name" are all regular macro invocations.
|
||||||
if (token.type == T_(SYMBOL)) {
|
if (token.type == T_(SYMBOL) && !sym_IsDotScope(identifier)) {
|
||||||
c = peek();
|
c = peek();
|
||||||
token.type = c == ':' ? T_(LABEL) : c == '?' ? T_(QMACRO) : T_(SYMBOL);
|
token.type = c == ':' ? T_(LABEL) : c == '?' ? T_(QMACRO) : T_(SYMBOL);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
#include "usage.hpp"
|
#include "usage.hpp"
|
||||||
#include "util.hpp" // UpperMap
|
#include "util.hpp" // UpperMap
|
||||||
#include "verbosity.hpp"
|
#include "verbosity.hpp"
|
||||||
#include "version.hpp"
|
|
||||||
|
|
||||||
#include "asm/charmap.hpp"
|
#include "asm/charmap.hpp"
|
||||||
#include "asm/fstack.hpp"
|
#include "asm/fstack.hpp"
|
||||||
@@ -296,7 +295,7 @@ static void parseArg(int ch, char *arg) {
|
|||||||
|
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
case 'V':
|
case 'V':
|
||||||
printf("rgbasm %s\n", get_package_version_string());
|
usage.printVersion(false);
|
||||||
exit(0);
|
exit(0);
|
||||||
|
|
||||||
case 'v':
|
case 'v':
|
||||||
@@ -381,7 +380,7 @@ static void verboseOutputConfig() {
|
|||||||
|
|
||||||
style_Set(stderr, STYLE_MAGENTA, false);
|
style_Set(stderr, STYLE_MAGENTA, false);
|
||||||
|
|
||||||
fprintf(stderr, "rgbasm %s\n", get_package_version_string());
|
usage.printVersion(true);
|
||||||
|
|
||||||
printVVVVVVerbosity();
|
printVVVVVVerbosity();
|
||||||
|
|
||||||
@@ -491,7 +490,7 @@ static void verboseOutputConfig() {
|
|||||||
fputs("\tGenerate phony dependencies\n", stderr);
|
fputs("\tGenerate phony dependencies\n", stderr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fputs("Ready.\n", stderr);
|
fputs("Ready for assembly\n", stderr);
|
||||||
|
|
||||||
style_Reset(stderr);
|
style_Reset(stderr);
|
||||||
}
|
}
|
||||||
@@ -559,11 +558,8 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
charmap_New(DEFAULT_CHARMAP_NAME, nullptr);
|
charmap_New(DEFAULT_CHARMAP_NAME, nullptr);
|
||||||
|
|
||||||
// Init lexer and file stack, providing file info
|
// Init lexer and file stack, and parse (`yy::parser` is auto-generated from `parser.y`)
|
||||||
fstk_Init(*localOptions.inputFileName);
|
if (yy::parser parser; fstk_Init(*localOptions.inputFileName) && parser.parse() != 0) {
|
||||||
|
|
||||||
// Perform parse (`yy::parser` is auto-generated from `parser.y`)
|
|
||||||
if (yy::parser parser; parser.parse() != 0) {
|
|
||||||
// Exited due to YYABORT or YYNOMEM
|
// Exited due to YYABORT or YYNOMEM
|
||||||
fatal("Unrecoverable error while parsing"); // LCOV_EXCL_LINE
|
fatal("Unrecoverable error while parsing"); // LCOV_EXCL_LINE
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,9 +120,8 @@ Section *sect_FindSectionByName(std::string const &name) {
|
|||||||
++nbSectErrors; \
|
++nbSectErrors; \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
static unsigned int mergeSectUnion(
|
static unsigned int
|
||||||
Section §, SectionType type, uint32_t org, uint8_t alignment, uint16_t alignOffset
|
mergeSectUnion(Section §, uint32_t org, uint8_t alignment, uint16_t alignOffset) {
|
||||||
) {
|
|
||||||
unsigned int nbSectErrors = 0;
|
unsigned int nbSectErrors = 0;
|
||||||
|
|
||||||
assume(alignment < 16); // Should be ensured by the caller
|
assume(alignment < 16); // Should be ensured by the caller
|
||||||
@@ -133,12 +132,6 @@ static unsigned int mergeSectUnion(
|
|||||||
uint32_t sectAlignSize = 1u << sect.align;
|
uint32_t sectAlignSize = 1u << sect.align;
|
||||||
uint32_t sectAlignMask = sectAlignSize - 1;
|
uint32_t sectAlignMask = sectAlignSize - 1;
|
||||||
|
|
||||||
// Unionized sections only need "compatible" constraints, and they end up with the strictest
|
|
||||||
// combination of both.
|
|
||||||
if (sectTypeHasData(type)) {
|
|
||||||
sectError("Cannot declare ROM sections as `UNION`");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (org != UINT32_MAX) {
|
if (org != UINT32_MAX) {
|
||||||
// If both are fixed, they must be the same
|
// If both are fixed, they must be the same
|
||||||
if (sect.org != UINT32_MAX && sect.org != org) {
|
if (sect.org != UINT32_MAX && sect.org != org) {
|
||||||
@@ -266,12 +259,10 @@ static void mergeSections(
|
|||||||
} else {
|
} else {
|
||||||
switch (mod) {
|
switch (mod) {
|
||||||
case SECTION_UNION:
|
case SECTION_UNION:
|
||||||
case SECTION_FRAGMENT:
|
case SECTION_FRAGMENT: {
|
||||||
nbSectErrors += mod == SECTION_UNION
|
unsigned int (*merge)(Section &, uint32_t, uint8_t, uint16_t) =
|
||||||
? mergeSectUnion(sect, type, org, alignment, alignOffset)
|
mod == SECTION_UNION ? mergeSectUnion : mergeFragments;
|
||||||
: mergeFragments(sect, org, alignment, alignOffset);
|
nbSectErrors += merge(sect, org, alignment, alignOffset);
|
||||||
|
|
||||||
// Common checks
|
|
||||||
|
|
||||||
// If the section's bank is unspecified, override it
|
// If the section's bank is unspecified, override it
|
||||||
if (sect.bank == UINT32_MAX) {
|
if (sect.bank == UINT32_MAX) {
|
||||||
@@ -282,6 +273,7 @@ static void mergeSections(
|
|||||||
sectError("Section already declared with different bank %" PRIu32, sect.bank);
|
sectError("Section already declared with different bank %" PRIu32, sect.bank);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case SECTION_NORMAL:
|
case SECTION_NORMAL:
|
||||||
errorNoTrace([&]() {
|
errorNoTrace([&]() {
|
||||||
@@ -513,6 +505,11 @@ void sect_NewSection(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mod == SECTION_UNION && sectTypeHasData(type)) {
|
||||||
|
error("Cannot declare ROM sections as `UNION`");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (currentLoadSection) {
|
if (currentLoadSection) {
|
||||||
sect_EndLoadSection("SECTION");
|
sect_EndLoadSection("SECTION");
|
||||||
}
|
}
|
||||||
@@ -932,7 +929,7 @@ bool sect_BinaryFile(std::string const &name, uint32_t startPos) {
|
|||||||
file = fopen(fullPath->c_str(), "rb");
|
file = fopen(fullPath->c_str(), "rb");
|
||||||
}
|
}
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return fstk_FileError(name, "INCBIN");
|
return fstk_FileError(name, "`INCBIN`");
|
||||||
}
|
}
|
||||||
Defer closeFile{[&] { fclose(file); }};
|
Defer closeFile{[&] { fclose(file); }};
|
||||||
|
|
||||||
@@ -987,7 +984,7 @@ bool sect_BinaryFileSlice(std::string const &name, uint32_t startPos, uint32_t l
|
|||||||
file = fopen(fullPath->c_str(), "rb");
|
file = fopen(fullPath->c_str(), "rb");
|
||||||
}
|
}
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return fstk_FileError(name, "INCBIN");
|
return fstk_FileError(name, "`INCBIN`");
|
||||||
}
|
}
|
||||||
Defer closeFile{[&] { fclose(file); }};
|
Defer closeFile{[&] { fclose(file); }};
|
||||||
|
|
||||||
@@ -1121,12 +1118,12 @@ std::string sect_PushSectionFragmentLiteral() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This section has data (ROM0 or ROMX), so it cannot be a UNION
|
||||||
|
assume(currentSection->modifier != SECTION_UNION);
|
||||||
|
|
||||||
if (currentLoadSection) {
|
if (currentLoadSection) {
|
||||||
fatal("`LOAD` blocks cannot contain fragment literals");
|
fatal("`LOAD` blocks cannot contain fragment literals");
|
||||||
}
|
}
|
||||||
if (currentSection->modifier == SECTION_UNION) {
|
|
||||||
fatal("`SECTION UNION` cannot contain fragment literals");
|
|
||||||
}
|
|
||||||
|
|
||||||
// A section containing a fragment literal has to become a fragment too
|
// A section containing a fragment literal has to become a fragment too
|
||||||
currentSection->modifier = SECTION_FRAGMENT;
|
currentSection->modifier = SECTION_FRAGMENT;
|
||||||
|
|||||||
@@ -53,6 +53,12 @@ bool sym_IsPC(Symbol const *sym) {
|
|||||||
return sym == PCSymbol;
|
return sym == PCSymbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool sym_IsDotScope(std::string const &symName) {
|
||||||
|
// Label scopes `.` and `..` are the only nonlocal identifiers that start with a dot.
|
||||||
|
// Three or more dots are considered a nonsensical local label.
|
||||||
|
return symName == "." || symName == "..";
|
||||||
|
}
|
||||||
|
|
||||||
void sym_ForEach(void (*callback)(Symbol &)) {
|
void sym_ForEach(void (*callback)(Symbol &)) {
|
||||||
for (auto &it : symbols) {
|
for (auto &it : symbols) {
|
||||||
callback(it.second);
|
callback(it.second);
|
||||||
@@ -215,8 +221,8 @@ static void redefinedError(Symbol const &sym) {
|
|||||||
|
|
||||||
static void assumeAlreadyExpanded(std::string const &symName) {
|
static void assumeAlreadyExpanded(std::string const &symName) {
|
||||||
// Either the symbol name is `Global.local` or entirely '.'s (for scopes `.` and `..`),
|
// Either the symbol name is `Global.local` or entirely '.'s (for scopes `.` and `..`),
|
||||||
// but cannot be unqualified `.local`
|
// but cannot be unqualified `.local` or more than two '.'s
|
||||||
assume(!symName.starts_with('.') || symName.find_first_not_of('.') == symName.npos);
|
assume(!symName.starts_with('.') || sym_IsDotScope(symName));
|
||||||
}
|
}
|
||||||
|
|
||||||
static Symbol &createSymbol(std::string const &symName) {
|
static Symbol &createSymbol(std::string const &symName) {
|
||||||
@@ -253,7 +259,7 @@ static bool isAutoScoped(std::string const &symName) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Label scopes `.` and `..` are the only nonlocal identifiers that start with a dot
|
// Label scopes `.` and `..` are the only nonlocal identifiers that start with a dot
|
||||||
if (dotPos == 0 && symName.find_first_not_of('.') == symName.npos) {
|
if (sym_IsDotScope(symName)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -581,7 +587,7 @@ static uint32_t anonLabelID = 0;
|
|||||||
Symbol *sym_AddAnonLabel() {
|
Symbol *sym_AddAnonLabel() {
|
||||||
if (anonLabelID == UINT32_MAX) {
|
if (anonLabelID == UINT32_MAX) {
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
error("Only %" PRIu32 " anonymous labels can be created!", anonLabelID);
|
error("Only %" PRIu32 " anonymous labels can be created", anonLabelID);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ static void incrementErrors() {
|
|||||||
style_Set(stderr, STYLE_RED, true);
|
style_Set(stderr, STYLE_RED, true);
|
||||||
fprintf(
|
fprintf(
|
||||||
stderr,
|
stderr,
|
||||||
"Assembly aborted after the maximum of %" PRIu64 " error%s!",
|
"Assembly aborted after the maximum of %" PRIu64 " error%s",
|
||||||
warnings.nbErrors,
|
warnings.nbErrors,
|
||||||
warnings.nbErrors == 1 ? "" : "s"
|
warnings.nbErrors == 1 ? "" : "s"
|
||||||
);
|
);
|
||||||
@@ -136,7 +136,7 @@ void requireZeroErrors() {
|
|||||||
style_Set(stderr, STYLE_RED, true);
|
style_Set(stderr, STYLE_RED, true);
|
||||||
fprintf(
|
fprintf(
|
||||||
stderr,
|
stderr,
|
||||||
"Assembly aborted with %" PRIu64 " error%s!\n",
|
"Assembly aborted with %" PRIu64 " error%s\n",
|
||||||
warnings.nbErrors,
|
warnings.nbErrors,
|
||||||
warnings.nbErrors == 1 ? "" : "s"
|
warnings.nbErrors == 1 ? "" : "s"
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#include "cli.hpp"
|
#include "cli.hpp"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@@ -21,10 +23,11 @@ static std::vector<size_t>
|
|||||||
|
|
||||||
std::filebuf file;
|
std::filebuf file;
|
||||||
if (!file.open(path, std::ios_base::in)) {
|
if (!file.open(path, std::ios_base::in)) {
|
||||||
|
int errnum = errno;
|
||||||
style_Set(stderr, STYLE_RED, true);
|
style_Set(stderr, STYLE_RED, true);
|
||||||
fputs("FATAL: ", stderr);
|
fputs("FATAL: ", stderr);
|
||||||
style_Reset(stderr);
|
style_Reset(stderr);
|
||||||
fprintf(stderr, "Failed to open at-file \"%s\": %s\n", path.c_str(), strerror(errno));
|
fprintf(stderr, "Failed to open at-file \"%s\": %s\n", path.c_str(), strerror(errnum));
|
||||||
usage.printAndExit(1);
|
usage.printAndExit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
#include "style.hpp"
|
#include "style.hpp"
|
||||||
#include "usage.hpp"
|
#include "usage.hpp"
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
#include "version.hpp"
|
|
||||||
|
|
||||||
#include "fix/fix.hpp"
|
#include "fix/fix.hpp"
|
||||||
#include "fix/mbc.hpp"
|
#include "fix/mbc.hpp"
|
||||||
@@ -252,7 +251,7 @@ static void parseArg(int ch, char *arg) {
|
|||||||
|
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
case 'V':
|
case 'V':
|
||||||
printf("rgbfix %s\n", get_package_version_string());
|
usage.printVersion(false);
|
||||||
exit(0);
|
exit(0);
|
||||||
|
|
||||||
case 'v':
|
case 'v':
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
#include "usage.hpp"
|
#include "usage.hpp"
|
||||||
#include "util.hpp"
|
#include "util.hpp"
|
||||||
#include "verbosity.hpp"
|
#include "verbosity.hpp"
|
||||||
#include "version.hpp"
|
|
||||||
|
|
||||||
#include "gfx/pal_spec.hpp"
|
#include "gfx/pal_spec.hpp"
|
||||||
#include "gfx/process.hpp"
|
#include "gfx/process.hpp"
|
||||||
@@ -127,7 +126,7 @@ static uint16_t readNumber(char const *&str, char const *errPrefix, uint16_t err
|
|||||||
error("%s: expected number, but found nothing", errPrefix);
|
error("%s: expected number, but found nothing", errPrefix);
|
||||||
return errVal;
|
return errVal;
|
||||||
} else if (*number > UINT16_MAX) {
|
} else if (*number > UINT16_MAX) {
|
||||||
error("%s: the number is too large!", errPrefix);
|
error("%s: the number is too large", errPrefix);
|
||||||
return errVal;
|
return errVal;
|
||||||
} else {
|
} else {
|
||||||
return *number;
|
return *number;
|
||||||
@@ -240,7 +239,7 @@ static void parseArg(int ch, char *arg) {
|
|||||||
case 'L':
|
case 'L':
|
||||||
options.inputSlice.left = readNumber(argPtr, "Input slice left coordinate");
|
options.inputSlice.left = readNumber(argPtr, "Input slice left coordinate");
|
||||||
if (options.inputSlice.left > INT16_MAX) {
|
if (options.inputSlice.left > INT16_MAX) {
|
||||||
error("Input slice left coordinate is out of range!");
|
error("Input slice left coordinate is out of range");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
skipBlankSpace(argPtr);
|
skipBlankSpace(argPtr);
|
||||||
@@ -261,7 +260,7 @@ static void parseArg(int ch, char *arg) {
|
|||||||
options.inputSlice.width = readNumber(argPtr, "Input slice width");
|
options.inputSlice.width = readNumber(argPtr, "Input slice width");
|
||||||
skipBlankSpace(argPtr);
|
skipBlankSpace(argPtr);
|
||||||
if (options.inputSlice.width == 0) {
|
if (options.inputSlice.width == 0) {
|
||||||
error("Input slice width may not be 0!");
|
error("Input slice width may not be 0");
|
||||||
}
|
}
|
||||||
if (*argPtr != ',') {
|
if (*argPtr != ',') {
|
||||||
error("Missing comma after width in \"%s\"", arg);
|
error("Missing comma after width in \"%s\"", arg);
|
||||||
@@ -271,7 +270,7 @@ static void parseArg(int ch, char *arg) {
|
|||||||
skipBlankSpace(argPtr);
|
skipBlankSpace(argPtr);
|
||||||
options.inputSlice.height = readNumber(argPtr, "Input slice height");
|
options.inputSlice.height = readNumber(argPtr, "Input slice height");
|
||||||
if (options.inputSlice.height == 0) {
|
if (options.inputSlice.height == 0) {
|
||||||
error("Input slice height may not be 0!");
|
error("Input slice height may not be 0");
|
||||||
}
|
}
|
||||||
if (*argPtr != '\0') {
|
if (*argPtr != '\0') {
|
||||||
error("Unexpected extra characters after slice spec in \"%s\"", arg);
|
error("Unexpected extra characters after slice spec in \"%s\"", arg);
|
||||||
@@ -331,9 +330,9 @@ static void parseArg(int ch, char *arg) {
|
|||||||
error("Number of palettes ('-n') must be a valid number, not \"%s\"", arg);
|
error("Number of palettes ('-n') must be a valid number, not \"%s\"", arg);
|
||||||
}
|
}
|
||||||
if (number > 256) {
|
if (number > 256) {
|
||||||
error("Number of palettes ('-n') must not exceed 256!");
|
error("Number of palettes ('-n') must not exceed 256");
|
||||||
} else if (number == 0) {
|
} else if (number == 0) {
|
||||||
error("Number of palettes ('-n') may not be 0!");
|
error("Number of palettes ('-n') may not be 0");
|
||||||
} else {
|
} else {
|
||||||
options.nbPalettes = number;
|
options.nbPalettes = number;
|
||||||
}
|
}
|
||||||
@@ -389,9 +388,9 @@ static void parseArg(int ch, char *arg) {
|
|||||||
error("Palette size ('-s') must be a valid number, not \"%s\"", arg);
|
error("Palette size ('-s') must be a valid number, not \"%s\"", arg);
|
||||||
}
|
}
|
||||||
if (options.nbColorsPerPal > 4) {
|
if (options.nbColorsPerPal > 4) {
|
||||||
error("Palette size ('-s') must not exceed 4!");
|
error("Palette size ('-s') must not exceed 4");
|
||||||
} else if (options.nbColorsPerPal == 0) {
|
} else if (options.nbColorsPerPal == 0) {
|
||||||
error("Palette size ('-s') may not be 0!");
|
error("Palette size ('-s') may not be 0");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -409,7 +408,7 @@ static void parseArg(int ch, char *arg) {
|
|||||||
|
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
case 'V':
|
case 'V':
|
||||||
printf("rgbgfx %s\n", get_package_version_string());
|
usage.printVersion(false);
|
||||||
exit(0);
|
exit(0);
|
||||||
|
|
||||||
case 'v':
|
case 'v':
|
||||||
@@ -481,7 +480,7 @@ static void verboseOutputConfig() {
|
|||||||
|
|
||||||
style_Set(stderr, STYLE_MAGENTA, false);
|
style_Set(stderr, STYLE_MAGENTA, false);
|
||||||
|
|
||||||
fprintf(stderr, "rgbgfx %s\n", get_package_version_string());
|
usage.printVersion(true);
|
||||||
|
|
||||||
printVVVVVVerbosity();
|
printVVVVVVerbosity();
|
||||||
|
|
||||||
@@ -609,7 +608,7 @@ static void verboseOutputConfig() {
|
|||||||
if (localOptions.reverse) {
|
if (localOptions.reverse) {
|
||||||
fprintf(stderr, "\tReverse image width: %" PRIu16 " tiles\n", options.reversedWidth);
|
fprintf(stderr, "\tReverse image width: %" PRIu16 " tiles\n", options.reversedWidth);
|
||||||
}
|
}
|
||||||
fputs("Ready.\n", stderr);
|
fputs("Ready for conversion\n", stderr);
|
||||||
|
|
||||||
style_Reset(stderr);
|
style_Reset(stderr);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ public:
|
|||||||
|
|
||||||
size_t size() const {
|
size_t size() const {
|
||||||
return std::count_if(RANGE(_colors), [](std::optional<Rgba> const &slot) {
|
return std::count_if(RANGE(_colors), [](std::optional<Rgba> const &slot) {
|
||||||
return slot.has_value() && !slot->isTransparent();
|
return slot.has_value() && slot->isOpaque();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
decltype(_colors) const &raw() const { return _colors; }
|
decltype(_colors) const &raw() const { return _colors; }
|
||||||
@@ -84,7 +84,13 @@ struct Image {
|
|||||||
Rgba &pixel(uint32_t x, uint32_t y) { return png.pixels[y * png.width + x]; }
|
Rgba &pixel(uint32_t x, uint32_t y) { return png.pixels[y * png.width + x]; }
|
||||||
Rgba const &pixel(uint32_t x, uint32_t y) const { return png.pixels[y * png.width + x]; }
|
Rgba const &pixel(uint32_t x, uint32_t y) const { return png.pixels[y * png.width + x]; }
|
||||||
|
|
||||||
bool isSuitableForGrayscale() const {
|
enum GrayscaleResult {
|
||||||
|
GRAY_OK,
|
||||||
|
GRAY_TOO_MANY,
|
||||||
|
GRAY_NONGRAY,
|
||||||
|
GRAY_CONFLICT,
|
||||||
|
};
|
||||||
|
std::pair<GrayscaleResult, std::optional<Rgba>> isSuitableForGrayscale() const {
|
||||||
// Check that all of the grays don't fall into the same "bin"
|
// Check that all of the grays don't fall into the same "bin"
|
||||||
if (colors.size() > options.maxOpaqueColors()) { // Apply the Pigeonhole Principle
|
if (colors.size() > options.maxOpaqueColors()) { // Apply the Pigeonhole Principle
|
||||||
verbosePrint(
|
verbosePrint(
|
||||||
@@ -93,7 +99,7 @@ struct Image {
|
|||||||
colors.size(),
|
colors.size(),
|
||||||
options.maxOpaqueColors()
|
options.maxOpaqueColors()
|
||||||
);
|
);
|
||||||
return false;
|
return {GrayscaleResult::GRAY_TOO_MANY, std::nullopt};
|
||||||
}
|
}
|
||||||
uint8_t bins = 0;
|
uint8_t bins = 0;
|
||||||
for (std::optional<Rgba> const &color : colors) {
|
for (std::optional<Rgba> const &color : colors) {
|
||||||
@@ -106,7 +112,7 @@ struct Image {
|
|||||||
"Found non-gray color #%08x, not using grayscale sorting\n",
|
"Found non-gray color #%08x, not using grayscale sorting\n",
|
||||||
color->toCSS()
|
color->toCSS()
|
||||||
);
|
);
|
||||||
return false;
|
return {GrayscaleResult::GRAY_NONGRAY, color};
|
||||||
}
|
}
|
||||||
uint8_t mask = 1 << color->grayIndex();
|
uint8_t mask = 1 << color->grayIndex();
|
||||||
if (bins & mask) { // Two in the same bin!
|
if (bins & mask) { // Two in the same bin!
|
||||||
@@ -115,11 +121,11 @@ struct Image {
|
|||||||
"Color #%08x conflicts with another one, not using grayscale sorting\n",
|
"Color #%08x conflicts with another one, not using grayscale sorting\n",
|
||||||
color->toCSS()
|
color->toCSS()
|
||||||
);
|
);
|
||||||
return false;
|
return {GrayscaleResult::GRAY_CONFLICT, color};
|
||||||
}
|
}
|
||||||
bins |= mask;
|
bins |= mask;
|
||||||
}
|
}
|
||||||
return true;
|
return {GrayscaleResult::GRAY_OK, std::nullopt};
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit Image(std::string const &path) {
|
explicit Image(std::string const &path) {
|
||||||
@@ -132,15 +138,15 @@ struct Image {
|
|||||||
|
|
||||||
// Validate input slice
|
// Validate input slice
|
||||||
if (options.inputSlice.width == 0 && png.width % 8 != 0) {
|
if (options.inputSlice.width == 0 && png.width % 8 != 0) {
|
||||||
fatal("Image width (%" PRIu32 " pixels) is not a multiple of 8!", png.width);
|
fatal("Image width (%" PRIu32 " pixels) is not a multiple of 8", png.width);
|
||||||
}
|
}
|
||||||
if (options.inputSlice.height == 0 && png.height % 8 != 0) {
|
if (options.inputSlice.height == 0 && png.height % 8 != 0) {
|
||||||
fatal("Image height (%" PRIu32 " pixels) is not a multiple of 8!", png.height);
|
fatal("Image height (%" PRIu32 " pixels) is not a multiple of 8", png.height);
|
||||||
}
|
}
|
||||||
if (options.inputSlice.right() > png.width || options.inputSlice.bottom() > png.height) {
|
if (options.inputSlice.right() > png.width || options.inputSlice.bottom() > png.height) {
|
||||||
error(
|
error(
|
||||||
"Image slice ((%" PRIu16 ", %" PRIu16 ") to (%" PRIu32 ", %" PRIu32
|
"Image slice ((%" PRIu16 ", %" PRIu16 ") to (%" PRIu32 ", %" PRIu32
|
||||||
")) is outside the image bounds (%" PRIu32 "x%" PRIu32 ")!",
|
")) is outside the image bounds (%" PRIu32 "x%" PRIu32 ")",
|
||||||
options.inputSlice.left,
|
options.inputSlice.left,
|
||||||
options.inputSlice.top,
|
options.inputSlice.top,
|
||||||
options.inputSlice.right(),
|
options.inputSlice.right(),
|
||||||
@@ -151,8 +157,8 @@ struct Image {
|
|||||||
if (options.inputSlice.width % 8 == 0 && options.inputSlice.height % 8 == 0) {
|
if (options.inputSlice.width % 8 == 0 && options.inputSlice.height % 8 == 0) {
|
||||||
fprintf(
|
fprintf(
|
||||||
stderr,
|
stderr,
|
||||||
"note: Did you mean the slice \"%" PRIu32 ",%" PRIu32 ":%" PRId32 ",%" PRId32
|
" (Did you mean the slice \"%" PRIu32 ",%" PRIu32 ":%" PRId32 ",%" PRId32
|
||||||
"\"? (width and height are in tiles, not pixels!)\n",
|
"\"? The width and height are in tiles, not pixels!)\n",
|
||||||
options.inputSlice.left,
|
options.inputSlice.left,
|
||||||
options.inputSlice.top,
|
options.inputSlice.top,
|
||||||
options.inputSlice.width / 8,
|
options.inputSlice.width / 8,
|
||||||
@@ -181,7 +187,7 @@ struct Image {
|
|||||||
if (uint32_t css = color.toCSS(); ambiguous.find(css) == ambiguous.end()) {
|
if (uint32_t css = color.toCSS(); ambiguous.find(css) == ambiguous.end()) {
|
||||||
error(
|
error(
|
||||||
"Color #%08x is neither transparent (alpha < %u) nor opaque (alpha >= "
|
"Color #%08x is neither transparent (alpha < %u) nor opaque (alpha >= "
|
||||||
"%u) [first seen at x: %" PRIu32 ", y: %" PRIu32 "]",
|
"%u) (first seen at (%" PRIu32 ", %" PRIu32 "))",
|
||||||
css,
|
css,
|
||||||
Rgba::transparency_threshold,
|
Rgba::transparency_threshold,
|
||||||
Rgba::opacity_threshold,
|
Rgba::opacity_threshold,
|
||||||
@@ -195,8 +201,8 @@ struct Image {
|
|||||||
if (std::pair fused{color.toCSS(), other->toCSS()};
|
if (std::pair fused{color.toCSS(), other->toCSS()};
|
||||||
fusions.find(fused) == fusions.end()) {
|
fusions.find(fused) == fusions.end()) {
|
||||||
warnx(
|
warnx(
|
||||||
"Fusing colors #%08x and #%08x into Game Boy color $%04x [first seen "
|
"Colors #%08x and #%08x both reduce to the same Game Boy color $%04x "
|
||||||
"at x: %" PRIu32 ", y: %" PRIu32 "]",
|
"(first seen at (%" PRIu32 ", %" PRIu32 "))",
|
||||||
fused.first,
|
fused.first,
|
||||||
fused.second,
|
fused.second,
|
||||||
color.cgbColor(),
|
color.cgbColor(),
|
||||||
@@ -319,7 +325,7 @@ static void generatePalSpec(Image const &image) {
|
|||||||
// Generate a palette spec from the first few colors in the embedded palette
|
// Generate a palette spec from the first few colors in the embedded palette
|
||||||
std::vector<Rgba> const &embPal = image.png.palette;
|
std::vector<Rgba> const &embPal = image.png.palette;
|
||||||
if (embPal.empty()) {
|
if (embPal.empty()) {
|
||||||
fatal("\"-c embedded\" was given, but the PNG does not have an embedded palette!");
|
fatal("\"-c embedded\" was given, but the PNG does not have an embedded palette");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore extraneous colors if they are unused
|
// Ignore extraneous colors if they are unused
|
||||||
@@ -381,7 +387,7 @@ static std::pair<std::vector<size_t>, std::vector<Palette>>
|
|||||||
"Sorting palette colors by PNG's embedded PLTE chunk without '-c/--colors embedded'"
|
"Sorting palette colors by PNG's embedded PLTE chunk without '-c/--colors embedded'"
|
||||||
);
|
);
|
||||||
sortIndexed(palettes, image.png.palette);
|
sortIndexed(palettes, image.png.palette);
|
||||||
} else if (image.isSuitableForGrayscale()) {
|
} else if (image.isSuitableForGrayscale().first == Image::GRAY_OK) {
|
||||||
sortGrayscale(palettes, image.colors.raw());
|
sortGrayscale(palettes, image.colors.raw());
|
||||||
} else {
|
} else {
|
||||||
sortRgb(palettes);
|
sortRgb(palettes);
|
||||||
@@ -396,7 +402,7 @@ static std::pair<std::vector<size_t>, std::vector<Palette>>
|
|||||||
for (auto [spec, pal] : zip(options.palSpec, palettes)) {
|
for (auto [spec, pal] : zip(options.palSpec, palettes)) {
|
||||||
for (size_t i = 0; i < options.nbColorsPerPal; ++i) {
|
for (size_t i = 0; i < options.nbColorsPerPal; ++i) {
|
||||||
// If the spec has a gap, there's no need to copy anything.
|
// If the spec has a gap, there's no need to copy anything.
|
||||||
if (spec[i].has_value() && !spec[i]->isTransparent()) {
|
if (spec[i].has_value() && spec[i]->isOpaque()) {
|
||||||
pal[i] = spec[i]->cgbColor();
|
pal[i] = spec[i]->cgbColor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -821,7 +827,7 @@ static UniqueTiles dedupTiles(
|
|||||||
if (inputWithoutOutput && matchType == TileData::NOPE) {
|
if (inputWithoutOutput && matchType == TileData::NOPE) {
|
||||||
error(
|
error(
|
||||||
"Tile at (%" PRIu32 ", %" PRIu32
|
"Tile at (%" PRIu32 ", %" PRIu32
|
||||||
") is not within the input tileset, and '-o' was not given!",
|
") is not within the input tileset, and '-o' was not given",
|
||||||
tile.x,
|
tile.x,
|
||||||
tile.y
|
tile.y
|
||||||
);
|
);
|
||||||
@@ -939,14 +945,24 @@ void process() {
|
|||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
|
|
||||||
if (options.palSpecType == Options::DMG) {
|
if (options.palSpecType == Options::DMG) {
|
||||||
|
char const *prefix =
|
||||||
|
"Image is not compatible with a DMG palette specification: it contains";
|
||||||
if (options.hasTransparentPixels) {
|
if (options.hasTransparentPixels) {
|
||||||
fatal(
|
fatal("%s transparent pixels", prefix);
|
||||||
"Image contains transparent pixels, not compatible with a DMG palette specification"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (!image.isSuitableForGrayscale()) {
|
switch (auto const [result, color] = image.isSuitableForGrayscale(); result) {
|
||||||
fatal("Image contains too many or non-gray colors, not compatible with a DMG palette "
|
case Image::GRAY_OK:
|
||||||
"specification");
|
break;
|
||||||
|
case Image::GRAY_TOO_MANY:
|
||||||
|
fatal("%s too many colors (%zu)", prefix, image.colors.size());
|
||||||
|
case Image::GRAY_NONGRAY:
|
||||||
|
fatal("%s a non-gray color #%08x", prefix, color->toCSS());
|
||||||
|
case Image::GRAY_CONFLICT:
|
||||||
|
fatal(
|
||||||
|
"%s a color #%08x that reduces to the same gray shade as another one",
|
||||||
|
prefix,
|
||||||
|
color->toCSS()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -965,7 +981,7 @@ void process() {
|
|||||||
for (uint32_t y = 0; y < 8; ++y) {
|
for (uint32_t y = 0; y < 8; ++y) {
|
||||||
for (uint32_t x = 0; x < 8; ++x) {
|
for (uint32_t x = 0; x < 8; ++x) {
|
||||||
if (Rgba color = tile.pixel(x, y);
|
if (Rgba color = tile.pixel(x, y);
|
||||||
!color.isTransparent() || !options.hasTransparentPixels) {
|
color.isOpaque() || !options.hasTransparentPixels) {
|
||||||
tileColors.insert(color.cgbColor());
|
tileColors.insert(color.cgbColor());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -973,7 +989,7 @@ void process() {
|
|||||||
|
|
||||||
if (tileColors.size() > options.maxOpaqueColors()) {
|
if (tileColors.size() > options.maxOpaqueColors()) {
|
||||||
fatal(
|
fatal(
|
||||||
"Tile at (%" PRIu32 ", %" PRIu32 ") has %zu colors, more than %" PRIu8 "!",
|
"Tile at (%" PRIu32 ", %" PRIu32 ") has %zu colors, more than %" PRIu8,
|
||||||
tile.x,
|
tile.x,
|
||||||
tile.y,
|
tile.y,
|
||||||
tileColors.size(),
|
tileColors.size(),
|
||||||
@@ -1001,7 +1017,7 @@ void process() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
fatal(
|
fatal(
|
||||||
"Tile (%" PRIu32 ", %" PRIu32 ") contains the background color (#%08x)!",
|
"Tile (%" PRIu32 ", %" PRIu32 ") contains the background color (#%08x)",
|
||||||
tile.x,
|
tile.x,
|
||||||
tile.y,
|
tile.y,
|
||||||
options.bgColor->toCSS()
|
options.bgColor->toCSS()
|
||||||
|
|||||||
@@ -112,15 +112,15 @@ void reverse() {
|
|||||||
// Check for weird flag combinations
|
// Check for weird flag combinations
|
||||||
|
|
||||||
if (options.output.empty()) {
|
if (options.output.empty()) {
|
||||||
fatal("Tile data must be provided when reversing an image!");
|
fatal("Tile data must be provided when reversing an image");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.allowDedup && options.tilemap.empty()) {
|
if (options.allowDedup && options.tilemap.empty()) {
|
||||||
warnx("Tile deduplication is enabled, but no tilemap is provided?");
|
warnx("Tile deduplication is enabled, but no tilemap is provided");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.useColorCurve) {
|
if (options.useColorCurve) {
|
||||||
warnx("The color curve is not yet supported in reverse mode...");
|
warnx("The color curve is not yet supported in reverse mode");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.inputSlice.left != 0 || options.inputSlice.top != 0
|
if (options.inputSlice.left != 0 || options.inputSlice.top != 0
|
||||||
@@ -149,13 +149,13 @@ void reverse() {
|
|||||||
|
|
||||||
// By default, assume tiles are not deduplicated, and add the (allegedly) trimmed tiles
|
// By default, assume tiles are not deduplicated, and add the (allegedly) trimmed tiles
|
||||||
size_t const nbTiles = tiles.size() / tileSize;
|
size_t const nbTiles = tiles.size() / tileSize;
|
||||||
verbosePrint(VERB_INFO, "Read %zu tiles.\n", nbTiles);
|
verbosePrint(VERB_INFO, "Read %zu tiles\n", nbTiles);
|
||||||
size_t mapSize = nbTiles + options.trim; // Image size in tiles
|
size_t mapSize = nbTiles + options.trim; // Image size in tiles
|
||||||
std::optional<std::vector<uint8_t>> tilemap;
|
std::optional<std::vector<uint8_t>> tilemap;
|
||||||
if (!options.tilemap.empty()) {
|
if (!options.tilemap.empty()) {
|
||||||
tilemap = readInto(options.tilemap);
|
tilemap = readInto(options.tilemap);
|
||||||
mapSize = tilemap->size();
|
mapSize = tilemap->size();
|
||||||
verbosePrint(VERB_INFO, "Read %zu tilemap entries.\n", mapSize);
|
verbosePrint(VERB_INFO, "Read %zu tilemap entries\n", mapSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mapSize == 0) {
|
if (mapSize == 0) {
|
||||||
@@ -224,7 +224,7 @@ void reverse() {
|
|||||||
break;
|
break;
|
||||||
} else if (nbRead != buf.size()) {
|
} else if (nbRead != buf.size()) {
|
||||||
fatal(
|
fatal(
|
||||||
"Palette data size (%zu) is not a multiple of %zu bytes!\n",
|
"Palette data size (%zu) is not a multiple of %zu bytes\n",
|
||||||
palettes.size() * buf.size() + nbRead,
|
palettes.size() * buf.size() + nbRead,
|
||||||
buf.size()
|
buf.size()
|
||||||
);
|
);
|
||||||
@@ -250,7 +250,7 @@ void reverse() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options.palSpecType == Options::EXPLICIT && palettes != options.palSpec) {
|
if (options.palSpecType == Options::EXPLICIT && palettes != options.palSpec) {
|
||||||
warnx("Colors in the palette file do not match those specified with '-c'!");
|
warnx("Colors in the palette file do not match those specified with '-c'");
|
||||||
// This spacing aligns "...versus with `-c`" above the column of `-c` palettes
|
// This spacing aligns "...versus with `-c`" above the column of `-c` palettes
|
||||||
fputs("Colors specified in the palette file: ...versus with '-c':\n", stderr);
|
fputs("Colors specified in the palette file: ...versus with '-c':\n", stderr);
|
||||||
for (size_t i = 0; i < palettes.size() && i < options.palSpec.size(); ++i) {
|
for (size_t i = 0; i < palettes.size() && i < options.palSpec.size(); ++i) {
|
||||||
@@ -272,7 +272,7 @@ void reverse() {
|
|||||||
}
|
}
|
||||||
} else if (options.palSpecType == Options::EMBEDDED) {
|
} else if (options.palSpecType == Options::EMBEDDED) {
|
||||||
warnx("An embedded palette was requested, but no palette file was specified; ignoring "
|
warnx("An embedded palette was requested, but no palette file was specified; ignoring "
|
||||||
"request.");
|
"request");
|
||||||
} else if (options.palSpecType == Options::EXPLICIT) {
|
} else if (options.palSpecType == Options::EXPLICIT) {
|
||||||
palettes = std::move(options.palSpec); // We won't be using it again.
|
palettes = std::move(options.palSpec); // We won't be using it again.
|
||||||
}
|
}
|
||||||
@@ -299,7 +299,8 @@ void reverse() {
|
|||||||
|
|
||||||
if (uint8_t palID = (attr & 0b111) - options.basePalID; palID > palettes.size()) {
|
if (uint8_t palID = (attr & 0b111) - options.basePalID; palID > palettes.size()) {
|
||||||
error(
|
error(
|
||||||
"Attribute map references palette #%u at (%zu, %zu), but there are only %zu!",
|
"Attribute map references palette #%u at (%zu, %zu), but there are only %zu "
|
||||||
|
"palettes",
|
||||||
palID,
|
palID,
|
||||||
tx,
|
tx,
|
||||||
ty,
|
ty,
|
||||||
|
|||||||
@@ -36,19 +36,6 @@ struct FreeSpace {
|
|||||||
// Table of free space for each bank
|
// Table of free space for each bank
|
||||||
static std::vector<std::deque<FreeSpace>> memory[SECTTYPE_INVALID];
|
static std::vector<std::deque<FreeSpace>> memory[SECTTYPE_INVALID];
|
||||||
|
|
||||||
// Init the free space-modelling structs
|
|
||||||
static void initFreeSpace() {
|
|
||||||
for (SectionType type : EnumSeq(SECTTYPE_INVALID)) {
|
|
||||||
memory[type].resize(sectTypeBanks(type));
|
|
||||||
for (std::deque<FreeSpace> &bankMem : memory[type]) {
|
|
||||||
bankMem.push_back({
|
|
||||||
.address = sectionTypeInfo[type].startAddr,
|
|
||||||
.size = sectionTypeInfo[type].size,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assigns a section to a given memory location
|
// Assigns a section to a given memory location
|
||||||
static void assignSection(Section §ion, MemoryLocation const &location) {
|
static void assignSection(Section §ion, MemoryLocation const &location) {
|
||||||
// Propagate the assigned location to all UNIONs/FRAGMENTs
|
// Propagate the assigned location to all UNIONs/FRAGMENTs
|
||||||
@@ -119,6 +106,16 @@ static std::optional<size_t> getPlacement(Section const §ion, MemoryLocation
|
|||||||
SectionTypeInfo const &typeInfo = sectionTypeInfo[section.type];
|
SectionTypeInfo const &typeInfo = sectionTypeInfo[section.type];
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
if (location.bank < typeInfo.firstBank
|
||||||
|
|| location.bank >= memory[section.type].size() + typeInfo.firstBank) {
|
||||||
|
fatal(
|
||||||
|
"Invalid bank for %s section \"%s\": %" PRIu32,
|
||||||
|
sectionTypeInfo[section.type].name.c_str(),
|
||||||
|
section.name.c_str(),
|
||||||
|
location.bank
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Switch to the beginning of the next bank
|
// Switch to the beginning of the next bank
|
||||||
std::deque<FreeSpace> &bankMem = memory[section.type][location.bank - typeInfo.firstBank];
|
std::deque<FreeSpace> &bankMem = memory[section.type][location.bank - typeInfo.firstBank];
|
||||||
size_t spaceIdx = 0;
|
size_t spaceIdx = 0;
|
||||||
@@ -208,50 +205,37 @@ static std::optional<size_t> getPlacement(Section const §ion, MemoryLocation
|
|||||||
}
|
}
|
||||||
|
|
||||||
static std::string getSectionDescription(Section const §ion) {
|
static std::string getSectionDescription(Section const §ion) {
|
||||||
std::string where;
|
std::string description =
|
||||||
|
"\"" + section.name + "\" (" + sectionTypeInfo[section.type].name + " section) ";
|
||||||
char bank[8], addr[8], mask[8], offset[8];
|
|
||||||
if (section.isBankFixed && sectTypeBanks(section.type) != 1) {
|
if (section.isBankFixed && sectTypeBanks(section.type) != 1) {
|
||||||
|
char bank[8];
|
||||||
snprintf(bank, sizeof(bank), "%02" PRIx32, section.bank);
|
snprintf(bank, sizeof(bank), "%02" PRIx32, section.bank);
|
||||||
}
|
|
||||||
if (section.isAddressFixed) {
|
|
||||||
snprintf(addr, sizeof(addr), "%04" PRIx16, section.org);
|
|
||||||
}
|
|
||||||
if (section.isAlignFixed) {
|
|
||||||
snprintf(mask, sizeof(mask), "%" PRIx16, static_cast<uint16_t>(~section.alignMask));
|
|
||||||
snprintf(offset, sizeof(offset), "%" PRIx16, section.alignOfs);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (section.isBankFixed && sectTypeBanks(section.type) != 1) {
|
|
||||||
if (section.isAddressFixed) {
|
if (section.isAddressFixed) {
|
||||||
where = "at $";
|
char addr[8];
|
||||||
where += bank;
|
snprintf(addr, sizeof(addr), "%04" PRIx16, section.org);
|
||||||
where += ":";
|
description = description + "at $" + bank + ":" + addr;
|
||||||
where += addr;
|
|
||||||
} else if (section.isAlignFixed) {
|
} else if (section.isAlignFixed) {
|
||||||
where = "in bank $";
|
char mask[8];
|
||||||
where += bank;
|
snprintf(mask, sizeof(mask), "%" PRIx16, static_cast<uint16_t>(~section.alignMask));
|
||||||
where += " with align mask $";
|
description = description + "in bank $" + bank + " with align mask $" + mask;
|
||||||
where += mask;
|
|
||||||
} else {
|
} else {
|
||||||
where = "in bank $";
|
description = description + "in bank $" + bank;
|
||||||
where += bank;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (section.isAddressFixed) {
|
if (section.isAddressFixed) {
|
||||||
where = "at address $";
|
char addr[8];
|
||||||
where += addr;
|
snprintf(addr, sizeof(addr), "%04" PRIx16, section.org);
|
||||||
|
description = description + "at address $" + addr;
|
||||||
} else if (section.isAlignFixed) {
|
} else if (section.isAlignFixed) {
|
||||||
where = "with align mask $";
|
char mask[8], offset[8];
|
||||||
where += mask;
|
snprintf(mask, sizeof(mask), "%" PRIx16, static_cast<uint16_t>(~section.alignMask));
|
||||||
where += " and offset $";
|
snprintf(offset, sizeof(offset), "%" PRIx16, section.alignOfs);
|
||||||
where += offset;
|
description = description + "with align mask $" + mask + " and offset $" + offset;
|
||||||
} else {
|
} else {
|
||||||
where = "anywhere";
|
description = description + "anywhere";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return description;
|
||||||
return where;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Places a section in a suitable location, or error out if it fails to.
|
// Places a section in a suitable location, or error out if it fails to.
|
||||||
@@ -310,19 +294,11 @@ static void placeSection(Section §ion) {
|
|||||||
|
|
||||||
if (!section.isBankFixed || !section.isAddressFixed) {
|
if (!section.isBankFixed || !section.isAddressFixed) {
|
||||||
// If a section failed to go to several places, nothing we can report
|
// If a section failed to go to several places, nothing we can report
|
||||||
fatal(
|
fatal("Unable to place %s", getSectionDescription(section).c_str());
|
||||||
"Unable to place \"%s\" (%s section) %s",
|
|
||||||
section.name.c_str(),
|
|
||||||
sectionTypeInfo[section.type].name.c_str(),
|
|
||||||
getSectionDescription(section).c_str()
|
|
||||||
);
|
|
||||||
} else if (section.org + section.size > sectTypeEndAddr(section.type) + 1) {
|
} else if (section.org + section.size > sectTypeEndAddr(section.type) + 1) {
|
||||||
// If the section just can't fit the bank, report that
|
// If the section just can't fit the bank, report that
|
||||||
fatal(
|
fatal(
|
||||||
"Unable to place \"%s\" (%s section) %s: section runs past end of region ($%04x > "
|
"Unable to place %s: section runs past end of region ($%04x > $%04x)",
|
||||||
"$%04x)",
|
|
||||||
section.name.c_str(),
|
|
||||||
sectionTypeInfo[section.type].name.c_str(),
|
|
||||||
getSectionDescription(section).c_str(),
|
getSectionDescription(section).c_str(),
|
||||||
section.org + section.size,
|
section.org + section.size,
|
||||||
sectTypeEndAddr(section.type) + 1
|
sectTypeEndAddr(section.type) + 1
|
||||||
@@ -330,9 +306,7 @@ static void placeSection(Section §ion) {
|
|||||||
} else {
|
} else {
|
||||||
// Otherwise there is overlap with another section
|
// Otherwise there is overlap with another section
|
||||||
fatal(
|
fatal(
|
||||||
"Unable to place \"%s\" (%s section) %s: section overlaps with \"%s\"",
|
"Unable to place %s: section overlaps with \"%s\"",
|
||||||
section.name.c_str(),
|
|
||||||
sectionTypeInfo[section.type].name.c_str(),
|
|
||||||
getSectionDescription(section).c_str(),
|
getSectionDescription(section).c_str(),
|
||||||
out_OverlappingSection(section)->name.c_str()
|
out_OverlappingSection(section)->name.c_str()
|
||||||
);
|
);
|
||||||
@@ -381,36 +355,74 @@ static void categorizeSection(Section §ion) {
|
|||||||
sections.insert(pos, §ion);
|
sections.insert(pos, §ion);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<Section const *> checkOverlayCompat() {
|
static void checkOverlayCompat() {
|
||||||
std::vector<Section const *> unfixedSections;
|
auto isFixed = [](uint8_t constraints) {
|
||||||
|
return (constraints & BANK_CONSTRAINED) && (constraints & ORG_CONSTRAINED);
|
||||||
|
};
|
||||||
|
|
||||||
if (!options.overlayFileName) {
|
std::string unfixedList;
|
||||||
return unfixedSections;
|
|
||||||
|
size_t nbUnfixedSections = 0;
|
||||||
|
for (uint8_t constraints = std::size(unassignedSections); constraints--;) {
|
||||||
|
if (!isFixed(constraints)) {
|
||||||
|
nbUnfixedSections += unassignedSections[constraints].size();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nbUnfixedSections == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t nbListed = 0;
|
||||||
for (uint8_t constraints = std::size(unassignedSections); constraints--;) {
|
for (uint8_t constraints = std::size(unassignedSections); constraints--;) {
|
||||||
if (((constraints & BANK_CONSTRAINED) && (constraints & ORG_CONSTRAINED))
|
if (isFixed(constraints)) {
|
||||||
|| unassignedSections[constraints].empty()) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Section *section : unassignedSections[constraints]) {
|
for (Section const *section : unassignedSections[constraints]) {
|
||||||
unfixedSections.push_back(section);
|
if (nbListed == 10) {
|
||||||
|
unfixedList += "\n- and ";
|
||||||
if (unfixedSections.size() == 10) {
|
unfixedList += std::to_string(nbUnfixedSections - nbListed);
|
||||||
return unfixedSections;
|
unfixedList += " more";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
unfixedList += "\n- \"";
|
||||||
|
unfixedList += section->name;
|
||||||
|
unfixedList += "\" (";
|
||||||
|
if (!(constraints & (BANK_CONSTRAINED | ORG_CONSTRAINED))) {
|
||||||
|
unfixedList += "bank and address";
|
||||||
|
} else if (!(constraints & BANK_CONSTRAINED)) {
|
||||||
|
unfixedList += "bank";
|
||||||
|
} else {
|
||||||
|
assume(!(constraints & ORG_CONSTRAINED));
|
||||||
|
unfixedList += "address";
|
||||||
|
}
|
||||||
|
unfixedList += " not specified)";
|
||||||
|
++nbListed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return unfixedSections;
|
fatal(
|
||||||
|
"All sections must be fixed when using an overlay file; %zu %s not:%s",
|
||||||
|
nbUnfixedSections,
|
||||||
|
nbUnfixedSections == 1 ? "is" : "are",
|
||||||
|
unfixedList.c_str()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void assign_AssignSections() {
|
void assign_AssignSections() {
|
||||||
verbosePrint(VERB_NOTICE, "Beginning assignment...\n");
|
verbosePrint(VERB_NOTICE, "Beginning assignment...\n");
|
||||||
|
|
||||||
// Initialize assignment
|
// Initialize the free space-modelling structs
|
||||||
initFreeSpace();
|
for (SectionType type : EnumSeq(SECTTYPE_INVALID)) {
|
||||||
|
memory[type].resize(sectTypeBanks(type));
|
||||||
|
for (std::deque<FreeSpace> &bankMem : memory[type]) {
|
||||||
|
bankMem.push_back({
|
||||||
|
.address = sectionTypeInfo[type].startAddr,
|
||||||
|
.size = sectionTypeInfo[type].size,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Generate linked lists of sections to assign
|
// Generate linked lists of sections to assign
|
||||||
static uint64_t nbSectionsToAssign = 0; // `static` so `sect_ForEach` callback can see it
|
static uint64_t nbSectionsToAssign = 0; // `static` so `sect_ForEach` callback can see it
|
||||||
@@ -420,26 +432,8 @@ void assign_AssignSections() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Overlaying requires only fully-constrained sections
|
// Overlaying requires only fully-constrained sections
|
||||||
if (std::vector<Section const *> unfixedSections = checkOverlayCompat();
|
if (options.overlayFileName) {
|
||||||
!unfixedSections.empty()) {
|
checkOverlayCompat();
|
||||||
size_t nbUnfixedSections = unfixedSections.size();
|
|
||||||
std::string unfixedList;
|
|
||||||
for (Section const *section : unfixedSections) {
|
|
||||||
unfixedList += "\n- \"";
|
|
||||||
unfixedList += section->name;
|
|
||||||
unfixedList += '"';
|
|
||||||
}
|
|
||||||
if (nbSectionsToAssign > nbUnfixedSections) {
|
|
||||||
unfixedList += "\n- and ";
|
|
||||||
unfixedList += std::to_string(nbSectionsToAssign - nbUnfixedSections);
|
|
||||||
unfixedList += " more";
|
|
||||||
}
|
|
||||||
fatal(
|
|
||||||
"All sections must be fixed when using an overlay file; %" PRIu64 " %s not:%s",
|
|
||||||
nbSectionsToAssign,
|
|
||||||
nbSectionsToAssign == 1 ? "is" : "are",
|
|
||||||
unfixedList.c_str()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign sections in decreasing constraint order
|
// Assign sections in decreasing constraint order
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
#include "link/fstack.hpp"
|
#include "link/fstack.hpp"
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -12,44 +14,47 @@
|
|||||||
|
|
||||||
#include "link/warning.hpp"
|
#include "link/warning.hpp"
|
||||||
|
|
||||||
using TraceNode = std::pair<std::string, uint32_t>;
|
void FileStackNode::printBacktrace(uint32_t curLineNo) const {
|
||||||
|
using TraceItem = std::pair<FileStackNode const *, uint32_t>;
|
||||||
static std::vector<TraceNode> backtrace(FileStackNode const &node, uint32_t curLineNo) {
|
std::vector<TraceItem> items;
|
||||||
if (node.isQuiet && !tracing.loud) {
|
for (TraceItem item{this, curLineNo};;) {
|
||||||
if (node.parent) {
|
auto &[node, itemLineNo] = item;
|
||||||
|
bool loud = !node->isQuiet || tracing.loud;
|
||||||
|
if (loud) {
|
||||||
|
items.emplace_back(node, itemLineNo);
|
||||||
|
}
|
||||||
|
if (!node->parent) {
|
||||||
|
assume(node->type != NODE_REPT && std::holds_alternative<std::string>(node->data));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (loud || node->type != NODE_REPT) {
|
||||||
// Quiet REPT nodes will pass their interior line number up to their parent,
|
// Quiet REPT nodes will pass their interior line number up to their parent,
|
||||||
// which is more precise than the parent's own line number (since that will be
|
// which is more precise than the parent's own line number (since that will be
|
||||||
// the line number of the "REPT?" or "FOR?" itself).
|
// the line number of the "REPT?" or "FOR?" itself).
|
||||||
return backtrace(*node.parent, node.type == NODE_REPT ? curLineNo : node.lineNo);
|
itemLineNo = node->lineNo;
|
||||||
}
|
}
|
||||||
return {}; // LCOV_EXCL_LINE
|
node = &*node->parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!node.parent) {
|
using TraceNode = std::pair<std::string, uint32_t>;
|
||||||
assume(node.type != NODE_REPT && std::holds_alternative<std::string>(node.data));
|
std::vector<TraceNode> traceNodes;
|
||||||
return {
|
traceNodes.reserve(items.size());
|
||||||
{node.name(), curLineNo}
|
for (auto &[node, itemLineNo] : reversed(items)) {
|
||||||
};
|
if (std::holds_alternative<std::vector<uint32_t>>(node->data)) {
|
||||||
}
|
assume(!traceNodes.empty()); // REPT nodes use their parent's name
|
||||||
|
std::string reptName = traceNodes.back().first;
|
||||||
std::vector<TraceNode> traceNodes = backtrace(*node.parent, node.lineNo);
|
if (std::vector<uint32_t> const &nodeIters = node->iters(); !nodeIters.empty()) {
|
||||||
if (std::holds_alternative<std::vector<uint32_t>>(node.data)) {
|
reptName.append(NODE_SEPARATOR REPT_NODE_PREFIX);
|
||||||
assume(!traceNodes.empty()); // REPT nodes use their parent's name
|
reptName.append(std::to_string(nodeIters.back()));
|
||||||
std::string reptName = traceNodes.back().first;
|
}
|
||||||
if (std::vector<uint32_t> const &nodeIters = node.iters(); !nodeIters.empty()) {
|
traceNodes.emplace_back(reptName, itemLineNo);
|
||||||
reptName.append(NODE_SEPARATOR REPT_NODE_PREFIX);
|
} else {
|
||||||
reptName.append(std::to_string(nodeIters.back()));
|
traceNodes.emplace_back(node->name(), itemLineNo);
|
||||||
}
|
}
|
||||||
traceNodes.emplace_back(reptName, curLineNo);
|
|
||||||
} else {
|
|
||||||
traceNodes.emplace_back(node.name(), curLineNo);
|
|
||||||
}
|
}
|
||||||
return traceNodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileStackNode::printBacktrace(uint32_t curLineNo) const {
|
|
||||||
trace_PrintBacktrace(
|
trace_PrintBacktrace(
|
||||||
backtrace(*this, curLineNo),
|
traceNodes,
|
||||||
[](TraceNode const &node) { return node.first.c_str(); },
|
[](TraceNode const &node) { return node.first.c_str(); },
|
||||||
[](TraceNode const &node) { return node.second; }
|
[](TraceNode const &node) { return node.second; }
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
#include "usage.hpp"
|
#include "usage.hpp"
|
||||||
#include "util.hpp" // UpperMap, printChar
|
#include "util.hpp" // UpperMap, printChar
|
||||||
#include "verbosity.hpp"
|
#include "verbosity.hpp"
|
||||||
#include "version.hpp"
|
|
||||||
|
|
||||||
#include "link/assign.hpp"
|
#include "link/assign.hpp"
|
||||||
#include "link/lexer.hpp"
|
#include "link/lexer.hpp"
|
||||||
@@ -283,7 +282,7 @@ static void parseArg(int ch, char *arg) {
|
|||||||
|
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
case 'V':
|
case 'V':
|
||||||
printf("rgblink %s\n", get_package_version_string());
|
usage.printVersion(false);
|
||||||
exit(0);
|
exit(0);
|
||||||
|
|
||||||
case 'v':
|
case 'v':
|
||||||
@@ -330,7 +329,7 @@ static void verboseOutputConfig() {
|
|||||||
|
|
||||||
style_Set(stderr, STYLE_MAGENTA, false);
|
style_Set(stderr, STYLE_MAGENTA, false);
|
||||||
|
|
||||||
fprintf(stderr, "rgblink %s\n", get_package_version_string());
|
usage.printVersion(true);
|
||||||
|
|
||||||
printVVVVVVerbosity();
|
printVVVVVVerbosity();
|
||||||
|
|
||||||
@@ -408,7 +407,7 @@ static void verboseOutputConfig() {
|
|||||||
}
|
}
|
||||||
// -n/--sym
|
// -n/--sym
|
||||||
printPath("Output sym file", options.symFileName);
|
printPath("Output sym file", options.symFileName);
|
||||||
fputs("Ready.\n", stderr);
|
fputs("Ready for linking\n", stderr);
|
||||||
|
|
||||||
style_Reset(stderr);
|
style_Reset(stderr);
|
||||||
}
|
}
|
||||||
@@ -447,11 +446,9 @@ int main(int argc, char *argv[]) {
|
|||||||
if (localOptions.linkerScriptName) {
|
if (localOptions.linkerScriptName) {
|
||||||
verbosePrint(VERB_NOTICE, "Reading linker script...\n");
|
verbosePrint(VERB_NOTICE, "Reading linker script...\n");
|
||||||
|
|
||||||
if (lexer_Init(*localOptions.linkerScriptName)) {
|
if (yy::parser parser; lexer_Init(*localOptions.linkerScriptName) && parser.parse() != 0) {
|
||||||
if (yy::parser parser; parser.parse() != 0) {
|
// Exited due to YYABORT or YYNOMEM
|
||||||
// Exited due to YYABORT or YYNOMEM
|
fatal("Unrecoverable error while reading linker script"); // LCOV_EXCL_LINE
|
||||||
fatal("Unrecoverable error while reading linker script"); // LCOV_EXCL_LINE
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the linker script produced any errors, some sections may be in an invalid state
|
// If the linker script produced any errors, some sections may be in an invalid state
|
||||||
@@ -468,4 +465,6 @@ int main(int argc, char *argv[]) {
|
|||||||
patch_ApplyPatches();
|
patch_ApplyPatches();
|
||||||
requireZeroErrors();
|
requireZeroErrors();
|
||||||
out_WriteFiles();
|
out_WriteFiles();
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ static int64_t readLong(FILE *file) {
|
|||||||
tryRead(readLong, int64_t, INT64_MAX, long, var, file, __VA_ARGS__)
|
tryRead(readLong, int64_t, INT64_MAX, long, var, file, __VA_ARGS__)
|
||||||
|
|
||||||
// Helper macro to read a byte from a file to a var, or error out if it fails to.
|
// Helper macro to read a byte from a file to a var, or error out if it fails to.
|
||||||
#define tryGetc(type, var, file, ...) tryRead(getc, int, EOF, type, var, file, __VA_ARGS__)
|
#define tryGetc(var, file, ...) tryRead(getc, int, EOF, uint8_t, var, file, __VA_ARGS__)
|
||||||
|
|
||||||
// Helper macro to read a '\0'-terminated string from a file, or error out if it fails to.
|
// Helper macro to read a '\0'-terminated string from a file, or error out if it fails to.
|
||||||
#define tryReadString(var, file, ...) \
|
#define tryReadString(var, file, ...) \
|
||||||
@@ -100,27 +100,31 @@ static void readFileStackNode(
|
|||||||
tryReadLong(
|
tryReadLong(
|
||||||
parentID, file, "%s: Cannot read node #%" PRIu32 "'s parent ID: %s", fileName, nodeID
|
parentID, file, "%s: Cannot read node #%" PRIu32 "'s parent ID: %s", fileName, nodeID
|
||||||
);
|
);
|
||||||
node.parent = parentID != UINT32_MAX ? &fileNodes[parentID] : nullptr;
|
if (parentID == UINT32_MAX) {
|
||||||
|
node.parent = nullptr;
|
||||||
|
} else if (parentID >= fileNodes.size()) {
|
||||||
|
fatal("%s: Node #%" PRIu32 " has invalid parent ID #%" PRIu32, fileName, nodeID, parentID);
|
||||||
|
} else {
|
||||||
|
node.parent = &fileNodes[parentID];
|
||||||
|
}
|
||||||
|
|
||||||
tryReadLong(
|
tryReadLong(
|
||||||
node.lineNo, file, "%s: Cannot read node #%" PRIu32 "'s line number: %s", fileName, nodeID
|
node.lineNo, file, "%s: Cannot read node #%" PRIu32 "'s line number: %s", fileName, nodeID
|
||||||
);
|
);
|
||||||
|
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
tryGetc(uint8_t, type, file, "%s: Cannot read node #%" PRIu32 "'s type: %s", fileName, nodeID);
|
tryGetc(type, file, "%s: Cannot read node #%" PRIu32 "'s type: %s", fileName, nodeID);
|
||||||
node.type = static_cast<FileStackNodeType>(type & ~(1 << FSTACKNODE_QUIET_BIT));
|
switch (type & ~(1 << FSTACKNODE_QUIET_BIT)) {
|
||||||
node.isQuiet = (type & (1 << FSTACKNODE_QUIET_BIT)) != 0;
|
|
||||||
|
|
||||||
switch (node.type) {
|
|
||||||
case NODE_FILE:
|
case NODE_FILE:
|
||||||
case NODE_MACRO:
|
case NODE_MACRO:
|
||||||
|
node.type = FileStackNodeType(type);
|
||||||
node.data = "";
|
node.data = "";
|
||||||
tryReadString(
|
tryReadString(
|
||||||
node.name(), file, "%s: Cannot read node #%" PRIu32 "'s file name: %s", fileName, nodeID
|
node.name(), file, "%s: Cannot read node #%" PRIu32 "'s file name: %s", fileName, nodeID
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NODE_REPT: {
|
case NODE_REPT: {
|
||||||
|
node.type = NODE_REPT;
|
||||||
uint32_t depth;
|
uint32_t depth;
|
||||||
tryReadLong(
|
tryReadLong(
|
||||||
depth, file, "%s: Cannot read node #%" PRIu32 "'s REPT depth: %s", fileName, nodeID
|
depth, file, "%s: Cannot read node #%" PRIu32 "'s REPT depth: %s", fileName, nodeID
|
||||||
@@ -143,8 +147,13 @@ static void readFileStackNode(
|
|||||||
nodeID
|
nodeID
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
fatal("%s: Node #%" PRIu32 " has unknown type 0x%02x", fileName, nodeID, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
node.isQuiet = (type & (1 << FSTACKNODE_QUIET_BIT)) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reads a symbol from a file.
|
// Reads a symbol from a file.
|
||||||
@@ -152,20 +161,25 @@ static void readSymbol(
|
|||||||
FILE *file, Symbol &symbol, char const *fileName, std::vector<FileStackNode> const &fileNodes
|
FILE *file, Symbol &symbol, char const *fileName, std::vector<FileStackNode> const &fileNodes
|
||||||
) {
|
) {
|
||||||
tryReadString(symbol.name, file, "%s: Cannot read symbol name: %s", fileName);
|
tryReadString(symbol.name, file, "%s: Cannot read symbol name: %s", fileName);
|
||||||
tryGetc(
|
|
||||||
ExportLevel,
|
uint8_t type;
|
||||||
symbol.type,
|
tryGetc(type, file, "%s: Cannot read `%s`'s type: %s", fileName, symbol.name.c_str());
|
||||||
file,
|
if (type >= SYMTYPE_INVALID) {
|
||||||
"%s: Cannot read `%s`'s type: %s",
|
fatal("%s: `%s` has unknown type 0x%02x", fileName, symbol.name.c_str(), type);
|
||||||
fileName,
|
} else {
|
||||||
symbol.name.c_str()
|
symbol.type = ExportLevel(type);
|
||||||
);
|
}
|
||||||
|
|
||||||
// If the symbol is defined in this file, read its definition
|
// If the symbol is defined in this file, read its definition
|
||||||
if (symbol.type != SYMTYPE_IMPORT) {
|
if (symbol.type != SYMTYPE_IMPORT) {
|
||||||
uint32_t nodeID;
|
uint32_t nodeID;
|
||||||
tryReadLong(
|
tryReadLong(
|
||||||
nodeID, file, "%s: Cannot read `%s`'s node ID: %s", fileName, symbol.name.c_str()
|
nodeID, file, "%s: Cannot read `%s`'s node ID: %s", fileName, symbol.name.c_str()
|
||||||
);
|
);
|
||||||
|
if (nodeID >= fileNodes.size()) {
|
||||||
|
fatal("%s: `%s` has invalid node ID #%" PRIu32, fileName, symbol.name.c_str(), nodeID);
|
||||||
|
}
|
||||||
|
|
||||||
symbol.src = &fileNodes[nodeID];
|
symbol.src = &fileNodes[nodeID];
|
||||||
tryReadLong(
|
tryReadLong(
|
||||||
symbol.lineNo,
|
symbol.lineNo,
|
||||||
@@ -212,6 +226,15 @@ static void readPatch(
|
|||||||
sectName.c_str(),
|
sectName.c_str(),
|
||||||
patchID
|
patchID
|
||||||
);
|
);
|
||||||
|
if (nodeID >= fileNodes.size()) {
|
||||||
|
fatal(
|
||||||
|
"%s: \"%s\"'s patch #%" PRIu32 " has invalid node ID #%" PRIu32,
|
||||||
|
fileName,
|
||||||
|
sectName.c_str(),
|
||||||
|
patchID,
|
||||||
|
nodeID
|
||||||
|
);
|
||||||
|
}
|
||||||
patch.src = &fileNodes[nodeID];
|
patch.src = &fileNodes[nodeID];
|
||||||
|
|
||||||
tryReadLong(
|
tryReadLong(
|
||||||
@@ -247,9 +270,8 @@ static void readPatch(
|
|||||||
patchID
|
patchID
|
||||||
);
|
);
|
||||||
|
|
||||||
PatchType type;
|
uint8_t type;
|
||||||
tryGetc(
|
tryGetc(
|
||||||
PatchType,
|
|
||||||
type,
|
type,
|
||||||
file,
|
file,
|
||||||
"%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s type: %s",
|
"%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s type: %s",
|
||||||
@@ -257,7 +279,17 @@ static void readPatch(
|
|||||||
sectName.c_str(),
|
sectName.c_str(),
|
||||||
patchID
|
patchID
|
||||||
);
|
);
|
||||||
patch.type = type;
|
if (type >= PATCHTYPE_INVALID) {
|
||||||
|
fatal(
|
||||||
|
"%s: \"%s\"'s patch #%" PRIu32 " has unknown type 0x%02x",
|
||||||
|
fileName,
|
||||||
|
sectName.c_str(),
|
||||||
|
patchID,
|
||||||
|
type
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
patch.type = PatchType(type);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t rpnSize;
|
uint32_t rpnSize;
|
||||||
tryReadLong(
|
tryReadLong(
|
||||||
@@ -281,13 +313,6 @@ static void readPatch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets a patch's `pcSection` from its `pcSectionID`.
|
|
||||||
static void
|
|
||||||
linkPatchToPCSect(Patch &patch, std::vector<std::unique_ptr<Section>> const &fileSections) {
|
|
||||||
patch.pcSection =
|
|
||||||
patch.pcSectionID != UINT32_MAX ? fileSections[patch.pcSectionID].get() : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reads a section from a file.
|
// Reads a section from a file.
|
||||||
static void readSection(
|
static void readSection(
|
||||||
FILE *file, Section §ion, char const *fileName, std::vector<FileStackNode> const &fileNodes
|
FILE *file, Section §ion, char const *fileName, std::vector<FileStackNode> const &fileNodes
|
||||||
@@ -296,11 +321,16 @@ static void readSection(
|
|||||||
uint8_t byte;
|
uint8_t byte;
|
||||||
|
|
||||||
tryReadString(section.name, file, "%s: Cannot read section name: %s", fileName);
|
tryReadString(section.name, file, "%s: Cannot read section name: %s", fileName);
|
||||||
|
|
||||||
uint32_t nodeID;
|
uint32_t nodeID;
|
||||||
tryReadLong(
|
tryReadLong(
|
||||||
nodeID, file, "%s: Cannot read \"%s\"'s node ID: %s", fileName, section.name.c_str()
|
nodeID, file, "%s: Cannot read \"%s\"'s node ID: %s", fileName, section.name.c_str()
|
||||||
);
|
);
|
||||||
|
if (nodeID >= fileNodes.size()) {
|
||||||
|
fatal("%s: \"%s\" has invalid node ID #%" PRIu32, fileName, section.name.c_str(), nodeID);
|
||||||
|
}
|
||||||
section.src = &fileNodes[nodeID];
|
section.src = &fileNodes[nodeID];
|
||||||
|
|
||||||
tryReadLong(
|
tryReadLong(
|
||||||
section.lineNo,
|
section.lineNo,
|
||||||
file,
|
file,
|
||||||
@@ -310,18 +340,23 @@ static void readSection(
|
|||||||
);
|
);
|
||||||
tryReadLong(tmp, file, "%s: Cannot read \"%s\"'s' size: %s", fileName, section.name.c_str());
|
tryReadLong(tmp, file, "%s: Cannot read \"%s\"'s' size: %s", fileName, section.name.c_str());
|
||||||
if (tmp < 0 || tmp > UINT16_MAX) {
|
if (tmp < 0 || tmp > UINT16_MAX) {
|
||||||
fatal("\"%s\"'s section size ($%" PRIx32 ") is invalid", section.name.c_str(), tmp);
|
fatal(
|
||||||
|
"%s: \"%s\"'s section size ($%" PRIx32 ") is invalid",
|
||||||
|
fileName,
|
||||||
|
section.name.c_str(),
|
||||||
|
tmp
|
||||||
|
);
|
||||||
}
|
}
|
||||||
section.size = tmp;
|
section.size = tmp;
|
||||||
section.offset = 0;
|
section.offset = 0;
|
||||||
tryGetc(
|
|
||||||
uint8_t, byte, file, "%s: Cannot read \"%s\"'s type: %s", fileName, section.name.c_str()
|
tryGetc(byte, file, "%s: Cannot read \"%s\"'s type: %s", fileName, section.name.c_str());
|
||||||
);
|
|
||||||
if (uint8_t type = byte & SECTTYPE_TYPE_MASK; type >= SECTTYPE_INVALID) {
|
if (uint8_t type = byte & SECTTYPE_TYPE_MASK; type >= SECTTYPE_INVALID) {
|
||||||
fatal("\"%s\" has unknown section type 0x%02x", section.name.c_str(), type);
|
fatal("%s: \"%s\" has unknown section type 0x%02x", fileName, section.name.c_str(), type);
|
||||||
} else {
|
} else {
|
||||||
section.type = SectionType(type);
|
section.type = SectionType(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (byte & (1 << SECTTYPE_UNION_BIT)) {
|
if (byte & (1 << SECTTYPE_UNION_BIT)) {
|
||||||
section.modifier = SECTION_UNION;
|
section.modifier = SECTION_UNION;
|
||||||
} else if (byte & (1 << SECTTYPE_FRAGMENT_BIT)) {
|
} else if (byte & (1 << SECTTYPE_FRAGMENT_BIT)) {
|
||||||
@@ -339,14 +374,7 @@ static void readSection(
|
|||||||
tryReadLong(tmp, file, "%s: Cannot read \"%s\"'s bank: %s", fileName, section.name.c_str());
|
tryReadLong(tmp, file, "%s: Cannot read \"%s\"'s bank: %s", fileName, section.name.c_str());
|
||||||
section.isBankFixed = tmp >= 0;
|
section.isBankFixed = tmp >= 0;
|
||||||
section.bank = tmp;
|
section.bank = tmp;
|
||||||
tryGetc(
|
tryGetc(byte, file, "%s: Cannot read \"%s\"'s alignment: %s", fileName, section.name.c_str());
|
||||||
uint8_t,
|
|
||||||
byte,
|
|
||||||
file,
|
|
||||||
"%s: Cannot read \"%s\"'s alignment: %s",
|
|
||||||
fileName,
|
|
||||||
section.name.c_str()
|
|
||||||
);
|
|
||||||
if (byte > 16) {
|
if (byte > 16) {
|
||||||
byte = 16;
|
byte = 16;
|
||||||
}
|
}
|
||||||
@@ -420,19 +448,15 @@ void obj_ReadFile(std::string const &filePath, size_t fileID) {
|
|||||||
}
|
}
|
||||||
Defer closeFile{[&] { fclose(file); }};
|
Defer closeFile{[&] { fclose(file); }};
|
||||||
|
|
||||||
// First, check if the object is a RGBDS object or a SDCC one. If the first byte is 'R',
|
// First, check if the object is a RGBDS object, a SDCC one, or neither.
|
||||||
// we'll assume it's a RGBDS object file, and otherwise, that it's a SDCC object file.
|
// A single `ungetc` is guaranteed to work.
|
||||||
int c = getc(file);
|
switch (ungetc(getc(file), file)) {
|
||||||
|
|
||||||
ungetc(c, file); // Guaranteed to work
|
|
||||||
switch (c) {
|
|
||||||
case EOF:
|
case EOF:
|
||||||
fatal("File \"%s\" is empty!", fileName);
|
fatal("File \"%s\" is empty", fileName);
|
||||||
|
|
||||||
case 'R':
|
case 'X':
|
||||||
break;
|
case 'D':
|
||||||
|
case 'Q': {
|
||||||
default:
|
|
||||||
// This is (probably) a SDCC object file, defer the rest of detection to it.
|
// 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
|
// Since SDCC does not provide line info, everything will be reported as coming from the
|
||||||
// object file. It's better than nothing.
|
// object file. It's better than nothing.
|
||||||
@@ -450,11 +474,16 @@ void obj_ReadFile(std::string const &filePath, size_t fileID) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Begin by reading the magic bytes
|
case 'R':
|
||||||
int matchedElems;
|
// Check the magic byte signature for a RGB object file.
|
||||||
|
if (char magic[literal_strlen(RGBDS_OBJECT_VERSION_STRING)];
|
||||||
|
fread(magic, 1, sizeof(magic), file) == sizeof(magic)
|
||||||
|
&& !memcmp(magic, RGBDS_OBJECT_VERSION_STRING, sizeof(magic))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
[[fallthrough]];
|
||||||
|
|
||||||
if (fscanf(file, RGBDS_OBJECT_VERSION_STRING "%n", &matchedElems) == 1
|
default:
|
||||||
&& matchedElems != literal_strlen(RGBDS_OBJECT_VERSION_STRING)) {
|
|
||||||
fatal("%s: Not a RGBDS object file", fileName);
|
fatal("%s: Not a RGBDS object file", fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -498,7 +527,16 @@ void obj_ReadFile(std::string const &filePath, size_t fileID) {
|
|||||||
readSymbol(file, sym, fileName, nodes[fileID]);
|
readSymbol(file, sym, fileName, nodes[fileID]);
|
||||||
sym_AddSymbol(sym);
|
sym_AddSymbol(sym);
|
||||||
if (std::holds_alternative<Label>(sym.data)) {
|
if (std::holds_alternative<Label>(sym.data)) {
|
||||||
++nbSymPerSect[std::get<Label>(sym.data).sectionID];
|
int32_t sectionID = std::get<Label>(sym.data).sectionID;
|
||||||
|
if (sectionID < 0 || static_cast<size_t>(sectionID) >= nbSymPerSect.size()) {
|
||||||
|
fatal(
|
||||||
|
"%s: `%s` has invalid section ID #%" PRId32,
|
||||||
|
fileName,
|
||||||
|
sym.name.c_str(),
|
||||||
|
sectionID
|
||||||
|
);
|
||||||
|
}
|
||||||
|
++nbSymPerSect[sectionID];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -521,15 +559,41 @@ void obj_ReadFile(std::string const &filePath, size_t fileID) {
|
|||||||
Assertion &assertion = patch_AddAssertion();
|
Assertion &assertion = patch_AddAssertion();
|
||||||
|
|
||||||
readAssertion(file, assertion, fileName, i, nodes[fileID]);
|
readAssertion(file, assertion, fileName, i, nodes[fileID]);
|
||||||
linkPatchToPCSect(assertion.patch, fileSections);
|
|
||||||
|
if (assertion.patch.pcSectionID == UINT32_MAX) {
|
||||||
|
assertion.patch.pcSection = nullptr;
|
||||||
|
} else if (assertion.patch.pcSectionID >= fileSections.size()) {
|
||||||
|
fatal(
|
||||||
|
"%s: Assertion #%" PRIu32 "'s patch has invalid section ID #%" PRIu32,
|
||||||
|
fileName,
|
||||||
|
i,
|
||||||
|
assertion.patch.pcSectionID
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
assertion.patch.pcSection = fileSections[assertion.patch.pcSectionID].get();
|
||||||
|
}
|
||||||
|
|
||||||
assertion.fileSymbols = &fileSymbols;
|
assertion.fileSymbols = &fileSymbols;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Give patches' PC section pointers to their sections
|
// Give patches' PC section pointers to their sections
|
||||||
for (std::unique_ptr<Section> const § : fileSections) {
|
for (std::unique_ptr<Section> const § : fileSections) {
|
||||||
if (sectTypeHasData(sect->type)) {
|
if (!sectTypeHasData(sect->type)) {
|
||||||
for (Patch &patch : sect->patches) {
|
continue;
|
||||||
linkPatchToPCSect(patch, fileSections);
|
}
|
||||||
|
for (size_t i = 0; i < sect->patches.size(); ++i) {
|
||||||
|
if (Patch &patch = sect->patches[i]; patch.pcSectionID == UINT32_MAX) {
|
||||||
|
patch.pcSection = nullptr;
|
||||||
|
} else if (patch.pcSectionID >= fileSections.size()) {
|
||||||
|
fatal(
|
||||||
|
"%s: \"%s\"'s patch #%zu has invalid section ID #%" PRIu32,
|
||||||
|
fileName,
|
||||||
|
sect->name.c_str(),
|
||||||
|
i,
|
||||||
|
patch.pcSectionID
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
patch.pcSection = fileSections[patch.pcSectionID].get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,9 +71,7 @@ void out_AddSection(Section const §ion) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
uint32_t targetBank = section.bank - sectionTypeInfo[section.type].firstBank;
|
uint32_t targetBank = section.bank - sectionTypeInfo[section.type].firstBank;
|
||||||
uint32_t minNbBanks = targetBank + 1;
|
if (targetBank >= maxNbBanks[section.type]) {
|
||||||
|
|
||||||
if (minNbBanks > maxNbBanks[section.type]) {
|
|
||||||
fatal(
|
fatal(
|
||||||
"Section \"%s\" has an invalid bank range (%" PRIu32 " > %" PRIu32 ")",
|
"Section \"%s\" has an invalid bank range (%" PRIu32 " > %" PRIu32 ")",
|
||||||
section.name.c_str(),
|
section.name.c_str(),
|
||||||
@@ -81,16 +79,14 @@ void out_AddSection(Section const §ion) {
|
|||||||
maxNbBanks[section.type] - 1
|
maxNbBanks[section.type] - 1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (sections[section.type].size() <= targetBank) {
|
||||||
for (uint32_t i = sections[section.type].size(); i < minNbBanks; ++i) {
|
sections[section.type].resize(targetBank + 1);
|
||||||
sections[section.type].emplace_back();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Insert section while keeping the list sorted by increasing org
|
||||||
std::deque<Section const *> &bankSections =
|
std::deque<Section const *> &bankSections =
|
||||||
section.size ? sections[section.type][targetBank].sections
|
section.size ? sections[section.type][targetBank].sections
|
||||||
: sections[section.type][targetBank].zeroLenSections;
|
: sections[section.type][targetBank].zeroLenSections;
|
||||||
|
|
||||||
// Insert section while keeping the list sorted by increasing org
|
|
||||||
auto pos = bankSections.begin();
|
auto pos = bankSections.begin();
|
||||||
while (pos != bankSections.end() && (*pos)->org < section.org) {
|
while (pos != bankSections.end() && (*pos)->org < section.org) {
|
||||||
++pos;
|
++pos;
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ static uint32_t getRPNByte(uint8_t const *&expression, int32_t &size, Patch cons
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Symbol const *getSymbol(std::vector<Symbol> const &symbolList, uint32_t index) {
|
static Symbol const *getSymbol(std::vector<Symbol> const &symbolList, uint32_t index) {
|
||||||
assume(index != UINT32_MAX); // PC needs to be handled specially, not here
|
assume(index != UINT32_MAX); // PC needs to be handled specially, not here
|
||||||
|
assume(index < symbolList.size()); // This needs to be checked before calling
|
||||||
Symbol const &symbol = symbolList[index];
|
Symbol const &symbol = symbolList[index];
|
||||||
|
|
||||||
// If the symbol is defined elsewhere...
|
// If the symbol is defined elsewhere...
|
||||||
@@ -270,17 +271,19 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
value = op_shift_right_unsigned(popRPN(patch), value);
|
value = op_shift_right_unsigned(popRPN(patch), value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_BANK_SYM:
|
case RPN_BANK_SYM: {
|
||||||
value = 0;
|
uint32_t symID = 0;
|
||||||
for (uint8_t shift = 0; shift < 32; shift += 8) {
|
for (uint8_t shift = 0; shift < 32; shift += 8) {
|
||||||
value |= getRPNByte(expression, size, patch) << shift;
|
symID |= getRPNByte(expression, size, patch) << shift;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Symbol const *symbol = getSymbol(fileSymbols, value); !symbol) {
|
if (symID >= fileSymbols.size()) {
|
||||||
|
fatalAt(patch, "Requested `BANK()` of invalid symbol ID #%" PRIu32, symID);
|
||||||
|
} else if (Symbol const *symbol = getSymbol(fileSymbols, symID); !symbol) {
|
||||||
errorAt(
|
errorAt(
|
||||||
patch,
|
patch,
|
||||||
"Requested `BANK()` of undefined symbol `%s`",
|
"Requested `BANK()` of undefined symbol `%s`",
|
||||||
fileSymbols[value].name.c_str()
|
fileSymbols[symID].name.c_str()
|
||||||
);
|
);
|
||||||
isError = true;
|
isError = true;
|
||||||
value = 1;
|
value = 1;
|
||||||
@@ -290,12 +293,13 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
errorAt(
|
errorAt(
|
||||||
patch,
|
patch,
|
||||||
"Requested `BANK()` of non-label symbol `%s`",
|
"Requested `BANK()` of non-label symbol `%s`",
|
||||||
fileSymbols[value].name.c_str()
|
fileSymbols[symID].name.c_str()
|
||||||
);
|
);
|
||||||
isError = true;
|
isError = true;
|
||||||
value = 1;
|
value = 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case RPN_BANK_SECT: {
|
case RPN_BANK_SECT: {
|
||||||
// `expression` is not guaranteed to be '\0'-terminated. If it is not,
|
// `expression` is not guaranteed to be '\0'-terminated. If it is not,
|
||||||
@@ -420,13 +424,13 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_SYM:
|
case RPN_SYM: {
|
||||||
value = 0;
|
uint32_t symID = 0;
|
||||||
for (uint8_t shift = 0; shift < 32; shift += 8) {
|
for (uint8_t shift = 0; shift < 32; shift += 8) {
|
||||||
value |= getRPNByte(expression, size, patch) << shift;
|
symID |= getRPNByte(expression, size, patch) << shift;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value == -1) { // PC
|
if (symID == UINT32_MAX) { // PC
|
||||||
if (patch.pcSection) {
|
if (patch.pcSection) {
|
||||||
value = patch.pcOffset + patch.pcSection->org;
|
value = patch.pcOffset + patch.pcSection->org;
|
||||||
} else {
|
} else {
|
||||||
@@ -434,9 +438,12 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
value = 0;
|
value = 0;
|
||||||
isError = true;
|
isError = true;
|
||||||
}
|
}
|
||||||
} else if (Symbol const *symbol = getSymbol(fileSymbols, value); !symbol) {
|
} else if (symID >= fileSymbols.size()) {
|
||||||
errorAt(patch, "Undefined symbol `%s`", fileSymbols[value].name.c_str());
|
fatalAt(patch, "Invalid symbol ID #%" PRIu32, symID);
|
||||||
sym_TraceLocalAliasedSymbols(fileSymbols[value].name);
|
} else if (Symbol const *symbol = getSymbol(fileSymbols, symID); !symbol) {
|
||||||
|
errorAt(patch, "Undefined symbol `%s`", fileSymbols[symID].name.c_str());
|
||||||
|
sym_TraceLocalAliasedSymbols(fileSymbols[symID].name);
|
||||||
|
value = 0;
|
||||||
isError = true;
|
isError = true;
|
||||||
} else if (std::holds_alternative<Label>(symbol->data)) {
|
} else if (std::holds_alternative<Label>(symbol->data)) {
|
||||||
Label const &label = std::get<Label>(symbol->data);
|
Label const &label = std::get<Label>(symbol->data);
|
||||||
@@ -446,6 +453,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pushRPN(value, isError);
|
pushRPN(value, isError);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -296,7 +296,7 @@ void sdobj_ReadFile(FileStackNode const &src, FILE *file, std::vector<Symbol> &f
|
|||||||
if (tmp > UINT16_MAX) {
|
if (tmp > UINT16_MAX) {
|
||||||
fatalAt(
|
fatalAt(
|
||||||
where,
|
where,
|
||||||
"Area \"%s\" is larger than the GB address space!?",
|
"Area \"%s\" is larger than the GB address space",
|
||||||
curSection->name.c_str()
|
curSection->name.c_str()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,14 +80,15 @@ void sym_TraceLocalAliasedSymbols(std::string const &name) {
|
|||||||
plural ? "are" : "is"
|
plural ? "are" : "is"
|
||||||
);
|
);
|
||||||
|
|
||||||
int count = 0;
|
size_t nbListed = 0;
|
||||||
for (Symbol *local : locals) {
|
for (Symbol *local : locals) {
|
||||||
assume(local->src);
|
if (nbListed == 3) {
|
||||||
local->src->printBacktrace(local->lineNo);
|
fprintf(stderr, " ...and %zu more\n", locals.size() - nbListed);
|
||||||
if (++count == 3 && locals.size() > 3) {
|
|
||||||
fprintf(stderr, " ...and %zu more\n", locals.size() - 3);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
assume(local->src);
|
||||||
|
local->src->printBacktrace(local->lineNo);
|
||||||
|
++nbListed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "platform.hpp"
|
#include "platform.hpp"
|
||||||
#include "style.hpp"
|
#include "style.hpp"
|
||||||
|
#include "version.hpp"
|
||||||
|
|
||||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||||
#define WIN32_LEAN_AND_MEAN // Include less from `windows.h`
|
#define WIN32_LEAN_AND_MEAN // Include less from `windows.h`
|
||||||
@@ -17,6 +18,10 @@
|
|||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void Usage::printVersion(bool error) const {
|
||||||
|
fprintf(error ? stderr : stdout, "%s %s\n", name.c_str(), get_package_version_string());
|
||||||
|
}
|
||||||
|
|
||||||
void Usage::printAndExit(int code) const {
|
void Usage::printAndExit(int code) const {
|
||||||
FILE *file;
|
FILE *file;
|
||||||
bool isTerminal;
|
bool isTerminal;
|
||||||
|
|||||||
@@ -24,6 +24,15 @@ void incrementVerbosity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void printVerbosely(char const *fmt, ...) {
|
||||||
|
va_list args;
|
||||||
|
style_Set(stderr, STYLE_MAGENTA, false);
|
||||||
|
va_start(args, fmt);
|
||||||
|
vfprintf(stderr, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
style_Reset(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
void printVVVVVVerbosity() {
|
void printVVVVVVerbosity() {
|
||||||
if (!checkVerbosity(VERB_VVVVVV)) {
|
if (!checkVerbosity(VERB_VVVVVV)) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -14,7 +14,13 @@
|
|||||||
#if !defined(NDEBUG) && defined(__SANITIZE_ADDRESS__) && !defined(__APPLE__)
|
#if !defined(NDEBUG) && defined(__SANITIZE_ADDRESS__) && !defined(__APPLE__)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
char const *__asan_default_options(void) {
|
char const *__asan_default_options(void) {
|
||||||
return "detect_leaks=1";
|
return "detect_leaks=1"
|
||||||
|
":detect_stack_use_after_return=1"
|
||||||
|
":detect_invalid_pointer_pairs=2"
|
||||||
|
":check_initialization_order=1"
|
||||||
|
":strict_init_order=1"
|
||||||
|
":strict_string_checks=1"
|
||||||
|
":print_legend=0";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
error: The absolute alignment offset (2) must be less than alignment size (2)
|
error: The absolute alignment offset (2) must be less than alignment size (2)
|
||||||
at align-large-ofs.asm(2)
|
at align-large-ofs.asm(2)
|
||||||
Assembly aborted with 1 error!
|
Assembly aborted with 1 error
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ error: Alignment must be between 0 and 16, not 17
|
|||||||
at align-large.asm(1)
|
at align-large.asm(1)
|
||||||
error: Alignment must be between 0 and 16, not 17
|
error: Alignment must be between 0 and 16, not 17
|
||||||
at align-large.asm(2)
|
at align-large.asm(2)
|
||||||
Assembly aborted with 2 errors!
|
Assembly aborted with 2 errors
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ error: The absolute alignment offset (18) must be less than alignment size (16)
|
|||||||
at align-offset.asm(4)
|
at align-offset.asm(4)
|
||||||
error: The absolute alignment offset (20) must be less than alignment size (16)
|
error: The absolute alignment offset (20) must be less than alignment size (16)
|
||||||
at align-offset.asm(6)
|
at align-offset.asm(6)
|
||||||
Assembly aborted with 2 errors!
|
Assembly aborted with 2 errors
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
error: Cannot output data outside of a `SECTION`
|
error: Cannot output data outside of a `SECTION`
|
||||||
at align-pc-outside-section.asm(1)
|
at align-pc-outside-section.asm(1)
|
||||||
Assembly aborted with 1 error!
|
Assembly aborted with 1 error
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
error: Section "X"'s alignment cannot be attained in WRAM0
|
error: Section "X"'s alignment cannot be attained in WRAM0
|
||||||
at align-unattainable.asm(3)
|
at align-unattainable.asm(3)
|
||||||
Assembly aborted with 1 error!
|
Assembly aborted with 1 error
|
||||||
|
|||||||
@@ -8,4 +8,4 @@ error: syntax error, unexpected anonymous label, expecting symbol or label or lo
|
|||||||
at anon-label-bad.asm(10)
|
at anon-label-bad.asm(10)
|
||||||
error: syntax error, unexpected ::
|
error: syntax error, unexpected ::
|
||||||
at anon-label-bad.asm(22)
|
at anon-label-bad.asm(22)
|
||||||
Assembly aborted with 5 errors!
|
Assembly aborted with 5 errors
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
error: PC has no bank outside of a section
|
error: PC has no bank outside of a section
|
||||||
at assert-nosect-bank.asm(1)
|
at assert-nosect-bank.asm(1)
|
||||||
Assembly aborted with 1 error!
|
Assembly aborted with 1 error
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
error: PC has no value outside of a section
|
error: PC has no value outside of a section
|
||||||
at assert@-no-sect.asm(1)
|
at assert@-no-sect.asm(1)
|
||||||
Assembly aborted with 1 error!
|
Assembly aborted with 1 error
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
error: Fixed-point precision must be between 1 and 31, not 42
|
error: Fixed-point precision must be between 1 and 31, not 42
|
||||||
at bad-precision.asm(1)
|
at bad-precision.asm(1)
|
||||||
Assembly aborted with 1 error!
|
Assembly aborted with 1 error
|
||||||
|
|||||||
@@ -20,4 +20,4 @@ error: Expected constant expression: `Label_u12`'s bank is not known
|
|||||||
at bank.asm::def_sect(8) <- bank.asm(22)
|
at bank.asm::def_sect(8) <- bank.asm(22)
|
||||||
error: `BANK` argument must be a label
|
error: `BANK` argument must be a label
|
||||||
at bank.asm(26)
|
at bank.asm(26)
|
||||||
Assembly aborted with 11 errors!
|
Assembly aborted with 11 errors
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ error: Unterminated block comment
|
|||||||
at block-comment-termination-error.asm(1)
|
at block-comment-termination-error.asm(1)
|
||||||
error: syntax error, unexpected end of buffer
|
error: syntax error, unexpected end of buffer
|
||||||
at block-comment-termination-error.asm(1)
|
at block-comment-termination-error.asm(1)
|
||||||
Assembly aborted with 2 errors!
|
Assembly aborted with 2 errors
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
error: Invalid character '2' after line continuation
|
error: Invalid character '2' after line continuation
|
||||||
at blue-paint-limits.asm::nth(40) <- blue-paint-limits.asm(43)
|
at blue-paint-limits.asm::nth(40) <- blue-paint-limits.asm(43)
|
||||||
Assembly aborted with 1 error!
|
Assembly aborted with 1 error
|
||||||
|
|||||||
@@ -10,4 +10,4 @@ error: Macro argument `\<2>` not defined
|
|||||||
at bracketed-macro-args.asm::bad(30) <- bracketed-macro-args.asm(33)
|
at bracketed-macro-args.asm::bad(30) <- bracketed-macro-args.asm(33)
|
||||||
error: Bracketed symbol `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz` does not exist
|
error: Bracketed symbol `abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz` does not exist
|
||||||
at bracketed-macro-args.asm::toolong(36) <- bracketed-macro-args.asm(39)
|
at bracketed-macro-args.asm::toolong(36) <- bracketed-macro-args.asm(39)
|
||||||
Assembly aborted with 6 errors!
|
Assembly aborted with 6 errors
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ error: `Label` does not have a constant value
|
|||||||
at bracketed-symbols.asm(20)
|
at bracketed-symbols.asm(20)
|
||||||
error: PC does not have a constant value; the current section is not fixed
|
error: PC does not have a constant value; the current section is not fixed
|
||||||
at bracketed-symbols.asm(21)
|
at bracketed-symbols.asm(21)
|
||||||
Assembly aborted with 3 errors!
|
Assembly aborted with 3 errors
|
||||||
|
|||||||
@@ -54,4 +54,4 @@ error: Built-in symbol `__ISO_8601_UTC__` cannot be redefined
|
|||||||
at builtin-overwrite.asm::tickle(29) <- builtin-overwrite.asm(37)
|
at builtin-overwrite.asm::tickle(29) <- builtin-overwrite.asm(37)
|
||||||
error: Built-in symbol `__ISO_8601_UTC__` cannot be redefined
|
error: Built-in symbol `__ISO_8601_UTC__` cannot be redefined
|
||||||
at builtin-overwrite.asm::tickle(30) <- builtin-overwrite.asm(37)
|
at builtin-overwrite.asm::tickle(30) <- builtin-overwrite.asm(37)
|
||||||
Assembly aborted with 28 errors!
|
Assembly aborted with 28 errors
|
||||||
|
|||||||
@@ -30,4 +30,4 @@ error: `.` is reserved for a built-in symbol
|
|||||||
at builtin-reserved.asm(29)
|
at builtin-reserved.asm(29)
|
||||||
error: `.` has no value outside of a label scope
|
error: `.` has no value outside of a label scope
|
||||||
at builtin-reserved.asm(32)
|
at builtin-reserved.asm(32)
|
||||||
Assembly aborted with 16 errors!
|
Assembly aborted with 16 errors
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ error: Illegal character escape '\' at end of input
|
|||||||
at character-escape-at-end.asm(1)
|
at character-escape-at-end.asm(1)
|
||||||
error: Unterminated string
|
error: Unterminated string
|
||||||
at character-escape-at-end.asm(1)
|
at character-escape-at-end.asm(1)
|
||||||
Assembly aborted with 2 errors!
|
Assembly aborted with 2 errors
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ error: Invalid character '\' in bracketed macro argument
|
|||||||
at character-escapes.asm::m(7) <- character-escapes.asm(10)
|
at character-escapes.asm::m(7) <- character-escapes.asm(10)
|
||||||
error: Invalid character '\t' in bracketed macro argument
|
error: Invalid character '\t' in bracketed macro argument
|
||||||
at character-escapes.asm::m(8) <- character-escapes.asm(10)
|
at character-escapes.asm::m(8) <- character-escapes.asm(10)
|
||||||
Assembly aborted with 3 errors!
|
Assembly aborted with 3 errors
|
||||||
|
|||||||
@@ -16,4 +16,4 @@ error: Unterminated character
|
|||||||
at character-literals.asm(35)
|
at character-literals.asm(35)
|
||||||
error: Character literals must be a single charmap unit
|
error: Character literals must be a single charmap unit
|
||||||
at character-literals.asm(35)
|
at character-literals.asm(35)
|
||||||
Assembly aborted with 5 errors!
|
Assembly aborted with 5 errors
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ error: Cannot map an empty string
|
|||||||
at charmap-empty.asm(1)
|
at charmap-empty.asm(1)
|
||||||
error: syntax error, unexpected end of line
|
error: syntax error, unexpected end of line
|
||||||
at charmap-empty.asm(2)
|
at charmap-empty.asm(2)
|
||||||
Assembly aborted with 2 errors!
|
Assembly aborted with 2 errors
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
error: Undefined base charmap `eggs`
|
error: Undefined base charmap `eggs`
|
||||||
at charmap-inheritance.asm(26)
|
at charmap-inheritance.asm(26)
|
||||||
Assembly aborted with 1 error!
|
Assembly aborted with 1 error
|
||||||
|
|||||||
@@ -6,4 +6,4 @@ error: CHARSIZE: No character mapping for "abcdef"
|
|||||||
at charsize.asm(19)
|
at charsize.asm(19)
|
||||||
error: CHARSIZE: No character mapping for "é"
|
error: CHARSIZE: No character mapping for "é"
|
||||||
at charsize.asm(20)
|
at charsize.asm(20)
|
||||||
Assembly aborted with 4 errors!
|
Assembly aborted with 4 errors
|
||||||
|
|||||||
@@ -12,4 +12,4 @@ warning: CHARVAL: Index starts at 0 [-Wbuiltin-args]
|
|||||||
at charval.asm(28)
|
at charval.asm(28)
|
||||||
warning: CHARVAL: Index 10 is past the end of the character mapping [-Wbuiltin-args]
|
warning: CHARVAL: Index 10 is past the end of the character mapping [-Wbuiltin-args]
|
||||||
at charval.asm(29)
|
at charval.asm(29)
|
||||||
Assembly aborted with 5 errors!
|
Assembly aborted with 5 errors
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ error: `FOO` already defined (should it be {interpolated} to define its contents
|
|||||||
at command-line-symbols.asm(3)
|
at command-line-symbols.asm(3)
|
||||||
and also:
|
and also:
|
||||||
at <command-line>
|
at <command-line>
|
||||||
Assembly aborted with 1 error!
|
Assembly aborted with 1 error
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
error: Expected constant expression: undefined symbol `UnDeFiNeD`
|
error: Expected constant expression: undefined symbol `UnDeFiNeD`
|
||||||
at compound-assignment.asm(35)
|
at compound-assignment.asm(35)
|
||||||
Assembly aborted with 1 error!
|
Assembly aborted with 1 error
|
||||||
|
|||||||
@@ -6,4 +6,4 @@ error: Expected constant expression: PC is not constant at assembly time
|
|||||||
at const-and.asm(17)
|
at const-and.asm(17)
|
||||||
error: Expected constant expression: `Floating` is not constant at assembly time
|
error: Expected constant expression: `Floating` is not constant at assembly time
|
||||||
at const-and.asm(20)
|
at const-and.asm(20)
|
||||||
Assembly aborted with 4 errors!
|
Assembly aborted with 4 errors
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ error: Expected constant expression: `Gamma` is not constant at assembly time
|
|||||||
at const-low.asm(10)
|
at const-low.asm(10)
|
||||||
error: Assertion failed
|
error: Assertion failed
|
||||||
at const-low.asm(10)
|
at const-low.asm(10)
|
||||||
Assembly aborted with 2 errors!
|
Assembly aborted with 2 errors
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
error: Assertion failed
|
error: Assertion failed
|
||||||
at const-not.asm(3)
|
at const-not.asm(3)
|
||||||
Assembly aborted with 1 error!
|
Assembly aborted with 1 error
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
error: Expected constant expression: PC is not constant at assembly time
|
error: Expected constant expression: PC is not constant at assembly time
|
||||||
at const-zero.asm::test_or(14) <- const-zero.asm(21)
|
at const-zero.asm::test_or(14) <- const-zero.asm(21)
|
||||||
Assembly aborted with 1 error!
|
Assembly aborted with 1 error
|
||||||
|
|||||||
1
test/asm/continues-after-missing-include/a.flags
Normal file
1
test/asm/continues-after-missing-include/a.flags
Normal file
@@ -0,0 +1 @@
|
|||||||
|
-MG -MC
|
||||||
13
test/asm/continues-after-missing-preinclude/a.asm
Normal file
13
test/asm/continues-after-missing-preinclude/a.asm
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
PUSHC
|
||||||
|
PUSHO
|
||||||
|
PUSHS
|
||||||
|
SECTION "test", WRAM0
|
||||||
|
UNION
|
||||||
|
INCLUDE "nonexistent1.inc"
|
||||||
|
WARN "still going!"
|
||||||
|
INCLUDE "nonexistent2.inc"
|
||||||
|
WARN "and going!"
|
||||||
|
ENDU
|
||||||
|
POPS
|
||||||
|
POPO
|
||||||
|
POPC
|
||||||
4
test/asm/continues-after-missing-preinclude/a.err
Normal file
4
test/asm/continues-after-missing-preinclude/a.err
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
warning: still going! [-Wuser]
|
||||||
|
at continues-after-missing-preinclude/a.asm(7)
|
||||||
|
warning: and going! [-Wuser]
|
||||||
|
at continues-after-missing-preinclude/a.asm(9)
|
||||||
1
test/asm/continues-after-missing-preinclude/a.flags
Normal file
1
test/asm/continues-after-missing-preinclude/a.flags
Normal file
@@ -0,0 +1 @@
|
|||||||
|
-MC -P nonexistent-pre.inc
|
||||||
4
test/asm/continues-after-missing-preinclude/a.out
Normal file
4
test/asm/continues-after-missing-preinclude/a.out
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
a.o: continues-after-missing-preinclude/a.asm
|
||||||
|
a.o: nonexistent-pre.inc
|
||||||
|
a.o: nonexistent1.inc
|
||||||
|
a.o: nonexistent2.inc
|
||||||
@@ -2,4 +2,4 @@ error: Section "code" cannot contain code or data (not `ROM0` or `ROMX`)
|
|||||||
at data-in-ram.asm(2)
|
at data-in-ram.asm(2)
|
||||||
error: Section "data" cannot contain code or data (not `ROM0` or `ROMX`)
|
error: Section "data" cannot contain code or data (not `ROM0` or `ROMX`)
|
||||||
at data-in-ram.asm(4)
|
at data-in-ram.asm(4)
|
||||||
Assembly aborted with 2 errors!
|
Assembly aborted with 2 errors
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ error: syntax error, unexpected local label, expecting symbol
|
|||||||
at def-scoped.asm(13)
|
at def-scoped.asm(13)
|
||||||
error: syntax error, unexpected local label, expecting symbol
|
error: syntax error, unexpected local label, expecting symbol
|
||||||
at def-scoped.asm(16)
|
at def-scoped.asm(16)
|
||||||
Assembly aborted with 3 errors!
|
Assembly aborted with 3 errors
|
||||||
|
|||||||
@@ -14,4 +14,4 @@ error: `name` already defined (should it be {interpolated} to define its content
|
|||||||
at def.asm(42)
|
at def.asm(42)
|
||||||
and also:
|
and also:
|
||||||
at def.asm(40)
|
at def.asm(40)
|
||||||
Assembly aborted with 4 errors!
|
Assembly aborted with 4 errors
|
||||||
|
|||||||
@@ -52,4 +52,4 @@ warning: STRSUB: Position 12 is past the end of the string [-Wbuiltin-args]
|
|||||||
at deprecated-functions.asm(28)
|
at deprecated-functions.asm(28)
|
||||||
error: STRSUB: Incomplete UTF-8 character
|
error: STRSUB: Incomplete UTF-8 character
|
||||||
at deprecated-functions.asm(28)
|
at deprecated-functions.asm(28)
|
||||||
Assembly aborted with 6 errors!
|
Assembly aborted with 6 errors
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ error: syntax error, unexpected '-' at the beginning of the line (is it a leftov
|
|||||||
at diff-marks.asm(2)
|
at diff-marks.asm(2)
|
||||||
error: syntax error, unexpected '+' at the beginning of the line (is it a leftover diff mark?)
|
error: syntax error, unexpected '+' at the beginning of the line (is it a leftover diff mark?)
|
||||||
at diff-marks.asm(3)
|
at diff-marks.asm(3)
|
||||||
Assembly aborted with 2 errors!
|
Assembly aborted with 2 errors
|
||||||
|
|||||||
2
test/asm/dots-constant.asm
Normal file
2
test/asm/dots-constant.asm
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
section "test", rom0
|
||||||
|
def #... equs "sublocal"
|
||||||
3
test/asm/dots-constant.err
Normal file
3
test/asm/dots-constant.err
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
error: syntax error, unexpected local label, expecting symbol
|
||||||
|
at dots-constant.asm(2)
|
||||||
|
Assembly aborted with 1 error
|
||||||
2
test/asm/dots-interpolate.asm
Normal file
2
test/asm/dots-interpolate.asm
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
section "test", rom0
|
||||||
|
println "{...}"
|
||||||
2
test/asm/dots-interpolate.err
Normal file
2
test/asm/dots-interpolate.err
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
FATAL: `...` is a nonsensical reference to a nested local label
|
||||||
|
at dots-interpolate.asm(2)
|
||||||
2
test/asm/dots-label.asm
Normal file
2
test/asm/dots-label.asm
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
section "test", rom0
|
||||||
|
#...:
|
||||||
2
test/asm/dots-label.err
Normal file
2
test/asm/dots-label.err
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
FATAL: `...` is a nonsensical reference to a nested local label
|
||||||
|
at dots-label.asm(2)
|
||||||
4
test/asm/dots-macro-arg.asm
Normal file
4
test/asm/dots-macro-arg.asm
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
MACRO test
|
||||||
|
println "\<...>"
|
||||||
|
ENDM
|
||||||
|
test 1, 2, 3, 4
|
||||||
2
test/asm/dots-macro-arg.err
Normal file
2
test/asm/dots-macro-arg.err
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
FATAL: `...` is a nonsensical reference to a nested local label
|
||||||
|
at dots-macro-arg.asm::test(2) <- dots-macro-arg.asm(4)
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
error: Undefined symbol `n` was already purged
|
error: Undefined symbol `n` was already purged
|
||||||
at double-purge.asm(3)
|
at double-purge.asm(3)
|
||||||
Assembly aborted with 1 error!
|
Assembly aborted with 1 error
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ error: Expected constant expression: undefined symbol `unknown`
|
|||||||
at ds-bad.asm(3)
|
at ds-bad.asm(3)
|
||||||
warning: Expression must be 8-bit; use `LOW()` to force 8-bit [-Wtruncation]
|
warning: Expression must be 8-bit; use `LOW()` to force 8-bit [-Wtruncation]
|
||||||
at ds-bad.asm(4)
|
at ds-bad.asm(4)
|
||||||
Assembly aborted with 1 error!
|
Assembly aborted with 1 error
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ error: Invalid character '?' in bracketed macro argument
|
|||||||
at empty-raw-identifier.asm::macro(3) <- empty-raw-identifier.asm(5)
|
at empty-raw-identifier.asm::macro(3) <- empty-raw-identifier.asm(5)
|
||||||
error: Empty raw symbol in bracketed macro argument
|
error: Empty raw symbol in bracketed macro argument
|
||||||
at empty-raw-identifier.asm::macro(3) <- empty-raw-identifier.asm(5)
|
at empty-raw-identifier.asm::macro(3) <- empty-raw-identifier.asm(5)
|
||||||
Assembly aborted with 2 errors!
|
Assembly aborted with 2 errors
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ warning: `LOAD` block without `ENDL` terminated by `ENDSECTION` [-Wunterminated-
|
|||||||
at endsection-in-load.asm(3)
|
at endsection-in-load.asm(3)
|
||||||
error: Found `ENDL` outside of a `LOAD` block
|
error: Found `ENDL` outside of a `LOAD` block
|
||||||
at endsection-in-load.asm(4)
|
at endsection-in-load.asm(4)
|
||||||
Assembly aborted with 1 error!
|
Assembly aborted with 1 error
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
error: Cannot output data outside of a `SECTION`
|
error: Cannot output data outside of a `SECTION`
|
||||||
at endsection.asm(4)
|
at endsection.asm(4)
|
||||||
Assembly aborted with 1 error!
|
Assembly aborted with 1 error
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ error: `x` already defined
|
|||||||
at error-limit.asm(2)
|
at error-limit.asm(2)
|
||||||
and also:
|
and also:
|
||||||
at error-limit.asm(1)
|
at error-limit.asm(1)
|
||||||
Assembly aborted after the maximum of 1 error! (configure with '-X/--max-errors')
|
Assembly aborted after the maximum of 1 error (configure with '-X/--max-errors')
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user