mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-21 02:32:06 +00:00
Compare commits
38 Commits
v0.9.0-rc2
...
v0.9.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d63955eccd | ||
|
|
2c4fc4cbe8 | ||
|
|
7d3c31b6d8 | ||
|
|
151f83db6d | ||
|
|
22838ce2d8 | ||
|
|
b058bb6e15 | ||
|
|
36b04b5dea | ||
|
|
a7296ecb31 | ||
|
|
92917ceb2f | ||
|
|
c1c5b10082 | ||
|
|
f44de0c7ae | ||
|
|
b18cfe6bdb | ||
|
|
a8ec9228d4 | ||
|
|
c1b85554a8 | ||
|
|
b877c81c32 | ||
|
|
e66da4c8c7 | ||
|
|
573e044b30 | ||
|
|
ceb43c7aa4 | ||
|
|
eae1ecb77e | ||
|
|
c4de6c402b | ||
|
|
0b147c9386 | ||
|
|
6982c8a116 | ||
|
|
d5f39c8dce | ||
|
|
a5d18d62df | ||
|
|
a27f704c25 | ||
|
|
9216485bca | ||
|
|
c33acb905b | ||
|
|
81c3521610 | ||
|
|
e0ee9dc3ad | ||
|
|
cb546f0cd8 | ||
|
|
a60186db2f | ||
|
|
d9f87a5721 | ||
|
|
a7fdb2c3d3 | ||
|
|
5efd303b7f | ||
|
|
0d3980d039 | ||
|
|
ab6244d81c | ||
|
|
7fcf4ba60f | ||
|
|
f048cbbb11 |
2
.github/scripts/install.sh
vendored
2
.github/scripts/install.sh
vendored
@@ -3,5 +3,5 @@
|
||||
install -d /usr/local/bin/ /usr/local/share/man/man1/ /usr/local/share/man/man5/ /usr/local/share/man/man7/
|
||||
install -s -m 755 rgbasm rgblink rgbfix rgbgfx /usr/local/bin/
|
||||
install -m 644 rgbasm.1 rgblink.1 rgbfix.1 rgbgfx.1 /usr/local/share/man/man1/
|
||||
install -m 644 rgbds.5 rgbasm.5 rgblink.5 /usr/local/share/man/man5/
|
||||
install -m 644 rgbds.5 rgbasm.5 rgblink.5 rgbasm-old.5 /usr/local/share/man/man5/
|
||||
install -m 644 rgbds.7 gbz80.7 /usr/local/share/man/man7/
|
||||
|
||||
17
.github/workflows/build-container.yml
vendored
17
.github/workflows/build-container.yml
vendored
@@ -1,18 +1,16 @@
|
||||
name: Build container image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
tags:
|
||||
- '*' # This triggers the action on all tag pushes
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
publish-docker-image:
|
||||
if: github.repository_owner == 'gbdev'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# So that the workflow can write to the ghcr an upload there
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
@@ -26,7 +24,6 @@ jobs:
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push the master container image
|
||||
# When a commit is pushed to master
|
||||
if: github.ref == 'refs/heads/master'
|
||||
run: |
|
||||
COMMIT_HASH=$(git rev-parse --short HEAD)
|
||||
@@ -35,10 +32,20 @@ jobs:
|
||||
docker push ghcr.io/gbdev/rgbds:master
|
||||
|
||||
- name: Build and push the version-tagged container image
|
||||
# When a tag is pushed
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
run: |
|
||||
TAG_NAME=${GITHUB_REF#refs/tags/}
|
||||
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
|
||||
|
||||
- name: Delete untagged container images
|
||||
if: github.repository_owner == 'gbdev'
|
||||
uses: Chizkiyahu/delete-untagged-ghcr-action@v5
|
||||
with:
|
||||
# Requires a personal access token with delete:packages permissions
|
||||
token: ${{ secrets.PAT_TOKEN }}
|
||||
package_name: 'rgbds'
|
||||
untagged_only: true
|
||||
except_untagged_multiplatform: true
|
||||
owner_type: 'org'
|
||||
|
||||
1
.github/workflows/update-master-docs.yml
vendored
1
.github/workflows/update-master-docs.yml
vendored
@@ -9,6 +9,7 @@ on:
|
||||
- man/rgbds.7
|
||||
- man/rgbasm.1
|
||||
- man/rgbasm.5
|
||||
- man/rgbasm-old.5
|
||||
- man/rgblink.1
|
||||
- man/rgblink.5
|
||||
- man/rgbfix.1
|
||||
|
||||
@@ -61,7 +61,7 @@ else()
|
||||
if(MORE_WARNINGS)
|
||||
add_compile_options(-Werror -Wextra
|
||||
-Walloc-zero -Wcast-align -Wcast-qual -Wduplicated-branches -Wduplicated-cond
|
||||
-Wfloat-equal -Wlogical-op -Wnull-dereference -Wshift-overflow=2
|
||||
-Wfloat-equal -Wlogical-op -Wnull-dereference -Wold-style-cast -Wshift-overflow=2
|
||||
-Wstringop-overflow=4 -Wundef -Wuninitialized -Wunused
|
||||
-Wshadow # TODO: -Wshadow=compatible-local?
|
||||
-Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1
|
||||
@@ -126,6 +126,7 @@ set(man1 "man/rgbasm.1"
|
||||
"man/rgbgfx.1"
|
||||
"man/rgblink.1")
|
||||
set(man5 "man/rgbasm.5"
|
||||
"man/rgbasm-old.5"
|
||||
"man/rgblink.5"
|
||||
"man/rgbds.5")
|
||||
set(man7 "man/gbz80.7"
|
||||
|
||||
@@ -174,3 +174,17 @@ Each one is a binary RNG file which is passed to the `rgbgfx_test` program.
|
||||
```sh
|
||||
test_downstream <owner> <repo> <makefile target> <build file> <sha1 hash of build file>
|
||||
```
|
||||
|
||||
## Container images
|
||||
|
||||
The CI will [take care](https://github.com/gbdev/rgbds/blob/master/.github/workflows/build-container.yml) of updating the [rgbds container](https://github.com/gbdev/rgbds/pkgs/container/rgbds) image tagged `master`.
|
||||
|
||||
When a git tag is pushed, the image is also tagged with that tag.
|
||||
|
||||
The image can be built locally and pushed to the GitHub container registry by manually running:
|
||||
|
||||
```bash
|
||||
# e.g. to build and tag as 'master'
|
||||
docker build . --tag ghcr.io/gbdev/rgbds:master
|
||||
docker push ghcr.io/gbdev/rgbds:master
|
||||
```
|
||||
@@ -1,6 +1,6 @@
|
||||
FROM debian:11-slim
|
||||
LABEL org.opencontainers.image.source=https://github.com/gbdev/rgbds
|
||||
ARG version=0.9.0-rc2
|
||||
ARG version=0.9.0
|
||||
WORKDIR /rgbds
|
||||
|
||||
COPY . .
|
||||
|
||||
4
Makefile
4
Makefile
@@ -185,7 +185,7 @@ install: all
|
||||
$Qinstall ${STRIP} -m ${BINMODE} rgbfix ${DESTDIR}${bindir}/rgbfix${SUFFIX}
|
||||
$Qinstall ${STRIP} -m ${BINMODE} rgbgfx ${DESTDIR}${bindir}/rgbgfx${SUFFIX}
|
||||
$Qinstall -m ${MANMODE} man/rgbasm.1 man/rgblink.1 man/rgbfix.1 man/rgbgfx.1 ${DESTDIR}${mandir}/man1/
|
||||
$Qinstall -m ${MANMODE} man/rgbds.5 man/rgbasm.5 man/rgblink.5 ${DESTDIR}${mandir}/man5/
|
||||
$Qinstall -m ${MANMODE} man/rgbds.5 man/rgbasm.5 man/rgbasm-old.5 man/rgblink.5 ${DESTDIR}${mandir}/man5/
|
||||
$Qinstall -m ${MANMODE} man/rgbds.7 man/gbz80.7 ${DESTDIR}${mandir}/man7/
|
||||
|
||||
# Target used to check for suspiciously missing changed files.
|
||||
@@ -201,7 +201,7 @@ checkdiff:
|
||||
develop:
|
||||
$Q${MAKE} WARNFLAGS="${WARNFLAGS} -Werror -Wextra \
|
||||
-Walloc-zero -Wcast-align -Wcast-qual -Wduplicated-branches -Wduplicated-cond \
|
||||
-Wfloat-equal -Wlogical-op -Wnull-dereference -Wshift-overflow=2 \
|
||||
-Wfloat-equal -Wlogical-op -Wnull-dereference -Wold-style-cast -Wshift-overflow=2 \
|
||||
-Wstringop-overflow=4 -Wundef -Wuninitialized -Wunused -Wshadow \
|
||||
-Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1 \
|
||||
-Wno-format-nonliteral -Wno-strict-overflow -Wno-unused-but-set-variable \
|
||||
|
||||
19
RELEASE.md
19
RELEASE.md
@@ -6,10 +6,12 @@ GitHub.
|
||||
1. Update the following files, then commit and push.
|
||||
You can use <code>git commit -m "Release <i><version></i>"</code> and `git push origin master`.
|
||||
|
||||
- [include/version.hpp](include/version.hpp): set appropriate values for `PACKAGE_VERSION_MAJOR`, `PACKAGE_VERSION_MINOR`, `PACKAGE_VERSION_PATCH`, and `PACKAGE_VERSION_RC`
|
||||
- [include/version.hpp](include/version.hpp): set appropriate values for `PACKAGE_VERSION_MAJOR`,
|
||||
`PACKAGE_VERSION_MINOR`, `PACKAGE_VERSION_PATCH`, and `PACKAGE_VERSION_RC`.
|
||||
**Only** define `PACKAGE_VERSION_RC` if you are publishing a release candidate!
|
||||
- [Dockerfile](Dockerfile): update `ARG version`.
|
||||
- [test/fetch-test-deps.sh](test/fetch-test-deps.sh): update test dependency commits (preferably, use the latest available).
|
||||
- [test/fetch-test-deps.sh](test/fetch-test-deps.sh): update test dependency commits
|
||||
(preferably, use the latest available).
|
||||
- [man/\*](man/): update dates and authors.
|
||||
|
||||
2. Create a Git tag formatted as <code>v<i><MAJOR></i>.<i><MINOR></i>.<i><PATCH></i></code>,
|
||||
@@ -55,8 +57,8 @@ GitHub.
|
||||
|
||||
If you do not have `groff` installed, you can change
|
||||
`groff -Tpdf -mdoc -wall` to `mandoc -Tpdf -I os=Linux` in
|
||||
[.github/actions/get-pages.sh](.github/actions/get-pages.sh) and it
|
||||
will suffice.
|
||||
[maintainer/man_to_html.sh](https://github.com/gbdev/rgbds-www/blob/master/maintainer/man_to_html.sh)
|
||||
and it will suffice.
|
||||
|
||||
4. Commit and push the documentation. You can use <code>git commit -m
|
||||
"Create RGBDS <i><tag></i> documentation"</code> and `git push origin master`
|
||||
@@ -67,3 +69,12 @@ GitHub.
|
||||
6. Click the "Publish release" button to publish it!
|
||||
|
||||
7. Update the `release` branch. You can use `git push origin release`.
|
||||
|
||||
8. Update the following related projects.
|
||||
|
||||
- [rgbobj](https://github.com/gbdev/rgbobj) and [rgbds-obj](https://github.com/gbdev/rgbds-obj):
|
||||
make sure that object files created by the latest RGBASM can be parsed and displayed.
|
||||
If the object file revision has been updated, rgbobj will need a corresponding release.
|
||||
- [rgbds-www](https://github.com/gbdev/rgbds-www): update
|
||||
[src/pages/versions.mdx](https://github.com/gbdev/rgbds-www/blob/master/src/pages/versions.mdx)
|
||||
to list the new release.
|
||||
|
||||
@@ -192,6 +192,7 @@ _rgbasm_completions() {
|
||||
shift-amount
|
||||
truncation
|
||||
unmapped-char
|
||||
unmatched-directive
|
||||
unterminated-load
|
||||
user
|
||||
all
|
||||
|
||||
@@ -26,6 +26,7 @@ _rgbasm_warnings() {
|
||||
'shift-amount:Warn when a shift'\''s operand it negative or \> 32'
|
||||
'truncation:Warn when implicit truncation loses bits'
|
||||
'unmapped-char:Warn on unmapped character'
|
||||
'unmatched-directive:Warn on unmatched directive pair'
|
||||
'unterminated-load:Warn on LOAD without ENDL'
|
||||
'user:Warn when executing the WARN built-in'
|
||||
)
|
||||
|
||||
@@ -18,6 +18,7 @@ void charmap_New(std::string const &name, std::string const *baseName);
|
||||
void charmap_Set(std::string const &name);
|
||||
void charmap_Push();
|
||||
void charmap_Pop();
|
||||
void charmap_CheckStack();
|
||||
void charmap_Add(std::string const &mapping, std::vector<int32_t> &&value);
|
||||
bool charmap_HasChar(std::string const &input);
|
||||
std::vector<int32_t> charmap_Convert(std::string const &input);
|
||||
|
||||
@@ -30,8 +30,8 @@ struct FileStackNode {
|
||||
// Meaningless at the root level, but gets written to the object file anyway, so init it
|
||||
uint32_t lineNo = 0;
|
||||
|
||||
// Set only if referenced: ID within the object file, -1 if not output yet
|
||||
uint32_t ID = -1;
|
||||
// Set only if referenced: ID within the object file, `UINT32_MAX` if not output yet
|
||||
uint32_t ID = UINT32_MAX;
|
||||
|
||||
// REPT iteration counts since last named node, in reverse depth order
|
||||
std::vector<uint32_t> &iters() { return data.get<std::vector<uint32_t>>(); }
|
||||
|
||||
@@ -14,5 +14,6 @@ void opt_Parse(char const *option);
|
||||
|
||||
void opt_Push();
|
||||
void opt_Pop();
|
||||
void opt_CheckStack();
|
||||
|
||||
#endif // RGBDS_ASM_OPT_HPP
|
||||
|
||||
@@ -53,7 +53,7 @@ struct Expression {
|
||||
void makeUnaryOp(RPNCommand op, Expression &&src);
|
||||
void makeBinaryOp(RPNCommand op, Expression &&src1, Expression const &src2);
|
||||
|
||||
void makeCheckHRAM();
|
||||
bool makeCheckHRAM();
|
||||
void makeCheckRST();
|
||||
|
||||
void checkNBit(uint8_t n) const;
|
||||
|
||||
@@ -102,5 +102,6 @@ void sect_BinaryFileSlice(std::string const &name, int32_t startPos, int32_t len
|
||||
void sect_EndSection();
|
||||
void sect_PushSection();
|
||||
void sect_PopSection();
|
||||
void sect_CheckStack();
|
||||
|
||||
#endif // RGBDS_ASM_SECTION_HPP
|
||||
|
||||
@@ -45,7 +45,7 @@ struct Symbol {
|
||||
>
|
||||
data;
|
||||
|
||||
uint32_t ID; // ID of the symbol in the object file (-1 if none)
|
||||
uint32_t ID; // ID of the symbol in the object file (`UINT32_MAX` if none)
|
||||
uint32_t defIndex; // Ordering of the symbol in the state file
|
||||
|
||||
bool isDefined() const { return type != SYM_REF; }
|
||||
@@ -55,7 +55,7 @@ struct Symbol {
|
||||
bool isConstant() const {
|
||||
if (type == SYM_LABEL) {
|
||||
Section const *sect = getSection();
|
||||
return sect && sect->org != (uint32_t)-1;
|
||||
return sect && sect->org != UINT32_MAX;
|
||||
}
|
||||
return type == SYM_EQU || type == SYM_VAR;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ enum WarningID {
|
||||
WARNING_OBSOLETE, // Obsolete/deprecated things
|
||||
WARNING_SHIFT, // Undefined `SHIFT` behavior
|
||||
WARNING_SHIFT_AMOUNT, // Strange `SHIFT` amount
|
||||
WARNING_UNMATCHED_DIRECTIVE, // `PUSH[C|O|S]` without `POP[C|O|S]`
|
||||
WARNING_UNTERMINATED_LOAD, // `LOAD` without `ENDL`
|
||||
WARNING_USER, // User-defined `WARN`ings
|
||||
|
||||
|
||||
@@ -43,11 +43,11 @@ private:
|
||||
// Generic field accessors; for internal use only.
|
||||
template<typename T>
|
||||
auto &field() {
|
||||
return pick((T *)nullptr);
|
||||
return pick(static_cast<T *>(nullptr));
|
||||
}
|
||||
template<typename T>
|
||||
auto const &field() const {
|
||||
return pick((T *)nullptr);
|
||||
return pick(static_cast<T *>(nullptr));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -53,7 +53,7 @@ struct Options {
|
||||
static constexpr uint8_t VERB_LOG_ACT = 2; // Log actions before doing them
|
||||
static constexpr uint8_t VERB_INTERM = 3; // Print some intermediate results
|
||||
static constexpr uint8_t VERB_DEBUG = 4; // Internals are logged
|
||||
static constexpr uint8_t VERB_UNMAPPED = 5; // Unused so far
|
||||
static constexpr uint8_t VERB_TRACE = 5; // Step-by-step algorithm details
|
||||
static constexpr uint8_t VERB_VVVVVV = 6; // What, can't I have a little fun?
|
||||
[[gnu::format(printf, 3, 4)]] void verbosePrint(uint8_t level, char const *fmt, ...) const;
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ struct Rgba {
|
||||
_5to8(cgbColor),
|
||||
_5to8(cgbColor >> 5),
|
||||
_5to8(cgbColor >> 10),
|
||||
(uint8_t)(cgbColor & 0x8000 ? 0x00 : 0xFF),
|
||||
static_cast<uint8_t>(cgbColor & 0x8000 ? 0x00 : 0xFF),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class EnumSeq {
|
||||
explicit Iterator(T value) : _value(value) {}
|
||||
|
||||
Iterator &operator++() {
|
||||
_value = (T)(_value + 1);
|
||||
_value = static_cast<T>(_value + 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class EnumSeq {
|
||||
};
|
||||
|
||||
public:
|
||||
explicit EnumSeq(T stop) : _start((T)0), _stop(stop) {}
|
||||
explicit EnumSeq(T stop) : _start(static_cast<T>(0)), _stop(stop) {}
|
||||
explicit EnumSeq(T start, T stop) : _start(start), _stop(stop) {}
|
||||
|
||||
Iterator begin() { return Iterator(_start); }
|
||||
|
||||
@@ -8,7 +8,6 @@ extern "C" {
|
||||
#define PACKAGE_VERSION_MAJOR 0
|
||||
#define PACKAGE_VERSION_MINOR 9
|
||||
#define PACKAGE_VERSION_PATCH 0
|
||||
#define PACKAGE_VERSION_RC 2
|
||||
|
||||
char const *get_package_version_string();
|
||||
}
|
||||
|
||||
612
man/gbz80.7
612
man/gbz80.7
File diff suppressed because it is too large
Load Diff
375
man/rgbasm-old.5
Normal file
375
man/rgbasm-old.5
Normal file
@@ -0,0 +1,375 @@
|
||||
'\" e
|
||||
.\"
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd December 25, 2024
|
||||
.Dt RGBASM-OLD 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm rgbasm-old
|
||||
.Nd obsolete language documentation
|
||||
.Sh DESCRIPTION
|
||||
This is the list of features that have been removed from the
|
||||
.Xr rgbasm 5
|
||||
assembly language over its decades of evolution, along with their modern alternatives.
|
||||
Its goal is to be a reference for backwards incompatibility, when upgrading an old assembly codebase to work with the latest RGBDS release.
|
||||
It does
|
||||
.Em not
|
||||
attempt to list syntax bugs that were fixed, nor new reserved keywords that may conflict with old identifiers.
|
||||
.Sh REMOVED
|
||||
These are features which have been completely removed, without any direct alternatives.
|
||||
Usually these features were limiting the addition of other features, or had awkward limits on their own intended effects.
|
||||
.Ss Automatic LD to LDH conversion (rgbasm -l)
|
||||
Deprecated in 0.7.0, removed in 0.8.0.
|
||||
.Pp
|
||||
.Xr rgbasm 1
|
||||
used to automatically treat
|
||||
.Ql LD
|
||||
as
|
||||
.Ql LDH
|
||||
if the address was known to be in the
|
||||
.Ad $FF00-$FFFF
|
||||
range, with the
|
||||
.Fl L
|
||||
flag to opt out.
|
||||
.Xr rgbasm 1
|
||||
0.6.0 added a
|
||||
.Fl l
|
||||
flag to opt in instead.
|
||||
.Pp
|
||||
Instead, use
|
||||
.Ql LDH ,
|
||||
and remove the
|
||||
.Fl L
|
||||
and
|
||||
.Fl l
|
||||
flags from
|
||||
.Xr rgbasm 1 .
|
||||
.Ss Automatic NOP after HALT (rgbasm -H)
|
||||
Deprecated in 0.7.0, removed in 0.8.0.
|
||||
.Pp
|
||||
.Xr rgbasm 1
|
||||
used to automatically insert a
|
||||
.Ql NOP
|
||||
after
|
||||
.Ql HALT ,
|
||||
with the
|
||||
.Fl h
|
||||
flag to opt out.
|
||||
.Xr rgbasm 1
|
||||
0.6.0 added a
|
||||
.Fl H
|
||||
flag to opt in instead.
|
||||
.Pp
|
||||
Instead, use an explicit
|
||||
.Ql NOP
|
||||
after
|
||||
.Ql HALT ,
|
||||
and remove the
|
||||
.Fl h
|
||||
and
|
||||
.Fl H
|
||||
flags from
|
||||
.Xr rgbasm 1 .
|
||||
.Ss Nested macro definitions
|
||||
Removed in 0.4.2.
|
||||
.Pp
|
||||
Instead, put the nested macro definition inside a quoted string (making sure that none of its lines start with
|
||||
.Ic ENDM ) ,
|
||||
then interpolate that string.
|
||||
For example:
|
||||
.Bd -literal -offset indent
|
||||
MACRO outer
|
||||
DEF definition EQUS """
|
||||
MACRO inner
|
||||
println (\e1) - (\e\e1)
|
||||
\enENDM"""
|
||||
{definition}
|
||||
PURGE definition
|
||||
ENDM
|
||||
outer 10
|
||||
inner 3 ; prints 7
|
||||
.Ed
|
||||
.Ss Negative DS
|
||||
Removed in 0.3.2.
|
||||
.Pp
|
||||
This was used to "rewind" the value of
|
||||
.Ic @
|
||||
in RAM sections, allowing labeled space allocations to overlap.
|
||||
.Pp
|
||||
Instead, use
|
||||
.Ic UNION .
|
||||
.Ss __FILE__ and __LINE__
|
||||
Deprecated in 0.6.0, removed in 0.7.0.
|
||||
.Pp
|
||||
Instead, use
|
||||
.Ic WARN
|
||||
or
|
||||
.Ic FAIL
|
||||
to print a complete trace of filenames and line numbers.
|
||||
.Ss _PI
|
||||
Deprecated in 0.5.0, removed in 0.6.0.
|
||||
.Pp
|
||||
Instead, use
|
||||
.Ql 3.141592653 .
|
||||
.Ss Treating multi-character strings as numbers
|
||||
Deprecated in 0.9.0.
|
||||
.Pp
|
||||
Instead, use a multi-value
|
||||
.Ic CHARMAP ,
|
||||
or explicitly combine the values of individual characters.
|
||||
.Ss rgbgfx -f/--fix and -F/--fix-and-save
|
||||
Removed in 0.6.0.
|
||||
.Pp
|
||||
Instead, use
|
||||
.Ql rgbgfx -c/--colors
|
||||
to explicitly specify a color palette.
|
||||
If using
|
||||
.Ql -c embedded ,
|
||||
arrange the PNG's indexed palette in a separate graphics editor.
|
||||
.Ss rgbgfx -D/--debug
|
||||
Removed in 0.6.0.
|
||||
.Sh REPLACED
|
||||
These are features whose syntax has been changed without affecting functionality.
|
||||
They can generally be updated with a single search-and-replace.
|
||||
.Ss Defining constants and variables without DEF
|
||||
Deprecated in 0.7.0, removed in 0.8.0.
|
||||
.Pp
|
||||
.Ic EQU , EQUS , = , RB , RW ,
|
||||
and
|
||||
.Ic RL
|
||||
definitions used to just start with the symbol name, but had to be typed in column 1.
|
||||
.Pp
|
||||
Instead, use
|
||||
.Ic DEF
|
||||
before constant and variable definitions.
|
||||
Note that
|
||||
.Ic EQUS
|
||||
expansion does not occur for the symbol name, so you have to use explicit
|
||||
.Ql {interpolation} .
|
||||
.Ss Defining macros like labels
|
||||
Deprecated in 0.6.0, removed in 0.7.0.
|
||||
.Pp
|
||||
Macros used to be defined as
|
||||
.Ql name: MACRO .
|
||||
.Pp
|
||||
Instead, use
|
||||
.Ql MACRO name .
|
||||
Note that
|
||||
.Ic EQUS
|
||||
expansion does not occur for the macro name, so you have to use explicit
|
||||
.Ql {interpolation} .
|
||||
.Ss Defining variables with SET
|
||||
Deprecated in 0.5.2, removed in 0.6.0.
|
||||
.Pp
|
||||
Variables used to be defined as
|
||||
.Ql name SET value .
|
||||
.Pp
|
||||
Instead, use
|
||||
.Ql DEF name = value .
|
||||
.Ss Global labels without colons
|
||||
Deprecated in 0.4.0, removed in 0.5.0.
|
||||
.Pp
|
||||
Labels used to be definable with just a name, but had to be typed in column 1.
|
||||
.Pp
|
||||
Instead, use explicit colons; for example,
|
||||
.Ql Label:
|
||||
or exported
|
||||
.Ql Label:: .
|
||||
.Ss '\e,' in strings within macro arguments
|
||||
Deprecated in 0.5.0, removed in 0.7.0.
|
||||
.Pp
|
||||
Macro arguments now handle quoted strings and parenthesized expressions as single arguments, so commas inside them are not argument separators and do not need escaping.
|
||||
.Pp
|
||||
Instead, just use commas without backslashes.
|
||||
.Ss '*' comments
|
||||
Deprecated in 0.4.1, removed in 0.5.0.
|
||||
.Pp
|
||||
These comments had to have the
|
||||
.Ql *
|
||||
typed in column 1.
|
||||
.Pp
|
||||
Instead, use
|
||||
.Ql \&;
|
||||
comments.
|
||||
.Ss PRINTT, PRINTI, PRINTV, and PRINTF
|
||||
Deprecated in 0.5.0, removed in 0.6.0.
|
||||
.Pp
|
||||
These directives were each specific to one type of value.
|
||||
.Pp
|
||||
Instead, use
|
||||
.Ic PRINT
|
||||
and
|
||||
.Ic PRINTLN ,
|
||||
with
|
||||
.Ic STRFMT
|
||||
or
|
||||
.Ql {interpolation}
|
||||
for type-specific formatting.
|
||||
.Ss IMPORT and XREF
|
||||
Removed in 0.4.0.
|
||||
.Pp
|
||||
Symbols are now automatically resolved if they were exported from elsewhere.
|
||||
.Pp
|
||||
Instead, just remove these directives.
|
||||
.Ss GLOBAL and XDEF
|
||||
Deprecated in 0.4.2, removed in 0.5.0.
|
||||
.Pp
|
||||
Instead, use
|
||||
.Ic EXPORT .
|
||||
.Ss HOME, CODE, DATA, and BSS
|
||||
Deprecated in 0.3.0, removed in 0.4.0.
|
||||
.Pp
|
||||
Instead of
|
||||
.Ic HOME ,
|
||||
use
|
||||
.Ic ROM0 ;
|
||||
instead of
|
||||
.Ic CODE
|
||||
and
|
||||
.Ic DATA ,
|
||||
use
|
||||
.Ic ROMX ;
|
||||
instead of
|
||||
.Ic BSS ,
|
||||
use
|
||||
.Ic WRAM0 .
|
||||
.Ss JP [HL]
|
||||
Deprecated in 0.3.0, removed in 0.4.0.
|
||||
.Pp
|
||||
Instead, use
|
||||
.Ql JP HL .
|
||||
.Ss LDI A, HL and LDD A, HL
|
||||
Deprecated in 0.3.0, removed in 0.4.0.
|
||||
.Pp
|
||||
Instead, use
|
||||
.Ql LDI A, [HL]
|
||||
and
|
||||
.Ql LDD A, [HL]
|
||||
(or
|
||||
.Ql LD A, [HLI]
|
||||
and
|
||||
.Ql LD A, [HLD] ;
|
||||
or
|
||||
.Ql LD A, [HL+]
|
||||
and
|
||||
.Ql LD A, [HL-] ) .
|
||||
.Ss LDIO
|
||||
Deprecated in 0.9.0.
|
||||
.Pp
|
||||
Instead, use
|
||||
.Ql LDH .
|
||||
.Ss LD [C], A and LD A, [C]
|
||||
Deprecated in 0.9.0.
|
||||
.Pp
|
||||
Instead, use
|
||||
.Ql LDH [C], A
|
||||
and
|
||||
.Ql LDH A, [C] .
|
||||
.Ss LDH [n8], A and LDH A, [n8]
|
||||
Deprecated in 0.9.0.
|
||||
.Pp
|
||||
.Ql LDH
|
||||
used to treat "addresses" from
|
||||
.Ad $00
|
||||
to
|
||||
.Ad $FF
|
||||
as if they were the low byte of an address from
|
||||
.Ad $FF00
|
||||
to
|
||||
.Ad $FFFF .
|
||||
.Pp
|
||||
Instead, use
|
||||
.Ql LDH [n16], A
|
||||
and
|
||||
.Ql LDH A, [n16] .
|
||||
.Ss LD HL, [SP + e8]
|
||||
Deprecated in 0.3.0, removed in 0.4.0.
|
||||
.Pp
|
||||
Instead, use
|
||||
.Ql LD HL, SP + e8 .
|
||||
.Ss LDHL, SP, e8
|
||||
Supported in ASMotor, removed in RGBDS.
|
||||
.Pp
|
||||
Instead, use
|
||||
.Ql LD HL, SP + e8 .
|
||||
.Ss rgbasm -i
|
||||
Deprecated in 0.6.0, removed in 0.8.0.
|
||||
.Pp
|
||||
Instead, use
|
||||
.Fl I
|
||||
or
|
||||
.Fl -include .
|
||||
.Ss rgbgfx -h
|
||||
Removed in 0.6.0.
|
||||
.Pp
|
||||
Instead, use
|
||||
.Fl Z
|
||||
or
|
||||
.Fl -columns .
|
||||
.Ss rgbgfx --output-*
|
||||
Deprecated in 0.7.0, removed in 0.8.0.
|
||||
.Pp
|
||||
Instead, use
|
||||
.Fl -auto-* .
|
||||
.Sh CHANGED
|
||||
These are breaking changes that did not alter syntax, and so could not practically be deprecated.
|
||||
.Ss Trigonometry function units
|
||||
Changed in 0.6.0.
|
||||
.Pp
|
||||
Instead of dividing a circle into 65536.0 "binary degrees", it is now divided into 1.0 "turns".
|
||||
.Pp
|
||||
For example, previously we had:
|
||||
.EQ
|
||||
delim $$
|
||||
.EN
|
||||
.Bl -bullet -offset indent
|
||||
.It
|
||||
.Ql SIN(0.25) == 0.00002 ,
|
||||
because 0.25 binary degrees = $0.25 / 65536.0$ turns = $0.000004 tau$ radians = $0.000008 pi$ radians, and $sin ( 0.000008 pi ) = 0.00002$
|
||||
.It
|
||||
.Ql SIN(16384.0) == 1.0 ,
|
||||
because 16384.0 binary degrees = $16384.0 / 65536.0$ turns = $0.25 tau$ radians = $pi / 2$ radians, and $sin ( pi / 2 ) = 1$
|
||||
.It
|
||||
.Ql ASIN(1.0) == 16384.0
|
||||
.El
|
||||
.Pp
|
||||
Instead, now we have:
|
||||
.Bl -bullet -offset indent
|
||||
.It
|
||||
.Ql SIN(0.25) == 1.0 ,
|
||||
because $0.25$ turns = $0.25 tau$ radians = $pi / 2$ radians, and $sin ( pi / 2 ) = 1$
|
||||
.It
|
||||
.Ql SIN(16384.0) == 0.0 ,
|
||||
because $16384$ turns = $16384 tau$ radians = $32768 pi$ radians, and $sin ( 32768 pi ) = 0$
|
||||
.It
|
||||
.Ql ASIN(1.0) == 0.25
|
||||
.El
|
||||
.EQ
|
||||
delim off
|
||||
.EN
|
||||
.Ss ** operator associativity
|
||||
Changed in 0.9.0.
|
||||
.Pp
|
||||
Instead of being left-associative,
|
||||
.Ql **
|
||||
is now right-associative.
|
||||
.Pp
|
||||
Previously we had
|
||||
.Ql p ** q ** r == (p ** q) ** r .
|
||||
.Pp
|
||||
Instead, now we have
|
||||
.Ql p ** q ** r == p ** (q ** r) .
|
||||
.Sh SEE ALSO
|
||||
.Xr rgbasm 1 ,
|
||||
.Xr gbz80 7 ,
|
||||
.Xr rgbds 5 ,
|
||||
.Xr rgbds 7
|
||||
.Sh HISTORY
|
||||
.Xr rgbasm 1
|
||||
was originally written by
|
||||
.An Carsten S\(/orensen
|
||||
as part of the ASMotor package, and was later repackaged in RGBDS by
|
||||
.An Justin Lloyd .
|
||||
It is now maintained by a number of contributors at
|
||||
.Lk https://github.com/gbdev/rgbds .
|
||||
16
man/rgbasm.1
16
man/rgbasm.1
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd October 21, 2024
|
||||
.Dd December 25, 2024
|
||||
.Dt RGBASM 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -200,7 +200,7 @@ section for a list of warnings.
|
||||
Disable all warning output, even when turned into errors.
|
||||
.It Fl X Ar max_errors , Fl \-max-errors Ar max_errors
|
||||
If more than this number of errors (not warnings) occur, then abort the assembly process;
|
||||
.Fl X 0
|
||||
.Fl X Ar 0
|
||||
disables this behavior.
|
||||
The default is 100 if
|
||||
.Nm
|
||||
@@ -370,6 +370,17 @@ only warns if the active charmap is not empty.
|
||||
.Fl Wunmapped-char=2
|
||||
warns if the active charmap is empty, and/or is not the default charmap
|
||||
.Sq main .
|
||||
.It Fl Wunmatched-directive
|
||||
Warn when a
|
||||
.Ic PUSHC , PUSHO ,
|
||||
or
|
||||
.Ic PUSHS
|
||||
directive does not have a corresponding
|
||||
.Ic POPC , POPO ,
|
||||
or
|
||||
.Ic POPS .
|
||||
This warning is enabled by
|
||||
.Fl Wextra .
|
||||
.It Fl Wunterminated-load
|
||||
Warn when a
|
||||
.Ic LOAD
|
||||
@@ -416,6 +427,7 @@ Please report bugs on
|
||||
.Xr rgbfix 1 ,
|
||||
.Xr rgbgfx 1 ,
|
||||
.Xr gbz80 7 ,
|
||||
.Xr rgbasm-old 5 ,
|
||||
.Xr rgbds 5 ,
|
||||
.Xr rgbds 7
|
||||
.Sh HISTORY
|
||||
|
||||
204
man/rgbasm.5
204
man/rgbasm.5
@@ -2,7 +2,7 @@
|
||||
.\"
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd October 21, 2024
|
||||
.Dd December 25, 2024
|
||||
.Dt RGBASM 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -253,7 +253,7 @@ Although, for these examples,
|
||||
.Ic STRFMT
|
||||
would be more appropriate; see
|
||||
.Sx String expressions
|
||||
further below.
|
||||
below.
|
||||
.Sh EXPRESSIONS
|
||||
An expression can be composed of many things.
|
||||
Numeric expressions are always evaluated using signed 32-bit math.
|
||||
@@ -267,7 +267,7 @@ This is generally always the case, unless a label is involved, as explained in t
|
||||
section.
|
||||
However, some operators can be constant even with non-constant operands, as explained in
|
||||
.Sx Operators
|
||||
further below.
|
||||
below.
|
||||
.Pp
|
||||
The instructions in the macro-language generally require constant expressions.
|
||||
.Ss Numeric formats
|
||||
@@ -309,26 +309,35 @@ is equivalent to
|
||||
.Pp
|
||||
You can also use symbols, which are implicitly replaced with their value.
|
||||
.Ss Operators
|
||||
A great number of operators you can use in expressions are available (listed from highest to lowest precedence):
|
||||
You can use these operators in numeric expressions (listed from highest to lowest precedence):
|
||||
.Bl -column -offset indent "!= == <= >= < >"
|
||||
.It Sy Operator Ta Sy Meaning
|
||||
.It Li \&( \&) Ta Precedence override
|
||||
.It Li \&( \&) Ta Grouping
|
||||
.It Li FUNC() Ta Built-in function call
|
||||
.It Li ** Ta Exponent
|
||||
.It Li ~ + - Ta Unary complement/plus/minus
|
||||
.It Li * / % Ta Multiply/divide/modulo
|
||||
.It Li << Ta Shift left
|
||||
.It Li >> Ta Signed shift right (sign-extension)
|
||||
.It Li >>> Ta Unsigned shift right (zero-extension)
|
||||
.It Li & \&| ^ Ta Binary and/or/xor
|
||||
.It Li + - Ta Add/subtract
|
||||
.It Li != == <= >= < > Ta Comparison
|
||||
.It Li && || Ta Boolean and/or
|
||||
.It Li \&! Ta Unary not
|
||||
.It Li ** Ta Exponentiation
|
||||
.It Li + - ~ \&! Ta Unary plus, minus (negation), complement (bitwise negation), and Boolean negation
|
||||
.It Li * / % Ta Multiplication, division, and modulo (remainder)
|
||||
.It Li << >> >>> Ta Bit shifts (left, sign-extended right, zero-extended right)
|
||||
.It Li & \&| ^ Ta Bitwise AND/OR/XOR
|
||||
.It Li + - Ta Addition and subtraction
|
||||
.It Li == != < > <= >= Ta Comparisons
|
||||
.It Li && Ta Boolean AND
|
||||
.It Li || Ta Boolean OR
|
||||
.El
|
||||
.Pp
|
||||
.Sq **
|
||||
raises a number to a non-negative power. It is the only
|
||||
.Em right-associative
|
||||
operator, meaning that
|
||||
.Ql p ** q ** r
|
||||
is equal to
|
||||
.Ql p ** (q ** r) ,
|
||||
not
|
||||
.Ql (p ** q) ** r .
|
||||
All other binary operators are left-associative.
|
||||
.Pp
|
||||
.Sq ~
|
||||
complements a value by inverting all its bits.
|
||||
complements a value by inverting all 32 of its bits.
|
||||
.Pp
|
||||
.Sq %
|
||||
is used to get the remainder of the corresponding division, so that
|
||||
@@ -968,12 +977,14 @@ block before performing its own function.
|
||||
.Ic LOAD
|
||||
blocks can use the
|
||||
.Ic UNION
|
||||
modifier as described below, but not the
|
||||
or
|
||||
.Ic FRAGMENT
|
||||
modifier.
|
||||
modifiers as described in
|
||||
.Sx Unionized sections
|
||||
below.
|
||||
.Ss Unionized sections
|
||||
When you're tight on RAM, you may want to define overlapping static memory allocations, as explained in the
|
||||
.Sx Unions
|
||||
.Sx Allocating overlapping spaces in RAM
|
||||
section.
|
||||
However, a
|
||||
.Ic UNION
|
||||
@@ -1014,7 +1025,7 @@ or
|
||||
.El
|
||||
.Pp
|
||||
Different declarations of the same unionized section are not appended, but instead overlaid on top of each other, just like
|
||||
.Sx Unions .
|
||||
.Sx Allocating overlapping spaces in RAM .
|
||||
Similarly, the size of an unionized section is the largest of all its declarations.
|
||||
.Ss Section fragments
|
||||
Section fragments are sections with a small twist: when several of the same name are encountered, they are concatenated instead of producing an error.
|
||||
@@ -1137,7 +1148,7 @@ otherwise, it is said to be
|
||||
.Dq exported ,
|
||||
explained in
|
||||
.Sx Exporting and importing symbols
|
||||
further below).
|
||||
below).
|
||||
More than one dot in label names is not allowed.
|
||||
.Pp
|
||||
For convenience, local labels can use a shorthand syntax: when a symbol name starting with a dot is found (for example, inside an expression, or when declaring a label), then the current
|
||||
@@ -1567,47 +1578,6 @@ environment variable if that is defined as a UNIX timestamp.
|
||||
Refer to the spec at
|
||||
.Lk https://reproducible-builds.org/docs/source-date-epoch/ reproducible-builds.org .
|
||||
.Sh DEFINING DATA
|
||||
.Ss Statically allocating space in RAM
|
||||
.Ic DS
|
||||
statically allocates a number of empty bytes.
|
||||
This is the preferred method of allocating space in a RAM section.
|
||||
You can also use
|
||||
.Ic DB , DW
|
||||
and
|
||||
.Ic DL
|
||||
without any arguments instead (see
|
||||
.Sx Defining constant data in ROM
|
||||
below).
|
||||
.Bd -literal -offset indent
|
||||
DS 42 ;\ Allocates 42 bytes
|
||||
.Ed
|
||||
.Pp
|
||||
Empty space in RAM sections will not be initialized.
|
||||
In ROM sections, it will be filled with the value passed to the
|
||||
.Fl p
|
||||
command-line option, except when using overlays with
|
||||
.Fl O .
|
||||
.Pp
|
||||
Instead of an exact number of bytes, you can specify
|
||||
.Ic ALIGN Ns Bq Ar align , offset
|
||||
to allocate however many bytes are required to align the subsequent data.
|
||||
Thus,
|
||||
.Sq Ic DS ALIGN Ns Bo Ar align , offset Bc , No ...
|
||||
is equivalent to
|
||||
.Sq Ic DS Ar n , No ...
|
||||
followed by
|
||||
.Sq Ic ALIGN Ns Bq Ar align , offset ,
|
||||
where
|
||||
.Ar n
|
||||
is the minimum value needed to satisfy the
|
||||
.Ic ALIGN
|
||||
constraint (see
|
||||
.Sx Requesting alignment
|
||||
below).
|
||||
Note that
|
||||
.Ic ALIGN Ns Bq Ar align
|
||||
is a shorthand for
|
||||
.Ic ALIGN Ns Bq Ar align , No 0 .
|
||||
.Ss Defining constant data in ROM
|
||||
.Ic DB
|
||||
defines a list of bytes that will be stored in the final image.
|
||||
@@ -1674,7 +1644,7 @@ can be used in a
|
||||
/
|
||||
.Ic SRAM
|
||||
section.
|
||||
.Ss Including binary files
|
||||
.Ss Including binary data files
|
||||
You probably have some graphics, level data, etc. you'd like to include.
|
||||
Use
|
||||
.Ic INCBIN
|
||||
@@ -1698,7 +1668,48 @@ INCBIN "data.bin", 78, 256
|
||||
.Pp
|
||||
The length argument is optional.
|
||||
If only the start position is specified, the bytes from the start position until the end of the file will be included.
|
||||
.Ss Unions
|
||||
.Ss Statically allocating space in RAM
|
||||
.Ic DS
|
||||
statically allocates a number of empty bytes.
|
||||
This is the preferred method of allocating space in a RAM section.
|
||||
You can also use
|
||||
.Ic DB , DW
|
||||
and
|
||||
.Ic DL
|
||||
without any arguments instead (see
|
||||
.Sx Defining constant data in ROM
|
||||
below).
|
||||
.Bd -literal -offset indent
|
||||
DS 42 ;\ Allocates 42 bytes
|
||||
.Ed
|
||||
.Pp
|
||||
Empty space in RAM sections will not be initialized.
|
||||
In ROM sections, it will be filled with the value passed to the
|
||||
.Fl p
|
||||
command-line option, except when using overlays with
|
||||
.Fl O .
|
||||
.Pp
|
||||
Instead of an exact number of bytes, you can specify
|
||||
.Ic ALIGN Ns Bq Ar align , offset
|
||||
to allocate however many bytes are required to align the subsequent data.
|
||||
Thus,
|
||||
.Sq Ic DS ALIGN Ns Bo Ar align , offset Bc , No ...
|
||||
is equivalent to
|
||||
.Sq Ic DS Ar n , No ...
|
||||
followed by
|
||||
.Sq Ic ALIGN Ns Bq Ar align , offset ,
|
||||
where
|
||||
.Ar n
|
||||
is the minimum value needed to satisfy the
|
||||
.Ic ALIGN
|
||||
constraint (see
|
||||
.Sx Requesting alignment
|
||||
below).
|
||||
Note that
|
||||
.Ic ALIGN Ns Bq Ar align
|
||||
is a shorthand for
|
||||
.Ic ALIGN Ns Bq Ar align , No 0 .
|
||||
.Ss Allocating overlapping spaces in RAM
|
||||
Unions allow multiple static memory allocations to overlap, like unions in C.
|
||||
This does not increase the amount of memory available, but allows re-using the same memory region for different purposes.
|
||||
.Pp
|
||||
@@ -1759,6 +1770,37 @@ Unions may be used in any section, but they may only contain space-allocating di
|
||||
.Ic DS
|
||||
(see
|
||||
.Sx Statically allocating space in RAM ) .
|
||||
.Ss Requesting alignment
|
||||
While
|
||||
.Ic ALIGN
|
||||
as presented in
|
||||
.Sx SECTIONS
|
||||
is often useful as-is, sometimes you instead want a particular piece of data (or code) in the middle of the section to be aligned.
|
||||
This is made easier through the use of mid-section
|
||||
.Ic ALIGN Ar align , offset .
|
||||
It will retroactively alter the section's attributes to ensure that the location the
|
||||
.Ic ALIGN
|
||||
directive is at, has its
|
||||
.Ar align
|
||||
lower bits equal to
|
||||
.Ar offset .
|
||||
.Pp
|
||||
If the constraint cannot be met (for example because the section is fixed at an incompatible address), an error is produced.
|
||||
Note that
|
||||
.Ic ALIGN Ar align
|
||||
is a shorthand for
|
||||
.Ic ALIGN Ar align , No 0 .
|
||||
.Pp
|
||||
There may be times when you don't just want to specify an alignment constraint at the current location, but also skip ahead until the constraint can be satisfied.
|
||||
In that case, you can use
|
||||
.Ic DS ALIGN Ns Bq Ar align , offset
|
||||
to allocate however many bytes are required to align the subsequent data.
|
||||
.Pp
|
||||
If the constraint cannot be met by skipping any amount of space, an error is produced.
|
||||
Note that
|
||||
.Ic ALIGN Ns Bq Ar align
|
||||
is a shorthand for
|
||||
.Ic ALIGN Ns Bq Ar align , No 0 .
|
||||
.Sh THE MACRO LANGUAGE
|
||||
.Ss Invoking macros
|
||||
A macro is invoked by using its name at the beginning of a line, like a directive, followed by any comma-separated arguments.
|
||||
@@ -2320,37 +2362,6 @@ PUSHO b.X, g.oOX
|
||||
DW `..ooOOXX
|
||||
POPO
|
||||
.Ed
|
||||
.Ss Requesting alignment
|
||||
While
|
||||
.Ic ALIGN
|
||||
as presented in
|
||||
.Sx SECTIONS
|
||||
is often useful as-is, sometimes you instead want a particular piece of data (or code) in the middle of the section to be aligned.
|
||||
This is made easier through the use of mid-section
|
||||
.Ic ALIGN Ar align , offset .
|
||||
It will alter the section's attributes to ensure that the location the
|
||||
.Ic ALIGN
|
||||
directive is at, has its
|
||||
.Ar align
|
||||
lower bits equal to
|
||||
.Ar offset .
|
||||
.Pp
|
||||
If the constraint cannot be met (for example because the section is fixed at an incompatible address), an error is produced.
|
||||
Note that
|
||||
.Ic ALIGN Ar align
|
||||
is a shorthand for
|
||||
.Ic ALIGN Ar align , No 0 .
|
||||
.Pp
|
||||
There may be times when you don't just want to specify an alignment constraint at the current location, but also skip ahead until the constraint can be satisfied.
|
||||
In that case, you can use
|
||||
.Ic DS ALIGN Ns Bq Ar align , offset
|
||||
to allocate however many bytes are required to align the subsequent data.
|
||||
.Pp
|
||||
If the constraint cannot be met by skipping any amount of space, an error is produced.
|
||||
Note that
|
||||
.Ic ALIGN Ns Bq Ar align
|
||||
is a shorthand for
|
||||
.Ic ALIGN Ns Bq Ar align , No 0 .
|
||||
.Sh SEE ALSO
|
||||
.Xr rgbasm 1 ,
|
||||
.Xr rgblink 1 ,
|
||||
@@ -2358,6 +2369,7 @@ is a shorthand for
|
||||
.Xr rgbfix 1 ,
|
||||
.Xr rgbgfx 1 ,
|
||||
.Xr gbz80 7 ,
|
||||
.Xr rgbasm-old 5 ,
|
||||
.Xr rgbds 5 ,
|
||||
.Xr rgbds 7
|
||||
.Sh HISTORY
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd October 21, 2024
|
||||
.Dd December 25, 2024
|
||||
.Dt RGBDS 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -254,7 +254,7 @@ Size of the
|
||||
below.
|
||||
.It Cm BYTE Ar RPNExpr Ns Bq RPNSize
|
||||
The patch's value, encoded as a RPN expression
|
||||
.Pq see Sx RPN EXPRESSIONS .
|
||||
.Pq see Sx RPN expressions .
|
||||
.El
|
||||
.It Cm ENDR
|
||||
.El
|
||||
@@ -294,14 +294,14 @@ Size of the
|
||||
below.
|
||||
.It Cm BYTE Ar RPNExpr Ns Bq RPNSize
|
||||
The patch's value, encoded as a RPN expression
|
||||
.Pq see Sx RPN EXPRESSIONS .
|
||||
.Pq see Sx RPN expressions .
|
||||
.It Cm STRING Ar Message
|
||||
The message displayed if the expression evaluates to a non-zero value.
|
||||
If empty, a generic message is displayed instead.
|
||||
.El
|
||||
.It Cm ENDR
|
||||
.El
|
||||
.Ss RPN EXPRESSIONS
|
||||
.Ss RPN expressions
|
||||
Expressions in the object file are stored as RPN, or
|
||||
.Dq Reverse Polish Notation ,
|
||||
which is a notation that allows computing arbitrary expressions with just a simple stack.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd October 21, 2024
|
||||
.Dd December 25, 2024
|
||||
.Dt RGBDS 7
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd October 21, 2024
|
||||
.Dd December 25, 2024
|
||||
.Dt RGBFIX 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
17
man/rgbgfx.1
17
man/rgbgfx.1
@@ -2,7 +2,7 @@
|
||||
.\"
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd October 21, 2024
|
||||
.Dd December 25, 2024
|
||||
.Dt RGBGFX 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -10,7 +10,7 @@
|
||||
.Nd Game Boy graphics converter
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl CmOuVZ
|
||||
.Op Fl CmOuVXYZ
|
||||
.Op Fl v Op Fl v No ...
|
||||
.Op Fl a Ar attrmap | Fl A
|
||||
.Op Fl b Ar base_ids
|
||||
@@ -229,9 +229,8 @@ The second number pair specifies how many tiles to process horizontally and vert
|
||||
.Pp
|
||||
.Fl L Sy is ignored in reverse mode , No no padding is inserted .
|
||||
.It Fl m , Fl \-mirror-tiles
|
||||
Deduplicate tiles that are symmetrical mirror images of each other.
|
||||
Deduplicate tiles that are horizontally and/or vertically symmetrical mirror images of each other.
|
||||
Only one of each unique tile will be saved in the tile data file, with mirror images counting as duplicates.
|
||||
Tiles are checked for horizontal, vertical, and horizontal-vertical mirroring.
|
||||
Useful with a tile map and attribute map together (see
|
||||
.Fl a
|
||||
and
|
||||
@@ -239,6 +238,8 @@ and
|
||||
to keep track of the duplicated tiles and the dimension(s) mirrored.
|
||||
Implies
|
||||
.Fl u .
|
||||
Equivalent to
|
||||
.Fl XY .
|
||||
.It Fl N Ar nb_tiles , Fl \-nb-tiles Ar nb_tiles
|
||||
Set a maximum number of tiles that can be placed in each VRAM bank.
|
||||
.Ar nb_tiles
|
||||
@@ -353,6 +354,10 @@ Some internal debug printing is enabled.
|
||||
The verbosity level does not go past 6.
|
||||
.Pp
|
||||
Note that verbose output is only intended to be consumed by humans, and may change without notice between RGBDS releases; relying on those for scripts is not advised.
|
||||
.It Fl X , Fl \-mirror-x
|
||||
Deduplicate tiles that are horizontally symmetrical mirror images of each other across the X axis.
|
||||
Implies
|
||||
.Fl u .
|
||||
.It Fl x Ar quantity , Fl \-trim-end Ar quantity
|
||||
Do not output the last
|
||||
.Ar quantity
|
||||
@@ -373,6 +378,10 @@ was enabled, so you probably don't want to use this option in combination with
|
||||
Note also that the tiles that don't get output will not count towards
|
||||
.Fl N Ap s
|
||||
limit.
|
||||
.It Fl Y , Fl \-mirror-y
|
||||
Deduplicate tiles that are vertically symmetrical mirror images of each other across the Y axis.
|
||||
Implies
|
||||
.Fl u .
|
||||
.It Fl Z , Fl \-columns
|
||||
Read squares from the PNG in column-major order (column by column), instead of the default row-major order (line by line).
|
||||
This primarily affects tile map and attribute map output, although it may also change generated tile data and palettes.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd October 21, 2024
|
||||
.Dd December 25, 2024
|
||||
.Dt RGBLINK 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.\" SPDX-License-Identifier: MIT
|
||||
.\"
|
||||
.Dd October 21, 2024
|
||||
.Dd December 25, 2024
|
||||
.Dt RGBLINK 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
|
||||
@@ -53,7 +53,7 @@ bool charmap_ForEach(
|
||||
mappings[nodeIdx] = mapping;
|
||||
for (unsigned c = 0; c < 256; c++) {
|
||||
if (size_t nextIdx = node.next[c]; nextIdx)
|
||||
prefixes.push({nextIdx, mapping + (char)c});
|
||||
prefixes.push({nextIdx, mapping + static_cast<char>(c)});
|
||||
}
|
||||
}
|
||||
mapFunc(charmap.name);
|
||||
@@ -64,7 +64,7 @@ bool charmap_ForEach(
|
||||
}
|
||||
|
||||
void charmap_New(std::string const &name, std::string const *baseName) {
|
||||
size_t baseIdx = (size_t)-1;
|
||||
size_t baseIdx = SIZE_MAX;
|
||||
|
||||
if (baseName != nullptr) {
|
||||
if (auto search = charmapMap.find(*baseName); search == charmapMap.end())
|
||||
@@ -82,7 +82,7 @@ void charmap_New(std::string const &name, std::string const *baseName) {
|
||||
charmapMap[name] = charmapList.size();
|
||||
Charmap &charmap = charmapList.emplace_back();
|
||||
|
||||
if (baseIdx != (size_t)-1)
|
||||
if (baseIdx != SIZE_MAX)
|
||||
charmap.nodes = charmapList[baseIdx].nodes; // Copies `charmapList[baseIdx].nodes`
|
||||
else
|
||||
charmap.nodes.emplace_back(); // Zero-init the root node
|
||||
@@ -113,6 +113,12 @@ void charmap_Pop() {
|
||||
charmapStack.pop();
|
||||
}
|
||||
|
||||
void charmap_CheckStack() {
|
||||
if (!charmapStack.empty()) {
|
||||
warning(WARNING_UNMATCHED_DIRECTIVE, "`PUSHC` without corresponding `POPC`\n");
|
||||
}
|
||||
}
|
||||
|
||||
void charmap_Add(std::string const &mapping, std::vector<int32_t> &&value) {
|
||||
if (mapping.empty()) {
|
||||
error("Cannot map an empty string\n");
|
||||
@@ -123,7 +129,7 @@ void charmap_Add(std::string const &mapping, std::vector<int32_t> &&value) {
|
||||
size_t nodeIdx = 0;
|
||||
|
||||
for (char c : mapping) {
|
||||
size_t &nextIdxRef = charmap.nodes[nodeIdx].next[(uint8_t)c];
|
||||
size_t &nextIdxRef = charmap.nodes[nodeIdx].next[static_cast<uint8_t>(c)];
|
||||
size_t nextIdx = nextIdxRef;
|
||||
|
||||
if (!nextIdx) {
|
||||
@@ -151,7 +157,7 @@ bool charmap_HasChar(std::string const &input) {
|
||||
size_t nodeIdx = 0;
|
||||
|
||||
for (char c : input) {
|
||||
nodeIdx = charmap.nodes[nodeIdx].next[(uint8_t)c];
|
||||
nodeIdx = charmap.nodes[nodeIdx].next[static_cast<uint8_t>(c)];
|
||||
|
||||
if (!nodeIdx)
|
||||
return false;
|
||||
@@ -178,7 +184,7 @@ size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output
|
||||
size_t inputIdx = 0;
|
||||
|
||||
for (size_t nodeIdx = 0; inputIdx < input.length();) {
|
||||
nodeIdx = charmap.nodes[nodeIdx].next[(uint8_t)input[inputIdx]];
|
||||
nodeIdx = charmap.nodes[nodeIdx].next[static_cast<uint8_t>(input[inputIdx])];
|
||||
|
||||
if (!nodeIdx)
|
||||
break;
|
||||
|
||||
@@ -29,7 +29,7 @@ static int32_t double2fix(double d, int32_t q) {
|
||||
return 0;
|
||||
if (isinf(d))
|
||||
return d < 0 ? INT32_MIN : INT32_MAX;
|
||||
return (int32_t)round(d * pow(2.0, q));
|
||||
return static_cast<int32_t>(round(d * pow(2.0, q)));
|
||||
}
|
||||
|
||||
static double turn2rad(double t) {
|
||||
|
||||
@@ -250,15 +250,16 @@ void FormatSpec::appendNumber(std::string &str, uint32_t value) const {
|
||||
}
|
||||
|
||||
double fval = fabs(value / pow(2.0, usePrec));
|
||||
if (useExact)
|
||||
snprintf(valueBuf, sizeof(valueBuf), "%.*fq%zu", (int)useFracWidth, fval, usePrec);
|
||||
if (int fracWidthArg = static_cast<int>(useFracWidth); useExact)
|
||||
snprintf(valueBuf, sizeof(valueBuf), "%.*fq%zu", fracWidthArg, fval, usePrec);
|
||||
else
|
||||
snprintf(valueBuf, sizeof(valueBuf), "%.*f", (int)useFracWidth, fval);
|
||||
snprintf(valueBuf, sizeof(valueBuf), "%.*f", fracWidthArg, fval);
|
||||
} else if (useType == 'd') {
|
||||
// Decimal numbers may be formatted with a '-' sign by `snprintf`, so `abs` prevents that,
|
||||
// with a special case for `INT32_MIN` since `labs(INT32_MIN)` is UB. The sign will be
|
||||
// printed later from `signChar`.
|
||||
uint32_t uval = value != (uint32_t)INT32_MIN ? labs((int32_t)value) : value;
|
||||
uint32_t uval =
|
||||
value != static_cast<uint32_t>(INT32_MIN) ? labs(static_cast<int32_t>(value)) : value;
|
||||
snprintf(valueBuf, sizeof(valueBuf), "%" PRIu32, uval);
|
||||
} else {
|
||||
char const *spec = useType == 'u' ? "%" PRIu32
|
||||
|
||||
@@ -162,7 +162,7 @@ bool yywrap() {
|
||||
// If the node is referenced outside this context, we can't edit it, so duplicate it
|
||||
if (context.fileInfo.use_count() > 1) {
|
||||
context.fileInfo = std::make_shared<FileStackNode>(*context.fileInfo);
|
||||
context.fileInfo->ID = -1; // The copy is not yet registered
|
||||
context.fileInfo->ID = UINT32_MAX; // The copy is not yet registered
|
||||
}
|
||||
|
||||
std::vector<uint32_t> &fileInfoIters = context.fileInfo->iters();
|
||||
@@ -170,8 +170,10 @@ bool yywrap() {
|
||||
// If this is a FOR, update the symbol value
|
||||
if (context.isForLoop && fileInfoIters.front() <= context.nbReptIters) {
|
||||
// Avoid arithmetic overflow runtime error
|
||||
uint32_t forValue = (uint32_t)context.forValue + (uint32_t)context.forStep;
|
||||
context.forValue = forValue <= INT32_MAX ? forValue : -(int32_t)~forValue - 1;
|
||||
uint32_t forValue =
|
||||
static_cast<uint32_t>(context.forValue) + static_cast<uint32_t>(context.forStep);
|
||||
context.forValue =
|
||||
forValue <= INT32_MAX ? forValue : -static_cast<int32_t>(~forValue) - 1;
|
||||
Symbol *sym = sym_AddVar(context.forName, context.forValue);
|
||||
|
||||
// This error message will refer to the current iteration
|
||||
@@ -347,9 +349,9 @@ void fstk_RunFor(
|
||||
|
||||
uint32_t count = 0;
|
||||
if (step > 0 && start < stop)
|
||||
count = ((int64_t)stop - start - 1) / step + 1;
|
||||
count = (static_cast<int64_t>(stop) - start - 1) / step + 1;
|
||||
else if (step < 0 && stop < start)
|
||||
count = ((int64_t)start - stop - 1) / -(int64_t)step + 1;
|
||||
count = (static_cast<int64_t>(start) - stop - 1) / -static_cast<int64_t>(step) + 1;
|
||||
else if (step == 0)
|
||||
error("FOR cannot have a step value of 0\n");
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ static char *mapFile(int fd, std::string const &path, size_t size) {
|
||||
printf("mmap(%s, MAP_PRIVATE) failed, retrying with MAP_SHARED\n", path.c_str());
|
||||
mappingAddr = mmap(nullptr, size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
}
|
||||
return mappingAddr != MAP_FAILED ? (char *)mappingAddr : nullptr;
|
||||
return mappingAddr != MAP_FAILED ? static_cast<char *>(mappingAddr) : nullptr;
|
||||
}
|
||||
|
||||
struct FileUnmapDeleter {
|
||||
@@ -102,7 +102,7 @@ struct FileUnmapDeleter {
|
||||
using namespace std::literals;
|
||||
|
||||
// Bison 3.6 changed token "types" to "kinds"; cast to int for simple compatibility
|
||||
#define T_(name) (int)yy::parser::token::name
|
||||
#define T_(name) static_cast<int>(yy::parser::token::name)
|
||||
|
||||
struct Token {
|
||||
int type;
|
||||
@@ -329,6 +329,8 @@ static std::unordered_map<std::string, int, CaseInsensitive, CaseInsensitive> ke
|
||||
{"OPT", T_(POP_OPT) },
|
||||
};
|
||||
|
||||
static auto ldio = keywordDict.find("LDIO");
|
||||
|
||||
static bool isWhitespace(int c) {
|
||||
return c == ' ' || c == '\t';
|
||||
}
|
||||
@@ -422,7 +424,7 @@ bool LexerState::setFileAsNextState(std::string const &filePath, bool updateStat
|
||||
|
||||
bool isMmapped = false;
|
||||
|
||||
if (size_t size = (size_t)statBuf.st_size; statBuf.st_size > 0) {
|
||||
if (size_t size = static_cast<size_t>(statBuf.st_size); statBuf.st_size > 0) {
|
||||
// Try using `mmap` for better performance
|
||||
if (char *mappingAddr = mapFile(fd, path, size); mappingAddr != nullptr) {
|
||||
close(fd);
|
||||
@@ -541,7 +543,7 @@ size_t BufferedContent::readMore(size_t startIndex, size_t nbChars) {
|
||||
size += nbReadChars;
|
||||
|
||||
// `nbReadChars` cannot be negative, so it's fine to cast to `size_t`
|
||||
return (size_t)nbReadChars;
|
||||
return static_cast<size_t>(nbReadChars);
|
||||
}
|
||||
|
||||
void lexer_SetMode(LexerMode mode) {
|
||||
@@ -707,20 +709,20 @@ int LexerState::peekChar() {
|
||||
// This is `.peekCharAhead()` modified for zero lookahead distance
|
||||
for (Expansion &exp : expansions) {
|
||||
if (exp.offset < exp.size())
|
||||
return (uint8_t)(*exp.contents)[exp.offset];
|
||||
return static_cast<uint8_t>((*exp.contents)[exp.offset]);
|
||||
}
|
||||
|
||||
if (content.holds<ViewedContent>()) {
|
||||
auto &view = content.get<ViewedContent>();
|
||||
if (view.offset < view.span.size)
|
||||
return (uint8_t)view.span.ptr[view.offset];
|
||||
return static_cast<uint8_t>(view.span.ptr[view.offset]);
|
||||
} else {
|
||||
auto &cbuf = content.get<BufferedContent>();
|
||||
if (cbuf.size == 0)
|
||||
cbuf.refill();
|
||||
assume(cbuf.offset < LEXER_BUF_SIZE);
|
||||
if (cbuf.size > 0)
|
||||
return (uint8_t)cbuf.buf[cbuf.offset];
|
||||
return static_cast<uint8_t>(cbuf.buf[cbuf.offset]);
|
||||
}
|
||||
|
||||
// If there aren't enough chars, give up
|
||||
@@ -736,21 +738,21 @@ int LexerState::peekCharAhead() {
|
||||
// and `.peekCharAhead()` will continue with its parent
|
||||
assume(exp.offset <= exp.size());
|
||||
if (exp.offset + distance < exp.size())
|
||||
return (uint8_t)(*exp.contents)[exp.offset + distance];
|
||||
return static_cast<uint8_t>((*exp.contents)[exp.offset + distance]);
|
||||
distance -= exp.size() - exp.offset;
|
||||
}
|
||||
|
||||
if (content.holds<ViewedContent>()) {
|
||||
auto &view = content.get<ViewedContent>();
|
||||
if (view.offset + distance < view.span.size)
|
||||
return (uint8_t)view.span.ptr[view.offset + distance];
|
||||
return static_cast<uint8_t>(view.span.ptr[view.offset + distance]);
|
||||
} else {
|
||||
auto &cbuf = content.get<BufferedContent>();
|
||||
assume(distance < LEXER_BUF_SIZE);
|
||||
if (cbuf.size <= distance)
|
||||
cbuf.refill();
|
||||
if (cbuf.size > distance)
|
||||
return (uint8_t)cbuf.buf[(cbuf.offset + distance) % LEXER_BUF_SIZE];
|
||||
return static_cast<uint8_t>(cbuf.buf[(cbuf.offset + distance) % LEXER_BUF_SIZE]);
|
||||
}
|
||||
|
||||
// If there aren't enough chars, give up
|
||||
@@ -1030,11 +1032,12 @@ static uint32_t readFractionalPart(uint32_t integer) {
|
||||
precision = fixPrecision;
|
||||
}
|
||||
|
||||
if (integer >= ((uint64_t)1 << (32 - precision)))
|
||||
if (integer >= (1ULL << (32 - precision)))
|
||||
warning(WARNING_LARGE_CONSTANT, "Magnitude of fixed-point constant is too large\n");
|
||||
|
||||
// Cast to unsigned avoids undefined overflow behavior
|
||||
uint32_t fractional = (uint32_t)round((double)value / divisor * pow(2.0, precision));
|
||||
uint32_t fractional =
|
||||
static_cast<uint32_t>(round(static_cast<double>(value) / divisor * pow(2.0, precision)));
|
||||
|
||||
return (integer << precision) | fractional;
|
||||
}
|
||||
@@ -1168,9 +1171,13 @@ static Token readIdentifier(char firstChar, bool raw) {
|
||||
|
||||
// Attempt to check for a keyword if the identifier is not raw
|
||||
if (!raw) {
|
||||
if (auto search = keywordDict.find(identifier); search != keywordDict.end())
|
||||
if (auto search = keywordDict.find(identifier); search != keywordDict.end()) {
|
||||
if (search == ldio) {
|
||||
warning(WARNING_OBSOLETE, "LDIO is deprecated; use LDH\n");
|
||||
}
|
||||
return Token(search->second);
|
||||
}
|
||||
}
|
||||
|
||||
// Label scopes `.` and `..` are the only nonlocal identifiers that start with a dot
|
||||
if (identifier.find_first_not_of('.') == identifier.npos)
|
||||
|
||||
@@ -55,10 +55,10 @@ void MacroArgs::appendArg(std::shared_ptr<std::string> arg) {
|
||||
|
||||
void MacroArgs::shiftArgs(int32_t count) {
|
||||
if (size_t nbArgs = args.size();
|
||||
count > 0 && ((uint32_t)count > nbArgs || shift > nbArgs - count)) {
|
||||
count > 0 && (static_cast<uint32_t>(count) > nbArgs || shift > nbArgs - count)) {
|
||||
warning(WARNING_MACRO_SHIFT, "Cannot shift macro arguments past their end\n");
|
||||
shift = nbArgs;
|
||||
} else if (count < 0 && shift < (uint32_t)-count) {
|
||||
} else if (count < 0 && shift < static_cast<uint32_t>(-count)) {
|
||||
warning(WARNING_MACRO_SHIFT, "Cannot shift macro arguments past their beginning\n");
|
||||
shift = 0;
|
||||
} else {
|
||||
|
||||
@@ -112,7 +112,7 @@ int main(int argc, char *argv[]) {
|
||||
// Support SOURCE_DATE_EPOCH for reproducible builds
|
||||
// https://reproducible-builds.org/docs/source-date-epoch/
|
||||
if (char const *sourceDateEpoch = getenv("SOURCE_DATE_EPOCH"); sourceDateEpoch)
|
||||
now = (time_t)strtoul(sourceDateEpoch, nullptr, 0);
|
||||
now = static_cast<time_t>(strtoul(sourceDateEpoch, nullptr, 0));
|
||||
|
||||
Defer closeDependFile{[&] {
|
||||
if (dependFile)
|
||||
@@ -384,6 +384,10 @@ int main(int argc, char *argv[]) {
|
||||
sect_CheckLoadClosed();
|
||||
sect_CheckSizes();
|
||||
|
||||
charmap_CheckStack();
|
||||
opt_CheckStack();
|
||||
sect_CheckStack();
|
||||
|
||||
if (nbErrors != 0)
|
||||
errx("Assembly aborted (%u error%s)!", nbErrors, nbErrors == 1 ? "" : "s");
|
||||
|
||||
|
||||
@@ -184,3 +184,9 @@ void opt_Pop() {
|
||||
warningsAreErrors = entry.warningsAreErrors;
|
||||
warningStates = entry.warningStates;
|
||||
}
|
||||
|
||||
void opt_CheckStack() {
|
||||
if (!stack.empty()) {
|
||||
warning(WARNING_UNMATCHED_DIRECTIVE, "`PUSHO` without corresponding `POPO`\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,10 +40,10 @@ static std::deque<std::shared_ptr<FileStackNode>> fileStackNodes;
|
||||
|
||||
static void putLong(uint32_t n, FILE *file) {
|
||||
uint8_t bytes[] = {
|
||||
(uint8_t)n,
|
||||
(uint8_t)(n >> 8),
|
||||
(uint8_t)(n >> 16),
|
||||
(uint8_t)(n >> 24),
|
||||
static_cast<uint8_t>(n),
|
||||
static_cast<uint8_t>(n >> 8),
|
||||
static_cast<uint8_t>(n >> 16),
|
||||
static_cast<uint8_t>(n >> 24),
|
||||
};
|
||||
fwrite(bytes, 1, sizeof(bytes), file);
|
||||
}
|
||||
@@ -55,25 +55,25 @@ static void putString(std::string const &s, FILE *file) {
|
||||
|
||||
void out_RegisterNode(std::shared_ptr<FileStackNode> node) {
|
||||
// If node is not already registered, register it (and parents), and give it a unique ID
|
||||
for (; node && node->ID == (uint32_t)-1; node = node->parent) {
|
||||
for (; node && node->ID == UINT32_MAX; node = node->parent) {
|
||||
node->ID = fileStackNodes.size();
|
||||
fileStackNodes.push_front(node);
|
||||
}
|
||||
}
|
||||
|
||||
// Return a section's ID, or -1 if the section is not in the list
|
||||
// Return a section's ID, or UINT32_MAX if the section is not in the list
|
||||
static uint32_t getSectIDIfAny(Section *sect) {
|
||||
if (!sect)
|
||||
return (uint32_t)-1;
|
||||
return UINT32_MAX;
|
||||
|
||||
if (auto search = sectionMap.find(sect->name); search != sectionMap.end())
|
||||
return (uint32_t)(sectionMap.size() - search->second - 1);
|
||||
return static_cast<uint32_t>(sectionMap.size() - search->second - 1);
|
||||
|
||||
fatalerror("Unknown section '%s'\n", sect->name.c_str());
|
||||
}
|
||||
|
||||
static void writePatch(Patch const &patch, FILE *file) {
|
||||
assume(patch.src->ID != (uint32_t)-1);
|
||||
assume(patch.src->ID != UINT32_MAX);
|
||||
|
||||
putLong(patch.src->ID, file);
|
||||
putLong(patch.lineNo, file);
|
||||
@@ -86,7 +86,7 @@ static void writePatch(Patch const &patch, FILE *file) {
|
||||
}
|
||||
|
||||
static void writeSection(Section const §, FILE *file) {
|
||||
assume(sect.src->ID != (uint32_t)-1);
|
||||
assume(sect.src->ID != UINT32_MAX);
|
||||
|
||||
putString(sect.name, file);
|
||||
|
||||
@@ -119,7 +119,7 @@ static void writeSymbol(Symbol const &sym, FILE *file) {
|
||||
if (!sym.isDefined()) {
|
||||
putc(SYMTYPE_IMPORT, file);
|
||||
} else {
|
||||
assume(sym.src->ID != (uint32_t)-1);
|
||||
assume(sym.src->ID != UINT32_MAX);
|
||||
|
||||
putc(sym.isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, file);
|
||||
putLong(sym.src->ID, file);
|
||||
@@ -131,7 +131,7 @@ static void writeSymbol(Symbol const &sym, FILE *file) {
|
||||
|
||||
static void registerUnregisteredSymbol(Symbol &sym) {
|
||||
// Check for `sym.src`, to skip any built-in symbol from rgbasm
|
||||
if (sym.src && sym.ID == (uint32_t)-1 && !sym_IsPC(&sym)) {
|
||||
if (sym.src && sym.ID == UINT32_MAX && !sym_IsPC(&sym)) {
|
||||
sym.ID = objectSymbols.size(); // Set the symbol's ID within the object file
|
||||
objectSymbols.push_back(&sym);
|
||||
out_RegisterNode(sym.src);
|
||||
@@ -288,7 +288,7 @@ static void writeAssert(Assertion &assert, FILE *file) {
|
||||
}
|
||||
|
||||
static void writeFileStackNode(FileStackNode const &node, FILE *file) {
|
||||
putLong(node.parent ? node.parent->ID : (uint32_t)-1, file);
|
||||
putLong(node.parent ? node.parent->ID : UINT32_MAX, file);
|
||||
putLong(node.lineNo, file);
|
||||
putc(node.type, file);
|
||||
if (node.type != NODE_REPT) {
|
||||
|
||||
134
src/asm/parser.y
134
src/asm/parser.y
@@ -140,7 +140,7 @@
|
||||
%left OP_SHL OP_SHR OP_USHR
|
||||
%left OP_MUL OP_DIV OP_MOD
|
||||
%precedence NEG // applies to unary OP_LOGICNOT, OP_ADD, OP_SUB, OP_NOT
|
||||
%left OP_EXP
|
||||
%right OP_EXP
|
||||
|
||||
// Assignment operators (only for variables)
|
||||
%token POP_EQUAL "="
|
||||
@@ -335,7 +335,7 @@
|
||||
%type <Expression> reloc_16bit_no_str
|
||||
|
||||
// Constant numbers
|
||||
%type <int32_t> const
|
||||
%type <int32_t> iconst
|
||||
%type <int32_t> const_no_str
|
||||
%type <int32_t> uconst
|
||||
// Constant numbers used only in specific contexts
|
||||
@@ -404,6 +404,14 @@ asm_file: lines;
|
||||
lines:
|
||||
%empty
|
||||
| lines diff_mark line
|
||||
// Continue parsing the next line on a syntax error
|
||||
| error {
|
||||
lexer_SetMode(LEXER_NORMAL);
|
||||
lexer_ToggleStringExpansion(true);
|
||||
} endofline {
|
||||
fstk_StopRept();
|
||||
yyerrok;
|
||||
}
|
||||
;
|
||||
|
||||
diff_mark:
|
||||
@@ -425,14 +433,6 @@ diff_mark:
|
||||
line:
|
||||
plain_directive endofline
|
||||
| line_directive // Directives that manage newlines themselves
|
||||
// Continue parsing the next line on a syntax error
|
||||
| error {
|
||||
lexer_SetMode(LEXER_NORMAL);
|
||||
lexer_ToggleStringExpansion(true);
|
||||
} endofline {
|
||||
fstk_StopRept();
|
||||
yyerrok;
|
||||
}
|
||||
;
|
||||
|
||||
endofline: NEWLINE | EOB;
|
||||
@@ -454,7 +454,7 @@ line_directive:
|
||||
;
|
||||
|
||||
if:
|
||||
POP_IF const NEWLINE {
|
||||
POP_IF iconst NEWLINE {
|
||||
lexer_IncIFDepth();
|
||||
|
||||
if ($2)
|
||||
@@ -465,7 +465,7 @@ if:
|
||||
;
|
||||
|
||||
elif:
|
||||
POP_ELIF const NEWLINE {
|
||||
POP_ELIF iconst NEWLINE {
|
||||
if (lexer_GetIFDepth() == 0)
|
||||
fatalerror("Found ELIF outside of an IF construct\n");
|
||||
|
||||
@@ -700,14 +700,14 @@ align_spec:
|
||||
$$.alignOfs = 0;
|
||||
}
|
||||
}
|
||||
| uconst COMMA const {
|
||||
| uconst COMMA iconst {
|
||||
if ($1 > 16) {
|
||||
::error("Alignment must be between 0 and 16, not %u\n", $1);
|
||||
$$.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",
|
||||
(uint32_t)($3 < 0 ? -$3 : $3),
|
||||
static_cast<uint32_t>($3 < 0 ? -$3 : $3),
|
||||
1 << $1
|
||||
);
|
||||
$$.alignment = $$.alignOfs = 0;
|
||||
@@ -817,11 +817,11 @@ assert:
|
||||
failAssertMsg($2, $5);
|
||||
}
|
||||
}
|
||||
| POP_STATIC_ASSERT assert_type const {
|
||||
| POP_STATIC_ASSERT assert_type iconst {
|
||||
if ($3 == 0)
|
||||
failAssert($2);
|
||||
}
|
||||
| POP_STATIC_ASSERT assert_type const COMMA string {
|
||||
| POP_STATIC_ASSERT assert_type iconst COMMA string {
|
||||
if ($3 == 0)
|
||||
failAssertMsg($2, $5);
|
||||
}
|
||||
@@ -841,7 +841,7 @@ shift_const:
|
||||
%empty {
|
||||
$$ = 1;
|
||||
}
|
||||
| const
|
||||
| iconst
|
||||
;
|
||||
|
||||
load:
|
||||
@@ -878,17 +878,17 @@ capture_rept:
|
||||
;
|
||||
|
||||
for_args:
|
||||
const {
|
||||
iconst {
|
||||
$$.start = 0;
|
||||
$$.stop = $1;
|
||||
$$.step = 1;
|
||||
}
|
||||
| const COMMA const {
|
||||
| iconst COMMA iconst {
|
||||
$$.start = $1;
|
||||
$$.stop = $3;
|
||||
$$.step = 1;
|
||||
}
|
||||
| const COMMA const COMMA const {
|
||||
| iconst COMMA iconst COMMA iconst {
|
||||
$$.start = $1;
|
||||
$$.stop = $3;
|
||||
$$.step = $5;
|
||||
@@ -1009,33 +1009,33 @@ dl:
|
||||
;
|
||||
|
||||
def_equ:
|
||||
def_id POP_EQU const {
|
||||
def_id POP_EQU iconst {
|
||||
$$ = std::move($1);
|
||||
sym_AddEqu($$, $3);
|
||||
}
|
||||
;
|
||||
|
||||
redef_equ:
|
||||
redef_id POP_EQU const {
|
||||
redef_id POP_EQU iconst {
|
||||
$$ = std::move($1);
|
||||
sym_RedefEqu($$, $3);
|
||||
}
|
||||
;
|
||||
|
||||
def_set:
|
||||
def_id POP_EQUAL const {
|
||||
def_id POP_EQUAL iconst {
|
||||
$$ = std::move($1);
|
||||
sym_AddVar($$, $3);
|
||||
}
|
||||
| redef_id POP_EQUAL const {
|
||||
| redef_id POP_EQUAL iconst {
|
||||
$$ = std::move($1);
|
||||
sym_AddVar($$, $3);
|
||||
}
|
||||
| def_id compound_eq const {
|
||||
| def_id compound_eq iconst {
|
||||
$$ = std::move($1);
|
||||
compoundAssignment($$, $2, $3);
|
||||
}
|
||||
| redef_id compound_eq const {
|
||||
| redef_id compound_eq iconst {
|
||||
$$ = std::move($1);
|
||||
compoundAssignment($$, $2, $3);
|
||||
}
|
||||
@@ -1135,12 +1135,12 @@ incbin:
|
||||
if (failedOnMissingInclude)
|
||||
YYACCEPT;
|
||||
}
|
||||
| POP_INCBIN string COMMA const {
|
||||
| POP_INCBIN string COMMA iconst {
|
||||
sect_BinaryFile($2, $4);
|
||||
if (failedOnMissingInclude)
|
||||
YYACCEPT;
|
||||
}
|
||||
| POP_INCBIN string COMMA const COMMA const {
|
||||
| POP_INCBIN string COMMA iconst COMMA iconst {
|
||||
sect_BinaryFileSlice($2, $4, $6);
|
||||
if (failedOnMissingInclude)
|
||||
YYACCEPT;
|
||||
@@ -1154,10 +1154,10 @@ charmap:
|
||||
;
|
||||
|
||||
charmap_args:
|
||||
const {
|
||||
iconst {
|
||||
$$.push_back(std::move($1));
|
||||
}
|
||||
| charmap_args COMMA const {
|
||||
| charmap_args COMMA iconst {
|
||||
$$ = std::move($1);
|
||||
$$.push_back(std::move($3));
|
||||
}
|
||||
@@ -1226,7 +1226,7 @@ print_expr:
|
||||
;
|
||||
|
||||
bit_const:
|
||||
const {
|
||||
iconst {
|
||||
$$ = $1;
|
||||
if ($$ < 0 || $$ > 7) {
|
||||
::error("Bit number must be between 0 and 7, not %" PRId32 "\n", $$);
|
||||
@@ -1448,49 +1448,49 @@ relocexpr_no_str:
|
||||
$$.makeNumber(sym_FindScopedValidSymbol($4) != nullptr);
|
||||
lexer_ToggleStringExpansion(true);
|
||||
}
|
||||
| OP_ROUND LPAREN const precision_arg RPAREN {
|
||||
| OP_ROUND LPAREN iconst precision_arg RPAREN {
|
||||
$$.makeNumber(fix_Round($3, $4));
|
||||
}
|
||||
| OP_CEIL LPAREN const precision_arg RPAREN {
|
||||
| OP_CEIL LPAREN iconst precision_arg RPAREN {
|
||||
$$.makeNumber(fix_Ceil($3, $4));
|
||||
}
|
||||
| OP_FLOOR LPAREN const precision_arg RPAREN {
|
||||
| OP_FLOOR LPAREN iconst precision_arg RPAREN {
|
||||
$$.makeNumber(fix_Floor($3, $4));
|
||||
}
|
||||
| OP_FDIV LPAREN const COMMA const precision_arg RPAREN {
|
||||
| OP_FDIV LPAREN iconst COMMA iconst precision_arg RPAREN {
|
||||
$$.makeNumber(fix_Div($3, $5, $6));
|
||||
}
|
||||
| OP_FMUL LPAREN const COMMA const precision_arg RPAREN {
|
||||
| OP_FMUL LPAREN iconst COMMA iconst precision_arg RPAREN {
|
||||
$$.makeNumber(fix_Mul($3, $5, $6));
|
||||
}
|
||||
| OP_FMOD LPAREN const COMMA const precision_arg RPAREN {
|
||||
| OP_FMOD LPAREN iconst COMMA iconst precision_arg RPAREN {
|
||||
$$.makeNumber(fix_Mod($3, $5, $6));
|
||||
}
|
||||
| OP_POW LPAREN const COMMA const precision_arg RPAREN {
|
||||
| OP_POW LPAREN iconst COMMA iconst precision_arg RPAREN {
|
||||
$$.makeNumber(fix_Pow($3, $5, $6));
|
||||
}
|
||||
| OP_LOG LPAREN const COMMA const precision_arg RPAREN {
|
||||
| OP_LOG LPAREN iconst COMMA iconst precision_arg RPAREN {
|
||||
$$.makeNumber(fix_Log($3, $5, $6));
|
||||
}
|
||||
| OP_SIN LPAREN const precision_arg RPAREN {
|
||||
| OP_SIN LPAREN iconst precision_arg RPAREN {
|
||||
$$.makeNumber(fix_Sin($3, $4));
|
||||
}
|
||||
| OP_COS LPAREN const precision_arg RPAREN {
|
||||
| OP_COS LPAREN iconst precision_arg RPAREN {
|
||||
$$.makeNumber(fix_Cos($3, $4));
|
||||
}
|
||||
| OP_TAN LPAREN const precision_arg RPAREN {
|
||||
| OP_TAN LPAREN iconst precision_arg RPAREN {
|
||||
$$.makeNumber(fix_Tan($3, $4));
|
||||
}
|
||||
| OP_ASIN LPAREN const precision_arg RPAREN {
|
||||
| OP_ASIN LPAREN iconst precision_arg RPAREN {
|
||||
$$.makeNumber(fix_ASin($3, $4));
|
||||
}
|
||||
| OP_ACOS LPAREN const precision_arg RPAREN {
|
||||
| OP_ACOS LPAREN iconst precision_arg RPAREN {
|
||||
$$.makeNumber(fix_ACos($3, $4));
|
||||
}
|
||||
| OP_ATAN LPAREN const precision_arg RPAREN {
|
||||
| OP_ATAN LPAREN iconst precision_arg RPAREN {
|
||||
$$.makeNumber(fix_ATan($3, $4));
|
||||
}
|
||||
| OP_ATAN2 LPAREN const COMMA const precision_arg RPAREN {
|
||||
| OP_ATAN2 LPAREN iconst COMMA iconst precision_arg RPAREN {
|
||||
$$.makeNumber(fix_ATan2($3, $5, $6));
|
||||
}
|
||||
| OP_STRCMP LPAREN string COMMA string RPAREN {
|
||||
@@ -1521,14 +1521,14 @@ relocexpr_no_str:
|
||||
;
|
||||
|
||||
uconst:
|
||||
const {
|
||||
iconst {
|
||||
$$ = $1;
|
||||
if ($$ < 0)
|
||||
fatalerror("Constant must not be negative: %d\n", $$);
|
||||
}
|
||||
;
|
||||
|
||||
const:
|
||||
iconst:
|
||||
relocexpr {
|
||||
$$ = $1.getConstVal();
|
||||
}
|
||||
@@ -1544,7 +1544,7 @@ precision_arg:
|
||||
%empty {
|
||||
$$ = fix_Precision();
|
||||
}
|
||||
| COMMA const {
|
||||
| COMMA iconst {
|
||||
$$ = $2;
|
||||
if ($$ < 1 || $$ > 31) {
|
||||
::error("Fixed-point precision must be between 1 and 31, not %" PRId32 "\n", $$);
|
||||
@@ -1557,19 +1557,19 @@ string:
|
||||
STRING {
|
||||
$$ = std::move($1);
|
||||
}
|
||||
| OP_STRSUB LPAREN string COMMA const COMMA uconst RPAREN {
|
||||
| OP_STRSUB LPAREN string COMMA iconst COMMA uconst RPAREN {
|
||||
size_t len = strlenUTF8($3);
|
||||
uint32_t pos = adjustNegativePos($5, len, "STRSUB");
|
||||
|
||||
$$ = strsubUTF8($3, pos, $7);
|
||||
}
|
||||
| OP_STRSUB LPAREN string COMMA const RPAREN {
|
||||
| OP_STRSUB LPAREN string COMMA iconst RPAREN {
|
||||
size_t len = strlenUTF8($3);
|
||||
uint32_t pos = adjustNegativePos($5, len, "STRSUB");
|
||||
|
||||
$$ = strsubUTF8($3, pos, pos > len ? 0 : len + 1 - pos);
|
||||
}
|
||||
| OP_CHARSUB LPAREN string COMMA const RPAREN {
|
||||
| OP_CHARSUB LPAREN string COMMA iconst RPAREN {
|
||||
size_t len = charlenUTF8($3);
|
||||
uint32_t pos = adjustNegativePos($5, len, "CHARSUB");
|
||||
|
||||
@@ -1636,7 +1636,7 @@ strfmt_va_args:
|
||||
%empty {}
|
||||
| strfmt_va_args COMMA const_no_str {
|
||||
$$ = std::move($1);
|
||||
$$.args.push_back((uint32_t)$3);
|
||||
$$.args.push_back(static_cast<uint32_t>($3));
|
||||
}
|
||||
| strfmt_va_args COMMA string {
|
||||
$$ = std::move($1);
|
||||
@@ -1702,7 +1702,7 @@ sect_org:
|
||||
}
|
||||
| LBRACK uconst RBRACK {
|
||||
$$ = $2;
|
||||
if ($$ < 0 || $$ >= 0x10000) {
|
||||
if ($$ < 0 || $$ > 0xFFFF) {
|
||||
::error("Address $%x is not 16-bit\n", $$);
|
||||
$$ = -1;
|
||||
}
|
||||
@@ -1752,8 +1752,8 @@ cpu_command:
|
||||
| z80_jr
|
||||
| z80_ld
|
||||
| z80_ldd
|
||||
| z80_ldh
|
||||
| z80_ldi
|
||||
| z80_ldio
|
||||
| z80_nop
|
||||
| z80_or
|
||||
| z80_pop
|
||||
@@ -1947,15 +1947,25 @@ z80_ldd:
|
||||
}
|
||||
;
|
||||
|
||||
z80_ldio:
|
||||
z80_ldh:
|
||||
Z80_LDH MODE_A COMMA op_mem_ind {
|
||||
$4.makeCheckHRAM();
|
||||
if ($4.makeCheckHRAM()) {
|
||||
warning(
|
||||
WARNING_OBSOLETE,
|
||||
"LDH is deprecated with values from $00 to $FF; use $FF00 to $FFFF\n"
|
||||
);
|
||||
}
|
||||
|
||||
sect_ConstByte(0xF0);
|
||||
sect_RelByte($4, 1);
|
||||
}
|
||||
| Z80_LDH op_mem_ind COMMA MODE_A {
|
||||
$2.makeCheckHRAM();
|
||||
if ($2.makeCheckHRAM()) {
|
||||
warning(
|
||||
WARNING_OBSOLETE,
|
||||
"LDH is deprecated with values from $00 to $FF; use $FF00 to $FFFF\n"
|
||||
);
|
||||
}
|
||||
|
||||
sect_ConstByte(0xE0);
|
||||
sect_RelByte($2, 1);
|
||||
@@ -1971,7 +1981,7 @@ z80_ldio:
|
||||
c_ind:
|
||||
LBRACK MODE_C RBRACK
|
||||
| LBRACK relocexpr OP_ADD MODE_C RBRACK {
|
||||
// This has to use `relocexpr`, not `const`, to avoid a shift/reduce conflict
|
||||
// This has to use `relocexpr`, not `iconst`, to avoid a shift/reduce conflict
|
||||
if ($2.getConstVal() != 0xFF00)
|
||||
::error("Base value must be equal to $FF00 for $FF00+C\n");
|
||||
}
|
||||
@@ -2022,6 +2032,7 @@ z80_ld_mem:
|
||||
|
||||
z80_ld_c_ind:
|
||||
Z80_LD c_ind COMMA MODE_A {
|
||||
warning(WARNING_OBSOLETE, "LD [C], A is deprecated; use LDH [C], A\n");
|
||||
sect_ConstByte(0xE2);
|
||||
}
|
||||
;
|
||||
@@ -2054,6 +2065,7 @@ z80_ld_a:
|
||||
sect_ConstByte(0x40 | ($2 << 3) | $4);
|
||||
}
|
||||
| Z80_LD reg_a COMMA c_ind {
|
||||
warning(WARNING_OBSOLETE, "LD A, [C] is deprecated; use LDH A, [C]\n");
|
||||
sect_ConstByte(0xF2);
|
||||
}
|
||||
| Z80_LD reg_a COMMA reg_rr {
|
||||
@@ -2456,7 +2468,7 @@ static uint32_t strToNum(std::vector<int32_t> const &s) {
|
||||
if (length == 1) {
|
||||
// The string is a single character with a single value,
|
||||
// which can be used directly as a number.
|
||||
return (uint32_t)s[0];
|
||||
return static_cast<uint32_t>(s[0]);
|
||||
}
|
||||
|
||||
warning(WARNING_OBSOLETE, "Treating multi-unit strings as numbers is deprecated\n");
|
||||
@@ -2601,7 +2613,7 @@ static uint32_t adjustNegativePos(int32_t pos, size_t len, char const *functionN
|
||||
warning(WARNING_BUILTIN_ARG, "%s: Position starts at 1\n", functionName);
|
||||
pos = 1;
|
||||
}
|
||||
return (uint32_t)pos;
|
||||
return static_cast<uint32_t>(pos);
|
||||
}
|
||||
|
||||
static std::string strrpl(std::string_view str, std::string const &old, std::string const &rep) {
|
||||
|
||||
@@ -48,7 +48,7 @@ int32_t Expression::getConstVal() const {
|
||||
Symbol const *Expression::symbolOf() const {
|
||||
if (!isSymbol)
|
||||
return nullptr;
|
||||
return sym_FindScopedSymbol((char const *)&rpn[1]);
|
||||
return sym_FindScopedSymbol(reinterpret_cast<char const *>(&rpn[1]));
|
||||
}
|
||||
|
||||
bool Expression::isDiffConstant(Symbol const *sym) const {
|
||||
@@ -65,7 +65,7 @@ bool Expression::isDiffConstant(Symbol const *sym) const {
|
||||
|
||||
void Expression::makeNumber(uint32_t value) {
|
||||
clear();
|
||||
data = (int32_t)value;
|
||||
data = static_cast<int32_t>(value);
|
||||
}
|
||||
|
||||
void Expression::makeSymbol(std::string const &symName) {
|
||||
@@ -89,7 +89,7 @@ void Expression::makeSymbol(std::string const &symName) {
|
||||
*ptr++ = RPN_SYM;
|
||||
memcpy(ptr, sym->name.c_str(), nameLen);
|
||||
} else {
|
||||
data = (int32_t)sym_GetConstantValue(symName);
|
||||
data = static_cast<int32_t>(sym_GetConstantValue(symName));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,12 +100,12 @@ void Expression::makeBankSymbol(std::string const &symName) {
|
||||
if (!currentSection) {
|
||||
error("PC has no bank outside of a section\n");
|
||||
data = 1;
|
||||
} else if (currentSection->bank == (uint32_t)-1) {
|
||||
} else if (currentSection->bank == UINT32_MAX) {
|
||||
data = "Current section's bank is not known";
|
||||
|
||||
*reserveSpace(1) = RPN_BANK_SELF;
|
||||
} else {
|
||||
data = (int32_t)currentSection->bank;
|
||||
data = static_cast<int32_t>(currentSection->bank);
|
||||
}
|
||||
return;
|
||||
} else if (sym && !sym->isLabel()) {
|
||||
@@ -115,9 +115,9 @@ void Expression::makeBankSymbol(std::string const &symName) {
|
||||
sym = sym_Ref(symName);
|
||||
assume(sym); // If the symbol didn't exist, it should have been created
|
||||
|
||||
if (sym->getSection() && sym->getSection()->bank != (uint32_t)-1) {
|
||||
if (sym->getSection() && sym->getSection()->bank != UINT32_MAX) {
|
||||
// Symbol's section is known and bank is fixed
|
||||
data = (int32_t)sym->getSection()->bank;
|
||||
data = static_cast<int32_t>(sym->getSection()->bank);
|
||||
} else {
|
||||
data = sym_IsPurgedScoped(symName)
|
||||
? "\""s + symName + "\"'s bank is not known; it was purged"
|
||||
@@ -135,8 +135,8 @@ void Expression::makeBankSymbol(std::string const &symName) {
|
||||
|
||||
void Expression::makeBankSection(std::string const §Name) {
|
||||
clear();
|
||||
if (Section *sect = sect_FindSectionByName(sectName); sect && sect->bank != (uint32_t)-1) {
|
||||
data = (int32_t)sect->bank;
|
||||
if (Section *sect = sect_FindSectionByName(sectName); sect && sect->bank != UINT32_MAX) {
|
||||
data = static_cast<int32_t>(sect->bank);
|
||||
} else {
|
||||
data = "Section \""s + sectName + "\"'s bank is not known";
|
||||
|
||||
@@ -151,7 +151,7 @@ void Expression::makeBankSection(std::string const §Name) {
|
||||
void Expression::makeSizeOfSection(std::string const §Name) {
|
||||
clear();
|
||||
if (Section *sect = sect_FindSectionByName(sectName); sect && sect->isSizeKnown()) {
|
||||
data = (int32_t)sect->size;
|
||||
data = static_cast<int32_t>(sect->size);
|
||||
} else {
|
||||
data = "Section \""s + sectName + "\"'s size is not known";
|
||||
|
||||
@@ -165,8 +165,8 @@ void Expression::makeSizeOfSection(std::string const §Name) {
|
||||
|
||||
void Expression::makeStartOfSection(std::string const §Name) {
|
||||
clear();
|
||||
if (Section *sect = sect_FindSectionByName(sectName); sect && sect->org != (uint32_t)-1) {
|
||||
data = (int32_t)sect->org;
|
||||
if (Section *sect = sect_FindSectionByName(sectName); sect && sect->org != UINT32_MAX) {
|
||||
data = static_cast<int32_t>(sect->org);
|
||||
} else {
|
||||
data = "Section \""s + sectName + "\"'s start is not known";
|
||||
|
||||
@@ -216,9 +216,9 @@ static bool tryConstLogNot(Expression const &expr) {
|
||||
Section const § = *sym->getSection();
|
||||
int32_t unknownBits = (1 << 16) - (1 << sect.align);
|
||||
|
||||
// `sym->getValue()` attempts to add the section's address, but that's "-1"
|
||||
// `sym->getValue()` attempts to add the section's address, but that's `UINT32_MAX`
|
||||
// because the section is floating (otherwise we wouldn't be here)
|
||||
assume(sect.org == (uint32_t)-1);
|
||||
assume(sect.org == UINT32_MAX);
|
||||
int32_t symbolOfs = sym->getValue() + 1;
|
||||
|
||||
int32_t knownBits = (symbolOfs + sect.alignOfs) & ~unknownBits;
|
||||
@@ -243,9 +243,9 @@ static int32_t tryConstLow(Expression const &expr) {
|
||||
if (sect.align < 8)
|
||||
return -1;
|
||||
|
||||
// `sym->getValue()` attempts to add the section's address, but that's "-1"
|
||||
// `sym->getValue()` attempts to add the section's address, but that's `UINT32_MAX`
|
||||
// because the section is floating (otherwise we wouldn't be here)
|
||||
assume(sect.org == (uint32_t)-1);
|
||||
assume(sect.org == UINT32_MAX);
|
||||
int32_t symbolOfs = sym->getValue() + 1;
|
||||
|
||||
return (symbolOfs + sect.alignOfs) & 0xFF;
|
||||
@@ -284,9 +284,9 @@ static int32_t tryConstMask(Expression const &lhs, Expression const &rhs) {
|
||||
if (int32_t unknownBits = (1 << 16) - (1 << sect.align); (unknownBits & mask) != 0)
|
||||
return -1;
|
||||
|
||||
// `sym.getValue()` attempts to add the section's address, but that's "-1"
|
||||
// `sym.getValue()` attempts to add the section's address, but that's `UINT32_MAX`
|
||||
// because the section is floating (otherwise we wouldn't be here)
|
||||
assume(sect.org == (uint32_t)-1);
|
||||
assume(sect.org == UINT32_MAX);
|
||||
int32_t symbolOfs = sym.getValue() + 1;
|
||||
|
||||
return (symbolOfs + sect.alignOfs) & mask;
|
||||
@@ -298,10 +298,11 @@ void Expression::makeUnaryOp(RPNCommand op, Expression &&src) {
|
||||
if (src.isKnown()) {
|
||||
// If the expressions is known, just compute the value
|
||||
int32_t val = src.value();
|
||||
uint32_t uval = static_cast<uint32_t>(val);
|
||||
|
||||
switch (op) {
|
||||
case RPN_NEG:
|
||||
data = (int32_t) - (uint32_t)val;
|
||||
data = static_cast<int32_t>(-uval);
|
||||
break;
|
||||
case RPN_NOT:
|
||||
data = ~val;
|
||||
@@ -310,16 +311,16 @@ void Expression::makeUnaryOp(RPNCommand op, Expression &&src) {
|
||||
data = !val;
|
||||
break;
|
||||
case RPN_HIGH:
|
||||
data = (int32_t)((uint32_t)val >> 8 & 0xFF);
|
||||
data = static_cast<int32_t>(uval >> 8 & 0xFF);
|
||||
break;
|
||||
case RPN_LOW:
|
||||
data = val & 0xFF;
|
||||
break;
|
||||
case RPN_BITWIDTH:
|
||||
data = val != 0 ? 32 - clz((uint32_t)val) : 0;
|
||||
data = val != 0 ? 32 - clz(uval) : 0;
|
||||
break;
|
||||
case RPN_TZCOUNT:
|
||||
data = val != 0 ? ctz((uint32_t)val) : 32;
|
||||
data = val != 0 ? ctz(uval) : 32;
|
||||
break;
|
||||
|
||||
case RPN_LOGOR:
|
||||
@@ -374,6 +375,7 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
||||
if (src1.isKnown() && src2.isKnown()) {
|
||||
// If both expressions are known, just compute the value
|
||||
int32_t lval = src1.value(), rval = src2.value();
|
||||
uint32_t ulval = static_cast<uint32_t>(lval), urval = static_cast<uint32_t>(rval);
|
||||
|
||||
switch (op) {
|
||||
case RPN_LOGOR:
|
||||
@@ -401,10 +403,10 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
||||
data = lval != rval;
|
||||
break;
|
||||
case RPN_ADD:
|
||||
data = (int32_t)((uint32_t)lval + (uint32_t)rval);
|
||||
data = static_cast<int32_t>(ulval + urval);
|
||||
break;
|
||||
case RPN_SUB:
|
||||
data = (int32_t)((uint32_t)lval - (uint32_t)rval);
|
||||
data = static_cast<int32_t>(ulval - urval);
|
||||
break;
|
||||
case RPN_XOR:
|
||||
data = lval ^ rval;
|
||||
@@ -452,7 +454,7 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
||||
data = op_shift_right_unsigned(lval, rval);
|
||||
break;
|
||||
case RPN_MUL:
|
||||
data = (int32_t)((uint32_t)lval * (uint32_t)rval);
|
||||
data = static_cast<int32_t>(ulval * urval);
|
||||
break;
|
||||
case RPN_DIV:
|
||||
if (rval == 0)
|
||||
@@ -522,10 +524,10 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
||||
uint32_t lval = src1.value();
|
||||
uint8_t bytes[] = {
|
||||
RPN_CONST,
|
||||
(uint8_t)lval,
|
||||
(uint8_t)(lval >> 8),
|
||||
(uint8_t)(lval >> 16),
|
||||
(uint8_t)(lval >> 24),
|
||||
static_cast<uint8_t>(lval),
|
||||
static_cast<uint8_t>(lval >> 8),
|
||||
static_cast<uint8_t>(lval >> 16),
|
||||
static_cast<uint8_t>(lval >> 24),
|
||||
};
|
||||
rpn.clear();
|
||||
rpnPatchSize = 0;
|
||||
@@ -546,10 +548,10 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
||||
uint32_t rval = src2.value();
|
||||
uint8_t bytes[] = {
|
||||
RPN_CONST,
|
||||
(uint8_t)rval,
|
||||
(uint8_t)(rval >> 8),
|
||||
(uint8_t)(rval >> 16),
|
||||
(uint8_t)(rval >> 24),
|
||||
static_cast<uint8_t>(rval),
|
||||
static_cast<uint8_t>(rval >> 8),
|
||||
static_cast<uint8_t>(rval >> 16),
|
||||
static_cast<uint8_t>(rval >> 24),
|
||||
};
|
||||
uint8_t *ptr = reserveSpace(sizeof(bytes) + 1, sizeof(bytes) + 1);
|
||||
memcpy(ptr, bytes, sizeof(bytes));
|
||||
@@ -566,16 +568,20 @@ void Expression::makeBinaryOp(RPNCommand op, Expression &&src1, Expression const
|
||||
}
|
||||
}
|
||||
|
||||
void Expression::makeCheckHRAM() {
|
||||
bool Expression::makeCheckHRAM() {
|
||||
isSymbol = false;
|
||||
if (!isKnown()) {
|
||||
*reserveSpace(1) = RPN_HRAM;
|
||||
} else if (int32_t val = value(); val >= 0xFF00 && val <= 0xFFFF) {
|
||||
// That range is valid, but only keep the lower byte
|
||||
data = val & 0xFF;
|
||||
} else if (val < 0 || val > 0xFF) {
|
||||
} else if (val >= 0 && val <= 0xFF) {
|
||||
// That range is valid, but deprecated
|
||||
return true;
|
||||
} else {
|
||||
error("Source address $%" PRIx32 " not between $FF00 to $FFFF\n", val);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Expression::makeCheckRST() {
|
||||
@@ -584,9 +590,6 @@ void Expression::makeCheckRST() {
|
||||
} else if (int32_t val = value(); val & ~0x38) {
|
||||
// A valid RST address must be masked with 0x38
|
||||
error("Invalid address $%" PRIx32 " for RST\n", val);
|
||||
} else {
|
||||
// The target is in the "0x38" bits, all other bits are set
|
||||
data = val | 0xC7;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -109,9 +109,9 @@ static unsigned int mergeSectUnion(
|
||||
if (sect_HasData(type))
|
||||
sectError("Cannot declare ROM sections as UNION\n");
|
||||
|
||||
if (org != (uint32_t)-1) {
|
||||
if (org != UINT32_MAX) {
|
||||
// If both are fixed, they must be the same
|
||||
if (sect.org != (uint32_t)-1 && sect.org != org)
|
||||
if (sect.org != UINT32_MAX && sect.org != org)
|
||||
sectError(
|
||||
"Section already declared as fixed at different address $%04" PRIx32 "\n", sect.org
|
||||
);
|
||||
@@ -127,7 +127,7 @@ static unsigned int mergeSectUnion(
|
||||
|
||||
} else if (alignment != 0) {
|
||||
// Make sure any fixed address given is compatible
|
||||
if (sect.org != (uint32_t)-1) {
|
||||
if (sect.org != UINT32_MAX) {
|
||||
if ((sect.org - alignOffset) & mask(alignment))
|
||||
sectError(
|
||||
"Section already declared as fixed at incompatible address $%04" PRIx32 "\n",
|
||||
@@ -159,11 +159,11 @@ static unsigned int
|
||||
// Fragments only need "compatible" constraints, and they end up with the strictest
|
||||
// combination of both.
|
||||
// The merging is however performed at the *end* of the original section!
|
||||
if (org != (uint32_t)-1) {
|
||||
if (org != UINT32_MAX) {
|
||||
uint16_t curOrg = org - sect.size;
|
||||
|
||||
// If both are fixed, they must be the same
|
||||
if (sect.org != (uint32_t)-1 && sect.org != curOrg)
|
||||
if (sect.org != UINT32_MAX && sect.org != curOrg)
|
||||
sectError(
|
||||
"Section already declared as fixed at incompatible address $%04" PRIx32 "\n",
|
||||
sect.org
|
||||
@@ -185,7 +185,7 @@ static unsigned int
|
||||
curOfs += 1U << alignment;
|
||||
|
||||
// Make sure any fixed address given is compatible
|
||||
if (sect.org != (uint32_t)-1) {
|
||||
if (sect.org != UINT32_MAX) {
|
||||
if ((sect.org - curOfs) & mask(alignment))
|
||||
sectError(
|
||||
"Section already declared as fixed at incompatible address $%04" PRIx32 "\n",
|
||||
@@ -238,10 +238,10 @@ static void mergeSections(
|
||||
// Common checks
|
||||
|
||||
// If the section's bank is unspecified, override it
|
||||
if (sect.bank == (uint32_t)-1)
|
||||
if (sect.bank == UINT32_MAX)
|
||||
sect.bank = bank;
|
||||
// If both specify a bank, it must be the same one
|
||||
else if (bank != (uint32_t)-1 && sect.bank != bank)
|
||||
else if (bank != UINT32_MAX && sect.bank != bank)
|
||||
sectError("Section already declared with different bank %" PRIu32 "\n", sect.bank);
|
||||
break;
|
||||
|
||||
@@ -312,7 +312,7 @@ static Section *getSection(
|
||||
|
||||
// First, validate parameters, and normalize them if applicable
|
||||
|
||||
if (bank != (uint32_t)-1) {
|
||||
if (bank != UINT32_MAX) {
|
||||
if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM && type != SECTTYPE_SRAM
|
||||
&& type != SECTTYPE_WRAMX)
|
||||
error("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections\n");
|
||||
@@ -338,7 +338,7 @@ static Section *getSection(
|
||||
alignOffset = 0;
|
||||
}
|
||||
|
||||
if (org != (uint32_t)-1) {
|
||||
if (org != UINT32_MAX) {
|
||||
if (org < sectionTypeInfo[type].startAddr || org > endaddr(type))
|
||||
error(
|
||||
"Section \"%s\"'s fixed address $%04" PRIx32 " is outside of range [$%04" PRIx16
|
||||
@@ -358,7 +358,7 @@ static Section *getSection(
|
||||
// It doesn't make sense to have both alignment and org set
|
||||
uint32_t mask = mask(alignment);
|
||||
|
||||
if (org != (uint32_t)-1) {
|
||||
if (org != UINT32_MAX) {
|
||||
if ((org - alignOffset) & mask)
|
||||
error("Section \"%s\"'s fixed address doesn't match its alignment\n", name.c_str());
|
||||
alignment = 0; // Ignore it if it's satisfied
|
||||
@@ -462,11 +462,6 @@ void sect_SetLoadSection(
|
||||
return;
|
||||
}
|
||||
|
||||
if (mod == SECTION_FRAGMENT) {
|
||||
error("`LOAD FRAGMENT` is not allowed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentLoadSection)
|
||||
sect_EndLoadSection("LOAD");
|
||||
|
||||
@@ -523,7 +518,7 @@ uint32_t sect_GetAlignBytes(uint8_t alignment, uint16_t offset) {
|
||||
if (!sect)
|
||||
return 0;
|
||||
|
||||
bool isFixed = sect->org != (uint32_t)-1;
|
||||
bool isFixed = sect->org != UINT32_MAX;
|
||||
|
||||
// If the section is not aligned, no bytes are needed
|
||||
// (fixed sections count as being maximally aligned for this purpose)
|
||||
@@ -544,7 +539,7 @@ void sect_AlignPC(uint8_t alignment, uint16_t offset) {
|
||||
Section *sect = sect_GetSymbolSection();
|
||||
uint32_t alignSize = 1 << alignment; // Size of an aligned "block"
|
||||
|
||||
if (sect->org != (uint32_t)-1) {
|
||||
if (sect->org != UINT32_MAX) {
|
||||
if ((sect->org + curOffset - offset) % alignSize)
|
||||
error(
|
||||
"Section's fixed address fails required alignment (PC = $%04" PRIx32 ")\n",
|
||||
@@ -978,6 +973,12 @@ void sect_PopSection() {
|
||||
std::swap(currentUnionStack, entry.unionStack);
|
||||
}
|
||||
|
||||
void sect_CheckStack() {
|
||||
if (!sectionStack.empty()) {
|
||||
warning(WARNING_UNMATCHED_DIRECTIVE, "`PUSHS` without corresponding `POPS`\n");
|
||||
}
|
||||
}
|
||||
|
||||
void sect_EndSection() {
|
||||
if (!currentSection)
|
||||
fatalerror("Cannot end the section outside of a SECTION\n");
|
||||
|
||||
@@ -123,7 +123,7 @@ static void updateSymbolFilename(Symbol &sym) {
|
||||
sym.fileLine = sym.src ? lexer_GetLineNo() : 0;
|
||||
|
||||
// If the old node was registered, ensure the new one is too
|
||||
if (oldSrc && oldSrc->ID != (uint32_t)-1)
|
||||
if (oldSrc && oldSrc->ID != UINT32_MAX)
|
||||
out_RegisterNode(sym.src);
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ static Symbol &createSymbol(std::string const &symName) {
|
||||
sym.section = nullptr;
|
||||
sym.src = fstk_GetFileStack();
|
||||
sym.fileLine = sym.src ? lexer_GetLineNo() : 0;
|
||||
sym.ID = -1;
|
||||
sym.ID = UINT32_MAX;
|
||||
sym.defIndex = nextDefIndex++;
|
||||
|
||||
return sym;
|
||||
@@ -258,7 +258,7 @@ void sym_Purge(std::string const &symName) {
|
||||
error("'%s' not defined\n", symName.c_str());
|
||||
} else if (sym->isBuiltin) {
|
||||
error("Built-in symbol '%s' cannot be purged\n", symName.c_str());
|
||||
} else if (sym->ID != (uint32_t)-1) {
|
||||
} else if (sym->ID != UINT32_MAX) {
|
||||
error("Symbol \"%s\" is referenced and thus cannot be purged\n", symName.c_str());
|
||||
} else {
|
||||
if (sym->isExported)
|
||||
@@ -460,7 +460,7 @@ static Symbol *addLabel(std::string const &symName) {
|
||||
}
|
||||
// If the symbol already exists as a ref, just "take over" it
|
||||
sym->type = SYM_LABEL;
|
||||
sym->data = (int32_t)sect_GetSymbolOffset();
|
||||
sym->data = static_cast<int32_t>(sect_GetSymbolOffset());
|
||||
// Don't export anonymous labels
|
||||
if (exportAll && !symName.starts_with('!'))
|
||||
sym->isExported = true;
|
||||
@@ -627,7 +627,7 @@ void sym_Init(time_t now) {
|
||||
sym_AddEqu("__RGBDS_RC__"s, PACKAGE_VERSION_RC)->isBuiltin = true;
|
||||
#endif
|
||||
|
||||
if (now == (time_t)-1) {
|
||||
if (now == static_cast<time_t>(-1)) {
|
||||
warn("Failed to determine current time");
|
||||
// Fall back by pretending we are at the Epoch
|
||||
now = 0;
|
||||
|
||||
@@ -56,6 +56,7 @@ static const WarningFlag warningFlags[NB_WARNINGS] = {
|
||||
{"obsolete", LEVEL_DEFAULT },
|
||||
{"shift", LEVEL_EVERYTHING},
|
||||
{"shift-amount", LEVEL_EVERYTHING},
|
||||
{"unmatched-directive", LEVEL_EXTRA },
|
||||
{"unterminated-load", LEVEL_EXTRA },
|
||||
{"user", LEVEL_DEFAULT },
|
||||
// Parametric warnings
|
||||
|
||||
@@ -227,7 +227,7 @@ static MbcType parseMBC(char const *name) {
|
||||
return MBC_BAD;
|
||||
if (mbc > 0xFF)
|
||||
return MBC_BAD_RANGE;
|
||||
return (MbcType)mbc;
|
||||
return static_cast<MbcType>(mbc);
|
||||
|
||||
} else {
|
||||
// Begin by reading the MBC type:
|
||||
@@ -568,7 +568,7 @@ static MbcType parseMBC(char const *name) {
|
||||
if (*ptr)
|
||||
return MBC_BAD;
|
||||
|
||||
return (MbcType)mbc;
|
||||
return static_cast<MbcType>(mbc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -882,9 +882,9 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
||||
report(
|
||||
"FATAL: \"%s\" too short, expected at least %jd ($%jx) bytes, got only %jd\n",
|
||||
name,
|
||||
(intmax_t)headerSize,
|
||||
(intmax_t)headerSize,
|
||||
(intmax_t)rom0Len
|
||||
static_cast<intmax_t>(headerSize),
|
||||
static_cast<intmax_t>(headerSize),
|
||||
static_cast<intmax_t>(rom0Len)
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -894,17 +894,27 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
||||
overwriteBytes(rom0, 0x0104, logo, sizeof(logo), logoFilename ? "logo" : "Nintendo logo");
|
||||
|
||||
if (title)
|
||||
overwriteBytes(rom0, 0x134, (uint8_t const *)title, titleLen, "title");
|
||||
overwriteBytes(rom0, 0x134, reinterpret_cast<uint8_t const *>(title), titleLen, "title");
|
||||
|
||||
if (gameID)
|
||||
overwriteBytes(rom0, 0x13F, (uint8_t const *)gameID, gameIDLen, "manufacturer code");
|
||||
overwriteBytes(
|
||||
rom0,
|
||||
0x13F,
|
||||
reinterpret_cast<uint8_t const *>(gameID),
|
||||
gameIDLen,
|
||||
"manufacturer code"
|
||||
);
|
||||
|
||||
if (model != DMG)
|
||||
overwriteByte(rom0, 0x143, model == BOTH ? 0x80 : 0xC0, "CGB flag");
|
||||
|
||||
if (newLicensee)
|
||||
overwriteBytes(
|
||||
rom0, 0x144, (uint8_t const *)newLicensee, newLicenseeLen, "new licensee code"
|
||||
rom0,
|
||||
0x144,
|
||||
reinterpret_cast<uint8_t const *>(newLicensee),
|
||||
newLicenseeLen,
|
||||
"new licensee code"
|
||||
);
|
||||
|
||||
if (sgb)
|
||||
@@ -1076,7 +1086,10 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
||||
if (fixSpec & TRASH_GLOBAL_SUM)
|
||||
globalSum = ~globalSum;
|
||||
|
||||
uint8_t bytes[2] = {(uint8_t)(globalSum >> 8), (uint8_t)(globalSum & 0xFF)};
|
||||
uint8_t bytes[2] = {
|
||||
static_cast<uint8_t>(globalSum >> 8),
|
||||
static_cast<uint8_t>(globalSum & 0xFF)
|
||||
};
|
||||
|
||||
overwriteBytes(rom0, 0x14E, bytes, sizeof(bytes), "global checksum");
|
||||
}
|
||||
@@ -1086,7 +1099,7 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
||||
// In case the output depends on the input, reset to the beginning of the file, and only
|
||||
// write the header
|
||||
if (input == output) {
|
||||
if (lseek(output, 0, SEEK_SET) == (off_t)-1) {
|
||||
if (lseek(output, 0, SEEK_SET) == static_cast<off_t>(-1)) {
|
||||
report("FATAL: Failed to rewind \"%s\": %s\n", name, strerror(errno));
|
||||
return;
|
||||
}
|
||||
@@ -1103,9 +1116,9 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
||||
} else if (writeLen < rom0Len) {
|
||||
report(
|
||||
"FATAL: Could only write %jd of \"%s\"'s %jd ROM0 bytes\n",
|
||||
(intmax_t)writeLen,
|
||||
static_cast<intmax_t>(writeLen),
|
||||
name,
|
||||
(intmax_t)rom0Len
|
||||
static_cast<intmax_t>(rom0Len)
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -1118,10 +1131,10 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
||||
if (writeLen == -1) {
|
||||
report("FATAL: Failed to write \"%s\"'s ROMX: %s\n", name, strerror(errno));
|
||||
return;
|
||||
} else if ((size_t)writeLen < totalRomxLen) {
|
||||
} else if (static_cast<size_t>(writeLen) < totalRomxLen) {
|
||||
report(
|
||||
"FATAL: Could only write %jd of \"%s\"'s %zu ROMX bytes\n",
|
||||
(intmax_t)writeLen,
|
||||
static_cast<intmax_t>(writeLen),
|
||||
name,
|
||||
totalRomxLen
|
||||
);
|
||||
@@ -1132,7 +1145,7 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
||||
// Output padding
|
||||
if (padValue != UNSPECIFIED) {
|
||||
if (input == output) {
|
||||
if (lseek(output, 0, SEEK_END) == (off_t)-1) {
|
||||
if (lseek(output, 0, SEEK_END) == static_cast<off_t>(-1)) {
|
||||
report("FATAL: Failed to seek to end of \"%s\": %s\n", name, strerror(errno));
|
||||
return;
|
||||
}
|
||||
@@ -1147,7 +1160,7 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
|
||||
|
||||
// The return value is either -1, or at most `thisLen`,
|
||||
// so it's fine to cast to `size_t`
|
||||
if ((size_t)ret != thisLen) {
|
||||
if (static_cast<size_t>(ret) != thisLen) {
|
||||
report("FATAL: Failed to write \"%s\"'s padding: %s\n", name, strerror(errno));
|
||||
break;
|
||||
}
|
||||
@@ -1188,7 +1201,7 @@ static bool processFilename(char const *name) {
|
||||
report(
|
||||
"FATAL: \"%s\" too short, expected at least 336 ($150) bytes, got only %jd\n",
|
||||
name,
|
||||
(intmax_t)stat.st_size
|
||||
static_cast<intmax_t>(stat.st_size)
|
||||
);
|
||||
} else {
|
||||
processFile(input, input, name, stat.st_size);
|
||||
|
||||
@@ -108,7 +108,7 @@ void Options::verbosePrint(uint8_t level, char const *fmt, ...) const {
|
||||
}
|
||||
|
||||
// Short options
|
||||
static char const *optstring = "-Aa:b:Cc:Dd:Ffhi:L:mN:n:Oo:Pp:Qq:r:s:Tt:U:uVvx:Z";
|
||||
static char const *optstring = "-Aa:b:Cc:d:i:L:mN:n:Oo:Pp:Qq:r:s:Tt:U:uVvXx:YZ";
|
||||
|
||||
/*
|
||||
* Equivalent long options
|
||||
@@ -139,6 +139,7 @@ static option const longopts[] = {
|
||||
{"auto-palette-map", no_argument, nullptr, 'Q'},
|
||||
{"palette-map", required_argument, nullptr, 'q'},
|
||||
{"reverse", required_argument, nullptr, 'r'},
|
||||
{"palette-size", required_argument, nullptr, 's'},
|
||||
{"auto-tilemap", no_argument, nullptr, 'T'},
|
||||
{"tilemap", required_argument, nullptr, 't'},
|
||||
{"unit-size", required_argument, nullptr, 'U'},
|
||||
|
||||
@@ -5,10 +5,13 @@
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <inttypes.h>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
#include <stdint.h>
|
||||
#include <type_traits>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
@@ -199,21 +202,44 @@ public:
|
||||
return colors.size() <= options.maxOpaqueColors();
|
||||
}
|
||||
|
||||
// The `relSizeOf` method below should compute the sum, for each color in `protoPal`, of
|
||||
// the reciprocal of the "multiplicity" of the color across "our" proto-palettes.
|
||||
// However, literally computing the reciprocals would involve floating-point division, which
|
||||
// leads to imprecision and even platform-specific differences.
|
||||
// We avoid this by multiplying the reciprocals by a factor such that division always produces
|
||||
// an integer; the LCM of all values the denominator can take is the smallest suitable factor.
|
||||
static constexpr uint32_t scaleFactor = [] {
|
||||
// Fold over 1..=17 with the associative LCM function
|
||||
// (17 is the largest the denominator in `relSizeOf` below can be)
|
||||
uint32_t factor = 1;
|
||||
for (uint32_t n = 2; n <= 17; ++n) {
|
||||
factor = std::lcm(factor, n);
|
||||
}
|
||||
return factor;
|
||||
}();
|
||||
|
||||
/*
|
||||
* Computes the "relative size" of a proto-palette on this palette
|
||||
* Computes the "relative size" of a proto-palette on this palette;
|
||||
* it's a measure of how much this proto-palette would "cost" to introduce.
|
||||
*/
|
||||
double relSizeOf(ProtoPalette const &protoPal) const {
|
||||
uint32_t relSizeOf(ProtoPalette const &protoPal) const {
|
||||
// NOTE: this function must not call `uniqueColors`, or one of its callers will break!
|
||||
double relSize = 0.;
|
||||
|
||||
uint32_t relSize = 0;
|
||||
for (uint16_t color : protoPal) {
|
||||
auto n = std::count_if(RANGE(*this), [this, &color](ProtoPalAttrs const &attrs) {
|
||||
auto multiplicity = // How many of our proto-palettes does this color also belong to?
|
||||
std::count_if(RANGE(*this), [this, &color](ProtoPalAttrs const &attrs) {
|
||||
ProtoPalette const &pal = (*_protoPals)[attrs.protoPalIndex];
|
||||
return std::find(RANGE(pal), color) != pal.end();
|
||||
});
|
||||
// NOTE: The paper and the associated code disagree on this: the code has
|
||||
// this `1 +`, whereas the paper does not; its lack causes a division by 0
|
||||
// if the symbol is not found anywhere, so I'm assuming the paper is wrong.
|
||||
relSize += 1. / (1 + n);
|
||||
// We increase the denominator by 1 here; the reference code does this,
|
||||
// but the paper does not. Not adding 1 makes a multiplicity of 0 cause a division by 0
|
||||
// (that is, if the color is not found in any proto-palette), and adding 1 still seems
|
||||
// to preserve the paper's reasoning.
|
||||
//
|
||||
// The scale factor should ensure integer divisions only.
|
||||
assume(scaleFactor % (multiplicity + 1) == 0);
|
||||
relSize += scaleFactor / (multiplicity + 1);
|
||||
}
|
||||
return relSize;
|
||||
}
|
||||
@@ -375,13 +401,15 @@ std::tuple<DefaultInitVec<size_t>, size_t>
|
||||
|
||||
for (; !queue.empty(); queue.pop()) {
|
||||
ProtoPalAttrs const &attrs = queue.front(); // Valid until the `queue.pop()`
|
||||
options.verbosePrint(Options::VERB_DEBUG, "Handling proto-pal %zu\n", attrs.protoPalIndex);
|
||||
options.verbosePrint(
|
||||
Options::VERB_TRACE, "Handling proto-palette %zu\n", attrs.protoPalIndex
|
||||
);
|
||||
|
||||
ProtoPalette const &protoPal = protoPalettes[attrs.protoPalIndex];
|
||||
size_t bestPalIndex = assignments.size();
|
||||
// We're looking for a palette where the proto-palette's relative size is less than
|
||||
// its actual size; so only overwrite the "not found" index on meeting that criterion
|
||||
double bestRelSize = protoPal.size();
|
||||
uint32_t bestRelSize = protoPal.size() * AssignedProtos::scaleFactor;
|
||||
|
||||
for (size_t i = 0; i < assignments.size(); ++i) {
|
||||
// Skip the page if this one is banned from it
|
||||
@@ -389,11 +417,11 @@ std::tuple<DefaultInitVec<size_t>, size_t>
|
||||
continue;
|
||||
}
|
||||
|
||||
double relSize = assignments[i].relSizeOf(protoPal);
|
||||
uint32_t relSize = assignments[i].relSizeOf(protoPal);
|
||||
options.verbosePrint(
|
||||
Options::VERB_DEBUG,
|
||||
"%zu/%zu: Rel size: %f (size = %zu)\n",
|
||||
i + 1,
|
||||
Options::VERB_TRACE,
|
||||
" Relative size to palette %zu (of %zu): %" PRIu32 " (size = %zu)\n",
|
||||
i,
|
||||
assignments.size(),
|
||||
relSize,
|
||||
protoPal.size()
|
||||
@@ -406,8 +434,20 @@ std::tuple<DefaultInitVec<size_t>, size_t>
|
||||
|
||||
if (bestPalIndex == assignments.size()) {
|
||||
// Found nowhere to put it, create a new page containing just that one
|
||||
options.verbosePrint(
|
||||
Options::VERB_TRACE,
|
||||
"Assigning proto-palette %zu to new palette %zu\n",
|
||||
attrs.protoPalIndex,
|
||||
bestPalIndex
|
||||
);
|
||||
assignments.emplace_back(protoPalettes, std::move(attrs));
|
||||
} else {
|
||||
options.verbosePrint(
|
||||
Options::VERB_TRACE,
|
||||
"Assigning proto-palette %zu to palette %zu\n",
|
||||
attrs.protoPalIndex,
|
||||
bestPalIndex
|
||||
);
|
||||
auto &bestPal = assignments[bestPalIndex];
|
||||
// Add the color to that palette
|
||||
bestPal.assign(std::move(attrs));
|
||||
@@ -415,7 +455,7 @@ std::tuple<DefaultInitVec<size_t>, size_t>
|
||||
// If this overloads the palette, get it back to normal (if possible)
|
||||
while (bestPal.volume() > options.maxOpaqueColors()) {
|
||||
options.verbosePrint(
|
||||
Options::VERB_DEBUG,
|
||||
Options::VERB_TRACE,
|
||||
"Palette %zu is overloaded! (%zu > %" PRIu8 ")\n",
|
||||
bestPalIndex,
|
||||
bestPal.volume(),
|
||||
@@ -423,28 +463,67 @@ std::tuple<DefaultInitVec<size_t>, size_t>
|
||||
);
|
||||
|
||||
// Look for a proto-pal minimizing "efficiency" (size / rel_size)
|
||||
auto efficiency = [&bestPal](ProtoPalette const &pal) {
|
||||
return pal.size() / bestPal.relSizeOf(pal);
|
||||
};
|
||||
auto [minEfficiencyIter, maxEfficiencyIter] = std::minmax_element(
|
||||
RANGE(bestPal),
|
||||
[&efficiency,
|
||||
&protoPalettes](ProtoPalAttrs const &lhs, ProtoPalAttrs const &rhs) {
|
||||
return efficiency(protoPalettes[lhs.protoPalIndex])
|
||||
< efficiency(protoPalettes[rhs.protoPalIndex]);
|
||||
[&bestPal, &protoPalettes](ProtoPalAttrs const &lhs, ProtoPalAttrs const &rhs) {
|
||||
ProtoPalette const &lhsProtoPal = protoPalettes[lhs.protoPalIndex];
|
||||
ProtoPalette const &rhsProtoPal = protoPalettes[rhs.protoPalIndex];
|
||||
size_t lhsSize = lhsProtoPal.size();
|
||||
size_t rhsSize = rhsProtoPal.size();
|
||||
uint32_t lhsRelSize = bestPal.relSizeOf(lhsProtoPal);
|
||||
uint32_t rhsRelSize = bestPal.relSizeOf(rhsProtoPal);
|
||||
|
||||
options.verbosePrint(
|
||||
Options::VERB_TRACE,
|
||||
" Proto-palettes %zu <=> %zu: Efficiency: %zu / %" PRIu32 " <=> %zu / "
|
||||
"%" PRIu32 "\n",
|
||||
lhs.protoPalIndex,
|
||||
rhs.protoPalIndex,
|
||||
lhsSize,
|
||||
lhsRelSize,
|
||||
rhsSize,
|
||||
rhsRelSize
|
||||
);
|
||||
// This comparison is algebraically equivalent to
|
||||
// `lhsSize / lhsRelSize < rhsSize / rhsRelSize`,
|
||||
// but without potential precision loss from floating-point division.
|
||||
return lhsSize * rhsRelSize < rhsSize * lhsRelSize;
|
||||
}
|
||||
);
|
||||
|
||||
// All efficiencies are identical iff min equals max
|
||||
// TODO: maybe not ideal to re-compute these two?
|
||||
// TODO: yikes for float comparison! I *think* this threshold is OK?
|
||||
if (efficiency(protoPalettes[maxEfficiencyIter->protoPalIndex])
|
||||
- efficiency(protoPalettes[minEfficiencyIter->protoPalIndex])
|
||||
< .001) {
|
||||
ProtoPalette const &minProtoPal = protoPalettes[minEfficiencyIter->protoPalIndex];
|
||||
ProtoPalette const &maxProtoPal = protoPalettes[maxEfficiencyIter->protoPalIndex];
|
||||
size_t minSize = minProtoPal.size();
|
||||
size_t maxSize = maxProtoPal.size();
|
||||
uint32_t minRelSize = bestPal.relSizeOf(minProtoPal);
|
||||
uint32_t maxRelSize = bestPal.relSizeOf(maxProtoPal);
|
||||
options.verbosePrint(
|
||||
Options::VERB_TRACE,
|
||||
" Proto-palettes %zu <= %zu: Efficiency: %zu / %" PRIu32 " <= %zu / %" PRIu32
|
||||
"\n",
|
||||
minEfficiencyIter->protoPalIndex,
|
||||
maxEfficiencyIter->protoPalIndex,
|
||||
minSize,
|
||||
minRelSize,
|
||||
maxSize,
|
||||
maxRelSize
|
||||
);
|
||||
// This comparison is algebraically equivalent to
|
||||
// `maxSize / maxRelSize == minSize / minRelSize`,
|
||||
// but without potential precision loss from floating-point division.
|
||||
if (maxSize * minRelSize == minSize * maxRelSize) {
|
||||
options.verbosePrint(Options::VERB_TRACE, " All efficiencies are identical\n");
|
||||
break;
|
||||
}
|
||||
|
||||
// Remove the proto-pal with minimal efficiency
|
||||
options.verbosePrint(
|
||||
Options::VERB_TRACE,
|
||||
" Removing proto-palette %zu\n",
|
||||
minEfficiencyIter->protoPalIndex
|
||||
);
|
||||
queue.emplace(std::move(*minEfficiencyIter));
|
||||
queue.back().banFrom(bestPalIndex); // Ban it from this palette
|
||||
bestPal.remove(minEfficiencyIter);
|
||||
@@ -479,7 +558,7 @@ std::tuple<DefaultInitVec<size_t>, size_t>
|
||||
if (iter == assignments.end()) { // No such page, create a new one
|
||||
options.verbosePrint(
|
||||
Options::VERB_DEBUG,
|
||||
"Adding new palette (%zu) for overflowing proto-pal %zu\n",
|
||||
"Adding new palette (%zu) for overflowing proto-palette %zu\n",
|
||||
assignments.size(),
|
||||
attrs.protoPalIndex
|
||||
);
|
||||
@@ -487,7 +566,7 @@ std::tuple<DefaultInitVec<size_t>, size_t>
|
||||
} else {
|
||||
options.verbosePrint(
|
||||
Options::VERB_DEBUG,
|
||||
"Assigning overflowing proto-pal %zu to palette %zu\n",
|
||||
"Assigning overflowing proto-palette %zu to palette %zu\n",
|
||||
attrs.protoPalIndex,
|
||||
iter - assignments.begin()
|
||||
);
|
||||
|
||||
@@ -48,7 +48,7 @@ void sortIndexed(
|
||||
void sortGrayscale(
|
||||
std::vector<Palette> &palettes, std::array<std::optional<Rgba>, 0x8001> const &colors
|
||||
) {
|
||||
options.verbosePrint(Options::VERB_LOG_ACT, "Sorting grayscale-only palette...\n");
|
||||
options.verbosePrint(Options::VERB_LOG_ACT, "Sorting palette by grayscale bins...\n");
|
||||
|
||||
// This method is only applicable if there are at most as many colors as colors per palette, so
|
||||
// we should only have a single palette.
|
||||
@@ -56,7 +56,7 @@ void sortGrayscale(
|
||||
|
||||
Palette &palette = palettes[0];
|
||||
std::fill(RANGE(palette.colors), Rgba::transparent);
|
||||
for (auto const &slot : colors) {
|
||||
for (std::optional<Rgba> const &slot : colors) {
|
||||
if (!slot.has_value() || slot->isTransparent()) {
|
||||
continue;
|
||||
}
|
||||
@@ -64,7 +64,7 @@ void sortGrayscale(
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int legacyLuminance(uint16_t color) {
|
||||
static unsigned int luminance(uint16_t color) {
|
||||
uint8_t red = color & 0b11111;
|
||||
uint8_t green = color >> 5 & 0b11111;
|
||||
uint8_t blue = color >> 10;
|
||||
@@ -72,11 +72,11 @@ static unsigned int legacyLuminance(uint16_t color) {
|
||||
}
|
||||
|
||||
void sortRgb(std::vector<Palette> &palettes) {
|
||||
options.verbosePrint(Options::VERB_LOG_ACT, "Sorting palettes by \"\"\"luminance\"\"\"...\n");
|
||||
options.verbosePrint(Options::VERB_LOG_ACT, "Sorting palettes by luminance...\n");
|
||||
|
||||
for (Palette &pal : palettes) {
|
||||
std::sort(RANGE(pal), [](uint16_t lhs, uint16_t rhs) {
|
||||
return legacyLuminance(lhs) > legacyLuminance(rhs);
|
||||
return luminance(lhs) > luminance(rhs);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ class Png {
|
||||
"bytes after reading %zu)",
|
||||
self->c_str(),
|
||||
length - nbBytesRead,
|
||||
(size_t)self->file->pubseekoff(0, std::ios_base::cur)
|
||||
static_cast<size_t>(self->file->pubseekoff(0, std::ios_base::cur))
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -193,7 +193,7 @@ public:
|
||||
options.verbosePrint(Options::VERB_INTERM, "PNG header signature is OK\n");
|
||||
|
||||
png = png_create_read_struct(
|
||||
PNG_LIBPNG_VER_STRING, (png_voidp)this, handleError, handleWarning
|
||||
PNG_LIBPNG_VER_STRING, static_cast<png_voidp>(this), handleError, handleWarning
|
||||
);
|
||||
if (!png) {
|
||||
fatal("Failed to allocate PNG structure: %s", strerror(errno));
|
||||
@@ -712,6 +712,9 @@ static void hashBitplanes(uint16_t bitplanes, uint16_t &hash) {
|
||||
}
|
||||
|
||||
class TileData {
|
||||
// Importantly, `TileData` is **always** 2bpp.
|
||||
// If the active bit depth is 1bpp, all tiles are processed as 2bpp nonetheless, but emitted as 1bpp.
|
||||
// This massively simplifies internal processing, since bit depth is always identical outside of I/O / serialization boundaries.
|
||||
std::array<uint8_t, 16> _data;
|
||||
// The hash is a bit lax: it's the XOR of all lines, and every other nibble is identical
|
||||
// if horizontal mirroring is in effect. It should still be a reasonable tie-breaker in
|
||||
@@ -755,11 +758,9 @@ public:
|
||||
hashBitplanes(bitplanes, _hash);
|
||||
|
||||
_data[writeIndex++] = bitplanes & 0xFF;
|
||||
if (options.bitDepth == 2) {
|
||||
_data[writeIndex++] = bitplanes >> 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto const &data() const { return _data; }
|
||||
uint16_t hash() const { return _hash; }
|
||||
@@ -1032,7 +1033,14 @@ static void outputTileData(UniqueTiles const &tiles) {
|
||||
TileData const *tile = *iter;
|
||||
assume(tile->tileID == tileID);
|
||||
++tileID;
|
||||
output->sputn(reinterpret_cast<char const *>(tile->data().data()), options.bitDepth * 8);
|
||||
if (options.bitDepth == 2) {
|
||||
output->sputn(reinterpret_cast<char const *>(tile->data().data()), 16);
|
||||
} else {
|
||||
assume(options.bitDepth == 1);
|
||||
for (size_t y = 0; y < 8; ++y) {
|
||||
output->sputc(tile->data()[y * 2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ void reverse() {
|
||||
// Pick the smallest width that will result in a landscape-aspect rectangular image.
|
||||
// Thus a prime number of tiles will result in a horizontal row.
|
||||
// This avoids redundancy with `-r 1` which results in a vertical column.
|
||||
width = (size_t)ceil(sqrt(mapSize));
|
||||
width = static_cast<size_t>(ceil(sqrt(mapSize)));
|
||||
for (; width < mapSize; ++width) {
|
||||
if (mapSize % width == 0)
|
||||
break;
|
||||
|
||||
@@ -248,7 +248,7 @@ static void placeSection(Section §ion) {
|
||||
bankMem.insert(
|
||||
bankMem.begin() + spaceIdx + 1,
|
||||
{.address = sectionEnd,
|
||||
.size = (uint16_t)(freeSpace.address + freeSpace.size - sectionEnd)}
|
||||
.size = static_cast<uint16_t>(freeSpace.address + freeSpace.size - sectionEnd)}
|
||||
);
|
||||
// **`freeSpace` cannot be reused from this point on, because `bankMem.insert`
|
||||
// invalidates all references to itself!**
|
||||
@@ -279,7 +279,7 @@ static void placeSection(Section §ion) {
|
||||
sizeof(where),
|
||||
"in bank $%02" PRIx32 " with align mask $%" PRIx16,
|
||||
section.bank,
|
||||
(uint16_t)~section.alignMask
|
||||
static_cast<uint16_t>(~section.alignMask)
|
||||
);
|
||||
else
|
||||
snprintf(where, sizeof(where), "in bank $%02" PRIx32, section.bank);
|
||||
@@ -291,7 +291,7 @@ static void placeSection(Section §ion) {
|
||||
where,
|
||||
sizeof(where),
|
||||
"with align mask $%" PRIx16 " and offset $%" PRIx16,
|
||||
(uint16_t)~section.alignMask,
|
||||
static_cast<uint16_t>(~section.alignMask),
|
||||
section.alignOfs
|
||||
);
|
||||
else
|
||||
|
||||
@@ -209,8 +209,8 @@ static void parseScrambleSpec(char const *spec) {
|
||||
// Remember where the region's name begins and ends
|
||||
char const *regionName = spec;
|
||||
size_t regionNameLen = strcspn(spec, "=, \t");
|
||||
// Length of region name string slice for printing, truncated if too long
|
||||
int regionNamePrintLen = regionNameLen > INT_MAX ? INT_MAX : (int)regionNameLen;
|
||||
// Length of region name string slice for print formatting, truncated if too long
|
||||
int regionNameFmtLen = regionNameLen > INT_MAX ? INT_MAX : static_cast<int>(regionNameLen);
|
||||
ScrambledRegion region = SCRAMBLE_UNK;
|
||||
|
||||
// If this trips, `spec` must be pointing at a ',' or '=' (or NUL) due to the assumption
|
||||
@@ -228,7 +228,7 @@ static void parseScrambleSpec(char const *spec) {
|
||||
spec += regionNameLen + strspn(&spec[regionNameLen], " \t");
|
||||
if (*spec != '\0' && *spec != ',' && *spec != '=') {
|
||||
argErr(
|
||||
'S', "Unexpected '%c' after region name \"%.*s\"", regionNamePrintLen, regionName
|
||||
'S', "Unexpected '%c' after region name \"%.*s\"", regionNameFmtLen, regionName
|
||||
);
|
||||
// Skip to next ',' or '=' (or NUL) and keep parsing
|
||||
spec += 1 + strcspn(&spec[1], ",=");
|
||||
@@ -246,7 +246,7 @@ static void parseScrambleSpec(char const *spec) {
|
||||
}
|
||||
|
||||
if (region == SCRAMBLE_UNK)
|
||||
argErr('S', "Unknown region \"%.*s\"", regionNamePrintLen, regionName);
|
||||
argErr('S', "Unknown region \"%.*s\"", regionNameFmtLen, regionName);
|
||||
|
||||
if (*spec == '=') {
|
||||
spec++; // `strtoul` will skip the whitespace on its own
|
||||
@@ -254,7 +254,7 @@ static void parseScrambleSpec(char const *spec) {
|
||||
char *endptr;
|
||||
|
||||
if (*spec == '\0' || *spec == ',') {
|
||||
argErr('S', "Empty limit for region \"%.*s\"", regionNamePrintLen, regionName);
|
||||
argErr('S', "Empty limit for region \"%.*s\"", regionNameFmtLen, regionName);
|
||||
goto next;
|
||||
}
|
||||
limit = strtoul(spec, &endptr, 10);
|
||||
@@ -263,7 +263,7 @@ static void parseScrambleSpec(char const *spec) {
|
||||
argErr(
|
||||
'S',
|
||||
"Invalid non-numeric limit for region \"%.*s\"",
|
||||
regionNamePrintLen,
|
||||
regionNameFmtLen,
|
||||
regionName
|
||||
);
|
||||
endptr = strchr(endptr, ',');
|
||||
@@ -274,7 +274,7 @@ static void parseScrambleSpec(char const *spec) {
|
||||
argErr(
|
||||
'S',
|
||||
"Limit for region \"%.*s\" may not exceed %" PRIu16,
|
||||
regionNamePrintLen,
|
||||
regionNameFmtLen,
|
||||
regionName,
|
||||
scrambleSpecs[region].max
|
||||
);
|
||||
@@ -298,7 +298,7 @@ static void parseScrambleSpec(char const *spec) {
|
||||
// Only WRAMX can be implied, since ROMX and SRAM size may vary
|
||||
scrambleWRAMX = 7;
|
||||
} else {
|
||||
argErr('S', "Cannot imply limit for region \"%.*s\"", regionNamePrintLen, regionName);
|
||||
argErr('S', "Cannot imply limit for region \"%.*s\"", regionNameFmtLen, regionName);
|
||||
}
|
||||
|
||||
next: // Can't `continue` a `for` loop with this nontrivial iteration logic
|
||||
|
||||
@@ -30,23 +30,21 @@ static std::vector<std::vector<FileStackNode>> nodes;
|
||||
|
||||
// Helper functions for reading object files
|
||||
|
||||
// Internal, DO NOT USE.
|
||||
// For helper wrapper macros defined below, such as `tryReadLong`
|
||||
// For internal use only by `tryReadLong` and `tryGetc`!
|
||||
#define tryRead(func, type, errval, vartype, var, file, ...) \
|
||||
do { \
|
||||
FILE *tmpFile = file; \
|
||||
type tmpVal = func(tmpFile); \
|
||||
/* TODO: maybe mark the condition as `unlikely`; how to do that portably? */ \
|
||||
if (tmpVal == (errval)) { \
|
||||
errx(__VA_ARGS__, feof(tmpFile) ? "Unexpected end of file" : strerror(errno)); \
|
||||
} \
|
||||
var = (vartype)tmpVal; \
|
||||
var = static_cast<vartype>(tmpVal); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Reads an unsigned long (32-bit) value from a file.
|
||||
* @param file The file to read from. This will read 4 bytes from the file.
|
||||
* @return The value read, cast to a int64_t, or -1 on failure.
|
||||
* @return The value read, cast to a int64_t, or `INT64_MAX` on failure.
|
||||
*/
|
||||
static int64_t readLong(FILE *file) {
|
||||
uint32_t value = 0;
|
||||
@@ -63,7 +61,7 @@ static int64_t readLong(FILE *file) {
|
||||
// `uint8_t`, because int is large enough to hold a byte. This
|
||||
// however causes values larger than 127 to be too large when
|
||||
// shifted, potentially triggering undefined behavior.
|
||||
value |= (unsigned int)byte << shift;
|
||||
value |= static_cast<unsigned int>(byte) << shift;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -128,7 +126,7 @@ static void readFileStackNode(
|
||||
uint32_t parentID;
|
||||
|
||||
tryReadLong(parentID, file, "%s: Cannot read node #%" PRIu32 "'s parent ID: %s", fileName, i);
|
||||
node.parent = parentID != (uint32_t)-1 ? &fileNodes[parentID] : nullptr;
|
||||
node.parent = parentID != UINT32_MAX ? &fileNodes[parentID] : nullptr;
|
||||
tryReadLong(
|
||||
node.lineNo, file, "%s: Cannot read node #%" PRIu32 "'s line number: %s", fileName, i
|
||||
);
|
||||
@@ -329,7 +327,7 @@ static void readPatch(
|
||||
static void
|
||||
linkPatchToPCSect(Patch &patch, std::vector<std::unique_ptr<Section>> const &fileSections) {
|
||||
patch.pcSection =
|
||||
patch.pcSectionID != (uint32_t)-1 ? fileSections[patch.pcSectionID].get() : nullptr;
|
||||
patch.pcSectionID != UINT32_MAX ? fileSections[patch.pcSectionID].get() : nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -266,15 +266,15 @@ static bool isLegalForSymName(char c) {
|
||||
|| c == '@' || c == '#' || c == '$' || c == '.';
|
||||
}
|
||||
|
||||
// Prints a symbol's name to `symFile`, assuming that the first character is legal.
|
||||
// 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(char const *name) {
|
||||
for (char const *ptr = name; *ptr != '\0';) {
|
||||
static void printSymName(std::string const &name, FILE *file) {
|
||||
for (char const *ptr = name.c_str(); *ptr != '\0';) {
|
||||
char c = *ptr;
|
||||
|
||||
if (isLegalForSymName(c)) {
|
||||
// Output legal ASCII characters as-is
|
||||
putc(c, symFile);
|
||||
putc(c, file);
|
||||
++ptr;
|
||||
} else {
|
||||
// Output illegal characters using Unicode escapes
|
||||
@@ -295,7 +295,7 @@ static void printSymName(char const *name) {
|
||||
++ptr;
|
||||
} while (state != 0);
|
||||
|
||||
fprintf(symFile, codepoint <= 0xFFFF ? "\\u%04" PRIx32 : "\\U%08" PRIx32, codepoint);
|
||||
fprintf(file, codepoint <= 0xFFFF ? "\\u%04" PRIx32 : "\\U%08" PRIx32, codepoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -363,7 +363,7 @@ static void writeSymBank(SortedSections const &bankSections, SectionType type, u
|
||||
if (!sym->name.empty() && canStartSymName(sym->name[0]))
|
||||
symList.push_back({
|
||||
.sym = sym,
|
||||
.addr = (uint16_t)(sym->label().offset + sect->org),
|
||||
.addr = static_cast<uint16_t>(sym->label().offset + sect->org),
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -376,7 +376,7 @@ static void writeSymBank(SortedSections const &bankSections, SectionType type, u
|
||||
|
||||
for (SortedSymbol &sym : symList) {
|
||||
fprintf(symFile, "%02" PRIx32 ":%04" PRIx16 " ", symBank, sym.addr);
|
||||
printSymName(sym.sym->name.c_str());
|
||||
printSymName(sym.sym->name, symFile);
|
||||
putc('\n', symFile);
|
||||
}
|
||||
}
|
||||
@@ -396,6 +396,31 @@ static void writeEmptySpace(uint16_t begin, uint16_t end) {
|
||||
}
|
||||
}
|
||||
|
||||
// Prints a section's name to a file.
|
||||
static void printSectionName(std::string const &name, FILE *file) {
|
||||
for (char c : name) {
|
||||
// Escape characters that need escaping
|
||||
switch (c) {
|
||||
case '\n':
|
||||
fputs("\\n", file);
|
||||
break;
|
||||
case '\r':
|
||||
fputs("\\r", file);
|
||||
break;
|
||||
case '\t':
|
||||
fputs("\\t", file);
|
||||
break;
|
||||
case '\\':
|
||||
case '"':
|
||||
putc('\\', file);
|
||||
[[fallthrough]];
|
||||
default:
|
||||
putc(c, file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a bank's contents to the map file
|
||||
*/
|
||||
@@ -427,35 +452,22 @@ static void writeMapBank(SortedSections const §List, SectionType type, uint3
|
||||
|
||||
prevEndAddr = sect->org + sect->size;
|
||||
|
||||
fprintf(mapFile, "\tSECTION: $%04" PRIx16, sect->org);
|
||||
if (sect->size != 0)
|
||||
fprintf(
|
||||
mapFile,
|
||||
"\tSECTION: $%04" PRIx16 "-$%04x ($%04" PRIx16 " byte%s) [\"%s\"]\n",
|
||||
sect->org,
|
||||
prevEndAddr - 1,
|
||||
sect->size,
|
||||
sect->size == 1 ? "" : "s",
|
||||
sect->name.c_str()
|
||||
);
|
||||
else
|
||||
fprintf(
|
||||
mapFile,
|
||||
"\tSECTION: $%04" PRIx16 " (0 bytes) [\"%s\"]\n",
|
||||
sect->org,
|
||||
sect->name.c_str()
|
||||
);
|
||||
fprintf(mapFile, "-$%04x", prevEndAddr - 1);
|
||||
fprintf(mapFile, " ($%04" PRIx16 " byte%s) [\"", sect->size, sect->size == 1 ? "" : "s");
|
||||
printSectionName(sect->name, mapFile);
|
||||
fputs("\"]\n", mapFile);
|
||||
|
||||
if (!noSymInMap) {
|
||||
// Also print symbols in the following "pieces"
|
||||
for (uint16_t org = sect->org; sect; sect = sect->nextu.get()) {
|
||||
for (Symbol *sym : sect->symbols)
|
||||
for (Symbol *sym : sect->symbols) {
|
||||
// Space matches "\tSECTION: $xxxx ..."
|
||||
fprintf(
|
||||
mapFile,
|
||||
"\t $%04" PRIx32 " = %s\n",
|
||||
sym->label().offset + org,
|
||||
sym->name.c_str()
|
||||
);
|
||||
fprintf(mapFile, "\t $%04" PRIx32 " = ", sym->label().offset + org);
|
||||
printSymName(sym->name, mapFile);
|
||||
putc('\n', mapFile);
|
||||
}
|
||||
|
||||
if (sect->nextu) {
|
||||
// Announce the following "piece"
|
||||
@@ -579,7 +591,9 @@ static void writeSym() {
|
||||
for (Symbol *sym : constants) {
|
||||
int32_t val = sym->data.get<int32_t>();
|
||||
int width = val < 0x100 ? 2 : val < 0x10000 ? 4 : 8;
|
||||
fprintf(symFile, "%0*" PRIx32 " %s\n", width, val, sym->name.c_str());
|
||||
fprintf(symFile, "%0*" PRIx32 " ", width, val);
|
||||
printSymName(sym->name, symFile);
|
||||
putc('\n', symFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ 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) {
|
||||
assume(index != (uint32_t)-1); // PC needs to be handled specially, not here
|
||||
assume(index != UINT32_MAX); // PC needs to be handled specially, not here
|
||||
Symbol const &symbol = symbolList[index];
|
||||
|
||||
// If the symbol is defined elsewhere...
|
||||
@@ -73,12 +73,12 @@ static Symbol const *getSymbol(std::vector<Symbol> const &symbolList, uint32_t i
|
||||
*/
|
||||
static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fileSymbols) {
|
||||
uint8_t const *expression = patch.rpnExpression.data();
|
||||
int32_t size = (int32_t)patch.rpnExpression.size();
|
||||
int32_t size = static_cast<int32_t>(patch.rpnExpression.size());
|
||||
|
||||
rpnStack.clear();
|
||||
|
||||
while (size > 0) {
|
||||
RPNCommand command = (RPNCommand)getRPNByte(expression, size, patch);
|
||||
RPNCommand command = static_cast<RPNCommand>(getRPNByte(expression, size, patch));
|
||||
int32_t value;
|
||||
|
||||
isError = false;
|
||||
@@ -150,11 +150,11 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
||||
|
||||
case RPN_BITWIDTH:
|
||||
value = popRPN(patch);
|
||||
value = value != 0 ? 32 - clz((uint32_t)value) : 0;
|
||||
value = value != 0 ? 32 - clz(static_cast<uint32_t>(value)) : 0;
|
||||
break;
|
||||
case RPN_TZCOUNT:
|
||||
value = popRPN(patch);
|
||||
value = value != 0 ? ctz((uint32_t)value) : 32;
|
||||
value = value != 0 ? ctz(static_cast<uint32_t>(value)) : 32;
|
||||
break;
|
||||
|
||||
case RPN_OR:
|
||||
@@ -249,7 +249,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
||||
case RPN_BANK_SECT: {
|
||||
// `expression` is not guaranteed to be '\0'-terminated. If it is not,
|
||||
// `getRPNByte` will have a fatal internal error.
|
||||
char const *name = (char const *)expression;
|
||||
char const *name = reinterpret_cast<char const *>(expression);
|
||||
while (getRPNByte(expression, size, patch))
|
||||
;
|
||||
|
||||
@@ -280,7 +280,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
||||
|
||||
case RPN_SIZEOF_SECT: {
|
||||
// This has assumptions commented in the `RPN_BANK_SECT` case above.
|
||||
char const *name = (char const *)expression;
|
||||
char const *name = reinterpret_cast<char const *>(expression);
|
||||
while (getRPNByte(expression, size, patch))
|
||||
;
|
||||
|
||||
@@ -301,7 +301,7 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
||||
|
||||
case RPN_STARTOF_SECT: {
|
||||
// This has assumptions commented in the `RPN_BANK_SECT` case above.
|
||||
char const *name = (char const *)expression;
|
||||
char const *name = reinterpret_cast<char const *>(expression);
|
||||
while (getRPNByte(expression, size, patch))
|
||||
;
|
||||
|
||||
@@ -356,6 +356,12 @@ static int32_t computeRPNExpr(Patch const &patch, std::vector<Symbol> const &fil
|
||||
isError = true;
|
||||
}
|
||||
value = 0;
|
||||
} else if (value >= 0 && value <= 0xFF) {
|
||||
warning(
|
||||
patch.src,
|
||||
patch.lineNo,
|
||||
"LDH is deprecated with values from $00 to $FF; use $FF00 to $FFFF"
|
||||
);
|
||||
}
|
||||
value &= 0xFF;
|
||||
break;
|
||||
@@ -428,7 +434,7 @@ void patch_CheckAssertions() {
|
||||
|
||||
for (Assertion &assert : assertions) {
|
||||
int32_t value = computeRPNExpr(assert.patch, *assert.fileSymbols);
|
||||
AssertionType type = (AssertionType)assert.patch.type;
|
||||
AssertionType type = static_cast<AssertionType>(assert.patch.type);
|
||||
|
||||
if (!isError && !value) {
|
||||
switch (type) {
|
||||
|
||||
@@ -557,8 +557,8 @@ static void alignTo(uint32_t alignment, uint32_t alignOfs) {
|
||||
"Cannot align: the next suitable address after $%04" PRIx16 " is $%04" PRIx16
|
||||
", past $%04" PRIx16,
|
||||
pc,
|
||||
(uint16_t)(pc + length),
|
||||
(uint16_t)(endaddr(activeType) + 1)
|
||||
static_cast<uint16_t>(pc + length),
|
||||
static_cast<uint16_t>(endaddr(activeType) + 1)
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -588,7 +588,7 @@ static void pad(uint32_t length) {
|
||||
"Cannot increase the current address by %u bytes: only %u bytes to $%04" PRIx16,
|
||||
length,
|
||||
typeInfo.size - offset,
|
||||
(uint16_t)(endaddr(activeType) + 1)
|
||||
static_cast<uint16_t>(endaddr(activeType) + 1)
|
||||
);
|
||||
} else {
|
||||
pc += length;
|
||||
@@ -689,7 +689,7 @@ static void placeSection(std::string const &name, bool isOptional) {
|
||||
name.c_str(),
|
||||
org,
|
||||
alignment,
|
||||
(uint16_t)(org & section->alignMask),
|
||||
static_cast<uint16_t>(org & section->alignMask),
|
||||
alignment,
|
||||
section->alignOfs
|
||||
);
|
||||
|
||||
@@ -468,7 +468,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
||||
getToken(nullptr, "'R' line is too short");
|
||||
areaIdx = parseByte(where, lineNo, token, numberType);
|
||||
getToken(nullptr, "'R' line is too short");
|
||||
areaIdx |= (uint16_t)parseByte(where, lineNo, token, numberType) << 8;
|
||||
areaIdx |= static_cast<uint16_t>(parseByte(where, lineNo, token, numberType)) << 8;
|
||||
if (areaIdx >= fileSections.size())
|
||||
fatal(
|
||||
&where,
|
||||
@@ -532,8 +532,9 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
||||
|
||||
if ((flags & 0xF0) == 0xF0) {
|
||||
getToken(nullptr, "Incomplete relocation");
|
||||
flags =
|
||||
(flags & 0x0F) | (uint16_t)parseByte(where, lineNo, token, numberType) << 4;
|
||||
flags = (flags & 0x0F)
|
||||
| static_cast<uint16_t>(parseByte(where, lineNo, token, numberType))
|
||||
<< 4;
|
||||
}
|
||||
|
||||
getToken(nullptr, "Incomplete relocation");
|
||||
@@ -560,7 +561,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
||||
uint16_t idx = parseByte(where, lineNo, token, numberType);
|
||||
|
||||
getToken(nullptr, "Incomplete relocation");
|
||||
idx |= (uint16_t)parseByte(where, lineNo, token, numberType);
|
||||
idx |= static_cast<uint16_t>(parseByte(where, lineNo, token, numberType));
|
||||
|
||||
// Loudly fail on unknown flags
|
||||
if (flags & (1 << RELOC_ZPAGE | 1 << RELOC_NPAGE))
|
||||
@@ -646,7 +647,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
||||
patch.rpnExpression.resize(1 + sym.name.length() - 2 + 1);
|
||||
patch.rpnExpression[0] = RPN_SIZEOF_SECT;
|
||||
memcpy(
|
||||
(char *)&patch.rpnExpression[1],
|
||||
reinterpret_cast<char *>(&patch.rpnExpression[1]),
|
||||
&sym.name.c_str()[2],
|
||||
sym.name.length() - 2 + 1
|
||||
);
|
||||
@@ -654,7 +655,7 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
||||
patch.rpnExpression.resize(1 + sym.name.length() - 2 + 1);
|
||||
patch.rpnExpression[0] = RPN_STARTOF_SECT;
|
||||
memcpy(
|
||||
(char *)&patch.rpnExpression[1],
|
||||
reinterpret_cast<char *>(&patch.rpnExpression[1]),
|
||||
&sym.name.c_str()[2],
|
||||
sym.name.length() - 2 + 1
|
||||
);
|
||||
@@ -700,7 +701,11 @@ void sdobj_ReadFile(FileStackNode const &where, FILE *file, std::vector<Symbol>
|
||||
patch.rpnExpression.resize(1 + name.length() + 1);
|
||||
patch.rpnExpression[0] = RPN_STARTOF_SECT;
|
||||
// The cast is fine, it's just different signedness
|
||||
memcpy((char *)&patch.rpnExpression[1], name.c_str(), name.length() + 1);
|
||||
memcpy(
|
||||
reinterpret_cast<char *>(&patch.rpnExpression[1]),
|
||||
name.c_str(),
|
||||
name.length() + 1
|
||||
);
|
||||
}
|
||||
|
||||
patch.rpnExpression.push_back(RPN_CONST);
|
||||
|
||||
@@ -48,7 +48,7 @@ int32_t op_shift_left(int32_t value, int32_t amount) {
|
||||
|
||||
// Use unsigned to force a bitwise shift
|
||||
// Casting back is OK because the types implement two's complement behavior
|
||||
return (uint32_t)value << amount;
|
||||
return static_cast<uint32_t>(value) << amount;
|
||||
}
|
||||
|
||||
int32_t op_shift_right(int32_t value, int32_t amount) {
|
||||
@@ -63,7 +63,7 @@ int32_t op_shift_right(int32_t value, int32_t amount) {
|
||||
return op_shift_left(value, -amount);
|
||||
|
||||
if (value > 0)
|
||||
return (uint32_t)value >> amount;
|
||||
return static_cast<uint32_t>(value) >> amount;
|
||||
|
||||
// Calculate an OR mask for sign extension
|
||||
// 1->0x80000000, 2->0xC0000000, ..., 31->0xFFFFFFFE
|
||||
@@ -71,7 +71,7 @@ int32_t op_shift_right(int32_t value, int32_t amount) {
|
||||
|
||||
// The C++ standard leaves shifting right negative values
|
||||
// undefined, so use a left shift manually sign-extended
|
||||
return ((uint32_t)value >> amount) | amount_high_bits;
|
||||
return (static_cast<uint32_t>(value) >> amount) | amount_high_bits;
|
||||
}
|
||||
|
||||
int32_t op_shift_right_unsigned(int32_t value, int32_t amount) {
|
||||
@@ -83,5 +83,5 @@ int32_t op_shift_right_unsigned(int32_t value, int32_t amount) {
|
||||
if (amount < 0)
|
||||
return op_shift_left(value, -amount);
|
||||
|
||||
return (uint32_t)value >> amount;
|
||||
return static_cast<uint32_t>(value) >> amount;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ char const *printChar(int c) {
|
||||
default: // Print as hex
|
||||
buf[0] = '0';
|
||||
buf[1] = 'x';
|
||||
snprintf(&buf[2], 3, "%02hhX", (uint8_t)c); // includes the '\0'
|
||||
snprintf(&buf[2], 3, "%02hhX", static_cast<uint8_t>(c)); // includes the '\0'
|
||||
return buf;
|
||||
}
|
||||
buf[0] = '\'';
|
||||
|
||||
31
test/asm/deprecated-ldio.asm
Normal file
31
test/asm/deprecated-ldio.asm
Normal file
@@ -0,0 +1,31 @@
|
||||
SECTION "LDIO", ROM0
|
||||
|
||||
ldh [c], a
|
||||
ldh a, [c]
|
||||
ldh [$11], a
|
||||
ldh a, [$11]
|
||||
|
||||
ld [$ff00+c], a
|
||||
ld a, [$ff00+c]
|
||||
ld [$ff11], a
|
||||
ld a, [$ff11]
|
||||
|
||||
ldio [c], a
|
||||
ldio a, [c]
|
||||
ldio [$ff11], a
|
||||
ldio a, [$ff11]
|
||||
|
||||
LDH [C], A
|
||||
LDH A, [C]
|
||||
LDH [$11], A
|
||||
LDH A, [$11]
|
||||
|
||||
LD [$FF00+C], A
|
||||
LD A, [$FF00+C]
|
||||
LD [$FF11], A
|
||||
LD A, [$FF11]
|
||||
|
||||
LDIO [C], A
|
||||
LDIO A, [C]
|
||||
LDIO [$FF11], A
|
||||
LDIO A, [$FF11]
|
||||
32
test/asm/deprecated-ldio.err
Normal file
32
test/asm/deprecated-ldio.err
Normal file
@@ -0,0 +1,32 @@
|
||||
warning: deprecated-ldio.asm(5): [-Wobsolete]
|
||||
LDH is deprecated with values from $00 to $FF; use $FF00 to $FFFF
|
||||
warning: deprecated-ldio.asm(6): [-Wobsolete]
|
||||
LDH is deprecated with values from $00 to $FF; use $FF00 to $FFFF
|
||||
warning: deprecated-ldio.asm(8): [-Wobsolete]
|
||||
LD [C], A is deprecated; use LDH [C], A
|
||||
warning: deprecated-ldio.asm(9): [-Wobsolete]
|
||||
LD A, [C] is deprecated; use LDH A, [C]
|
||||
warning: deprecated-ldio.asm(13): [-Wobsolete]
|
||||
LDIO is deprecated; use LDH
|
||||
warning: deprecated-ldio.asm(14): [-Wobsolete]
|
||||
LDIO is deprecated; use LDH
|
||||
warning: deprecated-ldio.asm(15): [-Wobsolete]
|
||||
LDIO is deprecated; use LDH
|
||||
warning: deprecated-ldio.asm(16): [-Wobsolete]
|
||||
LDIO is deprecated; use LDH
|
||||
warning: deprecated-ldio.asm(20): [-Wobsolete]
|
||||
LDH is deprecated with values from $00 to $FF; use $FF00 to $FFFF
|
||||
warning: deprecated-ldio.asm(21): [-Wobsolete]
|
||||
LDH is deprecated with values from $00 to $FF; use $FF00 to $FFFF
|
||||
warning: deprecated-ldio.asm(23): [-Wobsolete]
|
||||
LD [C], A is deprecated; use LDH [C], A
|
||||
warning: deprecated-ldio.asm(24): [-Wobsolete]
|
||||
LD A, [C] is deprecated; use LDH A, [C]
|
||||
warning: deprecated-ldio.asm(28): [-Wobsolete]
|
||||
LDIO is deprecated; use LDH
|
||||
warning: deprecated-ldio.asm(29): [-Wobsolete]
|
||||
LDIO is deprecated; use LDH
|
||||
warning: deprecated-ldio.asm(30): [-Wobsolete]
|
||||
LDIO is deprecated; use LDH
|
||||
warning: deprecated-ldio.asm(31): [-Wobsolete]
|
||||
LDIO is deprecated; use LDH
|
||||
1
test/asm/deprecated-ldio.out.bin
Normal file
1
test/asm/deprecated-ldio.out.bin
Normal file
@@ -0,0 +1 @@
|
||||
<EFBFBD><EFBFBD><EFBFBD><11><11><><EFBFBD><11><><11><><EFBFBD><EFBFBD><11><11><><EFBFBD><11><11><><EFBFBD><11><><11><><EFBFBD><EFBFBD><11>
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
SECTION "ff00+c or not to ff00+c", ROMX
|
||||
|
||||
ld a, [$ff00 + c]
|
||||
ld [65280 + c], a
|
||||
ldh a, [$ff00 + c]
|
||||
ldh [65280 + c], a
|
||||
|
||||
; Not ok
|
||||
ld a, [$ff01 + c]
|
||||
ld [xyz + c], a
|
||||
ldh a, [$ff01 + c]
|
||||
ldh [xyz + c], a
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
SECTION "test", ROM0[0]
|
||||
ld [ $ff00 + c ], a
|
||||
ldh [ $ff00 + c ], a
|
||||
; 257 spaces exceeds both LEXER_BUF_SIZE (42) and uint8_t limit (255)
|
||||
ld [ $ff00 + c ], a
|
||||
ld [ $ff00 + c ], a
|
||||
ldh [ $ff00 + c ], a
|
||||
ldh [ $ff00 + c ], a
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
SECTION "invalid", ROM0[$10000]
|
||||
ld [hl], [hl]
|
||||
ld a, [$00ff+c]
|
||||
ldh a, [$00ff+c]
|
||||
ld b, [c]
|
||||
ld b, [bc]
|
||||
ld b, [$4000]
|
||||
|
||||
@@ -1,2 +1,29 @@
|
||||
SECTION "A", ROM0
|
||||
AData::
|
||||
LOAD FRAGMENT "RAM", WRAM0
|
||||
AMem::
|
||||
db 0, 1, 2
|
||||
AMemEnd::
|
||||
ENDL
|
||||
ADataEnd::
|
||||
dw AMem
|
||||
|
||||
SECTION "B", ROM0
|
||||
BData::
|
||||
LOAD FRAGMENT "RAM", WRAM0
|
||||
BMem::
|
||||
db 3, 4, 5, 6, 7
|
||||
BMemEnd::
|
||||
ENDL
|
||||
BDataEnd::
|
||||
dw BMem
|
||||
|
||||
SECTION "C", ROM0
|
||||
CData::
|
||||
LOAD FRAGMENT "RAM", WRAM0
|
||||
CMem::
|
||||
db 8, 9
|
||||
CMemEnd::
|
||||
ENDL
|
||||
CDataEnd::
|
||||
dw CMem
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
error: load-fragment.asm(2):
|
||||
`LOAD FRAGMENT` is not allowed
|
||||
error: Assembly aborted (1 error)!
|
||||
BIN
test/asm/load-fragment.out.bin
Normal file
BIN
test/asm/load-fragment.out.bin
Normal file
Binary file not shown.
@@ -6,4 +6,6 @@ error: macro-syntax.asm(8):
|
||||
'\1' cannot be used outside of a macro
|
||||
error: macro-syntax.asm(9):
|
||||
syntax error, unexpected ENDM
|
||||
error: Assembly aborted (4 errors)!
|
||||
error: macro-syntax.asm(11):
|
||||
"old" is not a macro
|
||||
error: Assembly aborted (5 errors)!
|
||||
|
||||
31
test/asm/operator-associativity.asm
Normal file
31
test/asm/operator-associativity.asm
Normal file
@@ -0,0 +1,31 @@
|
||||
MACRO setup
|
||||
def result = (\2) \1 (\3) \1 (\4)
|
||||
def leftgroup = ((\2) \1 (\3)) \1 (\4)
|
||||
def rightgroup = (\2) \1 ((\3) \1 (\4))
|
||||
ENDM
|
||||
|
||||
MACRO left
|
||||
setup \#
|
||||
ASSERT result == leftgroup && result != rightgroup
|
||||
ENDM
|
||||
|
||||
MACRO right
|
||||
setup \#
|
||||
ASSERT result == rightgroup && result != leftgroup
|
||||
ENDM
|
||||
|
||||
left /, 24, 6, 2
|
||||
left %, 22, 13, 5
|
||||
|
||||
right **, 2, 3, 2
|
||||
|
||||
left ==, 0, 1, 2
|
||||
left !=, 1, 1, 2
|
||||
left <, 1, 2, 2
|
||||
left >, 2, 2, 1
|
||||
left <=, 1, 3, 2
|
||||
left >=, 2, 3, 1
|
||||
|
||||
left <<, 1, 2, 2
|
||||
left >>, 16, 2, 2
|
||||
left >>>, 16, 2, 2
|
||||
@@ -3,3 +3,4 @@ SECTION "This is invalid", ROM0
|
||||
PUSHS
|
||||
; We should be outside of section scope now
|
||||
db 69
|
||||
POPS
|
||||
|
||||
@@ -28,4 +28,4 @@ ENDM
|
||||
mac2 elif, 6 * 7 ; this prints "it's $2A" because it skips the `\1` line and takes the `else`
|
||||
mac2 elif, 6 * 9
|
||||
mac2 elif
|
||||
mac2
|
||||
mac2 ; this prints "args:" *and* "forty-two!" since it doesn't create an `elif`
|
||||
|
||||
@@ -2,4 +2,6 @@ error: skip-expansions.asm(31) -> skip-expansions.asm::mac2(21):
|
||||
Macro argument '\1' not defined
|
||||
error: skip-expansions.asm(31) -> skip-expansions.asm::mac2(21):
|
||||
syntax error, unexpected (
|
||||
error: Assembly aborted (2 errors)!
|
||||
error: skip-expansions.asm(31) -> skip-expansions.asm::mac2(21):
|
||||
Macro argument '\2' not defined
|
||||
error: Assembly aborted (3 errors)!
|
||||
|
||||
@@ -6,3 +6,4 @@ it's $2A
|
||||
it's $36
|
||||
args: elif
|
||||
args:
|
||||
forty-two!
|
||||
|
||||
4
test/asm/unmatched-directive.asm
Normal file
4
test/asm/unmatched-directive.asm
Normal file
@@ -0,0 +1,4 @@
|
||||
SECTION "test", ROM0
|
||||
PUSHC
|
||||
PUSHO
|
||||
PUSHS
|
||||
6
test/asm/unmatched-directive.err
Normal file
6
test/asm/unmatched-directive.err
Normal file
@@ -0,0 +1,6 @@
|
||||
warning: unmatched-directive.asm(5): [-Wunmatched-directive]
|
||||
`PUSHC` without corresponding `POPC`
|
||||
warning: unmatched-directive.asm(5): [-Wunmatched-directive]
|
||||
`PUSHO` without corresponding `POPO`
|
||||
warning: unmatched-directive.asm(5): [-Wunmatched-directive]
|
||||
`PUSHS` without corresponding `POPS`
|
||||
@@ -98,10 +98,10 @@ case "$actionname" in
|
||||
esac
|
||||
|
||||
if "$nonfree"; then
|
||||
action pret pokecrystal 2024-10-16 961fad9e150df324afc9bccd1ce15c3a65d3c124
|
||||
action pret pokered 2024-10-15 a891fc1168a7f998c570e1ea5f15014556df2d95
|
||||
action zladx LADX-Disassembly 2024-09-16 008d01541f8cab3f4590cbc94a690af2b9a7979f
|
||||
action pret pokecrystal 2024-12-24 fd64d7eb7062af60a37b265a361a91ceaeb25daa
|
||||
action pret pokered 2024-12-20 a59c2bbaf92d381acf45fd6313dc23329a8d08ce
|
||||
action zladx LADX-Disassembly 2024-12-05 8b9ddbd18fcfd82abd10931c64e136daaed4dc27
|
||||
fi
|
||||
action AntonioND ucity 2024-08-03 f3c6377f1fb1ea29644bcd90722abaaa5d478a74
|
||||
action pinobatch libbet 2024-10-20 71d04e850534cbe77d1c143153f664dac1960bc9
|
||||
action LIJI32 SameBoy 2024-10-12 52d5169cc82356288f337d30aa01fb3fa1b37155
|
||||
action pinobatch libbet 2024-12-20 b7ef8442f3ac1dfc4bc3ce186182b02a76ae9763
|
||||
action LIJI32 SameBoy 2024-12-12 da73f3a8c199b8505d9c8361620582dee689b579
|
||||
|
||||
1
test/gfx/color_curve.flags
Normal file
1
test/gfx/color_curve.flags
Normal file
@@ -0,0 +1 @@
|
||||
-C
|
||||
BIN
test/gfx/color_curve.out.2bpp
Normal file
BIN
test/gfx/color_curve.out.2bpp
Normal file
Binary file not shown.
BIN
test/gfx/color_curve.out.pal
Normal file
BIN
test/gfx/color_curve.out.pal
Normal file
Binary file not shown.
BIN
test/gfx/color_curve.png
Normal file
BIN
test/gfx/color_curve.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 881 B |
@@ -1,7 +1,7 @@
|
||||
|
||||
5721d9
|
||||
|
||||
a9d4fe
|
||||
A9D4FE
|
||||
|
||||
|
||||
ffffff
|
||||
ffFFfF
|
||||
|
||||
BIN
test/gfx/full_aco.aco
Normal file
BIN
test/gfx/full_aco.aco
Normal file
Binary file not shown.
1
test/gfx/full_aco.flags
Normal file
1
test/gfx/full_aco.flags
Normal file
@@ -0,0 +1 @@
|
||||
-c aco:full_aco.aco
|
||||
BIN
test/gfx/full_aco.out.2bpp
Normal file
BIN
test/gfx/full_aco.out.2bpp
Normal file
Binary file not shown.
BIN
test/gfx/full_aco.out.attrmap
Normal file
BIN
test/gfx/full_aco.out.attrmap
Normal file
Binary file not shown.
BIN
test/gfx/full_aco.out.pal
Normal file
BIN
test/gfx/full_aco.out.pal
Normal file
Binary file not shown.
BIN
test/gfx/full_aco.out.tilemap
Normal file
BIN
test/gfx/full_aco.out.tilemap
Normal file
Binary file not shown.
BIN
test/gfx/full_aco.png
Normal file
BIN
test/gfx/full_aco.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 927 B |
BIN
test/gfx/full_act.act
Normal file
BIN
test/gfx/full_act.act
Normal file
Binary file not shown.
1
test/gfx/full_act.flags
Normal file
1
test/gfx/full_act.flags
Normal file
@@ -0,0 +1 @@
|
||||
-c act:full_act.act
|
||||
BIN
test/gfx/full_act.out.2bpp
Normal file
BIN
test/gfx/full_act.out.2bpp
Normal file
Binary file not shown.
BIN
test/gfx/full_act.out.attrmap
Normal file
BIN
test/gfx/full_act.out.attrmap
Normal file
Binary file not shown.
BIN
test/gfx/full_act.out.pal
Normal file
BIN
test/gfx/full_act.out.pal
Normal file
Binary file not shown.
BIN
test/gfx/full_act.out.tilemap
Normal file
BIN
test/gfx/full_act.out.tilemap
Normal file
Binary file not shown.
BIN
test/gfx/full_act.png
Normal file
BIN
test/gfx/full_act.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 927 B |
1
test/gfx/full_gbc.flags
Normal file
1
test/gfx/full_gbc.flags
Normal file
@@ -0,0 +1 @@
|
||||
-c gbc:full_gbc.pal
|
||||
BIN
test/gfx/full_gbc.out.2bpp
Normal file
BIN
test/gfx/full_gbc.out.2bpp
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user