mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-21 10:42:07 +00:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b85875b67 | ||
|
|
7054d81650 | ||
|
|
5942117ac3 | ||
|
|
e7a3b9d90e | ||
|
|
b13d623ad4 | ||
|
|
37bf9fae01 | ||
|
|
612cf3b7dd | ||
|
|
089e366ddc | ||
|
|
fa9e29e4ce | ||
|
|
fa3d83a3d1 | ||
|
|
804db4e073 | ||
|
|
5d998ef483 | ||
|
|
126b1e5726 | ||
|
|
4f2400c15b | ||
|
|
063d284cbf | ||
|
|
205bf5a11d | ||
|
|
41c94aa448 | ||
|
|
d413870e6d | ||
|
|
e95ac6fb06 | ||
|
|
e1ae92709c |
1
.github/workflows/build-container.yml
vendored
1
.github/workflows/build-container.yml
vendored
@@ -38,6 +38,7 @@ jobs:
|
||||
sed -i "2i LABEL org.opencontainers.image.description=\"RGBDS container image for the release version $TAG_NAME\"" Dockerfile
|
||||
docker build . --tag ghcr.io/gbdev/rgbds:$TAG_NAME
|
||||
docker push ghcr.io/gbdev/rgbds:$TAG_NAME
|
||||
docker push ghcr.io/gbdev/rgbds:latest
|
||||
|
||||
- name: Delete untagged container images
|
||||
if: github.repository_owner == 'gbdev'
|
||||
|
||||
22
.github/workflows/create-release-artifacts.yml
vendored
22
.github/workflows/create-release-artifacts.yml
vendored
@@ -61,12 +61,12 @@ jobs:
|
||||
cmake --install build --verbose --prefix install_dir --strip
|
||||
- name: Package binaries
|
||||
run: |
|
||||
Compress-Archive -LiteralPath @("install_dir/bin/rgbasm.exe", "install_dir/bin/rgblink.exe", "install_dir/bin/rgbfix.exe", "install_dir/bin/rgbgfx.exe", "install_dir/bin/zlib1.dll", "install_dir/bin/libpng16.dll") "rgbds-${{ env.version }}-win${{ matrix.bits }}.zip"
|
||||
Compress-Archive -LiteralPath @("install_dir/bin/rgbasm.exe", "install_dir/bin/rgblink.exe", "install_dir/bin/rgbfix.exe", "install_dir/bin/rgbgfx.exe", "install_dir/bin/zlib1.dll", "install_dir/bin/libpng16.dll") "rgbds-win${{ matrix.bits }}.zip"
|
||||
- name: Upload Windows binaries
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: win${{ matrix.bits }}
|
||||
path: rgbds-${{ env.version }}-win${{ matrix.bits }}.zip
|
||||
path: rgbds-win${{ matrix.bits }}.zip
|
||||
|
||||
macos:
|
||||
runs-on: macos-14
|
||||
@@ -92,12 +92,12 @@ jobs:
|
||||
strip rgb{asm,link,fix,gfx}
|
||||
- name: Package binaries
|
||||
run: |
|
||||
zip --junk-paths rgbds-${{ env.version }}-macos.zip rgb{asm,link,fix,gfx} man/* .github/scripts/install.sh
|
||||
zip --junk-paths rgbds-macos.zip rgb{asm,link,fix,gfx} man/* .github/scripts/install.sh
|
||||
- name: Upload macOS binaries
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macos
|
||||
path: rgbds-${{ env.version }}-macos.zip
|
||||
path: rgbds-macos.zip
|
||||
|
||||
linux:
|
||||
runs-on: ubuntu-22.04 # Oldest supported, for best glibc compatibility.
|
||||
@@ -119,12 +119,12 @@ jobs:
|
||||
strip rgb{asm,link,fix,gfx}
|
||||
- name: Package binaries
|
||||
run: |
|
||||
tar caf rgbds-${{ env.version }}-linux-x86_64.tar.xz --transform='s#.*/##' rgb{asm,link,fix,gfx} man/* .github/scripts/install.sh
|
||||
tar caf rgbds-linux-x86_64.tar.xz --transform='s#.*/##' rgb{asm,link,fix,gfx} man/* .github/scripts/install.sh
|
||||
- name: Upload Linux binaries
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux
|
||||
path: rgbds-${{ env.version }}-linux-x86_64.tar.xz
|
||||
path: rgbds-linux-x86_64.tar.xz
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -155,11 +155,11 @@ jobs:
|
||||
draft: true # Don't publish the release quite yet...
|
||||
prerelease: ${{ contains(github.ref, '-rc') }}
|
||||
files: |
|
||||
win32/rgbds-${{ env.version }}-win32.zip
|
||||
win64/rgbds-${{ env.version }}-win64.zip
|
||||
macos/rgbds-${{ env.version }}-macos.zip
|
||||
linux/rgbds-${{ env.version }}-linux-x86_64.tar.xz
|
||||
rgbds-${{ env.version }}.tar.gz
|
||||
win32/rgbds-win32.zip
|
||||
win64/rgbds-win64.zip
|
||||
macos/rgbds-macos.zip
|
||||
linux/rgbds-linux-x86_64.tar.xz
|
||||
rgbds-source.tar.gz
|
||||
fail_on_unmatched_files: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
11
Dockerfile
11
Dockerfile
@@ -1,6 +1,6 @@
|
||||
FROM debian:12-slim
|
||||
LABEL org.opencontainers.image.source=https://github.com/gbdev/rgbds
|
||||
ARG version=0.9.2
|
||||
ARG version=0.9.3
|
||||
WORKDIR /rgbds
|
||||
|
||||
COPY . .
|
||||
@@ -8,7 +8,14 @@ COPY . .
|
||||
RUN apt-get update && \
|
||||
apt-get install sudo make cmake gcc build-essential -y
|
||||
|
||||
# Install dependencies and compile RGBDS
|
||||
RUN ./.github/scripts/install_deps.sh ubuntu-22.04
|
||||
RUN make -j CXXFLAGS="-O3 -flto -DNDEBUG -static" PKG_CONFIG="pkg-config --static" Q=
|
||||
|
||||
RUN tar caf rgbds-${version}-linux-x86_64.tar.xz --transform='s#.*/##' rgbasm rgblink rgbfix rgbgfx man/* .github/scripts/install.sh
|
||||
# Create an archive with the compiled executables and all the necessary to install it,
|
||||
# so it can be copied outside of the container and installed/used in another system
|
||||
RUN tar caf rgbds-linux-x86_64.tar.xz --transform='s#.*/##' rgbasm rgblink rgbfix rgbgfx man/* .github/scripts/install.sh
|
||||
|
||||
# Install RGBDS on the container so all the executables will be available in the PATH
|
||||
RUN cp man/* .
|
||||
RUN ./.github/scripts/install.sh
|
||||
|
||||
2
Makefile
2
Makefile
@@ -265,4 +265,4 @@ wine-shim:
|
||||
|
||||
dist:
|
||||
$Qgit ls-files | sed s~^~$${PWD##*/}/~ \
|
||||
| tar -czf rgbds-`git -c safe.directory='*' describe --tags | cut -c 2-`.tar.gz -C .. -T -
|
||||
| tar -czf rgbds-source.tar.gz -C .. -T -
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#ifndef RGBDS_ASM_CHARMAP_HPP
|
||||
#define RGBDS_ASM_CHARMAP_HPP
|
||||
|
||||
#include <optional>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
@@ -22,6 +23,7 @@ void charmap_CheckStack();
|
||||
void charmap_Add(std::string const &mapping, std::vector<int32_t> &&value);
|
||||
bool charmap_HasChar(std::string const &mapping);
|
||||
size_t charmap_CharSize(std::string const &mapping);
|
||||
std::optional<int32_t> charmap_CharValue(std::string const &mapping, size_t idx);
|
||||
std::vector<int32_t> charmap_Convert(std::string const &input);
|
||||
size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output);
|
||||
std::string charmap_Reverse(std::vector<int32_t> const &value, bool &unique);
|
||||
|
||||
@@ -71,7 +71,6 @@ void fstk_RunFor(
|
||||
int32_t reptLineNo,
|
||||
ContentSpan const &span
|
||||
);
|
||||
void fstk_StopRept();
|
||||
bool fstk_Break();
|
||||
|
||||
void fstk_NewRecursionDepth(size_t newDepth);
|
||||
|
||||
@@ -118,17 +118,8 @@ struct LexerState {
|
||||
extern char binDigits[2];
|
||||
extern char gfxDigits[4];
|
||||
|
||||
static inline void lexer_SetBinDigits(char const digits[2]) {
|
||||
binDigits[0] = digits[0];
|
||||
binDigits[1] = digits[1];
|
||||
}
|
||||
|
||||
static inline void lexer_SetGfxDigits(char const digits[4]) {
|
||||
gfxDigits[0] = digits[0];
|
||||
gfxDigits[1] = digits[1];
|
||||
gfxDigits[2] = digits[2];
|
||||
gfxDigits[3] = digits[3];
|
||||
}
|
||||
void lexer_SetBinDigits(char const digits[2]);
|
||||
void lexer_SetGfxDigits(char const digits[4]);
|
||||
|
||||
bool lexer_AtTopLevel();
|
||||
void lexer_RestartRept(uint32_t lineNo);
|
||||
|
||||
@@ -29,8 +29,10 @@ struct Options {
|
||||
NO_SPEC,
|
||||
EXPLICIT,
|
||||
EMBEDDED,
|
||||
DMG,
|
||||
} palSpecType = NO_SPEC; // -c
|
||||
std::vector<std::array<std::optional<Rgba>, 4>> palSpec{};
|
||||
uint8_t palSpecDmg = 0;
|
||||
uint8_t bitDepth = 2; // -d
|
||||
std::string inputTileset{}; // -i
|
||||
struct {
|
||||
@@ -65,6 +67,12 @@ struct Options {
|
||||
|
||||
mutable bool hasTransparentPixels = false;
|
||||
uint8_t maxOpaqueColors() const { return nbColorsPerPal - hasTransparentPixels; }
|
||||
|
||||
uint8_t dmgColors[4] = {};
|
||||
uint8_t dmgValue(uint8_t i) const {
|
||||
assume(i < 4);
|
||||
return (palSpecDmg >> (2 * i)) & 0b11;
|
||||
}
|
||||
};
|
||||
|
||||
extern Options options;
|
||||
@@ -119,27 +127,4 @@ static constexpr auto flipTable = ([]() constexpr {
|
||||
return table;
|
||||
})();
|
||||
|
||||
// Parsing helpers.
|
||||
|
||||
static constexpr uint8_t nibble(char c) {
|
||||
if (c >= 'a') {
|
||||
assume(c <= 'f');
|
||||
return c - 'a' + 10;
|
||||
} else if (c >= 'A') {
|
||||
assume(c <= 'F');
|
||||
return c - 'A' + 10;
|
||||
} else {
|
||||
assume(c >= '0' && c <= '9');
|
||||
return c - '0';
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr uint8_t toHex(char c1, char c2) {
|
||||
return nibble(c1) * 16 + nibble(c2);
|
||||
}
|
||||
|
||||
static constexpr uint8_t singleToHex(char c) {
|
||||
return toHex(c, c);
|
||||
}
|
||||
|
||||
#endif // RGBDS_GFX_MAIN_HPP
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
#ifndef RGBDS_GFX_PAL_SPEC_HPP
|
||||
#define RGBDS_GFX_PAL_SPEC_HPP
|
||||
|
||||
void parseInlinePalSpec(char const * const arg);
|
||||
void parseInlinePalSpec(char const * const rawArg);
|
||||
void parseExternalPalSpec(char const *arg);
|
||||
void parseDmgPalSpec(char const * const rawArg);
|
||||
|
||||
void parseBackgroundPalSpec(char const *arg);
|
||||
|
||||
#endif // RGBDS_GFX_PAL_SPEC_HPP
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
#ifndef RGBDS_UTIL_HPP
|
||||
#define RGBDS_UTIL_HPP
|
||||
|
||||
bool startsIdentifier(int c);
|
||||
bool continuesIdentifier(int c);
|
||||
|
||||
char const *printChar(int c);
|
||||
|
||||
#endif // RGBDS_UTIL_HPP
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#define PACKAGE_VERSION_MAJOR 0
|
||||
#define PACKAGE_VERSION_MINOR 9
|
||||
#define PACKAGE_VERSION_PATCH 2
|
||||
#define PACKAGE_VERSION_PATCH 3
|
||||
|
||||
char const *get_package_version_string();
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd May 4, 2025
|
||||
.Dd June 30, 2025
|
||||
.Dt GBZ80 7
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
.\"
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd May 4, 2025
|
||||
.Dd June 30, 2025
|
||||
.Dt RGBASM-OLD 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
35
man/rgbasm.1
35
man/rgbasm.1
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd May 4, 2025
|
||||
.Dd June 30, 2025
|
||||
.Dt RGBASM 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -51,8 +51,19 @@ is invalid because it could also be
|
||||
The arguments are as follows:
|
||||
.Bl -tag -width Ds
|
||||
.It Fl b Ar chars , Fl \-binary-digits Ar chars
|
||||
Change the two characters used for binary constants.
|
||||
The defaults are 01.
|
||||
Allow two characters to be used for binary constants in addition to the default
|
||||
.Sq 0
|
||||
and
|
||||
.Sq 1 .
|
||||
Valid characters are numbers other than
|
||||
.Sq 0
|
||||
and
|
||||
.Sq 1 ,
|
||||
letters,
|
||||
.Sq \&. ,
|
||||
.Sq # ,
|
||||
or
|
||||
.Sq @ .
|
||||
.It Fl D Ar name Ns Oo = Ns Ar value Oc , Fl \-define Ar name Ns Oo = Ns Ar value Oc
|
||||
Add a string symbol to the compiled source code.
|
||||
This is equivalent to
|
||||
@@ -65,7 +76,21 @@ is not specified.
|
||||
.It Fl E , Fl \-export-all
|
||||
Export all labels, including unreferenced and local labels.
|
||||
.It Fl g Ar chars , Fl \-gfx-chars Ar chars
|
||||
Change the four characters used for gfx constants.
|
||||
Allow four characters to be used for graphics constants in addition to the default
|
||||
.Sq 0 ,
|
||||
.Sq 1 ,
|
||||
.Sq 2 ,
|
||||
and
|
||||
.Sq 3 .
|
||||
Valid characters are numbers other than
|
||||
.Sq 0
|
||||
to
|
||||
.Sq 3 ,
|
||||
letters,
|
||||
.Sq \&. ,
|
||||
.Sq # ,
|
||||
or
|
||||
.Sq @ .
|
||||
The defaults are 0123.
|
||||
.It Fl h , Fl \-help
|
||||
Print help text for the program and exit.
|
||||
@@ -275,7 +300,7 @@ This warning is enabled by
|
||||
.Fl Wall .
|
||||
.It Fl Wbuiltin-args
|
||||
Warn about incorrect arguments to built-in functions, such as
|
||||
.Fn STRSUB
|
||||
.Fn STRSLICE
|
||||
with indexes outside of the string's bounds.
|
||||
This warning is enabled by
|
||||
.Fl Wall .
|
||||
|
||||
34
man/rgbasm.5
34
man/rgbasm.5
@@ -2,7 +2,7 @@
|
||||
.\"
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd May 4, 2025
|
||||
.Dd June 30, 2025
|
||||
.Dt RGBASM 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -548,7 +548,7 @@ There are a number of escape sequences you can use within a string:
|
||||
.El
|
||||
.Pp
|
||||
Multi-line strings are contained in triple quotes
|
||||
.Pq Ql \&"\&"\&"for instance\&"\&"\&" .
|
||||
.Pq Ql \&"\&"\&"for instance""" .
|
||||
Escape sequences work the same way in multi-line strings; however, literal newline characters will be included as-is, without needing to escape them with
|
||||
.Ql \er
|
||||
or
|
||||
@@ -560,10 +560,19 @@ Inside them, backslashes and braces are treated like regular characters, so they
|
||||
For example, the raw string
|
||||
.Ql #"\et\e1{s}\e"
|
||||
is equivalent to the regular string
|
||||
.Ql "\e\et\e\e1\e{s}\e\e" .
|
||||
.Ql \&"\e\et\e\e1\e{s}\e\e" .
|
||||
(Note that this prevents raw strings from including the double quote character.)
|
||||
Raw strings also may be contained in triple quotes for them to be multi-line, so they can include literal newline or quote characters (although still not three quotes in a row).
|
||||
.Pp
|
||||
You can use the
|
||||
.Sq ++
|
||||
operator to concatenate two strings.
|
||||
.Ql \&"str" ++ \&"ing"
|
||||
is equivalent to
|
||||
.Ql \&"string" ,
|
||||
or to
|
||||
.Ql STRCAT("str", \&"ing") .
|
||||
.Pp
|
||||
The following functions operate on string expressions, and return strings themselves.
|
||||
.Bl -column "STRSLICE(str, start, stop)"
|
||||
.It Sy Name Ta Sy Operation
|
||||
@@ -574,7 +583,7 @@ in uppercase.
|
||||
.It Fn STRLWR str Ta Returns Ar str No with all ASCII letters
|
||||
.Pq Ql A-Z
|
||||
in lowercase.
|
||||
.It Fn STRSLICE str start stop Ta Returns a substring of Ar str No starting at Ar start No and ending at Ar stop No (exclusive). If Ar stop No is not specified, the substring continues to the end of Ar str Ns .
|
||||
.It Fn STRSLICE str start stop Ta Returns a substring of Ar str No starting at Ar start No and ending at Ar stop No (exclusive). If Ar stop No is not specified, the substring continues to the end of Ar str .
|
||||
.It Fn STRRPL str old new Ta Returns Ar str No with each non-overlapping occurrence of the substring Ar old No replaced with Ar new .
|
||||
.It Fn STRFMT fmt args... Ta Returns the string Ar fmt No with each
|
||||
.Ql %spec
|
||||
@@ -595,18 +604,25 @@ The following functions operate on string expressions, but return integers.
|
||||
.It Fn STRCMP str1 str2 Ta Compares Ar str1 No and Ar str2 No according to ASCII ordering of their characters. Returns -1 if Ar str1 No is lower than Ar str2 Ns , 1 if Ar str1 No is greater than Ar str2 Ns , or 0 if they match.
|
||||
.It Fn STRFIND str sub Ta Returns the first index of Ar sub No in Ar str Ns , or -1 if it's not present.
|
||||
.It Fn STRRFIND str sub Ta Returns the last index of Ar sub No in Ar str Ns , or -1 if it's not present.
|
||||
.It Fn INCHARMAP str Ta Returns 1 if Ar str No has an entry in the current charmap, or 0 otherwise .
|
||||
.It Fn CHARLEN str Ta Returns the number of charmap entries in Ar str No with the current charmap .
|
||||
.It Fn INCHARMAP str Ta Returns 1 if Ar str No has an entry in the current charmap, or 0 otherwise.
|
||||
.It Fn CHARLEN str Ta Returns the number of charmap entries in Ar str No with the current charmap.
|
||||
.It Fn CHARCMP str1 str2 Ta Compares Ar str1 No and Ar str2 No according to their charmap entry values with the current charmap. Returns -1 if Ar str1 No is lower than Ar str2 Ns , 1 if Ar str1 No is greater than Ar str2 Ns , or 0 if they match.
|
||||
.It Fn CHARSIZE char Ta Returns how many values are in the charmap entry for Ar char No with the current charmap.
|
||||
.It Fn CHARVAL char idx Ta Returns the value at Ar idx No of the charmap entry for Ar char .
|
||||
.El
|
||||
.Pp
|
||||
Note that the first character of a string is at index 0, and the last is at index -1.
|
||||
Note that indexes count starting from 0 at the beginning, or from -1 at the end.
|
||||
The characters of a string are counted by
|
||||
.Ql STRLEN ;
|
||||
the charmap entries of a string are counted by
|
||||
.Ql CHARLEN ;
|
||||
and the values of a charmap entry are counted by
|
||||
.Ql CHARSIZE .
|
||||
.Pp
|
||||
The following legacy functions are similar to other functions that operate on string expressions, but for historical reasons, they count characters starting from
|
||||
The following legacy functions are similar to other functions that operate on string expressions, but for historical reasons, they count starting from
|
||||
.Em position 1 ,
|
||||
not from index 0!
|
||||
(Position -1 still counts from the last character.)
|
||||
(Position -1 still counts from the end.)
|
||||
.Bl -column "STRSUB(str, pos, len)"
|
||||
.It Sy Name Ta Sy Operation
|
||||
.It Fn STRSUB str pos len Ta Returns a substring of Ar str No starting at Ar pos No and Ar len No characters long. If Ar len No is not specified, the substring continues to the end of Ar str No .
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd May 4, 2025
|
||||
.Dd June 30, 2025
|
||||
.Dt RGBDS 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd May 4, 2025
|
||||
.Dd June 30, 2025
|
||||
.Dt RGBDS 7
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd May 4, 2025
|
||||
.Dd June 30, 2025
|
||||
.Dt RGBFIX 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
22
man/rgbgfx.1
22
man/rgbgfx.1
@@ -2,7 +2,7 @@
|
||||
.\"
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd May 4, 2025
|
||||
.Dd June 30, 2025
|
||||
.Dt RGBGFX 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -159,6 +159,24 @@ is the case-insensitive word
|
||||
then the first four colors of the input PNG's embedded palette are used.
|
||||
It is an error if the PNG is not indexed, or if colors other than these 4 are used.
|
||||
.Pq This is different from the default behavior of indexed PNGs, as then unused entries in the embedded palette are ignored, whereas they are not with Fl c Cm embedded .
|
||||
.It Sy DMG palette spec
|
||||
If
|
||||
.Ar pal_spec
|
||||
starts with case-insensitive
|
||||
.Cm dmg= ,
|
||||
then the following two-digit hexadecimal number specifies four grayscale DMG color indexes.
|
||||
The number functions like the DMG's $FF47
|
||||
.Sy BGP
|
||||
register
|
||||
(see
|
||||
.Lk https://gbdev.io/pandocs/Palettes.html Pan Docs
|
||||
for more information):
|
||||
the low two bits 0-1 specify which gray shade goes in color index 0,
|
||||
the next two bits 2-3 specify which gray shade goes in color index 1,
|
||||
and so on.
|
||||
Gray shade 0 is the lightest (white), 3 is the darkest (black).
|
||||
The same gray shade cannot go in two color indexes.
|
||||
To specify a DMG palette, the input PNG must have all its colors in shades of gray, without any transparent colors.
|
||||
.It Sy external palette spec
|
||||
Otherwise,
|
||||
.Ar pal_spec
|
||||
@@ -528,6 +546,8 @@ Otherwise, if the PNG only contains shades of gray, they will be categorized int
|
||||
.Dq bins
|
||||
as there are colors per palette, and the palette is set to these bins.
|
||||
The darkest gray will end up in bin #0, and so on; note that this is the opposite of the RGB method below.
|
||||
This is equivalent to having specified a DMG palette of
|
||||
.Fl c Cm dmg=E4 .
|
||||
If two distinct grays end up in the same bin, the RGB method is used instead.
|
||||
.Pp
|
||||
Be careful that
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd May 4, 2025
|
||||
.Dd June 30, 2025
|
||||
.Dt RGBLINK 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd May 4, 2025
|
||||
.Dd June 30, 2025
|
||||
.Dt RGBLINK 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
@@ -9,10 +9,10 @@ set(common_src
|
||||
)
|
||||
|
||||
find_package(BISON 3.0.0 REQUIRED)
|
||||
set(BISON_FLAGS "-Wall -Dparse.lac=full -Dlr.type=ielr")
|
||||
set(BISON_FLAGS "-Wall -Dlr.type=ielr")
|
||||
# Set some optimization flags on versions that support them
|
||||
if(BISON_VERSION VERSION_GREATER_EQUAL "3.5")
|
||||
set(BISON_FLAGS "${BISON_FLAGS} -Dapi.token.raw=true")
|
||||
set(BISON_FLAGS "${BISON_FLAGS} -Dparse.lac=full -Dapi.token.raw=true")
|
||||
endif()
|
||||
if(BISON_VERSION VERSION_GREATER_EQUAL "3.6")
|
||||
set(BISON_FLAGS "${BISON_FLAGS} -Dparse.error=detailed")
|
||||
|
||||
@@ -189,7 +189,7 @@ bool charmap_HasChar(std::string const &mapping) {
|
||||
return charmap.nodes[nodeIdx].isTerminal();
|
||||
}
|
||||
|
||||
size_t charmap_CharSize(std::string const &mapping) {
|
||||
static CharmapNode const *charmapEntry(std::string const &mapping) {
|
||||
Charmap const &charmap = *currentCharmap;
|
||||
size_t nodeIdx = 0;
|
||||
|
||||
@@ -197,12 +197,24 @@ size_t charmap_CharSize(std::string const &mapping) {
|
||||
nodeIdx = charmap.nodes[nodeIdx].next[static_cast<uint8_t>(c)];
|
||||
|
||||
if (!nodeIdx) {
|
||||
return 0;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
CharmapNode const &node = charmap.nodes[nodeIdx];
|
||||
return node.isTerminal() ? node.value.size() : 0;
|
||||
return &charmap.nodes[nodeIdx];
|
||||
}
|
||||
|
||||
size_t charmap_CharSize(std::string const &mapping) {
|
||||
CharmapNode const *node = charmapEntry(mapping);
|
||||
return node && node->isTerminal() ? node->value.size() : 0;
|
||||
}
|
||||
|
||||
std::optional<int32_t> charmap_CharValue(std::string const &mapping, size_t idx) {
|
||||
if (CharmapNode const *node = charmapEntry(mapping);
|
||||
node && node->isTerminal() && idx < node->value.size()) {
|
||||
return node->value[idx];
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<int32_t> charmap_Convert(std::string const &input) {
|
||||
|
||||
@@ -126,20 +126,20 @@ void fstk_SetPreIncludeFile(std::string const &path) {
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
static void printDep(std::string const &path) {
|
||||
if (dependFile) {
|
||||
fprintf(dependFile, "%s: %s\n", targetFileName.c_str(), path.c_str());
|
||||
if (generatePhonyDeps) {
|
||||
fprintf(dependFile, "%s:\n", path.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool isValidFilePath(std::string const &path) {
|
||||
struct stat statBuf;
|
||||
return stat(path.c_str(), &statBuf) == 0 && !S_ISDIR(statBuf.st_mode); // Reject directories
|
||||
}
|
||||
|
||||
static void printDep(std::string const &path) {
|
||||
if (dependFile) {
|
||||
fprintf(dependFile, "%s: %s\n", targetFileName.c_str(), path.c_str());
|
||||
if (generatePhonyDeps && isValidFilePath(path)) {
|
||||
fprintf(dependFile, "%s:\n", path.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> fstk_FindFile(std::string const &path) {
|
||||
for (std::string &incPath : includePaths) {
|
||||
if (std::string fullPath = incPath + path; isValidFilePath(fullPath)) {
|
||||
@@ -392,17 +392,13 @@ void fstk_RunFor(
|
||||
context.forName = symName;
|
||||
}
|
||||
|
||||
void fstk_StopRept() {
|
||||
contextStack.top().nbReptIters = 0; // Prevent more iterations
|
||||
}
|
||||
|
||||
bool fstk_Break() {
|
||||
if (contextStack.top().fileInfo->type != NODE_REPT) {
|
||||
error("BREAK can only be used inside a REPT/FOR block\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
fstk_StopRept();
|
||||
contextStack.top().nbReptIters = 0; // Prevent more iterations
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -136,9 +136,8 @@ struct CaseInsensitive {
|
||||
}
|
||||
};
|
||||
|
||||
// This map lists all RGBASM keywords which `yylex_NORMAL` lexes as identifiers
|
||||
// (see `startsIdentifier` and `continuesIdentifier` below). All non-identifier
|
||||
// tokens are lexed separately.
|
||||
// This map lists all RGBASM keywords which `yylex_NORMAL` lexes as identifiers.
|
||||
// All non-identifier tokens are lexed separately.
|
||||
static std::unordered_map<std::string, int, CaseInsensitive, CaseInsensitive> keywordDict = {
|
||||
{"ADC", T_(SM83_ADC) },
|
||||
{"ADD", T_(SM83_ADD) },
|
||||
@@ -260,6 +259,7 @@ static std::unordered_map<std::string, int, CaseInsensitive, CaseInsensitive> ke
|
||||
{"CHARLEN", T_(OP_CHARLEN) },
|
||||
{"CHARSIZE", T_(OP_CHARSIZE) },
|
||||
{"CHARSUB", T_(OP_CHARSUB) },
|
||||
{"CHARVAL", T_(OP_CHARVAL) },
|
||||
{"INCHARMAP", T_(OP_INCHARMAP) },
|
||||
{"REVCHAR", T_(OP_REVCHAR) },
|
||||
|
||||
@@ -611,9 +611,7 @@ static bool isMacroChar(char c) {
|
||||
// forward declarations for readBracketedMacroArgNum
|
||||
static int peek();
|
||||
static void shiftChar();
|
||||
static uint32_t readNumber(int radix, uint32_t baseValue);
|
||||
static bool startsIdentifier(int c);
|
||||
static bool continuesIdentifier(int c);
|
||||
static uint32_t readDecimalNumber(int initial);
|
||||
|
||||
static uint32_t readBracketedMacroArgNum() {
|
||||
bool disableMacroArgs = lexerState->disableMacroArgs;
|
||||
@@ -637,7 +635,7 @@ static uint32_t readBracketedMacroArgNum() {
|
||||
}
|
||||
|
||||
if (c >= '0' && c <= '9') {
|
||||
uint32_t n = readNumber(10, 0);
|
||||
uint32_t n = readDecimalNumber(0);
|
||||
if (n > INT32_MAX) {
|
||||
error("Number in bracketed macro argument is too large\n");
|
||||
return 0;
|
||||
@@ -1021,26 +1019,6 @@ static std::string readAnonLabelRef(char c) {
|
||||
return sym_MakeAnonLabelName(n, c == '-');
|
||||
}
|
||||
|
||||
static uint32_t readNumber(int radix, uint32_t baseValue) {
|
||||
uint32_t value = baseValue;
|
||||
|
||||
for (;; shiftChar()) {
|
||||
int c = peek();
|
||||
|
||||
if (c == '_') {
|
||||
continue;
|
||||
} else if (c < '0' || c > '0' + radix - 1) {
|
||||
break;
|
||||
}
|
||||
if (value > (UINT32_MAX - (c - '0')) / radix) {
|
||||
warning(WARNING_LARGE_CONSTANT, "Integer constant is too large\n");
|
||||
}
|
||||
value = value * radix + (c - '0');
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static uint32_t readFractionalPart(uint32_t integer) {
|
||||
uint32_t value = 0, divisor = 1;
|
||||
uint8_t precision = 0;
|
||||
@@ -1106,21 +1084,64 @@ static uint32_t readFractionalPart(uint32_t integer) {
|
||||
}
|
||||
|
||||
char binDigits[2];
|
||||
char gfxDigits[4];
|
||||
|
||||
static bool isValidDigit(char c) {
|
||||
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '.'
|
||||
|| c == '#' || c == '@';
|
||||
}
|
||||
|
||||
static bool checkDigitErrors(char const *digits, size_t n, char const *type) {
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
char c = digits[i];
|
||||
|
||||
if (!isValidDigit(c)) {
|
||||
error("Invalid digit for %s constant %s\n", type, printChar(c));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (c >= '0' && c < static_cast<char>(n + '0') && c != static_cast<char>(i + '0')) {
|
||||
error("Changed digit for %s constant %s\n", type, printChar(c));
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t j = i + 1; j < n; j++) {
|
||||
if (c == digits[j]) {
|
||||
error("Repeated digit for %s constant %s\n", type, printChar(c));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void lexer_SetBinDigits(char const digits[2]) {
|
||||
if (size_t n = std::size(binDigits); checkDigitErrors(digits, n, "binary")) {
|
||||
memcpy(binDigits, digits, n);
|
||||
}
|
||||
}
|
||||
|
||||
void lexer_SetGfxDigits(char const digits[4]) {
|
||||
if (size_t n = std::size(gfxDigits); checkDigitErrors(digits, n, "graphics")) {
|
||||
memcpy(gfxDigits, digits, n);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t readBinaryNumber() {
|
||||
uint32_t value = 0;
|
||||
bool empty = true;
|
||||
|
||||
for (;; shiftChar()) {
|
||||
int c = peek();
|
||||
int bit;
|
||||
|
||||
// Check for '_' after digits in case one of the digits is '_'
|
||||
if (c == binDigits[0]) {
|
||||
bit = 0;
|
||||
} else if (c == binDigits[1]) {
|
||||
bit = 1;
|
||||
} else if (c == '_') {
|
||||
if (c == '_' && !empty) {
|
||||
continue;
|
||||
} else if (c == '0' || c == binDigits[0]) {
|
||||
bit = 0;
|
||||
} else if (c == '1' || c == binDigits[1]) {
|
||||
bit = 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -1128,6 +1149,72 @@ static uint32_t readBinaryNumber() {
|
||||
warning(WARNING_LARGE_CONSTANT, "Integer constant is too large\n");
|
||||
}
|
||||
value = value * 2 + bit;
|
||||
|
||||
empty = false;
|
||||
}
|
||||
|
||||
if (empty) {
|
||||
error("Invalid integer constant, no digits after '%%'\n");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static uint32_t readOctalNumber() {
|
||||
uint32_t value = 0;
|
||||
bool empty = true;
|
||||
|
||||
for (;; shiftChar()) {
|
||||
int c = peek();
|
||||
|
||||
if (c == '_' && !empty) {
|
||||
continue;
|
||||
} else if (c >= '0' && c <= '7') {
|
||||
c = c - '0';
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
if (value > (UINT32_MAX - c) / 8) {
|
||||
warning(WARNING_LARGE_CONSTANT, "Integer constant is too large\n");
|
||||
}
|
||||
value = value * 8 + c;
|
||||
|
||||
empty = false;
|
||||
}
|
||||
|
||||
if (empty) {
|
||||
error("Invalid integer constant, no digits after '&'\n");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static uint32_t readDecimalNumber(int initial) {
|
||||
uint32_t value = initial ? initial - '0' : 0;
|
||||
bool empty = !initial;
|
||||
|
||||
for (;; shiftChar()) {
|
||||
int c = peek();
|
||||
|
||||
if (c == '_' && !empty) {
|
||||
continue;
|
||||
} else if (c >= '0' && c <= '9') {
|
||||
c = c - '0';
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
if (value > (UINT32_MAX - c) / 10) {
|
||||
warning(WARNING_LARGE_CONSTANT, "Integer constant is too large\n");
|
||||
}
|
||||
value = value * 10 + c;
|
||||
|
||||
empty = false;
|
||||
}
|
||||
|
||||
if (empty) {
|
||||
error("Invalid integer constant, no digits\n");
|
||||
}
|
||||
|
||||
return value;
|
||||
@@ -1140,14 +1227,14 @@ static uint32_t readHexNumber() {
|
||||
for (;; shiftChar()) {
|
||||
int c = peek();
|
||||
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
if (c == '_' && !empty) {
|
||||
continue;
|
||||
} else if (c >= 'a' && c <= 'f') {
|
||||
c = c - 'a' + 10;
|
||||
} else if (c >= 'A' && c <= 'F') {
|
||||
c = c - 'A' + 10;
|
||||
} else if (c >= '0' && c <= '9') {
|
||||
c = c - '0';
|
||||
} else if (c == '_' && !empty) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -1167,8 +1254,6 @@ static uint32_t readHexNumber() {
|
||||
return value;
|
||||
}
|
||||
|
||||
char gfxDigits[4];
|
||||
|
||||
static uint32_t readGfxConstant() {
|
||||
uint32_t bitPlaneLower = 0, bitPlaneUpper = 0;
|
||||
uint8_t width = 0;
|
||||
@@ -1177,17 +1262,16 @@ static uint32_t readGfxConstant() {
|
||||
int c = peek();
|
||||
uint32_t pixel;
|
||||
|
||||
// Check for '_' after digits in case one of the digits is '_'
|
||||
if (c == gfxDigits[0]) {
|
||||
pixel = 0;
|
||||
} else if (c == gfxDigits[1]) {
|
||||
pixel = 1;
|
||||
} else if (c == gfxDigits[2]) {
|
||||
pixel = 2;
|
||||
} else if (c == gfxDigits[3]) {
|
||||
pixel = 3;
|
||||
} else if (c == '_' && width > 0) {
|
||||
if (c == '_' && width > 0) {
|
||||
continue;
|
||||
} else if (c == '0' || c == gfxDigits[0]) {
|
||||
pixel = 0;
|
||||
} else if (c == '1' || c == gfxDigits[1]) {
|
||||
pixel = 1;
|
||||
} else if (c == '2' || c == gfxDigits[2]) {
|
||||
pixel = 2;
|
||||
} else if (c == '3' || c == gfxDigits[3]) {
|
||||
pixel = 3;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -1215,15 +1299,6 @@ static uint32_t readGfxConstant() {
|
||||
|
||||
// Functions to read identifiers and keywords
|
||||
|
||||
static bool startsIdentifier(int c) {
|
||||
// Anonymous labels internally start with '!'
|
||||
return (c <= 'Z' && c >= 'A') || (c <= 'z' && c >= 'a') || c == '.' || c == '_';
|
||||
}
|
||||
|
||||
static bool continuesIdentifier(int c) {
|
||||
return startsIdentifier(c) || (c <= '9' && c >= '0') || c == '#' || c == '$' || c == '@';
|
||||
}
|
||||
|
||||
static Token readIdentifier(char firstChar, bool raw) {
|
||||
std::string identifier(1, firstChar);
|
||||
int tokenType = firstChar == '.' ? T_(LOCAL) : T_(SYMBOL);
|
||||
@@ -1700,12 +1775,17 @@ static Token yylex_NORMAL() {
|
||||
|
||||
// Handle ambiguous 1- or 2-char tokens
|
||||
|
||||
case '+': // Either += or ADD
|
||||
if (peek() == '=') {
|
||||
case '+': // Either +=, ADD, or CAT
|
||||
switch (peek()) {
|
||||
case '=':
|
||||
shiftChar();
|
||||
return Token(T_(POP_ADDEQ));
|
||||
case '+':
|
||||
shiftChar();
|
||||
return Token(T_(OP_CAT));
|
||||
default:
|
||||
return Token(T_(OP_ADD));
|
||||
}
|
||||
return Token(T_(OP_ADD));
|
||||
|
||||
case '-': // Either -= or SUB
|
||||
if (peek() == '=') {
|
||||
@@ -1838,7 +1918,7 @@ static Token yylex_NORMAL() {
|
||||
case 'o':
|
||||
case 'O':
|
||||
shiftChar();
|
||||
return Token(T_(NUMBER), readNumber(8, 0));
|
||||
return Token(T_(NUMBER), readOctalNumber());
|
||||
case 'b':
|
||||
case 'B':
|
||||
shiftChar();
|
||||
@@ -1857,7 +1937,7 @@ static Token yylex_NORMAL() {
|
||||
case '7':
|
||||
case '8':
|
||||
case '9': {
|
||||
uint32_t n = readNumber(10, c - '0');
|
||||
uint32_t n = readDecimalNumber(c);
|
||||
|
||||
if (peek() == '.') {
|
||||
shiftChar();
|
||||
@@ -1875,7 +1955,7 @@ static Token yylex_NORMAL() {
|
||||
shiftChar();
|
||||
return Token(T_(OP_LOGICAND));
|
||||
} else if (c >= '0' && c <= '7') {
|
||||
return Token(T_(NUMBER), readNumber(8, 0));
|
||||
return Token(T_(NUMBER), readOctalNumber());
|
||||
}
|
||||
return Token(T_(OP_AND));
|
||||
|
||||
@@ -1884,7 +1964,7 @@ static Token yylex_NORMAL() {
|
||||
if (c == '=') {
|
||||
shiftChar();
|
||||
return Token(T_(POP_MODEQ));
|
||||
} else if (c == binDigits[0] || c == binDigits[1]) {
|
||||
} else if (c == '0' || c == '1' || c == binDigits[0] || c == binDigits[1]) {
|
||||
return Token(T_(NUMBER), readBinaryNumber());
|
||||
}
|
||||
return Token(T_(OP_MOD));
|
||||
|
||||
@@ -36,7 +36,7 @@ static std::string make_escape(std::string &str) {
|
||||
size_t pos = 0;
|
||||
for (;;) {
|
||||
// All dollars needs to be doubled
|
||||
size_t nextPos = str.find("$", pos);
|
||||
size_t nextPos = str.find('$', pos);
|
||||
if (nextPos == std::string::npos) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
#include "asm/warning.hpp"
|
||||
|
||||
struct OptStackEntry {
|
||||
char binary[2];
|
||||
char gbgfx[4];
|
||||
char binDigits[2];
|
||||
char gfxDigits[4];
|
||||
uint8_t fixPrecision;
|
||||
uint8_t fillByte;
|
||||
bool warningsAreErrors;
|
||||
@@ -151,13 +151,8 @@ void opt_Push() {
|
||||
OptStackEntry entry;
|
||||
|
||||
// Both of these are pulled from lexer.hpp
|
||||
entry.binary[0] = binDigits[0];
|
||||
entry.binary[1] = binDigits[1];
|
||||
|
||||
entry.gbgfx[0] = gfxDigits[0];
|
||||
entry.gbgfx[1] = gfxDigits[1];
|
||||
entry.gbgfx[2] = gfxDigits[2];
|
||||
entry.gbgfx[3] = gfxDigits[3];
|
||||
memcpy(entry.binDigits, binDigits, std::size(binDigits));
|
||||
memcpy(entry.gfxDigits, gfxDigits, std::size(gfxDigits));
|
||||
|
||||
entry.fixPrecision = fixPrecision; // Pulled from fixpoint.hpp
|
||||
|
||||
@@ -181,8 +176,8 @@ void opt_Pop() {
|
||||
OptStackEntry entry = stack.top();
|
||||
stack.pop();
|
||||
|
||||
opt_B(entry.binary);
|
||||
opt_G(entry.gbgfx);
|
||||
opt_B(entry.binDigits);
|
||||
opt_G(entry.gfxDigits);
|
||||
opt_P(entry.fillByte);
|
||||
opt_Q(entry.fixPrecision);
|
||||
opt_R(entry.maxRecursionDepth);
|
||||
|
||||
@@ -125,6 +125,9 @@
|
||||
%token OP_MUL "*" OP_DIV "/" OP_MOD "%"
|
||||
%token OP_EXP "**"
|
||||
|
||||
// String operators
|
||||
%token OP_CAT "++"
|
||||
|
||||
// Comparison operators
|
||||
%token OP_LOGICEQU "==" OP_LOGICNE "!="
|
||||
%token OP_LOGICLT "<" OP_LOGICGT ">"
|
||||
@@ -147,6 +150,7 @@
|
||||
%left OP_AND OP_OR OP_XOR
|
||||
%left OP_SHL OP_SHR OP_USHR
|
||||
%left OP_MUL OP_DIV OP_MOD
|
||||
%left OP_CAT
|
||||
%precedence NEG // applies to unary OP_LOGICNOT, OP_ADD, OP_SUB, OP_NOT
|
||||
%right OP_EXP
|
||||
|
||||
@@ -283,6 +287,7 @@
|
||||
%token OP_CHARLEN "CHARLEN"
|
||||
%token OP_CHARSIZE "CHARSIZE"
|
||||
%token OP_CHARSUB "CHARSUB"
|
||||
%token OP_CHARVAL "CHARVAL"
|
||||
%token OP_COS "COS"
|
||||
%token OP_DEF "DEF"
|
||||
%token OP_FDIV "FDIV"
|
||||
@@ -425,7 +430,6 @@ lines:
|
||||
lexer_SetMode(LEXER_NORMAL);
|
||||
lexer_ToggleStringExpansion(true);
|
||||
} endofline {
|
||||
fstk_StopRept();
|
||||
yyerrok;
|
||||
}
|
||||
;
|
||||
@@ -434,12 +438,12 @@ diff_mark:
|
||||
%empty // OK
|
||||
| OP_ADD {
|
||||
::error(
|
||||
"syntax error, unexpected + at the beginning of the line (is it a leftover diff mark?)\n"
|
||||
"syntax error, unexpected + at the beginning of the line (is it a leftover diff mark?)\n"
|
||||
);
|
||||
}
|
||||
| OP_SUB {
|
||||
::error(
|
||||
"syntax error, unexpected - at the beginning of the line (is it a leftover diff mark?)\n"
|
||||
"syntax error, unexpected - at the beginning of the line (is it a leftover diff mark?)\n"
|
||||
);
|
||||
}
|
||||
;
|
||||
@@ -704,9 +708,9 @@ align_spec:
|
||||
$$.alignment = $$.alignOfs = 0;
|
||||
} else if ($3 <= -(1 << $1) || $3 >= 1 << $1) {
|
||||
::error(
|
||||
"The absolute alignment offset (%" PRIu32 ") must be less than alignment size (%d)\n",
|
||||
static_cast<uint32_t>($3 < 0 ? -$3 : $3),
|
||||
1 << $1
|
||||
"The absolute alignment offset (%" PRIu32 ") must be less than alignment size (%d)\n",
|
||||
static_cast<uint32_t>($3 < 0 ? -$3 : $3),
|
||||
1 << $1
|
||||
);
|
||||
$$.alignment = $$.alignOfs = 0;
|
||||
} else {
|
||||
@@ -1577,6 +1581,24 @@ relocexpr_no_str:
|
||||
}
|
||||
$$.makeNumber(charSize);
|
||||
}
|
||||
| OP_CHARVAL LPAREN string COMMA iconst RPAREN {
|
||||
if (size_t len = charmap_CharSize($3); len != 0) {
|
||||
uint32_t idx = adjustNegativeIndex($5, len, "CHARVAL");
|
||||
if (std::optional<int32_t> val = charmap_CharValue($3, idx); val.has_value()) {
|
||||
$$.makeNumber(*val);
|
||||
} else {
|
||||
warning(
|
||||
WARNING_BUILTIN_ARG,
|
||||
"CHARVAL: Index %" PRIu32 " is past the end of the character mapping\n",
|
||||
idx
|
||||
);
|
||||
$$.makeNumber(0);
|
||||
}
|
||||
} else {
|
||||
::error("CHARVAL: No character mapping for \"%s\"\n", $3.c_str());
|
||||
$$.makeNumber(0);
|
||||
}
|
||||
}
|
||||
| LPAREN relocexpr RPAREN {
|
||||
$$ = std::move($2);
|
||||
}
|
||||
@@ -1614,6 +1636,10 @@ string_literal:
|
||||
STRING {
|
||||
$$ = std::move($1);
|
||||
}
|
||||
| string OP_CAT string {
|
||||
$$ = std::move($1);
|
||||
$$.append($3);
|
||||
}
|
||||
| OP_STRSLICE LPAREN string COMMA iconst COMMA iconst RPAREN {
|
||||
size_t len = strlenUTF8($3, false);
|
||||
uint32_t start = adjustNegativeIndex($5, len, "STRSLICE");
|
||||
@@ -1623,7 +1649,7 @@ string_literal:
|
||||
| OP_STRSLICE LPAREN string COMMA iconst RPAREN {
|
||||
size_t len = strlenUTF8($3, false);
|
||||
uint32_t start = adjustNegativeIndex($5, len, "STRSLICE");
|
||||
$$ = strsliceUTF8($3, start, len - 1);
|
||||
$$ = strsliceUTF8($3, start, len);
|
||||
}
|
||||
| OP_STRSUB LPAREN string COMMA iconst COMMA uconst RPAREN {
|
||||
size_t len = strlenUTF8($3, false);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "asm/symbol.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <unordered_map>
|
||||
@@ -9,6 +10,7 @@
|
||||
|
||||
#include "error.hpp"
|
||||
#include "helpers.hpp" // assume
|
||||
#include "util.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
#include "asm/fstack.hpp"
|
||||
@@ -130,6 +132,11 @@ static void updateSymbolFilename(Symbol &sym) {
|
||||
}
|
||||
}
|
||||
|
||||
static bool isValidIdentifier(std::string const &s) {
|
||||
return !s.empty() && startsIdentifier(s[0])
|
||||
&& std::all_of(s.begin() + 1, s.end(), [](char c) { return continuesIdentifier(c); });
|
||||
}
|
||||
|
||||
static void alreadyDefinedError(Symbol const &sym, char const *asType) {
|
||||
if (sym.isBuiltin && !sym_FindScopedValidSymbol(sym.name)) {
|
||||
// `DEF()` would return false, so we should not claim the symbol is already defined
|
||||
@@ -141,6 +148,15 @@ static void alreadyDefinedError(Symbol const &sym, char const *asType) {
|
||||
}
|
||||
fputs(" at ", stderr);
|
||||
dumpFilename(sym);
|
||||
if (sym.type == SYM_EQUS) {
|
||||
if (std::string const &contents = *sym.getEqus(); isValidIdentifier(contents)) {
|
||||
fprintf(
|
||||
stderr,
|
||||
" (should it be {interpolated} to define its contents \"%s\"?)\n",
|
||||
contents.c_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,11 +12,11 @@ if [ "$BISON_MAJOR" -lt 3 ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BISON_FLAGS="-Wall -Dparse.lac=full -Dlr.type=ielr"
|
||||
BISON_FLAGS="-Wall -Dlr.type=ielr"
|
||||
|
||||
# Set some optimization flags on versions that support them
|
||||
if [ "$BISON_MAJOR" -ge 4 ] || [ "$BISON_MAJOR" -eq 3 ] && [ "$BISON_MINOR" -ge 5 ]; then
|
||||
BISON_FLAGS="$BISON_FLAGS -Dapi.token.raw=true"
|
||||
BISON_FLAGS="$BISON_FLAGS -Dparse.lac=full -Dapi.token.raw=true"
|
||||
fi
|
||||
if [ "$BISON_MAJOR" -ge 4 ] || [ "$BISON_MAJOR" -eq 3 ] && [ "$BISON_MINOR" -ge 6 ]; then
|
||||
BISON_FLAGS="$BISON_FLAGS -Dparse.error=detailed"
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
#include "extern/getopt.hpp"
|
||||
#include "file.hpp"
|
||||
#include "helpers.hpp" // assume
|
||||
#include "platform.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
@@ -354,7 +353,6 @@ static char *parseArgv(int argc, char *argv[]) {
|
||||
for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) {
|
||||
char *arg = musl_optarg; // Make a copy for scanning
|
||||
uint16_t number;
|
||||
size_t size;
|
||||
switch (ch) {
|
||||
case 'A':
|
||||
localOptions.autoAttrmap = true;
|
||||
@@ -367,41 +365,7 @@ static char *parseArgv(int argc, char *argv[]) {
|
||||
options.attrmap = musl_optarg;
|
||||
break;
|
||||
case 'B':
|
||||
if (strcasecmp(musl_optarg, "transparent") == 0) {
|
||||
options.bgColor = Rgba(0x00, 0x00, 0x00, 0x00);
|
||||
break;
|
||||
}
|
||||
if (musl_optarg[0] != '#') {
|
||||
error("Background color specification must be `#rgb`, `#rrggbb`, or `transparent`");
|
||||
break;
|
||||
}
|
||||
size = strspn(&musl_optarg[1], "0123456789ABCDEFabcdef");
|
||||
switch (size) {
|
||||
case 3:
|
||||
options.bgColor = Rgba(
|
||||
singleToHex(musl_optarg[1]),
|
||||
singleToHex(musl_optarg[2]),
|
||||
singleToHex(musl_optarg[3]),
|
||||
0xFF
|
||||
);
|
||||
break;
|
||||
case 6:
|
||||
options.bgColor = Rgba(
|
||||
toHex(musl_optarg[1], musl_optarg[2]),
|
||||
toHex(musl_optarg[3], musl_optarg[4]),
|
||||
toHex(musl_optarg[5], musl_optarg[6]),
|
||||
0xFF
|
||||
);
|
||||
break;
|
||||
default:
|
||||
error("Unknown background color specification \"%s\"", musl_optarg);
|
||||
}
|
||||
if (musl_optarg[size + 1] != '\0') {
|
||||
error(
|
||||
"Unexpected text \"%s\" after background color specification",
|
||||
&musl_optarg[size + 1]
|
||||
);
|
||||
}
|
||||
parseBackgroundPalSpec(musl_optarg);
|
||||
break;
|
||||
case 'b':
|
||||
number = parseNumber(arg, "Bank 0 base tile ID", 0);
|
||||
@@ -442,18 +406,18 @@ static char *parseArgv(int argc, char *argv[]) {
|
||||
options.useColorCurve = true;
|
||||
break;
|
||||
case 'c':
|
||||
localOptions.externalPalSpec = nullptr; // Allow overriding a previous pal spec
|
||||
if (musl_optarg[0] == '#') {
|
||||
options.palSpecType = Options::EXPLICIT;
|
||||
parseInlinePalSpec(musl_optarg);
|
||||
} else if (strcasecmp(musl_optarg, "embedded") == 0) {
|
||||
// Use PLTE, error out if missing
|
||||
options.palSpecType = Options::EMBEDDED;
|
||||
} else if (strncasecmp(musl_optarg, "dmg=", literal_strlen("dmg=")) == 0) {
|
||||
options.palSpecType = Options::DMG;
|
||||
parseDmgPalSpec(&musl_optarg[literal_strlen("dmg=")]);
|
||||
} else {
|
||||
options.palSpecType = Options::EXPLICIT;
|
||||
// Can't parse the file yet, as "flat" color collections need to know the palette
|
||||
// size to be split; thus, we defer that.
|
||||
// If a following `-c` overrides a previous one, the `fmt` part of an overridden
|
||||
// external palette spec will not be validated, but I guess that's okay.
|
||||
localOptions.externalPalSpec = musl_optarg;
|
||||
}
|
||||
break;
|
||||
@@ -800,10 +764,31 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
if (options.verbosity >= Options::VERB_VVVVVV) {
|
||||
putc('\n', stderr);
|
||||
// clang-format off: vertically align values
|
||||
static std::array<uint16_t, 21> gfx{
|
||||
0x1FE, 0x3FF, 0x399, 0x399, 0x3FF, 0x3FF, 0x381, 0x3C3, 0x1FE, 0x078, 0x1FE,
|
||||
0x3FF, 0x3FF, 0x3FF, 0x37B, 0x37B, 0x0FC, 0x0CC, 0x1CE, 0x1CE, 0x1CE,
|
||||
0b0111111110,
|
||||
0b1111111111,
|
||||
0b1110011001,
|
||||
0b1110011001,
|
||||
0b1111111111,
|
||||
0b1111111111,
|
||||
0b1110000001,
|
||||
0b1111000011,
|
||||
0b0111111110,
|
||||
0b0001111000,
|
||||
0b0111111110,
|
||||
0b1111111111,
|
||||
0b1111111111,
|
||||
0b1111111111,
|
||||
0b1101111011,
|
||||
0b1101111011,
|
||||
0b0011111100,
|
||||
0b0011001100,
|
||||
0b0111001110,
|
||||
0b0111001110,
|
||||
0b0111001110,
|
||||
};
|
||||
// clang-format on
|
||||
static std::array<char const *, 3> textbox{
|
||||
" ,----------------------------------------.",
|
||||
" | Augh, dimensional interference again?! |",
|
||||
@@ -856,6 +841,8 @@ int main(int argc, char *argv[]) {
|
||||
return "Explicit";
|
||||
case Options::EMBEDDED:
|
||||
return "Embedded";
|
||||
case Options::DMG:
|
||||
return "DMG";
|
||||
}
|
||||
return "???";
|
||||
}());
|
||||
|
||||
@@ -299,7 +299,7 @@ static void decant(
|
||||
break;
|
||||
}
|
||||
auto attrs = from.begin();
|
||||
std::advance(attrs, (iter - processed.begin()));
|
||||
std::advance(attrs, iter - processed.begin());
|
||||
|
||||
// Build up the "component"...
|
||||
colors.clear();
|
||||
|
||||
@@ -28,6 +28,27 @@ static void skipWhitespace(Str const &str, size_t &pos) {
|
||||
pos = std::min(str.find_first_not_of(" \t"sv, pos), str.length());
|
||||
}
|
||||
|
||||
static constexpr uint8_t nibble(char c) {
|
||||
if (c >= 'a') {
|
||||
assume(c <= 'f');
|
||||
return c - 'a' + 10;
|
||||
} else if (c >= 'A') {
|
||||
assume(c <= 'F');
|
||||
return c - 'A' + 10;
|
||||
} else {
|
||||
assume(c >= '0' && c <= '9');
|
||||
return c - '0';
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr uint8_t toHex(char c1, char c2) {
|
||||
return nibble(c1) * 16 + nibble(c2);
|
||||
}
|
||||
|
||||
static constexpr uint8_t singleToHex(char c) {
|
||||
return toHex(c, c);
|
||||
}
|
||||
|
||||
void parseInlinePalSpec(char const * const rawArg) {
|
||||
// List of #rrggbb/#rgb colors (or #none); comma-separated.
|
||||
// Palettes are separated by colons.
|
||||
@@ -595,3 +616,61 @@ void parseExternalPalSpec(char const *arg) {
|
||||
|
||||
std::get<1> (*iter)(file);
|
||||
}
|
||||
|
||||
void parseDmgPalSpec(char const * const rawArg) {
|
||||
// Two hex digit DMG palette spec
|
||||
|
||||
std::string_view arg(rawArg);
|
||||
|
||||
if (arg.length() != 2
|
||||
|| arg.find_first_not_of("0123456789ABCDEFabcdef"sv) != std::string_view::npos) {
|
||||
error("Unknown DMG palette specification \"%s\"", rawArg);
|
||||
return;
|
||||
}
|
||||
|
||||
options.palSpecDmg = toHex(arg[0], arg[1]);
|
||||
|
||||
// Map gray shades to their DMG color indexes for fast lookup by `Rgba::grayIndex`
|
||||
for (uint8_t i = 0; i < 4; ++i) {
|
||||
options.dmgColors[options.dmgValue(i)] = i;
|
||||
}
|
||||
|
||||
// Validate that DMG palette spec does not have conflicting colors
|
||||
for (uint8_t i = 0; i < 3; ++i) {
|
||||
for (uint8_t j = i + 1; j < 4; ++j) {
|
||||
if (options.dmgValue(i) == options.dmgValue(j)) {
|
||||
error("DMG palette specification maps two gray shades to the same color index");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parseBackgroundPalSpec(char const *arg) {
|
||||
if (strcasecmp(arg, "transparent") == 0) {
|
||||
options.bgColor = Rgba(0x00, 0x00, 0x00, 0x00);
|
||||
return;
|
||||
}
|
||||
|
||||
if (arg[0] != '#') {
|
||||
error("Background color specification must be `#rgb`, `#rrggbb`, or `transparent`");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t size = strspn(&arg[1], "0123456789ABCDEFabcdef");
|
||||
switch (size) {
|
||||
case 3:
|
||||
options.bgColor = Rgba(singleToHex(arg[1]), singleToHex(arg[2]), singleToHex(arg[3]), 0xFF);
|
||||
break;
|
||||
case 6:
|
||||
options.bgColor =
|
||||
Rgba(toHex(arg[1], arg[2]), toHex(arg[3], arg[4]), toHex(arg[5], arg[6]), 0xFF);
|
||||
break;
|
||||
default:
|
||||
error("Unknown background color specification \"%s\"", arg);
|
||||
}
|
||||
|
||||
if (arg[size + 1] != '\0') {
|
||||
error("Unexpected text \"%s\" after background color specification", &arg[size + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -586,8 +586,10 @@ static std::tuple<DefaultInitVec<size_t>, std::vector<Palette>>
|
||||
}
|
||||
|
||||
// "Sort" colors in the generated palettes, see the man page for the flowchart
|
||||
auto [embPalSize, embPalRGB, embPalAlphaSize, embPalAlpha] = png.getEmbeddedPal();
|
||||
if (embPalRGB != nullptr) {
|
||||
if (options.palSpecType == Options::DMG) {
|
||||
sortGrayscale(palettes, png.getColors().raw());
|
||||
} else if (auto [embPalSize, embPalRGB, embPalAlphaSize, embPalAlpha] = png.getEmbeddedPal();
|
||||
embPalRGB != nullptr) {
|
||||
sortIndexed(palettes, embPalSize, embPalRGB, embPalAlphaSize, embPalAlpha);
|
||||
} else if (png.isSuitableForGrayscale()) {
|
||||
sortGrayscale(palettes, png.getColors().raw());
|
||||
@@ -1139,6 +1141,18 @@ void process() {
|
||||
}
|
||||
// LCOV_EXCL_STOP
|
||||
|
||||
if (options.palSpecType == Options::DMG) {
|
||||
if (options.hasTransparentPixels) {
|
||||
fatal(
|
||||
"Image contains transparent pixels, not compatible with a DMG palette specification"
|
||||
);
|
||||
}
|
||||
if (!png.isSuitableForGrayscale()) {
|
||||
fatal("Image contains too many or non-gray colors, not compatible with a DMG palette "
|
||||
"specification");
|
||||
}
|
||||
}
|
||||
|
||||
// Now, iterate through the tiles, generating proto-palettes as we go
|
||||
// We do this unconditionally because this performs the image validation (which we want to
|
||||
// perform even if no output is requested), and because it's necessary to generate any
|
||||
@@ -1248,9 +1262,10 @@ continue_visiting_tiles:;
|
||||
if (options.palSpecType == Options::EMBEDDED) {
|
||||
generatePalSpec(png);
|
||||
}
|
||||
auto [mappings, palettes] = options.palSpecType == Options::NO_SPEC
|
||||
? generatePalettes(protoPalettes, png)
|
||||
: makePalsAsSpecified(protoPalettes);
|
||||
auto [mappings, palettes] =
|
||||
options.palSpecType == Options::NO_SPEC || options.palSpecType == Options::DMG
|
||||
? generatePalettes(protoPalettes, png)
|
||||
: makePalsAsSpecified(protoPalettes);
|
||||
outputPalettes(palettes);
|
||||
|
||||
// If deduplication is not happening, we just need to output the tile data and/or maps as-is
|
||||
|
||||
@@ -195,10 +195,13 @@ void reverse() {
|
||||
Options::VERB_INTERM, "Reversed image dimensions: %zux%zu tiles\n", width, height
|
||||
);
|
||||
|
||||
std::vector<std::array<std::optional<Rgba>, 4>> palettes{
|
||||
{Rgba(0xFFFFFFFF), Rgba(0xAAAAAAFF), Rgba(0x555555FF), Rgba(0x000000FF)}
|
||||
Rgba const grayColors[4] = {
|
||||
Rgba(0xFFFFFFFF), Rgba(0xAAAAAAFF), Rgba(0x555555FF), Rgba(0x000000FF)
|
||||
};
|
||||
// If a palette file is used as input, it overrides the default colors.
|
||||
std::vector<std::array<std::optional<Rgba>, 4>> palettes{
|
||||
{grayColors[0], grayColors[1], grayColors[2], grayColors[3]}
|
||||
};
|
||||
// If a palette file or palette spec is used as input, it overrides the default colors.
|
||||
if (!options.palettes.empty()) {
|
||||
File file;
|
||||
if (!file.open(options.palettes, std::ios::in | std::ios::binary)) {
|
||||
@@ -255,6 +258,10 @@ void reverse() {
|
||||
putc('\n', stderr);
|
||||
}
|
||||
}
|
||||
} else if (options.palSpecType == Options::DMG) {
|
||||
for (size_t i = 0; i < palettes[0].size(); ++i) {
|
||||
palettes[0][i] = grayColors[options.dmgValue(i)];
|
||||
}
|
||||
} else if (options.palSpecType == Options::EMBEDDED) {
|
||||
warning("An embedded palette was requested, but no palette file was specified; ignoring "
|
||||
"request.");
|
||||
@@ -422,38 +429,73 @@ void reverse() {
|
||||
}
|
||||
png_set_write_fn(png, &pngFile, writePng, flushPng);
|
||||
|
||||
int pngColorType = options.palettes.empty() ? PNG_COLOR_TYPE_GRAY
|
||||
: palettes.size() == 1 ? PNG_COLOR_TYPE_PALETTE
|
||||
: PNG_COLOR_TYPE_RGB_ALPHA;
|
||||
int pngDepth = options.palettes.empty() ? options.bitDepth : 8;
|
||||
|
||||
png_set_IHDR(
|
||||
png,
|
||||
pngInfo,
|
||||
width * 8,
|
||||
height * 8,
|
||||
8,
|
||||
PNG_COLOR_TYPE_RGB_ALPHA,
|
||||
pngDepth,
|
||||
pngColorType,
|
||||
PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_DEFAULT,
|
||||
PNG_FILTER_TYPE_DEFAULT
|
||||
);
|
||||
|
||||
if (pngColorType != PNG_COLOR_TYPE_GRAY) {
|
||||
png_color_8 sbitChunk;
|
||||
sbitChunk.red = 5;
|
||||
sbitChunk.green = 5;
|
||||
sbitChunk.blue = 5;
|
||||
if (pngColorType == PNG_COLOR_TYPE_RGB_ALPHA) {
|
||||
sbitChunk.alpha = 1;
|
||||
}
|
||||
png_set_sBIT(png, pngInfo, &sbitChunk);
|
||||
}
|
||||
|
||||
if (pngColorType == PNG_COLOR_TYPE_PALETTE) {
|
||||
assume(palettes.size() == 1);
|
||||
png_color pngPalette[4] = {};
|
||||
png_byte pngTrans[4] = {};
|
||||
int nbPngColors = 0, nbPngTrans = 0;
|
||||
for (auto const &color : palettes[0]) {
|
||||
if (!color.has_value()) {
|
||||
continue;
|
||||
}
|
||||
pngPalette[nbPngColors].red = color->red;
|
||||
pngPalette[nbPngColors].green = color->green;
|
||||
pngPalette[nbPngColors].blue = color->blue;
|
||||
pngTrans[nbPngColors] = color->alpha;
|
||||
++nbPngColors;
|
||||
if (color->alpha < 255) {
|
||||
nbPngTrans = nbPngColors;
|
||||
}
|
||||
}
|
||||
png_set_PLTE(png, pngInfo, pngPalette, nbPngColors);
|
||||
if (nbPngTrans > 0) {
|
||||
png_set_tRNS(png, pngInfo, pngTrans, nbPngTrans, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
png_write_info(png, pngInfo);
|
||||
|
||||
png_color_8 sbitChunk;
|
||||
sbitChunk.red = 5;
|
||||
sbitChunk.green = 5;
|
||||
sbitChunk.blue = 5;
|
||||
sbitChunk.alpha = 1;
|
||||
png_set_sBIT(png, pngInfo, &sbitChunk);
|
||||
|
||||
constexpr uint8_t SIZEOF_TILE = 4 * 8; // 4 bytes/pixel (RGBA @ 8 bits/channel) * 8 pixels/tile
|
||||
size_t const SIZEOF_ROW = width * SIZEOF_TILE;
|
||||
std::vector<uint8_t> tileRow(8 * SIZEOF_ROW, 0xFF); // Data for 8 rows of pixels
|
||||
// N bits/pixel * 8 pixels/tile row / 8 bits/byte = N bytes/tile row
|
||||
uint8_t const bytesPerTileRow = pngColorType == PNG_COLOR_TYPE_RGB_ALPHA ? 32 : pngDepth;
|
||||
size_t const bytesPerRow = width * bytesPerTileRow;
|
||||
std::vector<uint8_t> tileRow(8 * bytesPerRow, 0xFF); // Data for 8 rows of pixels
|
||||
uint8_t * const rowPtrs[8] = {
|
||||
&tileRow.data()[0 * SIZEOF_ROW],
|
||||
&tileRow.data()[1 * SIZEOF_ROW],
|
||||
&tileRow.data()[2 * SIZEOF_ROW],
|
||||
&tileRow.data()[3 * SIZEOF_ROW],
|
||||
&tileRow.data()[4 * SIZEOF_ROW],
|
||||
&tileRow.data()[5 * SIZEOF_ROW],
|
||||
&tileRow.data()[6 * SIZEOF_ROW],
|
||||
&tileRow.data()[7 * SIZEOF_ROW],
|
||||
&tileRow.data()[0 * bytesPerRow],
|
||||
&tileRow.data()[1 * bytesPerRow],
|
||||
&tileRow.data()[2 * bytesPerRow],
|
||||
&tileRow.data()[3 * bytesPerRow],
|
||||
&tileRow.data()[4 * bytesPerRow],
|
||||
&tileRow.data()[5 * bytesPerRow],
|
||||
&tileRow.data()[6 * bytesPerRow],
|
||||
&tileRow.data()[7 * bytesPerRow],
|
||||
};
|
||||
|
||||
for (size_t ty = 0; ty < height; ++ty) {
|
||||
@@ -486,19 +528,36 @@ void reverse() {
|
||||
bitplane0 = flipTable[bitplane0];
|
||||
bitplane1 = flipTable[bitplane1];
|
||||
}
|
||||
uint8_t *ptr = &rowPtrs[y][tx * SIZEOF_TILE];
|
||||
|
||||
uint8_t *ptr = &rowPtrs[y][tx * bytesPerTileRow];
|
||||
uint16_t gray = 0;
|
||||
for (uint8_t x = 0; x < 8; ++x) {
|
||||
uint8_t bit0 = bitplane0 & 0x80, bit1 = bitplane1 & 0x80;
|
||||
Rgba const &pixel = *palette[bit0 >> 7 | bit1 >> 6];
|
||||
*ptr++ = pixel.red;
|
||||
*ptr++ = pixel.green;
|
||||
*ptr++ = pixel.blue;
|
||||
*ptr++ = pixel.alpha;
|
||||
uint8_t colorID = bit0 >> 7 | bit1 >> 6;
|
||||
Rgba const &pixel = *palette[colorID];
|
||||
|
||||
if (pngColorType == PNG_COLOR_TYPE_GRAY) {
|
||||
gray = gray << pngDepth | (pixel.red & ((1 << pngDepth) - 1));
|
||||
} else if (pngColorType == PNG_COLOR_TYPE_PALETTE) {
|
||||
*ptr++ = palID * 4 + colorID;
|
||||
} else {
|
||||
*ptr++ = pixel.red;
|
||||
*ptr++ = pixel.green;
|
||||
*ptr++ = pixel.blue;
|
||||
*ptr++ = pixel.alpha;
|
||||
}
|
||||
|
||||
// Shift the pixel out
|
||||
bitplane0 <<= 1;
|
||||
bitplane1 <<= 1;
|
||||
}
|
||||
|
||||
if (pngDepth == 1) {
|
||||
*ptr = gray;
|
||||
} else if (pngDepth == 2) {
|
||||
*ptr++ = gray >> 8;
|
||||
*ptr = gray & 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
// We never modify the pointers, and neither should libpng, despite the overly lax function
|
||||
|
||||
@@ -58,7 +58,15 @@ uint16_t Rgba::cgbColor() const {
|
||||
|
||||
uint8_t Rgba::grayIndex() const {
|
||||
assume(isGray());
|
||||
// Convert from 0..<256 to hasTransparentPixels..<nbColorsPerPal
|
||||
// 2bpp shades are inverted from RGB PNG; %00 = white, %11 = black
|
||||
uint8_t gray = 255 - red;
|
||||
if (options.palSpecType == Options::DMG) {
|
||||
assume(!options.hasTransparentPixels);
|
||||
// Reduce gray shade from 0..<256 to 0..<4, then map to color index,
|
||||
// then reduce to 0..<nbColorsPerPal
|
||||
return options.dmgColors[gray * 4 / 256] * options.nbColorsPerPal / 4;
|
||||
}
|
||||
// Reduce gray shade from 0..<256 to hasTransparentPixels..<nbColorsPerPal
|
||||
// Note that `maxOpaqueColors()` already takes `hasTransparentPixels` into account
|
||||
return (255 - red) * options.maxOpaqueColors() / 256 + options.hasTransparentPixels;
|
||||
return gray * options.maxOpaqueColors() / 256 + options.hasTransparentPixels;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "helpers.hpp"
|
||||
#include "linkdefs.hpp"
|
||||
#include "platform.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
#include "link/main.hpp"
|
||||
#include "link/section.hpp"
|
||||
@@ -30,6 +31,7 @@ FILE *mapFile;
|
||||
struct SortedSymbol {
|
||||
Symbol const *sym;
|
||||
uint16_t addr;
|
||||
uint16_t parentAddr;
|
||||
};
|
||||
|
||||
struct SortedSections {
|
||||
@@ -259,29 +261,13 @@ static void writeROM() {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks whether a symbol is legal for a sym file or map file.
|
||||
// Eliminates anonymous labels, which start with a '!'.
|
||||
static bool isLegalSymbol(Symbol const &sym) {
|
||||
if (sym.name.empty()) {
|
||||
return false;
|
||||
}
|
||||
char c = sym.name[0];
|
||||
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_';
|
||||
}
|
||||
|
||||
// Checks whether this character is legal in a symbol's name in a sym file
|
||||
static bool isLegalForSymName(char c) {
|
||||
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_'
|
||||
|| c == '@' || c == '#' || c == '$' || c == '.';
|
||||
}
|
||||
|
||||
// Prints a symbol's name to a file, assuming that the first character is legal.
|
||||
// Illegal characters are UTF-8-decoded (errors are replaced by U+FFFD) and emitted as '\u'/'\U'.
|
||||
static void printSymName(std::string const &name, FILE *file) {
|
||||
for (char const *ptr = name.c_str(); *ptr != '\0';) {
|
||||
char c = *ptr;
|
||||
|
||||
if (isLegalForSymName(c)) {
|
||||
if (continuesIdentifier(c)) {
|
||||
// Output legal ASCII characters as-is
|
||||
putc(c, file);
|
||||
++ptr;
|
||||
@@ -311,33 +297,28 @@ static void printSymName(std::string const &name, FILE *file) {
|
||||
}
|
||||
|
||||
// Comparator function for `std::stable_sort` to sort symbols
|
||||
// Symbols are ordered by address, then by parentage
|
||||
static bool compareSymbols(SortedSymbol const &sym1, SortedSymbol const &sym2) {
|
||||
// First, sort by address
|
||||
if (sym1.addr != sym2.addr) {
|
||||
return sym1.addr < sym2.addr;
|
||||
}
|
||||
|
||||
// Second, sort by locality (global before local)
|
||||
std::string const &sym1_name = sym1.sym->name;
|
||||
std::string const &sym2_name = sym2.sym->name;
|
||||
bool sym1_local = sym1_name.find(".") != std::string::npos;
|
||||
bool sym2_local = sym2_name.find(".") != std::string::npos;
|
||||
|
||||
bool sym1_local = sym1_name.find('.') != std::string::npos;
|
||||
bool sym2_local = sym2_name.find('.') != std::string::npos;
|
||||
if (sym1_local != sym2_local) {
|
||||
size_t sym1_len = sym1_name.length();
|
||||
size_t sym2_len = sym2_name.length();
|
||||
|
||||
// Sort parent labels before their child local labels
|
||||
if (sym2_name.starts_with(sym1_name) && sym2_name[sym1_len] == '.') {
|
||||
return true;
|
||||
}
|
||||
if (sym1_name.starts_with(sym2_name) && sym1_name[sym2_len] == '.') {
|
||||
return false;
|
||||
}
|
||||
// Sort local labels before unrelated global labels
|
||||
return sym1_local;
|
||||
return sym1_local < sym2_local;
|
||||
}
|
||||
|
||||
return false;
|
||||
// Third, sort by parent address
|
||||
if (sym1.parentAddr != sym2.parentAddr) {
|
||||
return sym1.parentAddr < sym2.parentAddr;
|
||||
}
|
||||
|
||||
// Fourth, sort by name
|
||||
return sym1_name < sym2_name;
|
||||
}
|
||||
|
||||
// Write a bank's contents to the sym file
|
||||
@@ -373,11 +354,20 @@ static void writeSymBank(SortedSections const &bankSections, SectionType type, u
|
||||
forEachSortedSection(sect, {
|
||||
for (Symbol const *sym : sect->symbols) {
|
||||
// Don't output symbols that begin with an illegal character
|
||||
if (isLegalSymbol(*sym)) {
|
||||
symList.push_back({
|
||||
.sym = sym,
|
||||
.addr = static_cast<uint16_t>(sym->label().offset + sect->org),
|
||||
});
|
||||
if (!sym->name.empty() && startsIdentifier(sym->name[0])) {
|
||||
uint16_t addr = static_cast<uint16_t>(sym->label().offset + sect->org);
|
||||
uint16_t parentAddr = addr;
|
||||
if (auto pos = sym->name.find('.'); pos != std::string::npos) {
|
||||
std::string parentName = sym->name.substr(0, pos);
|
||||
if (Symbol const *parentSym = sym_GetSymbol(parentName);
|
||||
parentSym && parentSym->data.holds<Label>()) {
|
||||
auto const &parentLabel = parentSym->label();
|
||||
assume(parentLabel.section != nullptr);
|
||||
parentAddr =
|
||||
static_cast<uint16_t>(parentLabel.offset + parentLabel.section->org);
|
||||
}
|
||||
}
|
||||
symList.push_back({.sym = sym, .addr = addr, .parentAddr = parentAddr});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -477,7 +467,7 @@ static void writeMapBank(SortedSections const §List, SectionType type, uint3
|
||||
for (uint16_t org = sect->org; sect; sect = sect->nextu.get()) {
|
||||
for (Symbol *sym : sect->symbols) {
|
||||
// Don't output symbols that begin with an illegal character
|
||||
if (isLegalSymbol(*sym)) {
|
||||
if (!sym->name.empty() && startsIdentifier(sym->name[0])) {
|
||||
// Space matches "\tSECTION: $xxxx ..."
|
||||
fprintf(mapFile, "\t $%04" PRIx32 " = ", sym->label().offset + org);
|
||||
printSymName(sym->name, mapFile);
|
||||
|
||||
@@ -251,10 +251,16 @@ yy::parser::symbol_type yylex() {
|
||||
if (c == EOF) {
|
||||
// Basically yywrap().
|
||||
if (lexerStack.size() != 1) {
|
||||
lexerStack.pop_back();
|
||||
return yylex();
|
||||
if (!atEof) {
|
||||
// Inject a newline at EOF to simplify parsing.
|
||||
atEof = true;
|
||||
return yy::parser::make_newline();
|
||||
} else {
|
||||
lexerStack.pop_back();
|
||||
return yylex();
|
||||
}
|
||||
} else if (!atEof) {
|
||||
// Inject a newline at EOF, to avoid errors for files that don't end with one.
|
||||
// Inject a newline at EOF to simplify parsing.
|
||||
atEof = true;
|
||||
return yy::parser::make_newline();
|
||||
} else {
|
||||
|
||||
@@ -6,6 +6,15 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
bool startsIdentifier(int c) {
|
||||
// This returns false for anonymous labels, which internally start with a '!'
|
||||
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '.' || c == '_';
|
||||
}
|
||||
|
||||
bool continuesIdentifier(int c) {
|
||||
return startsIdentifier(c) || (c >= '0' && c <= '9') || c == '#' || c == '$' || c == '@';
|
||||
}
|
||||
|
||||
char const *printChar(int c) {
|
||||
// "'A'" + '\0': 4 bytes
|
||||
// "'\\n'" + '\0': 5 bytes
|
||||
|
||||
23
test/asm/charval.asm
Normal file
23
test/asm/charval.asm
Normal file
@@ -0,0 +1,23 @@
|
||||
charmap "a", 1
|
||||
charmap "b", 2, 3
|
||||
charmap "cdef", 4
|
||||
charmap "ghi", 5, 6, 7, 8, 9
|
||||
charmap "jkl", 123, 456, 789
|
||||
charmap "mno", 123456789
|
||||
|
||||
assert charval("a", 0) == 1
|
||||
assert charval("a", -1) == 1
|
||||
assert charval("b", 0) == 2
|
||||
assert charval("b", 1) == 3
|
||||
assert charval("b", -1) == 3
|
||||
assert charval("b", -2) == 2
|
||||
assert charval("cdef", 0) == 4
|
||||
assert charval("ghi", 2) == charval("ghi", -3)
|
||||
assert charval("jkl", -1) == 789
|
||||
assert charval("mno", 0) == 123456789
|
||||
|
||||
assert charval("abc", 0) == 0
|
||||
assert charval("cd", 1) == 0
|
||||
assert charval("xyz", 2) == 0
|
||||
assert charval("ghi", -10) == 5
|
||||
assert charval("ghi", 10) == 0
|
||||
11
test/asm/charval.err
Normal file
11
test/asm/charval.err
Normal file
@@ -0,0 +1,11 @@
|
||||
error: charval.asm(19):
|
||||
CHARVAL: No character mapping for "abc"
|
||||
error: charval.asm(20):
|
||||
CHARVAL: No character mapping for "cd"
|
||||
error: charval.asm(21):
|
||||
CHARVAL: No character mapping for "xyz"
|
||||
warning: charval.asm(22): [-Wbuiltin-args]
|
||||
CHARVAL: Index starts at 0
|
||||
warning: charval.asm(23): [-Wbuiltin-args]
|
||||
CHARVAL: Index 10 is past the end of the character mapping
|
||||
error: Assembly aborted (3 errors)!
|
||||
@@ -1,3 +1,4 @@
|
||||
error: command-line-symbols.asm(3):
|
||||
'FOO' already defined at <command-line>
|
||||
(should it be {interpolated} to define its contents "hello"?)
|
||||
error: Assembly aborted (1 error)!
|
||||
|
||||
@@ -28,3 +28,15 @@ redef string equs "there"
|
||||
|
||||
redef constant equ 6*9
|
||||
println constant
|
||||
|
||||
macro mac
|
||||
endm
|
||||
|
||||
redef mac equ 42
|
||||
|
||||
def name equs "constant2"
|
||||
def name equ 1337
|
||||
|
||||
redef name equs "mac2"
|
||||
macro name
|
||||
endm
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
error: def.asm(23):
|
||||
'constant' already defined at def.asm(10)
|
||||
error: Assembly aborted (1 error)!
|
||||
error: def.asm(35):
|
||||
'mac' already defined as non-EQU at def.asm(32)
|
||||
error: def.asm(38):
|
||||
'name' already defined at def.asm(37)
|
||||
(should it be {interpolated} to define its contents "constant2"?)
|
||||
error: def.asm(42):
|
||||
'name' already defined at def.asm(40)
|
||||
(should it be {interpolated} to define its contents "mac2"?)
|
||||
error: Assembly aborted (4 errors)!
|
||||
|
||||
@@ -3,6 +3,5 @@ warning: equs-newline.asm(3): [-Wuser]
|
||||
while expanding symbol "ACT"
|
||||
warning: equs-newline.asm(3): [-Wuser]
|
||||
Second
|
||||
while expanding symbol "ACT"
|
||||
warning: equs-newline.asm(4): [-Wuser]
|
||||
Third
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
println 42, 1 2 3 4
|
||||
|
||||
for n, 5
|
||||
for n, 3
|
||||
println "start {d:n}"
|
||||
println syntax error
|
||||
println "finish {d:n}"
|
||||
|
||||
@@ -2,4 +2,8 @@ error: error-recovery.asm(3):
|
||||
syntax error, unexpected number
|
||||
error: error-recovery.asm(5) -> error-recovery.asm::REPT~1(7):
|
||||
syntax error, unexpected symbol
|
||||
error: Assembly aborted (2 errors)!
|
||||
error: error-recovery.asm(5) -> error-recovery.asm::REPT~2(7):
|
||||
syntax error, unexpected symbol
|
||||
error: error-recovery.asm(5) -> error-recovery.asm::REPT~3(7):
|
||||
syntax error, unexpected symbol
|
||||
error: Assembly aborted (4 errors)!
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
begin
|
||||
$2Astart 0
|
||||
finish 0
|
||||
end 0
|
||||
start 1
|
||||
finish 1
|
||||
start 2
|
||||
finish 2
|
||||
end 3
|
||||
|
||||
@@ -6,6 +6,7 @@ warning: for.asm(20): [-Wbackwards-for]
|
||||
FOR goes backwards from 1 to 2 by -1
|
||||
error: for.asm(46):
|
||||
's' already defined as constant at for.asm(39)
|
||||
(should it be {interpolated} to define its contents "x"?)
|
||||
error: for.asm(48) -> for.asm::REPT~4(54):
|
||||
'v' already defined as constant at for.asm(48) -> for.asm::REPT~4(52)
|
||||
FATAL: for.asm(48) -> for.asm::REPT~4(54):
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
opt b123
|
||||
opt b_1
|
||||
opt b10
|
||||
opt b00
|
||||
opt g12345
|
||||
opt g012_
|
||||
opt g$123
|
||||
opt g0234
|
||||
opt gxxyy
|
||||
opt gxyzy
|
||||
opt pxy
|
||||
opt p1234
|
||||
opt Qxy
|
||||
|
||||
@@ -1,23 +1,39 @@
|
||||
error: invalid-opt.asm(1):
|
||||
Must specify exactly 2 characters for option 'b'
|
||||
error: invalid-opt.asm(2):
|
||||
Must specify exactly 4 characters for option 'g'
|
||||
Invalid digit for binary constant '_'
|
||||
error: invalid-opt.asm(3):
|
||||
Invalid argument for option 'p'
|
||||
Changed digit for binary constant '1'
|
||||
error: invalid-opt.asm(4):
|
||||
Invalid argument for option 'p'
|
||||
Repeated digit for binary constant '0'
|
||||
error: invalid-opt.asm(5):
|
||||
Invalid argument for option 'Q'
|
||||
Must specify exactly 4 characters for option 'g'
|
||||
error: invalid-opt.asm(6):
|
||||
Invalid argument for option 'Q'
|
||||
Invalid digit for graphics constant '_'
|
||||
error: invalid-opt.asm(7):
|
||||
Argument for option 'Q' must be between 1 and 31
|
||||
Invalid digit for graphics constant '$'
|
||||
error: invalid-opt.asm(8):
|
||||
Argument to 'r' is out of range ("99999999999999999999999999")
|
||||
Changed digit for graphics constant '2'
|
||||
error: invalid-opt.asm(9):
|
||||
Must specify an argument for option 'W'
|
||||
Repeated digit for graphics constant 'x'
|
||||
error: invalid-opt.asm(10):
|
||||
syntax error, unexpected end of line, expecting string
|
||||
Repeated digit for graphics constant 'y'
|
||||
error: invalid-opt.asm(11):
|
||||
Invalid argument for option 'p'
|
||||
error: invalid-opt.asm(12):
|
||||
Invalid argument for option 'p'
|
||||
error: invalid-opt.asm(13):
|
||||
Invalid argument for option 'Q'
|
||||
error: invalid-opt.asm(14):
|
||||
Invalid argument for option 'Q'
|
||||
error: invalid-opt.asm(15):
|
||||
Argument for option 'Q' must be between 1 and 31
|
||||
error: invalid-opt.asm(16):
|
||||
Argument to 'r' is out of range ("99999999999999999999999999")
|
||||
error: invalid-opt.asm(17):
|
||||
Must specify an argument for option 'W'
|
||||
error: invalid-opt.asm(18):
|
||||
syntax error, unexpected end of line, expecting string
|
||||
error: invalid-opt.asm(19):
|
||||
No entries in the option stack
|
||||
error: Assembly aborted (11 errors)!
|
||||
error: Assembly aborted (19 errors)!
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
SECTION "test", ROM0
|
||||
; '\0' is not special here; it's lexed as a line continuation...
|
||||
DEF foo\0bar EQU 42
|
||||
db foo\0bar
|
||||
DEF foo\0qux EQU 42
|
||||
db foo\0qux
|
||||
; ...just like any other non-whitespace character
|
||||
DEF spam\Xeggs EQU 69
|
||||
db spam\Xeggs
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
PRINTLN `pqpq_rsrs
|
||||
|
||||
OPT g.x0X
|
||||
PRINTLN `.x.x_0X0X
|
||||
OPT g.xOX
|
||||
PRINTLN `.x.x_OXOX
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
error: redef-equ.asm(25):
|
||||
'N' already defined as non-EQU at redef-equ.asm(24)
|
||||
(should it be {interpolated} to define its contents "X"?)
|
||||
error: Assembly aborted (1 error)!
|
||||
|
||||
26
test/asm/string-concat.asm
Normal file
26
test/asm/string-concat.asm
Normal file
@@ -0,0 +1,26 @@
|
||||
SECTION "test", ROM0
|
||||
|
||||
MACRO test
|
||||
assert !strcmp(\1, \2)
|
||||
ENDM
|
||||
|
||||
test "a"++"b", "ab"
|
||||
test "a"++""++"b", "ab"
|
||||
test "a"++"b", strcat("a", "b")
|
||||
test "a"++"b"++"c", strcat("a","b","c")
|
||||
test "" ++ "", ""
|
||||
test strupr("a") ++ strlwr("B"), "Ab"
|
||||
|
||||
def str equs "hi"
|
||||
test #str ++ strupr(#str), "hiHI"
|
||||
test "a" ++ """b""" ++ strupr("c") ++ strslice(#str, 0, 0), "abC"
|
||||
|
||||
charmap "a", 1
|
||||
charmap "b", 2
|
||||
charmap "ab", 12
|
||||
assert "a" + "b" == 3
|
||||
assert "a" ++ "b" == 12
|
||||
|
||||
; errors
|
||||
assert 2 ++ 2 == 4
|
||||
ld a, [hl++]
|
||||
5
test/asm/string-concat.err
Normal file
5
test/asm/string-concat.err
Normal file
@@ -0,0 +1,5 @@
|
||||
error: string-concat.asm(25):
|
||||
syntax error, unexpected ++
|
||||
error: string-concat.asm(26):
|
||||
syntax error, unexpected ++, expecting ] or + or -
|
||||
error: Assembly aborted (2 errors)!
|
||||
@@ -5,10 +5,10 @@ STRSLICE("ABC",-3,-2): A
|
||||
STRSLICE("ABC",-2,-1): B
|
||||
STRSLICE("ABC",-1,-0):
|
||||
STRSLICE("ABC",-1,3): C
|
||||
STRSLICE("ABC",1): B
|
||||
STRSLICE("ABC",-2): B
|
||||
STRSLICE("ABC",1): BC
|
||||
STRSLICE("ABC",-2): BC
|
||||
STRSLICE("ABC",4):
|
||||
STRSLICE("ABC",-4): AB
|
||||
STRSLICE("ABC",-4): ABC
|
||||
STRSLICE("ABC",0,2): AB
|
||||
STRSLICE("ABC",1,3): BC
|
||||
STRSLICE("ABC",1,31): BC
|
||||
|
||||
@@ -22,7 +22,7 @@ _1234::
|
||||
dl 6_._283_185 ; fixed point
|
||||
dw `0123_3210, `00_33_22_11_ ; gfx
|
||||
|
||||
; underscores as digits
|
||||
opt g_ABC, b_X
|
||||
db %_X_X__XX
|
||||
dw `_A_B_C__
|
||||
; underscores with custom digits
|
||||
opt g.ABC, b.X
|
||||
db %.X.X_..XX_
|
||||
dw `.A.B_.C.._
|
||||
|
||||
@@ -3,41 +3,30 @@ error: unique-id.asm(11):
|
||||
while expanding symbol "warn_unique"
|
||||
warning: unique-id.asm(11): [-Wuser]
|
||||
!
|
||||
while expanding symbol "warn_unique"
|
||||
warning: unique-id.asm(12) -> unique-id.asm::m(4): [-Wuser]
|
||||
_u1!
|
||||
while expanding symbol "warn_unique"
|
||||
warning: unique-id.asm(12) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~1(6): [-Wuser]
|
||||
_u2!
|
||||
while expanding symbol "warn_unique"
|
||||
warning: unique-id.asm(12) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~2(6): [-Wuser]
|
||||
_u3!
|
||||
while expanding symbol "warn_unique"
|
||||
warning: unique-id.asm(12) -> unique-id.asm::m(8): [-Wuser]
|
||||
_u1!
|
||||
while expanding symbol "warn_unique"
|
||||
error: unique-id.asm(13):
|
||||
'\@' cannot be used outside of a macro or REPT/FOR block
|
||||
while expanding symbol "warn_unique"
|
||||
warning: unique-id.asm(13): [-Wuser]
|
||||
!
|
||||
while expanding symbol "warn_unique"
|
||||
warning: unique-id.asm(14) -> unique-id.asm::m(4): [-Wuser]
|
||||
_u4!
|
||||
while expanding symbol "warn_unique"
|
||||
warning: unique-id.asm(14) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~1(6): [-Wuser]
|
||||
_u5!
|
||||
while expanding symbol "warn_unique"
|
||||
warning: unique-id.asm(14) -> unique-id.asm::m(5) -> unique-id.asm::m::REPT~2(6): [-Wuser]
|
||||
_u6!
|
||||
while expanding symbol "warn_unique"
|
||||
warning: unique-id.asm(14) -> unique-id.asm::m(8): [-Wuser]
|
||||
_u4!
|
||||
while expanding symbol "warn_unique"
|
||||
error: unique-id.asm(15):
|
||||
'\@' cannot be used outside of a macro or REPT/FOR block
|
||||
while expanding symbol "warn_unique"
|
||||
warning: unique-id.asm(15): [-Wuser]
|
||||
!
|
||||
while expanding symbol "warn_unique"
|
||||
error: Assembly aborted (3 errors)!
|
||||
|
||||
@@ -107,10 +107,10 @@ if ! "$external"; then
|
||||
fi
|
||||
|
||||
if "$nonfree"; then
|
||||
action pret pokecrystal 2025-04-28 f480cb9e247a63e95aa353daef3ecd44fd526659
|
||||
action pret pokered 2025-04-19 d47f74ee1fed608f902dbc0fd51634dd7bafc6e4
|
||||
action zladx LADX-Disassembly 2025-04-12 8db9dbbd1dcbbdb4df7c455f56d0ad1fa52b124b
|
||||
action pret pokecrystal 2025-06-29 de396249e10eb294f41afd0e82245a4a68a3c8e8
|
||||
action pret pokered 2025-06-29 1e997474be15950eb3176864b346b96504760e67
|
||||
action zladx LADX-Disassembly 2025-05-30 f685f6aaff2f2e0e36d1856d4ed8fd58f833a1f2
|
||||
fi
|
||||
action AntonioND ucity 2025-03-20 04cdda3dda35e62501cf9543b73343935039ee4d
|
||||
action pinobatch libbet 2025-03-28 c564dc01baa91bd6123ec64d877630dd392e74b5
|
||||
action LIJI32 SameBoy 2025-04-07 1cf84a5436c49413ae756c9e9c18ce71e96d7990
|
||||
action AntonioND ucity 2025-05-30 83e5c697cbd9e10a0bc72b02bcb6146c35e2c328
|
||||
action pinobatch libbet 2025-05-20 bb6cfc026644aa1034eee6d9c49bb4705601c9f6
|
||||
action LIJI32 SameBoy 2025-06-28 33d237706e18d92fb79e3fd7313d5181d8a806cd
|
||||
|
||||
BIN
test/gfx/dmg_1bit_round_trip.1bpp
Normal file
BIN
test/gfx/dmg_1bit_round_trip.1bpp
Normal file
Binary file not shown.
2
test/gfx/dmg_1bit_round_trip.flags
Normal file
2
test/gfx/dmg_1bit_round_trip.flags
Normal file
@@ -0,0 +1,2 @@
|
||||
-d 1
|
||||
-c dmg=1b
|
||||
2
test/gfx/dmg_2bit.flags
Normal file
2
test/gfx/dmg_2bit.flags
Normal file
@@ -0,0 +1,2 @@
|
||||
-c nonexistent.gpl
|
||||
-c DMG=E4
|
||||
BIN
test/gfx/dmg_2bit.out.2bpp
Normal file
BIN
test/gfx/dmg_2bit.out.2bpp
Normal file
Binary file not shown.
BIN
test/gfx/dmg_2bit.out.pal
Normal file
BIN
test/gfx/dmg_2bit.out.pal
Normal file
Binary file not shown.
BIN
test/gfx/dmg_2bit.png
Normal file
BIN
test/gfx/dmg_2bit.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 79 B |
2
test/gfx/dmg_plte.flags
Normal file
2
test/gfx/dmg_plte.flags
Normal file
@@ -0,0 +1,2 @@
|
||||
-c embedded
|
||||
-c DMG=E4
|
||||
BIN
test/gfx/dmg_plte.out.2bpp
Normal file
BIN
test/gfx/dmg_plte.out.2bpp
Normal file
Binary file not shown.
BIN
test/gfx/dmg_plte.out.pal
Normal file
BIN
test/gfx/dmg_plte.out.pal
Normal file
Binary file not shown.
BIN
test/gfx/dmg_plte.png
Normal file
BIN
test/gfx/dmg_plte.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 118 B |
BIN
test/gfx/dmg_round_trip.2bpp
Normal file
BIN
test/gfx/dmg_round_trip.2bpp
Normal file
Binary file not shown.
1
test/gfx/dmg_round_trip.flags
Normal file
1
test/gfx/dmg_round_trip.flags
Normal file
@@ -0,0 +1 @@
|
||||
-c dmg=72
|
||||
3
test/link/constant-parent/a.asm
Normal file
3
test/link/constant-parent/a.asm
Normal file
@@ -0,0 +1,3 @@
|
||||
section "a", rom0
|
||||
export def parent = 42
|
||||
db 1, 2, 3
|
||||
4
test/link/constant-parent/b.asm
Normal file
4
test/link/constant-parent/b.asm
Normal file
@@ -0,0 +1,4 @@
|
||||
section "b", rom0
|
||||
db 4, 5, 6
|
||||
parent.child::
|
||||
db 7, 8, 9
|
||||
0
test/link/constant-parent/out.err
Normal file
0
test/link/constant-parent/out.err
Normal file
1
test/link/constant-parent/ref.out.bin
Normal file
1
test/link/constant-parent/ref.out.bin
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
3
test/link/constant-parent/ref.out.sym
Normal file
3
test/link/constant-parent/ref.out.sym
Normal file
@@ -0,0 +1,3 @@
|
||||
; File generated by rgblink
|
||||
00:0003 parent.child
|
||||
2a parent
|
||||
@@ -2,8 +2,8 @@
|
||||
00:0000 Part1
|
||||
00:0004 Part1End
|
||||
00:0004 Part2
|
||||
00:0010 Part3
|
||||
00:0010 Part2End
|
||||
00:0010 Part3
|
||||
00:0014 Part3End
|
||||
00:c000 wPart1
|
||||
00:c004 wPart3
|
||||
|
||||
2
test/link/script-include/a.asm
Normal file
2
test/link/script-include/a.asm
Normal file
@@ -0,0 +1,2 @@
|
||||
section "a", rom0
|
||||
db $11
|
||||
3
test/link/script-include/a.inc
Normal file
3
test/link/script-include/a.inc
Normal file
@@ -0,0 +1,3 @@
|
||||
rom0
|
||||
org 0
|
||||
"a" ; no newline
|
||||
2
test/link/script-include/b.asm
Normal file
2
test/link/script-include/b.asm
Normal file
@@ -0,0 +1,2 @@
|
||||
section "b", rom0
|
||||
db $22
|
||||
3
test/link/script-include/b.inc
Normal file
3
test/link/script-include/b.inc
Normal file
@@ -0,0 +1,3 @@
|
||||
rom0
|
||||
org 1
|
||||
"b" ; yes newline
|
||||
0
test/link/script-include/out.err
Normal file
0
test/link/script-include/out.err
Normal file
1
test/link/script-include/ref.out.bin
Normal file
1
test/link/script-include/ref.out.bin
Normal file
@@ -0,0 +1 @@
|
||||
"
|
||||
2
test/link/script-include/script.link
Normal file
2
test/link/script-include/script.link
Normal file
@@ -0,0 +1,2 @@
|
||||
include "script-include/a.inc"
|
||||
include "script-include/b.inc"
|
||||
@@ -1,16 +1,16 @@
|
||||
; File generated by rgblink
|
||||
00:0000 Beta
|
||||
00:0003 End
|
||||
00:0003 Alpha
|
||||
00:0003 End
|
||||
00:0006 End
|
||||
00:c000 wStart
|
||||
00:c000 wStart
|
||||
00:c000 wStart.word1
|
||||
00:c000 wStart.long1
|
||||
00:c000 wStart.word1
|
||||
00:c002 wStart.word2
|
||||
00:c004 wEnd
|
||||
00:c004 wEnd
|
||||
00:c004 wBeta
|
||||
00:c007 wBeta.End
|
||||
00:c004 wEnd
|
||||
00:c004 wEnd
|
||||
00:c007 wAlpha
|
||||
00:c007 wBeta.End
|
||||
00:c00a wAlpha.End
|
||||
|
||||
@@ -149,6 +149,17 @@ rgblinkQuiet -o "$gbtemp" "$otemp" "$gbtemp2" 2>"$outtemp"
|
||||
tryDiff "$test"/out.err "$outtemp"
|
||||
evaluateTest
|
||||
|
||||
test="constant-parent"
|
||||
startTest
|
||||
"$RGBASM" -o "$otemp" "$test"/a.asm
|
||||
"$RGBASM" -o "$gbtemp2" "$test"/b.asm
|
||||
continueTest
|
||||
rgblinkQuiet -o "$gbtemp" -n "$outtemp2" "$otemp" "$gbtemp2" 2>"$outtemp"
|
||||
tryDiff "$test"/out.err "$outtemp"
|
||||
tryDiff "$test"/ref.out.sym "$outtemp2"
|
||||
tryCmpRom "$test"/ref.out.bin
|
||||
evaluateTest
|
||||
|
||||
for test in fragment-align/*; do
|
||||
startTest
|
||||
"$RGBASM" -o "$otemp" "$test"/a.asm
|
||||
@@ -251,6 +262,16 @@ tryDiff "$test"/out.err "$outtemp"
|
||||
tryCmpRomSize "$gbtemp" 65536
|
||||
evaluateTest
|
||||
|
||||
test="script-include"
|
||||
startTest
|
||||
"$RGBASM" -o "$otemp" "$test"/a.asm
|
||||
"$RGBASM" -o "$gbtemp2" "$test"/b.asm
|
||||
continueTest
|
||||
rgblinkQuiet -o "$gbtemp" -l "$test"/script.link "$otemp" "$gbtemp2" 2>"$outtemp"
|
||||
tryDiff "$test"/out.err "$outtemp"
|
||||
tryCmpRom "$test"/ref.out.bin
|
||||
evaluateTest
|
||||
|
||||
test="sdcc/good"
|
||||
startTest
|
||||
"$RGBASM" -o "$otemp" "$test"/a.asm
|
||||
|
||||
Reference in New Issue
Block a user