Compare commits

...
416 Commits
Author SHA1 Message Date
Rangi 307846b03e Release v1.0.3 2026-08-01 11:27:43 -04:00
Rangi e7e671b249 Update external test commits 2026-08-01 11:24:42 -04:00
Rangi 0c772ea049 Update external testing CI with libbet's new way of running Python
libbet now uses `py -3` if the `py` binary exists, instead of if
`COMSPEC` is defined. So we don't need to `unset COMSPEC`, we just
need to avoid Windows' `py` launcher from being in Cygwin's `PATH`.

It turns out that `C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 ...`
was not effectively preventing Windows' `PATH` from being inherited
by Cygwin's `PATH`, because to take effect it needs to run Cygwin's
`/etc/profile` script. The simple way to do that is using a login
shell. So we don't need the `PREFIX`/`--prefix` from the previous
commit after all.
2026-08-01 04:49:43 +02:00
Rangi 6f64fd00f6 Fix CI testing of installing RGBDS on Cygwin (#2038)
We need to pass `-e` to Cygwin `bash` explicitly, since specifying
it as the non-default `shell:` no longer automatically adds `-e`
behavior to `run` blocks.

We also need to install to `/usr/bin` (which `cygwin-install-action`
automatically adds to the `PATH`), not `/usr/local/bin` (which is not
in the default `PATH`). This tests our `PREFIX`/`--prefix` handling.
2026-07-31 10:25:59 -04:00
Rangi 4384223951 CI testing in Cygwin 64-bit now uses CMake and runs external tests (#2037) 2026-07-30 12:59:53 -04:00
Rangi 4e87f61656 Edit some comments and formatting in testing.yml for consistency 2026-07-29 22:08:21 -04:00
Rangi 9d854114ea Use Ninja as the generator for CMake on FreeBSD 2026-07-30 02:33:47 +02:00
Rangi 70d8d67ffc Run external tests for FreeBSD (#2035)
This requires aliasing `make` to `gmake` and `python` to `python3`
2026-07-29 19:45:07 -04:00
Rangi ca2b8e4cde Support the COLUMNS environment variable for help/usage output 2026-07-24 14:46:05 +02:00
Rangi 80c8e77b19 Fixed-point FMOD and LOG explicitly return 0 for some invalid inputs
This is consistent with `fix_Div`'s explicit error handling,
and does not rely on subtle C/C++ `fmod` or `log` IEEE 754 behavior.
2026-07-23 00:38:44 -04:00
Rangi 604c69e048 Fix palette-packing decant logic to avoid UB from invalid iterator (#2031)
The "Iterate through the component's color sets, and transfer them"
logic shifted `from.begin()`, which would make subsequent `std::advance`
calls go past `_assigned.end()` and cause UB. This rewrite uses absolute
numeric indexes into `_assigned` to avoid that potential problem.

I haven't been able to craft a test case that actually *reaches*
that piece of logic, and I suspect that it's unreachable given how
we sort and process color sets before decanting, but cannot prove
that; so it stays in. (Maybe if we added 4bpp 16-colors-per-palette
support, it would become reachable?)
2026-07-22 23:48:02 -04:00
Rangi ec5e6cbabb Handle -Wtruncation for link-time jr values
Fixes #2028
2026-07-21 14:51:58 -04:00
Rangi 50678d0942 Use _fseeki64/_ftelli64 on Windows since fseek/fell are limited by 32-bit long 2026-07-21 14:38:14 -04:00
Rangi c691ec6607 Use std::optional<uint64_t> instead of long with a special -1 sentinel for seekSize 2026-07-21 14:31:05 -04:00
Rangi c3073f4318 Assume that InsertionOrderedMap never adds duplicate keys 2026-07-21 14:07:45 -04:00
Rangi e5b03e3cb1 Zero-initialize Png struct width and height just in case 2026-07-21 13:58:31 -04:00
Rangi 0d379e9016 Assume clz/ctz argument is nonzero in our fallback implementations 2026-07-21 13:56:57 -04:00
Rangi 9cbfec30c7 Avoid redundant "Unexpected text" error message after "Unknown" error 2026-07-21 13:54:11 -04:00
Rangi 9def9c9aa8 Fix an SDAS object error message to use the valid section name variable 2026-07-21 13:41:53 -04:00
Rangi b5681c0d34 Set binary or text mode on stdout as well as stdin 2026-07-21 13:39:28 -04:00
Rangi ec43f06bd9 Fix handling of at-files containing -- followed by positional arguments 2026-07-21 13:35:39 -04:00
Rangi 106ec399d5 Fix reading value of relocations symbol/section index from SDAS objects 2026-07-21 13:14:35 -04:00
Rangi d9c0d37fd5 Explicitly allow JR offsets to wrap around in RGBLINK
This reverts commit f27a8132d9.
2026-07-20 19:23:11 -04:00
Eldred Habert 3b1606c63c Fix typo'd APT option in release workflow
Fixing up bcff14b596
2026-07-20 21:53:43 +02:00
Rangi fa49e238a9 Release v1.0.2 2026-07-20 14:08:09 -04:00
Rangi 3426f685a0 Update libpng to 1.6.58 2026-07-20 14:08:09 -04:00
Rangi c0e2f8340b Output negative values with correct with in .sym files 2026-07-20 00:09:06 -04:00
Rangi 8883251bf9 Avoid UB from FileStackNodeType(type) on an invalid type 2026-07-20 00:06:09 -04:00
Rangi f27a8132d9 Avoid truncating too-large JR offset from an invalid object file 2026-07-20 00:02:39 -04:00
Rangi 0af28de791 Fix assertion failure on nonzero base palette ID without an attribute map or palette map
Also fix palette map IDs to wrap at 256, not at 8 like attributes
2026-07-19 23:59:38 -04:00
Rangi 8d2275dc48 Use rpnErrorAt wrapper to ensure setting isError on all RPN errors
Two possible RPN errors were missing `isError = true;`
2026-07-19 23:45:45 -04:00
Rangi 7e11ab4d8f uint16_t tileIdx type matches options.baseTileIDs
Tile IDs wrap at 8 bits, but that's handled by `uint8_t tileID`
2026-07-19 23:33:01 -04:00
Rangi e84bcf83b4 Use Procedure concept to type-check decantOn lambda 2026-07-15 23:27:08 -04:00
Rangi 57c9bab3a5 Addresses are 16-bit in .map files, consistent with .sym files 2026-07-15 22:59:50 -04:00
Rangi 4ada3e85d8 Avoid overflow for very large ROMs (e.g. TPP1) 2026-07-15 22:57:25 -04:00
Rangi ba4a7f2e6a Explicitly assume what was verified earlier by readSection 2026-07-15 22:56:43 -04:00
Rangi b0948566bb Fail-safe treat 16-bit alignment as a fixed address even in RGBLINK
RGBASM does this itself, so should not output sections with 16-bit
alignment, but an invalid object file could otherwise cause
`getPlacement` to infinitely loop.
2026-07-15 22:46:57 -04:00
Rangi 5aa7067876 Comment on how overflow to 0 is explicitly allowed for tile and palette IDs 2026-07-15 22:28:08 -04:00
Rangi dff176791c Correct grammar "is" vs "are" in RGBGFX reverse mode error messages 2026-07-14 11:29:30 -04:00
Rangi 07f5969005 Consistency check the palette map, not just the attribute map 2026-07-14 11:27:41 -04:00
Rangi d61c0561c1 Correct condition to completely print both palettes and options.palSpec 2026-07-14 11:23:03 -04:00
Rangi b13b0c8eb8 Fix the same shifting UB for SDAS objects as for RGBDS ones 2026-07-14 11:13:30 -04:00
Rangi ca2de0f429 Add issue template 2026-07-14 10:34:43 -04:00
Rangi a821badc3e Reorganize the readme
- Use sub-headings to let the reader skim more easily
- Link specifically to the Discord server before less-useful
  "other community channels"
2026-07-14 10:34:43 -04:00
Rangi cb34fe18f7 Use transparent instead of repeating 0x8000 2026-07-14 10:34:27 -04:00
Rangi 1ed22cc518 Mask blue for consistency (avoids transparent bit bugs) 2026-07-14 10:34:27 -04:00
Rangi 9a15b723f2 Format CGB colors in error/verbose messages as "GB:rr,gg,bb"
These should be more debuggable than packed "$xxxx" format.
2026-07-14 10:34:27 -04:00
Rangi 7e2a3491a5 Encapsulate fseek+ftell idiom in seekSize
This allows a single check for the return value of `seekSize`
to verify all three `fseek`+`ftell`+`fseek` calls.
2026-07-14 01:32:18 -04:00
Rangi e7855ea1b8 Fix reverse mode with -s/--palette-size too small for the tile data 2026-07-14 01:11:11 -04:00
Rangi 19e60d9864 Fix large ds in linker script overflowing the PC 2026-07-14 00:42:42 -04:00
Rangi c0ff3d92ad Fix reverse palette offsets when base palette ID wraps around to 0 2026-07-13 23:08:20 -04:00
Rangi e4583e7ac2 RGBGFX reverse mode checks for too-low palette IDs, not just too-high 2026-07-13 23:08:20 -04:00
Rangi 5869e0dc60 Fix RGBLINK scrambling algorithm to work for SRAM
Since SRAM sections start from 0, we had been using a signed
`int8_t curScrambleSRAM` counter to allow 0 as a scrambled bank,
but this actually just placed all floating SRAM sections in bank 0.
2026-07-13 22:39:41 -04:00
Rangi 196d50c1d7 Add XXX: comment tag to working but fragile test cases 2026-07-13 22:39:41 -04:00
Rangi 8b951bc047 Fix a flaky/random-seed rgbgfx_test test failure
Commit 21eaa2d2cc missed some
intentional unsigned underflow that is needed to set `nbTilesInBank`.
Without it, some of the randomly-seeded RGBGFX tests, such as
`./rgbgfx_test seed8.bin -b 207 -N 54,256`, can fail with "Color
mismatch after round-tripping" errors.
2026-07-13 21:31:05 -04:00
Rangi 59b6b15106 Run make format 2026-07-13 21:30:23 -04:00
Rangi 304cbb464f Factor out verboseDo to encapsulate magenta output color 2026-07-13 18:05:55 -04:00
Rangi ea3a8ee171 Use magenta color for some verbose output which was missing it 2026-07-13 17:47:38 -04:00
Rangi f0419eb183 More guards against invalid object file patch data 2026-07-13 17:47:38 -04:00
Rangi 4a6ea35299 Avoid overflow in counting tiles for unrealistically large images 2026-07-13 17:47:38 -04:00
Rangi 08354cf573 Explicitly encode assumption that an overlapping section exists 2026-07-13 17:47:38 -04:00
Rangi 417b5212d4 Fix OOM for too-small overlay files in 32K mode (rgblink -t) 2026-07-13 16:17:30 -04:00
Rangi 66312f55dd Avoid unsigned overflow/truncation from bad offset values in invalid object files 2026-07-13 13:17:09 -04:00
Rangi 21eaa2d2cc Fix a > to >=, and avoid underflow in some sanity checks 2026-07-13 13:14:06 -04:00
Rangi e9fa1e4e94 Avoid closing stdin/stdout/stderr standard streams 2026-07-13 13:02:46 -04:00
Rangi 8ab6190f96 Fix relative links in Markdown docs 2026-07-11 16:19:27 -04:00
Eldred Habert 22bd8a9bd5 Enable automatic generation of compile_flags.txt for clang-tidy via make tidy (#2008) 2026-07-11 16:11:14 -04:00
Rangi 96bc58b7f7 Add test case for local labels that superficially look like keywords 2026-07-11 13:51:53 -04:00
Rangi 4857ad1e00 Fix crash on image without any colors (all transparent) 2026-07-11 13:18:40 -04:00
Rangi 3b33c942ae Fix a crash and a limitation in RGBGFX unoptimized output
- If both banks 0 and 1 were completely filled, then
  `assume(bank == 0)` would false and potentially crash.
- If the input image had fully-background tiles, they would be
  incorrectly counted towards `nbTiles` and could cause the
  "Image contains N tiles, exceeding the limit" check to fail.
2026-07-11 12:32:31 -04:00
Rangi 5525dd902b Update external test commits 2026-07-10 23:29:19 -04:00
Rangi 4350a3252d Upgrade FreeBSD testing from 14.3 to 15.1 2026-07-10 23:14:39 -04:00
Rangi a8442bccc7 Group the two dep install commands together in Dockerfile 2026-07-10 23:12:49 -04:00
Rangi 00006a9d00 Use set -u too 2026-07-10 23:12:49 -04:00
Rangi a383bec96e Rename install_deps.sh to install-deps.sh for consistency 2026-07-10 23:12:49 -04:00
Rangi 226031c18b Use #!/usr/bin/env bash, not #!/bin/bash 2026-07-10 23:12:49 -04:00
Rangi 24a896f60a Move CI-only check-diff script to .github/ 2026-07-10 23:12:49 -04:00
Rangi a6f5e3efd1 Move CI-only check-format script to .github/ 2026-07-10 23:12:49 -04:00
Rangi c02b234bb1 Add CI job to check syntax of contrib scripts
Note that `zsh` is not installed in `ubuntu-slim` by default.
2026-07-10 23:12:49 -04:00
Rangi be4c1161bd Fix RGBGFX crash when trimming more tiles than exist after deduplication 2026-07-10 17:35:32 -04:00
Rangi 0e1e5e80f6 Use correct PRIu32 format spec for uint32_t usedTotal 2026-07-10 17:01:43 -04:00
Rangi 118f1f7543 Correct option letter in error message 2026-07-10 16:59:30 -04:00
Rangi eaa35ed26d Remove dead return after no-return fatal call 2026-07-10 14:36:46 -04:00
Rangi 7b39bcd412 Refactor expression for clarity
`tileData[realY + 1 % options.bitDepth]` relied on order of
operations and the fact that `1 % 1 == 0` but `1 % 2 == 1` for
correct indexing. However, it was not obvious at first glance,
and confusable with the more common `(base + offset) % size`
pattern. (And would need changing anyway if we ever support 4bpp.)
2026-07-10 14:30:25 -04:00
Rangi 255cd7f7f6 Fix a typo in a comment 2026-07-10 14:29:32 -04:00
Rangi b8011947a8 Correct assumption to avoid UB with 1 << 31 2026-07-09 21:36:34 -04:00
Rangi 39e7e064c9 Print correct color set limit value in error message 2026-07-09 21:29:02 -04:00
Rangi 7b97d7ce15 Fix RGBGFX crash on deduplicating transparent tiles 2026-07-09 21:24:59 -04:00
Rangi c667bac93a Use trailing return type for begin()/end() 2026-07-08 16:57:52 -04:00
Rangi db96144d49 Use [[nodiscard]] instead of [[gnu::warn_unused_result]] 2026-07-08 16:44:15 -04:00
Rangi 276a1a7952 Use const_cast more precisely to remove const without changing type 2026-07-08 16:42:53 -04:00
Rangi b219e03b38 Use more structured bindings 2026-07-08 16:38:32 -04:00
Rangi 824ebd80f9 Use [[fallthrough]]; attribute instead of // fallthrough comment 2026-07-08 16:30:52 -04:00
Rangi 5aedf31b46 Prevent 32-bit multiplication overflow
Basically impossible to achieve, but just in case
2026-07-07 17:02:46 -04:00
Rangi d94d0bcc2f Make explicit the assumption that targetFileName is checked before ever calling printDep 2026-07-07 17:02:46 -04:00
Rangi ed8e3e4d02 Reset errno = 0 before parseWholeNumber calls and errno == ERANGE checks 2026-07-07 17:02:46 -04:00
Rangi 30fd4514dc Add fail-safe for invalid RPN commands in bad object files 2026-07-07 17:02:46 -04:00
Rangi 08e83601f5 Fix CRLF check in parsing SDAS objects 2026-07-07 17:02:46 -04:00
Rangi e606f4e997 Fix condition for invalid bank range check (#2016) 2026-07-07 17:02:35 -04:00
Rangi 9008d8391c Consistently use sudo apt install -Uyq in workflows (two other cases) 2026-07-07 02:57:48 -04:00
Rangi 21682e8814 Consistently handle negative shifted macro args (#2014)
Negative macro arguments count from the end, i.e. `\<-1>`
is equivalent to `\<_NARG>`, even after `shift`ing them.

Negative arguments cannot be used to access shifted values.
2026-07-07 02:19:37 -04:00
Rangi f82b0838a1 Disallow minus sign before symbol name in bracketed macro arguments (#2015)
This was being silently allowed but without actually negating the symbol's value
2026-07-07 00:42:09 -04:00
Rangi dabd0a08d2 Remove impossible condition (uint64_t > UINT64_MAX) 2026-07-06 23:08:05 -04:00
Rangi c3e10dbb03 Safeguard against localtime or gmtime failure 2026-07-06 23:05:19 -04:00
Rangi 3883c2a2b8 Reuse scrambleLimit instead of its equivalent expression 2026-07-06 22:50:31 -04:00
Rangi cd0caaaceb Clarify comment about -Wno-* behavior with parametric warnings 2026-07-06 22:44:39 -04:00
Rangi 1e4a060d9e Guard against undefined behavior in double2fix
I haven't found input which triggers any, but this is more
technically correct just in case.
2026-07-06 22:38:06 -04:00
Rangi fe3b238dea Do not allow defining labels outside sections (#2013)
Creating a `Symbol` with `type = SYM_LABEL` but `section = nullptr`
is inconsistent and dangerous. I was not able to cause any buggy
behavior so far, but it's safer and reasonable to not create such
a symbol in the first place.

The main consequence is that `DEF(LabelOutsideSection)` will now
evaluate as 0.
2026-07-06 16:18:42 -04:00
Rangi 46a6966b70 Fix $8000_0000 % -1 to warn with -Wdiv like $8000_0000 / -1 does (#2012) 2026-07-06 15:08:44 -04:00
Rangi be3fc61859 Fix included linker scripts with no newline at EOF (#2011) 2026-07-06 14:01:40 -04:00
Rangi 0adbb92e50 Same-line shellcheck comment explanations
These are obvious even with `git grep shellcheck`.
2026-07-06 04:43:08 +02:00
Rangi 09da4e001a Use Bash arrays instead of POSIX splitting when possible 2026-07-06 04:43:08 +02:00
ISSOtm 3eb8c7e8f2 Let tests know they are being run under Cygwin 2026-07-05 20:58:50 -04:00
Rangi 189c679e13 Parallelize build jobs according to CPU count 2026-07-05 20:58:50 -04:00
Rangi 5d5000011a Remove CMAKE_INSTALL_PARALLEL_LEVEL
This value "has no impact unless `INSTALL_PARALLEL` is enabled",
and since our install procedure is so simple, I don't think we need
to add that. (The `make install` commands don't use `-j` either.)
2026-07-05 20:58:50 -04:00
Rangi f81f5fe63c Use git clean -fdx instead of make clean in external projects 2026-07-05 20:58:50 -04:00
Rangi 6e643406a2 Refactor warning diagnostic code to reduce repeated output logic 2026-07-04 23:02:16 -04:00
Rangi de36a11be7 Prevent rgblink -Wno-* disabled warnings from printing a location trace anyway
Fixes #2005
2026-07-04 23:02:16 -04:00
Rangi 799c55b4dd Add [[gnu::format(printf, 1, 2)]] attribute to variadic function sectError 2026-07-04 23:02:16 -04:00
Rangi ddd8f37508 Remove unnecessary default constructor for Token 2026-07-04 18:48:03 -04:00
Rangi c55dec254d Remove unnecessary const_casting methods 2026-07-04 18:45:41 -04:00
Rangi 0996a2f5ed Encapuslate UTF-8 decoder state in a struct 2026-07-03 19:35:23 -04:00
Rangi e3c594c250 Explain why we do not use file(GLOB ext_projects CONFIGURE_DEPENDS "external/*.cfg") 2026-07-03 18:38:07 -04:00
Rangi daba94b495 Remove pointless std::move from non-move constructor 2026-07-03 13:12:29 -04:00
Rangi 2f604bfa9a Fix error message when an rgbgfx output file cannot be created 2026-07-03 13:08:28 -04:00
Rangi 9bbb2247a7 Fix rgbasm -Werror=... not adding to the final error count 2026-07-03 13:05:33 -04:00
Rangi 9b4df11473 Describe how to patch external projects 2026-07-02 12:37:26 -04:00
ISSOtm 4a77c42ed7 Remove redundant labels from tests
It's possible to filter tests by name (`-R`)
2026-07-02 10:39:59 -04:00
ISSOtm b12eac356e Try to automatically determine the test OS name 2026-07-02 10:39:59 -04:00
ISSOtm 82702d8149 Point out an ordering dep isn't a success one 2026-07-02 10:39:59 -04:00
ISSOtm 1f9cc9651a Avoid fetching some repos twice
Also enables a nice bit of logic cleanup
2026-07-02 10:39:59 -04:00
ISSOtm 04b9c7fa3d Move external tests files to tests/external/ 2026-07-02 10:39:59 -04:00
ISSOtm 5e8a6cde20 Remove the need to explicitly list external tests
I like having a single source of truth!
2026-07-02 10:39:59 -04:00
ISSOtm d59081a918 Shorten external test prop names slightly
Makes the scripts a bit easier to read, I think
2026-07-02 10:39:59 -04:00
Rangi 250a94c588 Tell Dependabot to ignore minor and patch releases (#2001)
We specify major-only versions, e.g. `actions/checkout@v7`,
which automatically uses the latest minor+patch of that release.
2026-07-02 13:55:16 +02:00
dependabot[bot]dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>Rangi
37818a1847 Updates actions/checkout from 6 to 7 (#1999)
---------

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Rangi <[email protected]>
2026-07-01 23:06:12 -04:00
Rangi 0e3d1df129 Generate install.sh with make -n install (#1996) 2026-06-30 21:28:45 +02:00
Rangi 586c3073f9 Revert "Remove static compile flags"
This reverts commit e3fc46f933.

We need compile_flags.txt to support clang-tidy
2026-06-30 11:16:14 -04:00
ISSOtm e3fc46f933 Remove static compile flags
We support CMake now, which can generate a more accurate `compile_commands.json` database
(and our `develop` preset automatically enables them)
2026-06-30 16:52:31 +02:00
Rangi 3941a28661 Remove unused variables 2026-06-30 10:30:31 -04:00
Rangi a829e6c067 Refactor the test harness for external repo tests (#1994)
- Use CTest labels to filter tests ("internal"/"external", "free"/"nonfree",
  and individual tool+project names) instead of defining `TESTS_RUN_NONFREE`
- Allow each external test to run independently in CTest
- Remove the unused no-op `fetch-test-deps.sh --only-internal` option
2026-06-30 16:19:41 +02:00
Rangi b252877b05 Comment why we use Ninja as the generator for CMake 2026-06-28 17:38:50 -04:00
Rangi 22a3625144 Explicitly test installation with Make and CMake (#1992) 2026-06-28 17:31:24 -04:00
Eldred HabertandRangi 6cba5518e9 Install Pillow together with the rest of our CI deps (#1952)
We shouldn't assume how the user wants to install things.
In particular, `--break-system-packages` is egregious.

---------

Co-authored-by: Rangi <[email protected]>
2026-06-28 13:06:40 -04:00
Rangi caf561457d Warn about CMake <3.27 for Windows-targeted builds (#1989)
We only use `$<INSTALL_PREFIX>`` when cross-compiling for MinGW on Linux,
so the relevant block is now wrapped in `if(WIN32)`
so a user can more easily lower the `cmake_minimum_required` version.
2026-06-24 23:53:11 +02:00
Rangi fc7d9ad573 Handle more numeric literal syntax errors in linker scripts 2026-06-09 18:46:51 -04:00
Rangi 0b91bf654b Rename BaseV to ValidBaseV 2026-06-09 17:55:38 -04:00
Rangi 6b2b6e6000 Fix off-by-one error with INCLUDEd linker script line numbers 2026-06-09 16:16:21 -04:00
Rangi42 f72a4d53e2 Add -Wlarge-constant to RGBLINK as it is for RGBASM 2026-06-09 16:16:21 -04:00
vulcandth 998f636495 Fix rgbgfx -Z palette overgeneration on merged color sets (#1912)
- Fix logic for color set comparison (which affects sorting them)
- Prune color sets which are proper subsets of newly-encountered ones
  (a comment implied we were already doing this, but we weren't)
- Add more verbose logging to debug this behavior
2026-06-07 09:55:05 -04:00
Rangi 075f132d77 Use a smaller unordered_map just for looking up line-leading keywords 2026-05-27 22:01:20 -04:00
Rangi 31e1d2ec87 Do not build up a std::string when skipping leading keywords 2026-05-27 22:01:20 -04:00
Rangi 673c62414f Use std::string_view keys for UpperMap
This avoids constructing temporary `std::string` objects on lookup
2026-05-27 22:01:20 -04:00
Rangi 007672f080 Intern identifier strings only after checking for keywords 2026-05-25 23:55:57 -04:00
Rangi 7eaca1577d Update .gitattributes for test/asm/crlf.asm
A fresh clone showed a diff even without any bytes changed.
2026-05-25 22:57:52 -04:00
Rangi 996469ee28 Ensure CRLF line endings are preserved when necessary
Some test cases need CRLF line endings checked out even on Unix.
Also some source files had inadvertently contained CR bytes.
2026-05-25 22:00:45 -04:00
Rangi 55db252a8f Simplify the lexer by removing BufferedContent and inlining ViewedContent (#1981)
Instead of reading 64 characters at a time into a rolling buffer
as `shiftChar()` is called, we read 8192 characters at a time into
a complete buffer before any `peek()`/`shiftChar()` operations.
2026-05-25 21:05:59 -04:00
Rangi cfa0adf295 Intern strings used as identifiers (for labels, constants, macros, charmaps, etc) (#1980) 2026-05-25 16:22:29 -04:00
Rangi ed19806434 Don't hard-code std::string as the key type for InsertionOrderedMap 2026-05-25 14:21:18 -04:00
Rangi a0d96a0856 Factor out repeated expandedSymName logic 2026-05-25 14:21:18 -04:00
Rangi 4b992bfea5 Use the name of sym_GetPC() instead of hard-coding "@" again 2026-05-25 14:21:14 -04:00
Rangi 141a7fe22b Add assume checks for expected Token types, like RPNValue has for RPNCommands 2026-05-25 14:14:34 -04:00
Rangi 43ffc89bb4 Correct comment SIZE_MAX to UINT16_MAX 2026-05-25 13:55:12 -04:00
Rangi 72a410c007 Encapsulate charmap_Init, like sym_Init 2026-05-25 13:54:26 -04:00
Rangi 090768e2c9 Some Expansion references can be const 2026-05-23 13:06:09 -04:00
Rangi42 5f2ee530b8 Pass -Wno-unused-but-set-variable by default, not just in make develop
The Bison-generated parser skeleton otherwise triggers this warning
with Apple clang++ for `yynerrs_`.
2026-05-23 01:40:13 -04:00
Rangi 4a173130b9 Optimize skipToLeadingKeyword for the common ViewedContent case (#1968) 2026-05-22 19:06:12 -04:00
Rangi ef2f021892 Simplify sectError handling, without the need for an empty-string sentinel
The `errorNoTrace` case for the empty-string sentinel was unreachable,
so we can just have a `fatalNoTrace` error right away.
2026-05-22 18:17:25 -04:00
Rangi dce14fd4b8 Use C++20 concepts to require constraints on template parameters (#1977) 2026-05-22 17:54:34 -04:00
Rangi 728bed39d5 Do not support GCC 9 (#1978)
This will let us use C++20 features that GCC 9's experimental
C++20 support did not yet cover, such as "concepts".

This reverts some commits:
- 6bcd79b997
- d5ce5329ea
- 728d14879b
2026-05-22 16:46:46 -04:00
Rangi 48fcd9a0ca Use templates to reduce the redundant number-lexing functions (#1963) 2026-05-21 23:13:09 +02:00
Eldred Habert 1871b0a0b8 Use Windows git for checkout's post-job cleanup, not Cygwin's git (#1953)
This fixes an error from using Cygwin's git in checkout's post-job cleanup
2026-05-21 15:25:29 -04:00
Rangi d56dbbb4bf Refactor section creation errors, and simplify output for single errors (#1964) 2026-05-21 13:01:08 -04:00
Rangi 728d14879b Consistently use -std=c++2a, not -std=c++20
Commit 63a911e657 switched
to `-std=c++20` since at the time we required GCC 10 or higher.

Commit d5ce5329ea partially reverted
to `-std=c++2a` since we once again support GCC 9, which did not
yet handle `std=c++20`.
2026-05-20 17:09:23 -04:00
ISSOtm 0ed8c2f8ee Docu-comment an implicit correctness assumption 2026-05-14 14:47:23 +02:00
ISSOtm d5186b088b Link against (non-debug) MSVC runtime statically 2026-05-14 14:47:23 +02:00
ISSOtm fee177d0b9 Link zlib and libpng statically by default
Only when we are building them ourselves, that is.
2026-05-14 14:47:23 +02:00
Rangi 42e3da837c Comment the commands to use for profiling and code coverage 2026-05-09 20:52:45 -04:00
Rangi 358e8fe636 Add more tests of lexer's skipToLeadingKeyword behavior (#1971)
The character after the ending keyword gets blue-painted.
2026-05-09 15:56:17 -04:00
Rangi c50a849757 Verbose tracing shows lexed and captured values (#1969) 2026-05-09 12:01:29 -04:00
Rangi 57111b219a Describe the wine-shim Makefile target (#1967) 2026-05-09 15:27:08 +02:00
Rangi be2d028ae6 Format getopt errors more like others (sentence case, quoted options) (#1966) 2026-05-09 13:27:34 +02:00
ISSOtm 1af8bdda16 Have CMake put RGBDS executables in our own dir
Not the top-level if we are part of a larger project!
2026-05-06 18:49:19 +02:00
ISSOtm fa22b78e7d Use type instead of which in shell scripts
The latter is not a Bash built-in,
and some environments turn out to produce non-conforming
return codes.

`type` is provided by Bash itself,
so we can rely on it better.
2026-05-05 16:03:33 +02:00
Rangi42 f0161b41c8 Update readHexNumber for consistency with other routines 2026-04-29 12:31:00 +02:00
Rangi42 ca23210f18 Refactor and rename some numeric literal lexing for asm and linkerscript consistency 2026-04-29 12:15:14 +02:00
Rangi42 85e044f5be Use std::move when relevant in Token constructor 2026-04-28 13:32:45 +02:00
Rangi42 60e76b2da4 Avoid UB if new OOMs by removing std:nothrow 2026-04-28 13:28:32 +02:00
Rangi42 3c7488c131 Some refactoring, renaming, and debug assertions for clarity and safety
- Add more `assume()` checks in the lexer
- Replace double negative "`disable* = false`" with "`enable* = true`"
- Naming convention of "`std::deque<> *Stack`" when relying on
  `.push_front()` and `.pop_back()`
2026-04-27 18:47:06 +02:00
Rangi42 9fd0c0297f Fix invalid character in bracketed macro argument
The invalid character should not be consumed by `shiftChar()`.
2026-04-27 17:11:20 +02:00
Rangi42 c1c7e64249 Add two assume calls to verify lexer arguments 2026-04-27 15:35:10 +02:00
Rangi42 a18b2f1049 Consistently lex local labels after keywords, even when skipping/capturing 2026-04-27 15:28:30 +02:00
Rangi42 3f144b7713 Add test case for local labels after block-ending keywords 2026-04-27 15:04:12 +02:00
Rangi42 eeb3a73210 Small optimization to skipToLeadingKeyword 2026-04-27 14:26:47 +02:00
ISSOtm 37d39f91f6 Build using GCC 9 in CI
Checking that we don't regress this later.
Ubuntu doesn't package GCC 8, and we don't support it either,
so that is a good compromise for now.
2026-04-26 23:30:59 +02:00
ISSOtm d5ce5329ea Fix compilation with GCC 9 2026-04-26 23:30:59 +02:00
ISSOtm 6bcd79b997 Revert "Derive operator!= from operator== (#1660)"
This reverts commit 993879a2ed.
2026-04-26 23:30:59 +02:00
ISSOtm decc5f7153 Let parallelism be determined automatically
Ninja is parallel by default;
however, it is not used on FreeBSD, so we still have to specify it there.
2026-04-20 15:32:54 +02:00
ISSOtm a3c953ea46 Pass C++ compiler to CMake via env var
This is a little terser
2026-04-20 15:32:54 +02:00
ISSOtm d43049c84d Imply CMake source dir
Shortening our command lines slightly
2026-04-20 15:32:54 +02:00
ISSOtm b6ff54acc3 Avoid libpng compilation on Windows spamming the Microsoft banner 2026-04-20 15:32:54 +02:00
ISSOtm 71fc449452 Use Ninja on Unix platforms
It has nicer UX, only showing full command lines when they fail.
It could also be slightly faster, though I've seen no measurable improvement.
2026-04-20 15:32:54 +02:00
ISSOtm 59dd1fbbf8 Correct naming of MSBuild CMake presets
You can build with MSVC without MSBuild
2026-04-20 15:32:54 +02:00
ISSOtm 2502d130eb Use Ninja on Windows
MSBuild is slow, and its output is hard to parse visually
(it's all interwoven, like Make without `--output-sync`).
Since we use CMake anyway, we don't care about the build system.
2026-04-20 15:32:54 +02:00
ISSOtm a78a268cb4 Pass convenience options to Make invoked via CMake
Can't pass them to plain Make invocations,
because we try to be compatible with macOS' old Make.
2026-04-20 15:32:54 +02:00
Rangi42 0553491107 Replace a FIXME comment with an explanation of why we can't/won't "fix" it 2026-04-20 14:51:38 +02:00
Eldred Habert 6edac50cc4 Target an even older version of Mac OS X
We can, so let's not ask if we should!

More realistically, we can right now so it's nice to mark that we do, but this is subject to be re-evaluated if any later changes conflicts with this.
Note also that this only affects the binaries we distribute with our releases!
It may very well be possible to compile RGBDS on even older versions of Mac OS X if you have the appropriate compiler setup;
we're just not testing it nor offering such binaries ourselves, but third-party packagers are welcome to do so.
(Looking at you, TigerBrew! ;)
2026-04-20 14:33:59 +02:00
Rangi 77dceaf43a Update libpng to 1.6.57 (#1958) 2026-04-20 13:55:27 +02:00
Rangi cfec017fed Consolidate, refactor, and bugfix the lexer's handling of captures and skips (#1957)
- Do not error about local labels following keywords in skips or captures (fixes #1955)
- Do not incompletely attempt to handle line continuations in skips (fixes #1956)
- Rename `skipToLeadingIdentifier` to `skipToLeadingKeyword`, refactor to merge
  `skipToEOL` into it, and use it for both skips and captures
2026-04-20 13:04:20 +02:00
Rangi42 12186fdccc Keep more non-declaration initialization within the for loop clause 2026-04-19 22:01:56 +02:00
Rangi 71dfab3365 Use charmapEntry to simplify charmap_HasChar 2026-04-17 15:39:30 -04:00
Rangi 20b11039c9 Keep trie edges sorted for O(log N) lookup 2026-04-17 15:39:30 -04:00
Rangi eb9e9c0f33 Replace charmaps' fixed-size 256-value array with a vector of pairs
This should save memory at the cost of O(n) instead of O(1) access,
which should be okay because the `next` vector is generally small.
2026-04-17 15:39:30 -04:00
Rangi ac2a022124 Set the Cygwin shell once as a default for its job 2026-04-15 23:54:12 -04:00
Rangi 1249c4ef58 Revert "Set up Cygwin before cloning our repo"
This reverts commit aeedfb35a6.
2026-04-15 21:53:29 -04:00
ISSOtm aeedfb35a6 Set up Cygwin before cloning our repo
Otherwise different Gits set up and tear down the repo,
leading to issues.
2026-04-16 03:16:56 +02:00
ISSOtm 63609871f4 Cache downloaded dependencies for MinGW build also
Forgot about you!
2026-04-16 02:56:14 +02:00
ISSOtm 393eaa9dc1 Fix a Fix typo ;) 2026-04-16 02:54:40 +02:00
Rangi b3c3981a90 Use a separate CI step to install clang-format before running it
Also use short `-Yqu` flags like our other invocations
2026-04-15 20:12:48 -04:00
ISSOtm 185a5c5369 Add necessary flags to apt install for checkformat 2026-04-16 01:58:42 +02:00
ISSOtm 4ea15ed85a Touch up checkformat.bash
The logic can be slightly simplified,
and the script doesn't require Bash after all.
2026-04-16 01:05:03 +02:00
ISSOtm b218fd65a0 Install clang-format on ubuntu-slim runner
Unlike the “fat” runners, that's not installed by default.
Why that causes `find` to nonetheless exit normally, I have no idea.
2026-04-16 01:02:30 +02:00
ISSOtm 81edc27b6e Fix two warnings on MinGW 2026-04-16 00:57:33 +02:00
ISSOtm dc52487d31 Make -Wformat non-fatal on MinGW
See the comment for a rationale.
2026-04-16 00:57:33 +02:00
ISSOtm 6b994b1737 Fix some warnings raised by MSVC 2026-04-16 00:57:33 +02:00
ISSOtm 0849eb801e Decouple -Werror from MORE_WARNINGS 2026-04-16 00:57:33 +02:00
ISSOtm e6916360f4 Set some flags only for our own code 2026-04-16 00:57:33 +02:00
ISSOtm a802bcb320 Switch MinGW building to CMake
Automatically grabs and compiles libpng, and avoids having hacky lines in our Makefile
(the compiler specification *should* be orthogonal to the build target!)

Also move the MinGW package install to the `install_deps.sh` script,
to move logic off of the YAML.
2026-04-16 00:57:33 +02:00
ISSOtm 846aa975f1 Stop trying to install md5sha1sum
macOS already provides it and Homebrew doesn't override it,
so that's a no-op anyway.
2026-04-16 00:25:58 +02:00
ISSOtm b65649ca73 Tidy up system info 2026-04-16 00:25:58 +02:00
ISSOtm 62ddabb570 Avoid hardcoding Brew's prefix 2026-04-16 00:25:58 +02:00
ISSOtm d48d6fe5d9 Bump our macOS CI runners 2026-04-16 00:25:58 +02:00
Eldred Habert ac0f93ffda Delete GCC static analyzer workflow (#1950)
As for GCC 15, it is stated not to be suitable for C++ code,
and we have gotten annoyingly many false positives out of it,
so let's just disable it (as per
https://github.com/gbdev/rgbds/pull/1950#issuecomment-4243959126).
2026-04-14 22:04:20 +02:00
ISSOtm a1712cb6d9 Run patch format check on ubuntu-slim 2026-04-14 22:03:29 +02:00
Eldred Habert 2968bee86f Run macos-static tests using CTest (#1947)
We can, so why not? It has benefits.
2026-04-14 01:25:47 +02:00
ISSOtm dec7a74166 Fix macOS static build
- Turns out the variable wasn't being set, and thus the flags weren't being passed
- Passing the flags via those variables is basically a bad idea, and it
  turns out to break libpng's config script
- The flags need to be passed during link as well, otherwise the binary
  isn't linked as a fat one
2026-04-14 00:01:14 +02:00
Eldred Habert 9c4e53d1c4 Transition CI from pkg-config to pkgconf (#1942)
Upstream has changed the providing implementation,
so that package will eventually be removed.
2026-04-13 19:46:05 +02:00
Eldred HabertandRangi 463edc3c01 Enable color output in our Linux and macOS CI tests (#1939)
Co-authored-by: Rangi <[email protected]>
2026-04-13 19:31:01 +02:00
Rangi 73941c3249 Add .git-blame-ignore-revs as a blame.ignoreRevsFile 2026-04-13 13:26:29 -04:00
Rangi42 7596ae67b5 Fix internal link to docs/CONTRIBUTING.md 2026-04-13 02:34:46 -04:00
Rangi fc08ab6aee Print a GitHub Actions error annotation if install_deps.sh fails
See https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-commands#setting-an-error-message
2026-04-13 02:04:56 -04:00
Rangi c7663423d6 Update our Dockerfile to base on Debian 13 slim 2026-04-12 22:36:41 -04:00
Rangi 1ff73cd5e0 Revert "Stop trying to explicitly install pkg-config in CI (#1940)"
This reverts commit bc3572ec37.

Turns out that our Dockerfile *does* need to install `pkg-config`
on the `debian:12-slim` image.
2026-04-12 22:33:25 -04:00
Eldred Habert 65ec1af1e8 Switch macOS static build to CMake (#1938)
This moves the tortuous flags out of our CI scripts,
and allows us to benefit from the libpng download caching.
2026-04-12 22:26:15 -04:00
Eldred Habert bc3572ec37 Stop trying to explicitly install pkg-config in CI (#1940)
Turns out it's already installed by default,
but under a different name that's causing extra ops
for no real gain.
2026-04-12 22:08:38 -04:00
Eldred Habert 78281a4aaa Break down CTest cases into each test suite (#1931)
This allows parallelising the individual programs'
test suites, which can save a few minutes!
2026-04-12 21:29:12 -04:00
ISSOtm 6e51ab6f55 Remove a duplicate warning flag
We always pass this one
2026-04-13 01:52:25 +02:00
ISSOtm 52aaf7e3b3 Fix MSVC dependent option syntax
This is not how CMake works lol
2026-04-13 01:37:38 +02:00
ISSOtm 09db4f25dd Bump the maximum version of CMake tested with
Future-proofing!
This is both the version on my own machine,
and what's distributed by Homebrew (thus on GitHub's runners).
2026-04-13 00:33:31 +02:00
Eldred Habert fed5db7743 Move documentation outside of the root directory (#1936) 2026-04-12 18:09:40 -04:00
ISSOtm c6dbec7830 Stop trying to run version test on Cygwin and FreeBSD
Fixes #1934
2026-04-12 23:40:39 +02:00
Eldred Habert 017b0119e6 Overhaul checkdiff CI (#1930) 2026-04-12 16:06:28 -04:00
Eldred Habert 0e6e91d048 Avoid copying RGBFIX binary when testing it (#1933)
This is just cleaner in general,
and also avoids separating it from any DLLs it may be needing.
2026-04-12 13:23:51 -04:00
Rangi df0b557638 Don't allow invalid interpolations to occur (#1925)
Fixes #1921
2026-04-11 18:16:07 +02:00
Eldred Habert 123201ecc6 Avoid repeatedly specifying bash as the command shell in CI (#1926) 2026-04-10 22:04:16 -04:00
Eldred Habert 422e8eec20 Generate Bison output in CMake build tree (#1929)
Again, moving away from in-tree builds
2026-04-10 18:46:57 -04:00
Rangi 49a4da2235 Revert "Generate Bison output in CMake build tree"
This reverts commit 4577ff16bc.
2026-04-10 17:18:40 -04:00
ISSOtm 4577ff16bc Generate Bison output in CMake build tree
Again, moving away from in-tree builds
2026-04-10 22:56:59 +02:00
ISSOtm b430d5a8b8 Note why we are not using a CMake list to pass Bison flags 2026-04-10 22:33:54 +02:00
ISSOtmandvulcandth a5be1d886e Copy DLLs to appropriate dir for install-less Windows testing
Co-authored-by: vulcandth <[email protected]>
2026-04-10 18:01:16 +02:00
vulcandth c3b47abcc8 Use CPack for Windows packaging 2026-04-10 18:01:16 +02:00
Rangi 2cfb2b2533 Remove the claim that only exported symbols appear in sym and map files
Fixes #1916

This claim was true in v0.4.2, but intentionally changed in v0.5.0.

rgblink(1) already explains that "all visible labels and exported numeric constants"
will appear, which is a more appropriate location than rgbasm(5).
2026-04-09 21:28:07 -04:00
Rangi 187e1a55b8 Add examples for using character literals instead of strings as numbers
Fixes #1890
2026-04-08 21:42:10 -04:00
Rangi 143d503c6c Update libpng to 1.6.56 (#1922)
Fixes #1920

This also switches from Homebrew-installed `sha2`
to macOS-preinstalled `shasum`.
2026-04-08 20:58:41 -04:00
Rangi 6e13779b8b Use C++-sryle static_cast, not C-style explicit cast
`uint32_t(param)` is actually functional notation of explicit casting,
not a direct constructor call. `uint32_t{param}` would be uniform
initialization syntax for a constructor call, but would not allow
narrowing from `uint64_t`. See issue #1904 for discussion.
2026-04-08 20:05:36 -04:00
ISSOtm ace2f3e613 Document why lib src cache is cross-OS
See https://github.com/gbdev/rgbds/pull/1919#discussion_r3048475606
2026-04-08 12:25:43 +02:00
ISSOtm 8d1a60d6b5 Switch release process to a lightweight runner
Easier to provision, less concurrency overhead for us
2026-04-08 12:25:43 +02:00
ISSOtm 11ac22d8f5 Switch from an Action to gh release
Fewer supply chain risks whooooo
2026-04-08 12:25:43 +02:00
ISSOtm 812e1f6d4b Remove dead code checking for version tag
It was also vulnerable to template injection attacks.
2026-04-08 12:25:43 +02:00
ISSOtm 8dc052475b Avoid caching in our release workflow
This is a malware distrib vector,
and we run this workflow sporadically enough
that we can afford to do so uncached.
2026-04-08 12:25:43 +02:00
ISSOtm 133eaa4e92 Build Windows release in, well, Release mode 2026-04-08 12:25:43 +02:00
ISSOtm 57f1894fbd Fix installing Windows build deps on release 2026-04-08 12:25:43 +02:00
ISSOtm 021fe01eb0 Avoid CMake looking at test dir if testing is disabled
This saves building the two test support programs.
2026-04-08 12:25:43 +02:00
Rangi 9b4b4a581b Check for overflow in fixed-point precision suffix (#1918)
Fixes #1917
2026-04-07 17:06:54 -04:00
ISSOtm bcff14b596 Clean up docs workflows
Usethe `ubuntu-slim` runner to (hopefully) get faster setup times
2026-04-07 21:48:26 +02:00
Rangi 11f6278d95 Refactor lexing of fixed-point numbers (#1915)
This incidentally fixes a bug with too-long fixed-point literals
that have precision suffixes.
2026-04-06 21:45:34 -04:00
Rangi ede9405daf Fix some fixed-point literal lexing issues (#1914)
- No fractional digits are necessary after the decimal point,
  e.g. `42.` is valid instead of `42.0`
- Error messages refer to "fixed-point" not "integer" constants
- Test more carefully for lexing unrelated to underscores
2026-04-04 13:41:45 -04:00
Rangi 25bf0e9e2c SOURCE_DATE_EPOCH must be in base 10 2026-03-30 21:31:37 -04:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 167a7ee80c Bump the actions group with 6 updates (#1910)
- Updates `actions/checkout` from 4 to 6
- Updates `docker/login-action` from 3 to 4
- Updates `Chizkiyahu/delete-untagged-ghcr-action` from 5 to 6
- Updates `actions/upload-artifact` from 4 to 7
- Updates `actions/download-artifact` from 4 to 8
- Updates `cygwin/cygwin-install-action` from 4 to 6

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-30 21:28:45 -04:00
Rangi 4ce4162da4 A little .yml cleanup 2026-03-30 20:29:06 -04:00
Rangi 18b074711e Fix rgbasm -s not overriding a previous -s 2026-03-30 20:18:19 -04:00
Eldred Habert 44b6a2c453 Add a key that turns out to be required
The documentation is super flaky tbh
2026-03-31 02:14:24 +02:00
Eldred Habert 3fc3130364 Fix some YAML syntax
I prefer TOML and JSON anyway...
2026-03-31 02:12:29 +02:00
Eldred Habert 5090395ca7 Enable GH Actions to be automatically bumped
Turns out those get new major releases somewhat often, and we get deprecation warnings and brownouts and all that.
Automatic bumps should help us avoid that, though I've set a *very* large scan interval to shield us from churn.
2026-03-31 02:03:37 +02:00
Rangi efd4373a56 Check for a SDAS "Def" or "Ref" line before reading a subsequent integer
Without checking `token[0]` through `token[2], `readInt` starting at
`&token[3]` could go out of bounds on a badly-formed SDAS .rel object file
2026-03-30 19:45:44 -04:00
Rangi 92a31ff7df Correct list numbering 2026-03-30 19:23:42 -04:00
ISSOtm 8025c2ae66 Require uploaded artifacts to be non-empty
We can get silent failures otherwise
2026-03-28 11:31:49 +01:00
ISSOtm f7831ce8da Simplify CTest pre-run test command 2026-03-28 01:21:08 +01:00
ISSOtm b736fa22ff Suppress status messages when fetching test deps
Let's avoid status message spam.
2026-03-27 22:07:28 +01:00
ISSOtm 0692d1d28c Avoid passing a C++-only flag when compiling C files
Avoids compiler warnings. This can happen because zlib and libpng are both C projects,
and we build them from source if they are unavailable.
2026-03-25 17:11:46 +01:00
Eldred Habert 4b69d5cc8a Fix name of test repo cache used by macos-static (#1909) 2026-03-24 09:02:28 -04:00
Eldred Habert 52aa518e20 Enable Git colours in CI 2026-03-24 12:40:34 +01:00
Rangi42 919a5dba89 Correct CONTRIBUTING.md to accurately describe test/fetch-test-deps.sh 2026-03-23 11:13:23 -04:00
Eldred Habert d9932b9031 Always clone deps with --depth=1 (#1908) 2026-03-23 11:02:29 -04:00
Rangi 66e521e9e5 Factor out common error-handling functions for lexing numbers of different bases (#1888) 2026-03-19 15:32:25 -04:00
ISSOtm b8e425b41d Avoid passing irrelevant flags to our deps 2026-03-19 20:30:12 +01:00
ISSOtm 6b0658fb59 Cache library deps on Windows
Besides the immediate performance improvement, reducing
the frequency of downloads should make spurious
failures (network, rate-limiting...) less bothersome.
2026-03-19 20:30:12 +01:00
ISSOtm 91fdfcd179 Specify MSVC architecture using presets
Shortens CI command lines slightly,
and documents the requirement.
2026-03-19 20:30:12 +01:00
ISSOtm 5abebcf61f Specify build type implicitly
Again, shortening the CMake command line.
Unfortunately, this doesn't apply to `--install`.
2026-03-19 20:30:12 +01:00
ISSOtm 145473d9bd Use develop preset on FreeBSD
Leak detection is not supported, so we have to disable it.
2026-03-19 20:30:12 +01:00
ISSOtm 2fb64816a9 Indicate more clearly that LTO is not used in debug mode
This has made me worried once or twice
2026-03-19 20:30:12 +01:00
ISSOtm 7cf330e3f9 Enable CMake parallelism in a central place
Shortening command lines is useful
2026-03-19 20:30:12 +01:00
ISSOtm 2e66515096 Invoke CTest directly
This allows passing it arguments directly, like `--verbose` here
(which is more appropriate for CI use).
2026-03-19 20:30:12 +01:00
ISSOtm c93f377bfb Enable colour diagnostics in CI
At least for CMake.
2026-03-19 20:30:12 +01:00
ISSOtm d6d1844d60 Seek lib deps via CMake FetchContent
`FetchContent` respects existing installs, and downloads and compiles the libs
if they aren't found.
This is admittedly a little finicky, since this ignores the usual `Find*`
modules provided by CMake, requiring a bit of glue on our side.

But, one upside is that this moves that logic from our CI into the build system,
which can thus benefit other downstream users.
This also opens the door to some improvements in upcoming commits.

Doing this with Bison turned out to be much more painful, however, due to
`FindBISON` providing the specific `bison_target` command; thus, it remains
installed externally, so that it can be picked up by `FindBISON`.

This also bumps our CMake version requirement slightly, though it's
possible that older versions keep working, or could be supported with small patches;
however, our CI doesn't provide anything below 3.31, so we can't check.
2026-03-19 20:30:12 +01:00
ISSOtm fd38a3dbc4 Add CMake and YAML files to EditorConfig
Since we use a different style for those
2026-03-19 20:30:12 +01:00
ISSOtm 60d58723aa Use a modern GNU Make for CMake CI on macOS
We can assume that CMake handles old Make correctly,
but we'd like to benefit from improvements
like output batching.
2026-03-19 20:30:12 +01:00
ISSOtm a9b6cda77b Reorder CMakeLists commands by topic 2026-03-17 08:18:50 +01:00
ISSOtm 091452d591 Remove call to configuring version file
Missed in 5c4ca36c5a
2026-03-17 05:28:23 +01:00
ISSOtm df1d909853 Improve documentation of CMake bin output dir 2026-03-17 05:27:39 +01:00
ISSOtm fb4e08ce71 Tidy up CMake sanitizer flags
Set the flags in the build itself instead of trying to override the cache
(this was an artifact of when I didn't understand the CMake variable model),
which avoids them being duplicated on each re-configuration.

Also remove `-g`, since it's defined by the build type by default
(we leave it up to the user to choose Debug or RelWithDebInfo).
2026-03-16 20:23:47 -04:00
ISSOtm 7244b9d17e Silence the same warning as in the Makefile
As requested by https://github.com/gbdev/rgbds/pull/1899#discussion_r2943039607
2026-03-16 20:23:47 -04:00
ISSOtm 5c4ca36c5a Harmonise passing build version
Avoid running some configure step, instead pass it as a define like the Makefile
2026-03-16 20:23:47 -04:00
ISSOtm 292c297518 Sync Windows release steps with new CI 2026-03-16 20:23:47 -04:00
ISSOtm a5bb431d47 Fix debug-mode building on Windows
ASan seems broken, and the debug-mode DLL names are different
2026-03-16 20:23:47 -04:00
ISSOtm 10e1c9d187 Avoid passing sanitizer options to MSVC linker
Apparently it does not support them
2026-03-16 20:23:47 -04:00
ISSOtm d8722b6705 Use imported targets for linking to libpng
This massively simplifies the process lol
2026-03-16 20:23:47 -04:00
ISSOtm 2d0ac7eeb0 Disable exceptions and RTTI when building with CMake 2026-03-16 20:23:47 -04:00
ISSOtm d5033a2bd9 Handle project version better in CMake
Parse the version from `version.hpp`, and check its consistency with
the Git tag to help catch release blunders.
2026-03-16 20:23:47 -04:00
ISSOtm 45fff0fd58 Harmonise naming of CMake test knobs
Also reorganise the file a little
2026-03-16 20:23:47 -04:00
ISSOtm c963c7b1c7 Simplify include directory directive 2026-03-16 20:23:47 -04:00
ISSOtm 63c7835e71 Stop overriding the binaries' install dir
This is equivalent to the default, except the default accepts being overridden
2026-03-16 20:23:47 -04:00
ISSOtm 35af3185eb Enable LTO even for multi-config CMake generators
See https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#build-configurations
for the rationale.
2026-03-16 20:23:47 -04:00
ISSOtm db9d9c7653 Install our man pages in a more standard-configurable manner 2026-03-16 20:23:47 -04:00
ISSOtm 9e5a9107f2 Remove enforced CTest verbosity
It can be controlled from the command line instead, and that's better
2026-03-16 20:23:47 -04:00
ISSOtm 275fcf8fcf Improve CMake-in-tree fatal error message 2026-03-16 20:23:47 -04:00
ISSOtm bc37b0ece6 Skip trying to link to libm explicitly
This doesn't seem to be necessary: it is automatically linked to,
at least on my machine. \*shrugs\*
2026-03-16 20:23:47 -04:00
ISSOtm e63ce24e48 Add a CMake preset for maintainer flags 2026-03-16 20:23:47 -04:00
ISSOtm d524884799 Run CI tests using CTest when possible 2026-03-16 20:23:47 -04:00
ISSOtm 34e1f8dcc5 Remove useless MSVC flag
CMake only passes a single file at a time anyway
2026-03-16 20:23:47 -04:00
ISSOtm cd64de9323 Remove a warning triggered by MSVC's own standard library
No longer triggers now, so we can remove our workaround for their breakage.
2026-03-16 20:23:47 -04:00
ISSOtm a01b805e4b Remove an outdated comment 2026-03-16 20:23:47 -04:00
ISSOtm a139cbc744 Refactor the source file listing
Make the common files into an object library, which lets them
be compiled only once (saving 41 build steps)
This also lends itself well to removing the per-program loop,
which simplifies the code somewhat.
2026-03-16 20:23:47 -04:00
ISSOtm 06e5386d93 Move FreeBSD deps install to common CI script
Making the OS filter in test driver consistent as per
https://github.com/gbdev/rgbds/pull/1899#discussion_r2942998932
2026-03-16 20:23:47 -04:00
ISSOtm 37f57a752f Print a more graceful error message for run-tests.sh --unk 2026-03-16 20:23:47 -04:00
ISSOtm 670bdf2095 Document run-tests.sh --os
Also switch to heredoc syntax for ease of editing
2026-03-16 20:23:47 -04:00
ISSOtm 6270f654a1 Make sure to init all variables involved in CMake tests 2026-03-16 20:23:47 -04:00
ISSOtm 676db46c01 Add a few details to the project() call
We can use them now that the minimum version has been bumped
2026-03-16 20:23:47 -04:00
ISSOtm 8979b54281 Ignore a CMake file that is not intended to be checked in 2026-03-16 20:23:47 -04:00
ISSOtm a6f03530ed Correct minimum CMake version required
Turns out the status messages use 3.17
2026-03-16 20:23:47 -04:00
ISSOtm b56b56a695 Reformat some long warning-flag-setting lines 2026-03-16 20:23:47 -04:00
ISSOtm dc5fc9f327 Remove obsolete CMake version comment 2026-03-16 20:23:47 -04:00
ISSOtm 3d35457eeb Avoid showing the "more warnings" option for MSVC 2026-03-16 20:23:47 -04:00
ISSOtm aea39ec8c8 Log with sanitizers are enabled under CMake
This can help bring attention to the MSVC difference (no UBSan there)
2026-03-16 20:23:47 -04:00
ISSOtm 9293068370 Properly specify our max CMake version supported
Also bump it to the version I just tested it with
2026-03-16 20:23:47 -04:00
ISSOtm d054f07703 Fix some truncation warnings
Amusingly, reported by MSVC only
2026-03-10 01:31:23 -04:00
Rangi 65b0b49720 Update zlib to 1.3.2
Note that the DLL name created by CMake with `BUILD_SHARED_LIBS`
has changed from `zlib1.dll` to `z.dll`. This was caused by the
"Continued rewrite of CMake build [Vollstrecker]" from its
changelog <https://zlib.net/ChangeLog.txt>, and has been reported
in <https://github.com/madler/zlib/issues/1181>.

The MinGW package `libz-mingw-w64-dev` still calls it `libz1.dll`.
2026-03-06 22:28:09 -05:00
Rangi 48d58ba095 Pass -Wdangling-alias to Bison 3.5 or newer
`bison -Wall` enables all diagnostics except `-Wcounterexamples`
(added in 3.7; we already pass it), `-Wyacc` (which we do not need),
and `-Wdangling-alias` (added in 3.5).
2026-03-06 22:28:09 -05:00
Rangi af0505b4cd Explicitly clarify that SECTION(@) returns the name of the current section 2026-02-06 19:37:49 -05:00
Eldred Habert 7c1e1fe515 Reference the file that actually provides the linker script lexer 2026-01-27 12:54:06 +01:00
Rangi d053025070 Avoid writing "XXX" since it's a conventional "TODO/FIXME" comment 2026-01-22 13:01:10 -05:00
Rangi ade898d815 List -B in rgbgfx options 2026-01-13 09:46:33 -05:00
Rangi e738b90c69 Refactor to combine similar functions into one 2026-01-07 16:36:04 -05:00
Rangi a91d26192d Fix SECTION UNION alignment depending on piece order 2026-01-07 16:36:04 -05:00
Rangi 92bfe5d930 Release v1.0.1 2026-01-01 00:43:43 -05:00
Rangi 41fe1d8f25 Correct error message for unconstrained sections with overlay (#1879) 2025-12-28 19:21:53 -05:00
Rangi 63a911e657 Specify -std=c++20 not c++2a (#1877) 2025-12-21 11:44:14 -05:00
Rangi a9ab248fed Improve some RGBGFX error messages (#1876)
* Improve some RGBGFX error messages

* Fix assertion failure on ambiguous transparent/opaque pixels
2025-12-19 13:00:05 -05:00
Rangi 9b22ff3491 Fix link in man page 2025-12-12 18:25:47 -05:00
Rangi a4af81f250 Fix two "Using a macro as first argument cancels effect of .Li" man page warnings
These were observed in https://udd.debian.org/lintian/?packages=rgbds
and can be reproduced by running:

    for f in man/*; do
        echo $f
        LC_ALL=C.UTF-8 MANROFFSEQ='' MANWIDTH=120 \
            man --warnings -E UTF-8 -l -Tutf8 -Z "$f" >/dev/null
    done
2025-12-12 12:31:03 -05:00
Rangi e08de399db Clarify usage versus encoding of jr and ldh (#1875) 2025-12-11 17:46:39 -05:00
Rangi d4e0ca5f90 Update libpng to 1.6.53 2025-12-11 11:48:18 -05:00
Rangi 2666dcbc26 Remove exclamation marks and periods from error messages (#1874) 2025-12-10 11:50:33 -05:00
Rangi42 27dca5680c Fix formatting typo in man/rgbasm-old.5 2025-12-09 10:33:24 -05:00
Rangi b0e0dfc56e Handle a missing -P/--preinclude file the same as an INCLUDE (#1873) 2025-12-08 14:39:34 -05:00
Rangi 33475e2c36 Factor out version-printing to usage.cpp (#1870) 2025-12-05 23:04:49 -05:00
Rangi c8161be23a Add missing SPDX-License-Identifier: MIT comments 2025-12-05 22:41:12 -05:00
Rangi 2c5c453ab8 Refactor FileStackNode::printBacktrace from recursive to iterative
This avoids a potential stack overflow for very long backtraces,
or for corrupt object files with cyclic backtraces
2025-12-05 22:41:12 -05:00
Rangi c3e245c13e Correct typo in rgbds(5) 2025-12-05 22:39:30 -05:00
Rangi 3631fab63c Fix bug where an object's invalid ROMX bank of 0 could break rgblink (#1868) 2025-12-05 11:21:00 -05:00
Rangi 1c00123b33 Add missing return 0; to rgblink main() 2025-12-04 21:06:46 -05:00
Rangi 131bb97ebc Fix some rgblink object file input bugs found via fuzzing with AFL++ (#1867)
- ID numbers (for fstack nodes, sections, symbols, patches, etc)
  might be too large for their associated collection
- Enum values might be invalid
- Bank values might be out of range for their section types
2025-12-04 20:49:16 -05:00
Rangi 8d6c617875 Use 4 spaces per tab in LCOV coverage report 2025-12-04 17:56:16 -05:00
Rangi42 752e2b3620 Symbol names with more than two '.'s could be defined as constants
Dot-only names could also trip an assertion in `make develop`
when used as labels
2025-12-04 15:15:41 -05:00
Rangi42 ad3188f038 Fix garbage characters at EOF causing an infinite loop 2025-12-04 15:15:41 -05:00
Rangi a6eb6457d8 Clarify documentation of rgbgfx -C/--color-curve (#1864) 2025-12-04 10:49:23 -05:00
Rangi 0d3276975e Update test dependencies (#1865) 2025-12-04 10:29:40 -05:00
Rangi d961c697d7 Update libpng to 1.6.51 (#1862) 2025-11-22 19:05:52 -05:00
Rangi 1eb4eb3339 Reuse the usage.name for printing version info 2025-11-18 22:32:45 -05:00
Rangi a3d3e1525a Fix RGBLINK object type detection 2025-11-18 22:01:43 -05:00
Rangi 3553c9c4da Fix RGBLINK evaluation of undefined RPN symbols
This was the only RPN case to not assign a deliberate value
in all possible branches.

Fixes #1858
2025-11-18 16:40:24 -05:00
Rangi 5c2c893ced Refactor getSectionDescription in src/link/assign.cpp 2025-11-16 17:37:01 -05:00
Rangi 0f266d1c66 Specify more ASan options (#1860) 2025-11-16 17:11:09 -05:00
Rangi 8ab4602ae5 Add -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG to develop builds (#1859) 2025-11-16 13:11:08 -05:00
Rangi 04e3a904c2 Avoid calling style_Set/Reset before strerror(errno),
since they may call `isatty` which can change `errno`

Fixes #1857
2025-11-08 12:06:16 -05:00
Rangi 395b03e88e Disallow SECTION UNION for ROM sections
Fixes #1855
2025-11-07 11:38:06 -05:00
Rangi42 fb9fa6038c Release version 1.0.0 2025-10-31 20:19:24 -04:00
Rangi 35e5808423 Update rgbasm(5) docs based on @nummacway's feedback (#1852) 2025-10-31 18:36:53 -04:00
Rangi42 558d3ca0fc Disallow labels before ENDC 2025-10-31 18:21:59 -04:00
Rangi42 df5162edca Use loops instead of tail calls and musttail
gcc 15.2.1 20250813 complains "address of automatic variable can
escape to `musttail` call" from `-Wmaybe-musttail-local-addr`,
and guaranteeing tail-call optimization cross-platform is more
trouble than it's worth.
2025-10-27 12:05:27 -04:00
Rangi42 2519d1e698 Mention REDEF and FOR regarding EQUS expansion
Fixes #1851
2025-10-27 10:54:16 -04:00
Rangi42 ca383c91ca Revert "More accurate 8-bit <=> 5-bit RGB color conversion (#1827)"
This reverts commit 223b3d1921.
2025-10-24 13:32:59 -04:00
Rangi 8bedd710d7 Simplify musl's getopt, including removing optind, optopt, opterr, idx, and longonly 2025-10-23 13:39:28 -04:00
Rangi efb5a88edb Show conventional colored "error:"/"FATAL:" for CLI option errors 2025-10-23 12:40:29 -04:00
Rangi f065243cd2 Enable RGBGFX's CLI "at-files" for all programs (#1848) 2025-10-22 17:05:59 -04:00
Rangi a0bb830679 More consistent man page descriptions for CLI options 2025-10-22 14:48:24 -04:00
Rangi 7654c6e27a More consistent man page descriptions for warning diagnostics 2025-10-22 14:03:34 -04:00
Rangi 400375b2e5 Share some handling between two tests of rgbasm -M - 2025-10-20 20:57:48 -04:00
Rangi 7462bccb72 Move struct Palette into its own file (#1850) 2025-10-20 16:59:24 -04:00
Rangi 2873e0b8c8 Use musttail attribute to guarantee tail recursion (#1849) 2025-10-20 15:56:22 -04:00
Rangi 1badba03d8 Clean up some #define callables
These are used where anonymous functions would not be sufficient
2025-10-13 13:14:49 -04:00
Rangi42 64bcef99bd Lower default -Wtrunction= level to 1 2025-10-13 11:48:33 -04:00
Rangi42 aa672bbec9 Rephrase PURGE documentation and raise the default level to 2
Fixes #1847
2025-10-13 11:43:29 -04:00
Rangi42 651877e094 Avoid reusing a static local variable (too fragile) 2025-10-08 21:19:23 -04:00
Rangi 26c48cc409 Add RGBGFX test for libpng warning with invalid bKGD chunk 2025-10-08 15:44:15 -04:00
Rangi 23b9039716 Give clearer names to template parameters 2025-10-08 14:55:43 -04:00
Rangi 711fba5e35 Add more tests for things that only the external tests had covered 2025-10-08 13:32:48 -04:00
Rangi 089fc11e31 A local label starting with a keyword (e.g. jr.local) is an error 2025-10-08 12:23:08 -04:00
Rangi42 837f552987 Fix bank increment never happening due to unsigned overflow 2025-10-07 16:20:24 -04:00
Rangi cb8c973453 Add test for undefined __SCOPE__ 2025-10-06 17:51:21 -04:00
Rangi cca3794dd0 Mention libpng in its internal warning and error messages 2025-10-06 17:03:51 -04:00
Rangi 02c2408f58 Implement reversed template for reversing for-each loops 2025-10-06 16:50:47 -04:00
Rangi fba0562650 Fix repeated REPT nodes in backtraces 2025-10-06 16:36:55 -04:00
Rangi 0c9920d4a6 Use C++ iterator for fragment/union "pieces" of RGBLINK sections 2025-10-05 15:07:25 -04:00
Rangi 7733ccdeb6 Implement __SCOPE__ (#1845) 2025-10-04 16:41:21 -04:00
Rangi 13e85b5151 Replace all ctype.h functions with locale-independent ones 2025-10-03 12:52:24 -04:00
620 changed files with 8367 additions and 5782 deletions
+1
View File
@@ -71,6 +71,7 @@ Language: Cpp
MaxEmptyLinesToKeep: 1 MaxEmptyLinesToKeep: 1
NamespaceIndentation: None NamespaceIndentation: None
PPIndentWidth: -1 PPIndentWidth: -1
PenaltyBreakScopeResolution: 1000
PointerAlignment: Right PointerAlignment: Right
QualifierAlignment: Right QualifierAlignment: Right
ReflowComments: true ReflowComments: true
+10 -1
View File
@@ -1,5 +1,6 @@
[*]
root = true root = true
[*]
indent_style = tab indent_style = tab
indent_size = tab indent_size = tab
tab_width = 4 tab_width = 4
@@ -7,3 +8,11 @@ charset = utf-8
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
end_of_line = lf end_of_line = lf
[{CMakeLists.txt,*.cmake}]
indent_style = space
indent_size = 2
[*.yml]
indent_style = space
indent_size = 2
+19
View File
@@ -0,0 +1,19 @@
# This file contains a list of commits that are probably not what you
# are looking for in a `git blame`, such as mass reformatting or renaming.
#
# You can set this file as a default ignore file for `git blame` by running
# the following command:
#
# $ git config blame.ignoreRevsFile .git-blame-ignore-revs
# 2024-03-04: Run `clang-format` on everything
e74073e4805fa3cce90a99946fc9994ae2970f9b
# 2009-06-11: Run `indent' on the whole tree
b6c749ffbd30138de7beef13fb9265372a461a96
# 2009-06-11: Switch line terminators from CRLF to LF
94fad97aedcd8bd85c53a505db28390e3f82577b
# 2009-06-11: Use spaces instead of tabs after CPP directives
0d9739535e313b0a2ad19c89b9c85fc7b75237a3
+1
View File
@@ -1,6 +1,7 @@
# Shell scripts need Unix line endings (see https://github.com/gbdev/rgbds/issues/841) # Shell scripts need Unix line endings (see https://github.com/gbdev/rgbds/issues/841)
*.sh text eol=lf *.sh text eol=lf
*.bash text eol=lf *.bash text eol=lf
*.cfg text eol=lf
# Flags also need Unix line endings (see https://github.com/gbdev/rgbds/issues/955) # Flags also need Unix line endings (see https://github.com/gbdev/rgbds/issues/955)
*.flags text eol=lf *.flags text eol=lf
+4
View File
@@ -0,0 +1,4 @@
---
name: Update to RGBDS
about: Suggest a feature or report a bug in RGBDS.
---
+5
View File
@@ -0,0 +1,5 @@
blank_issues_enabled: false
contact_links:
- name: GBDev Discord
url: https://discord.gg/RjJKA8wrD4
about: If you have questions about the RGBDS code or its organization, join our Discord server!
+19
View File
@@ -0,0 +1,19 @@
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "quarterly" # We do not need *immediate* bumps, only frequent enough to avoid falling out of support range.
cooldown: # Avoid bumping deps immediately, to give time for security audits to be conducted
default-days: 15
include: [ "*" ]
groups:
actions:
patterns: [ "*" ]
ignore:
- dependency-name: "*"
update-types:
- "version-update:semver-minor"
- "version-update:semver-patch"
labels: [ builds ] # Since those are CI-related updates...
-27
View File
@@ -1,27 +0,0 @@
#!/bin/bash
set -euo pipefail
pngver=1.6.50
## Grab sources and check them
curl -LOJ "http://prdownloads.sourceforge.net/libpng/libpng-$pngver.tar.xz?download"
# Brew doesn't provide any sha256sum, so we're making do with `sha2` instead.
if [ "$(sha2 -q -256 libpng-$pngver.tar.xz)" != 4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 ]; then
sha2 -256 libpng-$pngver.tar.xz
echo Checksum mismatch! Aborting. >&2
exit 1
fi
## Extract sources and patch them
tar -xvf libpng-$pngver.tar.xz
## Start building!
mkdir -p build
cd build
../libpng-$pngver/configure --disable-shared --enable-static \
CFLAGS="-O3 -flto -DNDEBUG -mmacosx-version-min=10.9 -arch x86_64 -arch arm64 -fno-exceptions"
make -kj
make install prefix="$PWD/../libpng-staging"
+8
View File
@@ -0,0 +1,8 @@
#!/usr/bin/env sh
set -eu
bash --version
zsh --version
find contrib -type f \( -name '*.sh' -o -name '*.bash' \) -exec bash --norc -n {} \;
find contrib/zsh_compl -type f -name '_rgb*' -exec zsh -n {} \;
@@ -3,17 +3,17 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
declare -A FILES declare -A FILES
while read -r -d '' file; do while IFS= read -r -d '' file; do
FILES["$file"]="true" FILES["$file"]=true
done < <(git diff --name-only -z "$1" HEAD) done < <(git diff --name-only -z "$1" HEAD)
edited () { edited () {
${FILES["$1"]:-"false"} ${FILES["$1"]:-false}
} }
dependency () { dependency () {
if edited "$1" && ! edited "$2"; then if edited "$1" && ! edited "$2"; then
echo "'$1' was modified, but not '$2'! $3" | xargs echo "'$1' was modified, but not '$2'! $3"
fi fi
} }
@@ -79,15 +79,15 @@ dependency src/gfx/main.cpp contrib/zsh_compl/_rgbgfx \
dependency src/gfx/main.cpp contrib/bash_compl/_rgbgfx.bash \ dependency src/gfx/main.cpp contrib/bash_compl/_rgbgfx.bash \
"Did the rgbgfx CLI change?" "Did the rgbgfx CLI change?"
dependency test/fetch-test-deps.sh CONTRIBUTING.md \ dependency test/external/fetch-repos.sh docs/CONTRIBUTING.md \
"Did the test protocol change?" "Did the test protocol change?"
dependency test/run-tests.sh CONTRIBUTING.md \ dependency test/run-tests.sh docs/CONTRIBUTING.md \
"Did the test protocol change?" "Did the test protocol change?"
dependency test/asm/test.sh CONTRIBUTING.md \ dependency test/asm/test.sh docs/CONTRIBUTING.md \
"Did the RGBASM test protocol change?" "Did the RGBASM test protocol change?"
dependency test/link/test.sh CONTRIBUTING.md \ dependency test/link/test.sh docs/CONTRIBUTING.md \
"Did the RGBLINK test protocol change?" "Did the RGBLINK test protocol change?"
dependency test/fix/test.sh CONTRIBUTING.md \ dependency test/fix/test.sh docs/CONTRIBUTING.md \
"Did the RGBFIX test protocol change?" "Did the RGBFIX test protocol change?"
dependency test/gfx/test.sh CONTRIBUTING.md \ dependency test/gfx/test.sh docs/CONTRIBUTING.md \
"Did the RGBGFX test protocol change?" "Did the RGBGFX test protocol change?"
@@ -1,10 +1,10 @@
#!/usr/bin/env bash #!/usr/bin/env sh
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
clang-format --version clang-format --version
find . -type f \( -iname '*.hpp' -o -iname '*.cpp' \) -exec clang-format -i {} + find . -type f -iname '*.[ch]pp' -exec clang-format -i {} +
if ! git diff-index --quiet HEAD --; then if ! git diff-index --quiet HEAD --; then
echo 'Unformatted files:' echo 'Unformatted files:'
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env bash
set -euo pipefail
tag=$1
[ $# -eq 1 ]
flags=(--draft --fail-on-no-commits --verify-tag)
if [[ "$tag" = v*-rc* ]]; then
flags+=(--prerelease)
fi
files=(
win64/rgbds-win64.zip
win32/rgbds-win32.zip
macos/rgbds-macos.zip
linux/rgbds-linux-x86_64.tar.xz
rgbds-source.tar.gz
)
set -x
gh release create "${flags[@]}" --notes-file - "$tag" "${files[@]}" <<"EOF"
Please ensure that the packages below work properly.
Once that's done, replace this text with the changelog, un-draft the release, and update the `release` branch.
By the way, if you forgot to update `include/version.hpp`, RGBASM's version test is going to fail in the tag's regression testing! (Use `git push --delete origin <tag>` to delete it)
EOF
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env sh
local_bin_dir="$PWD/.local/bin"
mkdir -p "$local_bin_dir"
# Alias `make` to `gmake` and `python` to `python3` for use by external tests.
ln -s "$(which gmake)" "$local_bin_dir/make"
ln -s "$(which python3)" "$local_bin_dir/python"
export PATH="$local_bin_dir:$PATH"
-23
View File
@@ -1,23 +0,0 @@
function getlibrary ([string] $URI, [string] $filename, [string] $hash, [string] $destdir) {
$wc = New-Object Net.WebClient
[string] $downloadhash = $null
try {
$wc.DownloadFile($URI, $filename)
$downloadhash = $(Get-FileHash $filename -Algorithm SHA256).Hash
} catch {
Write-Host "${filename}: failed to download"
exit 1
}
if ($hash -ne $downloadhash) {
Write-Host "${filename}: SHA256 mismatch ($downloadhash)"
exit 1
}
Expand-Archive -DestinationPath $destdir $filename
}
getlibrary 'https://www.zlib.net/zlib131.zip' 'zlib.zip' '72af66d44fcc14c22013b46b814d5d2514673dda3d115e64b690c1ad636e7b17' .
getlibrary 'https://github.com/pnggroup/libpng/archive/refs/tags/v1.6.50.zip' 'libpng.zip' 'f6bb2544d2cf5465af3a695dee0b7eacff82f11a50aa4672ef0e19df6e16d455' .
getlibrary 'https://github.com/lexxmark/winflexbison/releases/download/v2.5.25/win_flex_bison-2.5.25.zip' 'winflexbison.zip' '8d324b62be33604b2c45ad1dd34ab93d722534448f55a16ca7292de32b6ac135' install_dir
Move-Item zlib-1.3.1 zlib
Move-Item libpng-1.6.50 libpng
+102
View File
@@ -0,0 +1,102 @@
#!/bin/sh
# This script requires `sh` instead of `bash` because the latter is not always installed on FreeBSD.
set -eu
# Python and the Pillow library are dependencies for libbet, a repo built by our external tests.
# Other dependencies are for building rgbds itself.
case $# in
1) OS="$1"; TOOLSET= ;;
2) OS="$1"; TOOLSET="$2" ;;
*) echo >&2 "Usage: $0 <os> [<toolset>]" && exit 1 ;;
esac
case "${OS%%-*}" in
ubuntu|debian)
pkgs=bison
case "$TOOLSET" in
mingw32)
pkgs="$pkgs libz-mingw-w64-dev g++-mingw-w64-i686-win32"
TOOLSET=
;;
mingw64)
pkgs="$pkgs libz-mingw-w64-dev g++-mingw-w64-x86-64-win32"
TOOLSET=
;;
g++-10 | lcov)
pkgs="$pkgs libpng-dev pkgconf python3-pil $TOOLSET"
TOOLSET=
;;
'' | g++ | clang++)
pkgs="$pkgs libpng-dev pkgconf python3-pil"
TOOLSET=
;;
esac
sudo apt-get update -qq
# shellcheck disable=SC2086 # (This word splitting is intentional.)
sudo apt-get install -yq $pkgs
;;
macos)
pkgs="bison make pillow"
case "$TOOLSET" in
lld)
pkgs="$pkgs $TOOLSET"
TOOLSET=
;;
clang++)
TOOLSET=
;;
esac
# macOS bundles GNU Make 3.81, which doesn't support synced output.
# We leave it as the default in `PATH`, to test that our Makefile works with it.
# However, CMake automatically uses Homebrew's `gmake`, so our CI has synced output.
# shellcheck disable=SC2086 # (This word splitting is intentional.)
brew install $pkgs
# Export `bison` to allow using the version we install from Homebrew,
# instead of the outdated one preinstalled on macOS (which doesn't even support `-Wall`...).
export PATH="$(brew --prefix)/opt/bison/bin:$PATH"
printf 'PATH=%s\n' "$PATH" >>"$GITHUB_ENV" # Make it available to later CI steps too.
;;
freebsd)
# GNU `gmake` and `gcc` are dependencies for most repos built by our external tests.
pkg install -y bash bison cmake ninja git png gmake lang/gcc python3 py312-pillow
;;
windows)
# GitHub Actions' hosted runners ship CMake 3.x, but versions prior to 4.0.0 ignore `CPACK_PACKAGE_FILE_NAME`.
choco install -y winflexbison3 cmake
# The version-printing code below will invoke `bison`, not the Windows-specific name `win_bison`.
bison() { win_bison "$@"; } # An alias doesn't work, so we use a function instead.
py -3 -m pip install pillow
;;
windowsmingw)
py -3 -m pip install pillow
;;
*)
echo "Cannot install deps for OS '$1'"
exit 1
;;
esac
if [ -n "$TOOLSET" ]; then
printf >&2 'Unknown toolset `%s` for OS `%s`\n' "$TOOLSET" "$OS"
exit 1
fi
# We do not build RGBDS *in* MinGW on Windows, so skip printing build tool info.
if [ "$OS" = "windowsmingw" ]; then
exit 0
fi
# Print some system info, for easier debugging.
# https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-commands#grouping-log-lines
echo ::group::PATH
echo "PATH=($PATH)" | sed 's/:/\n /g'
echo ::endgroup::
for prog in bison make cmake; do
printf '::group::' # No line terminator, the next command's first line becomes the group's title.
$prog --version
type $prog
echo ::endgroup::
done
-7
View File
@@ -1,7 +0,0 @@
#!/usr/bin/env bash
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 rgbasm-old.5 /usr/local/share/man/man5/
install -m 644 rgbds.7 gbz80.7 /usr/local/share/man/man7/
-23
View File
@@ -1,23 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
case "${1%-*}" in
ubuntu)
sudo apt-get -qq update
sudo apt-get install -yq bison libpng-dev pkg-config
;;
macos)
brew install bison sha2 md5sha1sum
# Export `bison` to allow using the version we install from Homebrew,
# instead of the outdated one preinstalled on macOS (which doesn't even support `-Wall`...)
export PATH="/opt/homebrew/opt/bison/bin:$PATH"
printf 'PATH=%s\n' "$PATH" >>"$GITHUB_ENV" # Make it available to later CI steps too
;;
*)
echo "WARNING: Cannot install deps for OS '$1'"
;;
esac
bison --version
make --version
cmake --version
-28
View File
@@ -1,28 +0,0 @@
#!/bin/bash
set -euo pipefail
pngver=1.6.50
arch="$1"
## Grab sources and check them
wget http://downloads.sourceforge.net/project/libpng/libpng16/$pngver/libpng-$pngver.tar.xz
echo 4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 libpng-$pngver.tar.xz | sha256sum -c -
## Extract sources and patch them
tar -xf libpng-$pngver.tar.xz
## Start building!
mkdir -p build
cd build
../libpng-$pngver/configure \
--host="$arch" --target="$arch" \
--prefix="/usr/$arch" \
--enable-shared --disable-static \
CPPFLAGS="-D_FORTIFY_SOURCE=2" \
CFLAGS="-O2 -pipe -fno-plt -fno-exceptions --param=ssp-buffer-size=4" \
LDFLAGS="-Wl,-O1,--sort-common,--as-needed -fstack-protector"
make -kj
sudo make install
+4
View File
@@ -0,0 +1,4 @@
@rem From https://github.com/microsoft/vswhere/wiki/Start-Developer-Command-Prompt#using-batch
for /f "delims=" %%p in ('vswhere.exe -prerelease -latest -property installationPath') do set vspath=%%p
@rem From https://learn.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=msvc-170#developer_command_file_locations
call "%vspath%\VC\Auxiliary\Build\vcvars%1.bat"
-18
View File
@@ -1,18 +0,0 @@
name: Static analysis
on:
- push
- pull_request
jobs:
analysis:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Install deps
shell: bash
run: |
./.github/scripts/install_deps.sh ubuntu-latest
- name: Static analysis
run: | # Silence warnings with too many false positives (https://stackoverflow.com/a/73913076)
make -kj CXX=g++-14 CXXFLAGS="-fanalyzer -fanalyzer-verbosity=0 -Wno-analyzer-use-of-uninitialized-value -DNDEBUG" Q=
+9 -3
View File
@@ -6,6 +6,12 @@ on:
tags: tags:
- '*' - '*'
env:
# Force colored output (see https://bixense.com/clicolors/ and https://force-color.org/)
TERM: xterm-256color
CLICOLOR: 1
CLICOLOR_FORCE: 1
jobs: jobs:
publish-docker-image: publish-docker-image:
if: github.repository_owner == 'gbdev' if: github.repository_owner == 'gbdev'
@@ -14,10 +20,10 @@ jobs:
packages: write packages: write
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v7
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v3 uses: docker/login-action@v4
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
@@ -43,7 +49,7 @@ jobs:
- name: Delete untagged container images - name: Delete untagged container images
if: github.repository_owner == 'gbdev' if: github.repository_owner == 'gbdev'
uses: Chizkiyahu/delete-untagged-ghcr-action@v5 uses: Chizkiyahu/delete-untagged-ghcr-action@v6
with: with:
# Requires a personal access token with delete:packages permissions # Requires a personal access token with delete:packages permissions
token: ${{ secrets.PAT_TOKEN }} token: ${{ secrets.PAT_TOKEN }}
+23
View File
@@ -0,0 +1,23 @@
name: Contrib script syntax checking
on:
- push
- pull_request
env:
# Force colored output (see https://bixense.com/clicolors/ and https://force-color.org/)
TERM: xterm-256color
CLICOLOR: 1
CLICOLOR_FORCE: 1
jobs:
check-contrib:
runs-on: ubuntu-slim
steps:
- name: Checkout repo
uses: actions/checkout@v7
- name: Install zsh
run: |
sudo apt install -Uyq zsh
- name: Check contrib scripts
run: |
.github/scripts/check-contrib.sh
+27
View File
@@ -0,0 +1,27 @@
name: Diff completeness check
on: pull_request
env:
# Force colored output (see https://bixense.com/clicolors/ and https://force-color.org/)
TERM: xterm-256color
CLICOLOR: 1
CLICOLOR_FORCE: 1
jobs:
check-diff:
runs-on: ubuntu-slim
steps:
- name: Clone repo
uses: actions/checkout@v7
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Check diff
shell: bash # Bash is the default, but specifying it explicitly enables `-o pipefail`.
run: |
MERGE_BASE=$(gh api "/repos/{owner}/{repo}/compare/$BASE_SHA...$HEAD_SHA" --jq '.merge_base_commit.sha')
git fetch origin "$MERGE_BASE"
.github/scripts/check-diff.bash "$MERGE_BASE" | sed -E 's/^/::warning::/'
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
GH_TOKEN: ${{ github.token }}
+21
View File
@@ -0,0 +1,21 @@
name: Code format checking
on: pull_request
env:
# Force colored output (see https://bixense.com/clicolors/ and https://force-color.org/)
TERM: xterm-256color
CLICOLOR: 1
CLICOLOR_FORCE: 1
jobs:
check-format:
runs-on: ubuntu-slim
steps:
- name: Checkout repo
uses: actions/checkout@v7
- name: Install clang-format
run: |
sudo apt install -Uyq clang-format
- name: Check format
run: |
.github/scripts/check-format.sh
-17
View File
@@ -1,17 +0,0 @@
name: Diff completeness check
on: pull_request
jobs:
checkdiff:
runs-on: ubuntu-latest
steps:
- name: Set up repo
run: |
git clone -b "${{ github.event.pull_request.head.ref }}" "${{ github.event.pull_request.head.repo.clone_url }}" rgbds
cd rgbds
git remote add upstream "${{ github.event.pull_request.base.repo.clone_url }}"
git fetch upstream
- name: Check diff
working-directory: rgbds
run: |
make checkdiff "BASE_REF=${{ github.event.pull_request.base.sha }}" Q= | tee log
-12
View File
@@ -1,12 +0,0 @@
name: Code format checking
on: pull_request
jobs:
checkformat:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Check format
run: |
contrib/checkformat.bash
+12 -13
View File
@@ -3,28 +3,26 @@ on:
- push - push
- pull_request - pull_request
env:
# Force colored output (see https://bixense.com/clicolors/ and https://force-color.org/)
TERM: xterm-256color
CLICOLOR: 1
CLICOLOR_FORCE: 1
jobs: jobs:
coverage: coverage:
runs-on: ubuntu-24.04 runs-on: ubuntu-latest
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v7
- name: Install deps - name: Install deps
shell: bash
run: | run: |
./.github/scripts/install_deps.sh ubuntu .github/scripts/install-deps.sh ubuntu-latest lcov
- name: Install LCOV
run: |
sudo apt-get install lcov
- name: Install test dependency dependencies
shell: bash
run: |
test/fetch-test-deps.sh --get-deps ubuntu
- name: Generate coverage report - name: Generate coverage report
run: | run: |
contrib/coverage.bash ubuntu-ci contrib/coverage.bash --os ubuntu-latest --jobs "$(getconf _NPROCESSORS_ONLN)"
- name: Upload coverage report - name: Upload coverage report
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v7
with: with:
name: coverage-report name: coverage-report
# Workaround for keeping the top-level coverage/ directory # Workaround for keeping the top-level coverage/ directory
@@ -32,3 +30,4 @@ jobs:
path: | path: |
coverage coverage
dummy-file-to-keep-directory-structure.txt dummy-file-to-keep-directory-structure.txt
if-no-files-found: error
+59 -99
View File
@@ -4,162 +4,122 @@ on:
tags: tags:
- v[0-9]* - v[0-9]*
env:
# Force colored output (see https://bixense.com/clicolors/ and https://force-color.org/)
TERM: xterm-256color
CLICOLOR: 1
CLICOLOR_FORCE: 1
CMAKE_COLOR_DIAGNOSTICS: ON # Tells CMake-generated build systems to have colored output.
CMAKE_BUILD_TYPE: Release # `cmake -S` now implies `-DCMAKE_BUILD_TYPE=Release`.
CMAKE_CONFIG_TYPE: Release # `cmake --build` now implies `--config Release`.
# We instruct CMake to download and build third-party projects in the same place as in `testing.yml`
# to prevent divergence between testing and release workflows.
DEPS_ROOT_DIR: ~/_deps
# We use Ninja as the generator for CMake below because it provides synchronous output by default,
# and only shows full command lines when they fail.
jobs: jobs:
windows: windows:
runs-on: windows-2022 runs-on: windows-2022
strategy: strategy:
matrix: matrix:
bits: [32, 64] bits: [32, 64]
include:
- bits: 32
arch: x86
platform: Win32
- bits: 64
arch: x86_x64
platform: x64
fail-fast: false fail-fast: false
steps: steps:
- name: Get version from tag
shell: bash
run: | # Turn "vX.Y.Z" into "X.Y.Z"
VERSION="${{ github.ref_name }}"
echo "version=${VERSION#v}" >> $GITHUB_ENV
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v7
- name: Install deps - name: Install deps
run: .github/scripts/get_win_deps.ps1
- name: Check libraries cache
id: cache
uses: actions/cache@v4
with:
path: |
zbuild
pngbuild
key: ${{ matrix.arch }}-${{ hashFiles('zlib/**', 'libpng/**') }}
- name: Build zlib
if: steps.cache.outputs.cache-hit != 'true'
run: | # BUILD_SHARED_LIBS causes the output DLL to be correctly called `zlib1.dll`
cmake -S zlib -B zbuild -A ${{ matrix.platform }} -Wno-dev -DCMAKE_INSTALL_PREFIX=install_dir -DBUILD_SHARED_LIBS=ON
cmake --build zbuild --config Release -j
- name: Install zlib
run: | run: |
cmake --install zbuild bash .github/scripts/install-deps.sh windows
- name: Build libpng
if: steps.cache.outputs.cache-hit != 'true'
shell: bash
run: |
cmake -S libpng -B pngbuild -A ${{ matrix.platform }} -Wno-dev -DCMAKE_INSTALL_PREFIX=install_dir -DPNG_SHARED=ON -DPNG_STATIC=OFF -DPNG_TESTS=OFF
cmake --build pngbuild --config Release -j
- name: Install libpng
run: |
cmake --install pngbuild
- name: Build Windows binaries - name: Build Windows binaries
shell: bash shell: cmd
run: | run: |
cmake -S . -B build -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DCMAKE_BUILD_TYPE=Release call .github\scripts\msvc-env.bat ${{ matrix.bits }}
cmake --build build --config Release -j --verbose cmake -B build -G Ninja -DFETCHCONTENT_BASE_DIR="${{ env.DEPS_ROOT_DIR }}" -DCMAKE_C_FLAGS="/nologo"
cmake --install build --verbose --prefix install_dir --strip cmake --build build -- -k 0
- name: Package binaries - name: Package binaries
working-directory: build
run: | run: |
Compress-Archive -LiteralPath @("install_dir/bin/rgbasm.exe", "install_dir/bin/rgblink.exe", "install_dir/bin/rgbfix.exe", "install_dir/bin/rgbgfx.exe", "install_dir/bin/zlib1.dll", "install_dir/bin/libpng16.dll") "rgbds-win${{ matrix.bits }}.zip" cpack -DCPACK_PACKAGE_FILE_NAME=rgbds-win${{ matrix.bits }} -G ZIP --verbose
- name: Upload Windows binaries - name: Upload Windows binaries
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v7
with: with:
name: win${{ matrix.bits }} name: win${{ matrix.bits }}
path: rgbds-win${{ matrix.bits }}.zip path: build/rgbds-win${{ matrix.bits }}.zip
if-no-files-found: error
macos: macos:
runs-on: macos-14 runs-on: macos-26
steps: steps:
- name: Get version from tag
shell: bash
run: | # Turn "refs/tags/vX.Y.Z" into "X.Y.Z"
VERSION="${{ github.ref_name }}"
echo "version=${VERSION#v}" >> $GITHUB_ENV
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v7
- name: Install deps - name: Install deps
shell: bash
run: | run: |
./.github/scripts/install_deps.sh macos ./.github/scripts/install-deps.sh macos lld
- name: Build libpng
run: |
./.github/scripts/build_libpng.sh
# We force linking libpng statically; the other libs are provided by macOS itself
- name: Build binaries - name: Build binaries
run: | run: |
make -kj CXXFLAGS="-O3 -flto -DNDEBUG -mmacosx-version-min=10.9 -arch x86_64 -arch arm64" PNGCFLAGS="-I libpng-staging/include" PNGLDLIBS="libpng-staging/lib/libpng.a -lz" Q= cmake -B build -G Ninja --preset macos-static -DFETCHCONTENT_BASE_DIR="${{ env.DEPS_ROOT_DIR }}"
cmake --build build -- -k 0
strip rgb{asm,link,fix,gfx} strip rgb{asm,link,fix,gfx}
env:
LDFLAGS: -fuse-ld=lld # cmake/macos-static.cmake comments explain why we use lld.
- name: Create install script
run: |
make install.sh
- name: Package binaries - name: Package binaries
run: | run: |
zip --junk-paths rgbds-macos.zip rgb{asm,link,fix,gfx} man/* .github/scripts/install.sh zip --junk-paths rgbds-macos.zip rgb{asm,link,fix,gfx} man/* install.sh
- name: Upload macOS binaries - name: Upload macOS binaries
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v7
with: with:
name: macos name: macos
path: rgbds-macos.zip path: rgbds-macos.zip
if-no-files-found: error
linux: linux:
runs-on: ubuntu-22.04 # Oldest supported, for best glibc compatibility. runs-on: ubuntu-22.04 # Oldest supported, for best glibc compatibility.
steps: steps:
- name: Get version from tag
shell: bash
run: | # Turn "refs/tags/vX.Y.Z" into "X.Y.Z"
VERSION="${{ github.ref_name }}"
echo "version=${VERSION#v}" >> $GITHUB_ENV
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v7
- name: Install deps - name: Install deps
shell: bash
run: | run: |
./.github/scripts/install_deps.sh ubuntu-22.04 ./.github/scripts/install-deps.sh ubuntu-22.04
- name: Build binaries - name: Build binaries
run: | run: |
make -kj WARNFLAGS="-Wall -Wextra -pedantic -static" PKG_CONFIG="pkg-config --static" Q= make -k -j "$(getconf _NPROCESSORS_ONLN)" WARNFLAGS="-Wall -Wextra -pedantic -static" PKG_CONFIG="pkg-config --static" Q=
strip rgb{asm,link,fix,gfx} strip rgb{asm,link,fix,gfx}
- name: Create install script
run: |
make install.sh Q=
- name: Package binaries - name: Package binaries
run: | run: |
tar caf rgbds-linux-x86_64.tar.xz --transform='s#.*/##' rgb{asm,link,fix,gfx} man/* .github/scripts/install.sh tar caf rgbds-linux-x86_64.tar.xz --transform='s#.*/##' rgb{asm,link,fix,gfx} man/* install.sh
- name: Upload Linux binaries - name: Upload Linux binaries
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v7
with: with:
name: linux name: linux
path: rgbds-linux-x86_64.tar.xz path: rgbds-linux-x86_64.tar.xz
if-no-files-found: error
release: release:
runs-on: ubuntu-latest runs-on: ubuntu-slim
needs: [windows, macos, linux] needs: [windows, macos, linux]
permissions: permissions:
contents: write contents: write
steps: steps:
- name: Get version from tag
shell: bash
run: | # Turn "refs/tags/vX.Y.Z" into "X.Y.Z"
VERSION="${{ github.ref_name }}"
echo "version=${VERSION#v}" >> $GITHUB_ENV
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v7
- name: Package sources - name: Package sources
run: | run: |
make dist Q= make dist Q=
ls - name: Download all binary packages
- name: Download Linux binaries uses: actions/download-artifact@v8
uses: actions/download-artifact@v4 - name: Draft the release
- name: Release run: |
uses: softprops/action-gh-release@v2 .github/scripts/draft-release.sh "${ref#refs/tags/}"
with:
body: |
Please ensure that the packages below work properly.
Once that's done, replace this text with the changelog, un-draft the release, and update the `release` branch.
By the way, if you forgot to update `include/version.hpp`, RGBASM's version test is going to fail in the tag's regression testing! (Use `git push --delete origin <tag>` to delete it)
draft: true # Don't publish the release quite yet...
prerelease: ${{ contains(github.ref, '-rc') }}
files: |
win32/rgbds-win32.zip
win64/rgbds-win64.zip
macos/rgbds-macos.zip
linux/rgbds-linux-x86_64.tar.xz
rgbds-source.tar.gz
fail_on_unmatched_files: true
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ref: ${{ github.ref_name }}
GH_TOKEN: ${{ github.token }} # Required by the `gh` command.
+21 -20
View File
@@ -4,24 +4,32 @@ on:
types: types:
- released # This avoids triggering on pre-releases - released # This avoids triggering on pre-releases
env:
# Force colored output (see https://bixense.com/clicolors/ and https://force-color.org/)
TERM: xterm-256color
CLICOLOR: 1
CLICOLOR_FORCE: 1
jobs: jobs:
build: build:
if: github.repository_owner == 'gbdev' if: github.repository_owner == 'gbdev'
runs-on: ubuntu-latest runs-on: ubuntu-slim
steps: steps:
- name: Checkout rgbds@release - name: Checkout rgbds@release
uses: actions/checkout@v4 uses: actions/checkout@v7
with: with:
path: rgbds path: rgbds
- name: Checkout rgbds-www@master sparse-checkout: man # Avoid checking out other files, we don't use them here.
uses: actions/checkout@v4 - name: Checkout rgbds-www
uses: actions/checkout@v7
with: with:
repository: ${{ github.repository_owner }}/rgbds-www
path: rgbds-www path: rgbds-www
repository: ${{ github.repository_owner }}/rgbds-www
ref: master # Need to check out the branch to be able to commit on and push it.
ssh-key: !!str ${{ secrets.SSH_KEY_SECRET }}
- name: Install groff and mandoc - name: Install groff and mandoc
run: | run: |
sudo apt-get -qq update sudo apt install -Uyq groff mandoc -o APT::Color=yes -o Acquire::Queue-Mode=access
sudo apt-get install -yq groff mandoc
- name: Update pages - name: Update pages
working-directory: rgbds/man working-directory: rgbds/man
run: | # The ref appears to be in the format "refs/tags/<version>", so strip that run: | # The ref appears to be in the format "refs/tags/<version>", so strip that
@@ -29,19 +37,12 @@ jobs:
../../rgbds-www/maintainer/new_release.sh ${GITHUB_REF##*/} ../../rgbds-www/maintainer/new_release.sh ${GITHUB_REF##*/}
- name: Push new pages - name: Push new pages
working-directory: rgbds-www working-directory: rgbds-www
env:
GIT_AUTHOR_NAME: !!str ${{ github.actor }}
GIT_AUTHOR_EMAIL: !!str ${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com
GIT_COMMITTER_NAME: github-actions[bot]
GIT_COMMITTER_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com
run: | run: |
mkdir -p -m 700 ~/.ssh
cat > ~/.ssh/id_ed25519 <<<"${{ secrets.SSH_KEY_SECRET }}"
chmod 0600 ~/.ssh/id_ed25519
eval $(ssh-agent -s)
ssh-add ~/.ssh/id_ed25519
git config --global user.name "GitHub Action"
git config --global user.email "[email protected]"
git add -A git add -A
git commit -m "Create RGBDS ${GITHUB_REF##*/} documentation" git commit -m "Create RGBDS ${GITHUB_REF##*/} documentation"
if git remote | grep -q origin; then git push
git remote set-url origin [email protected]:${{ github.repository_owner }}/rgbds-www.git
else
git remote add origin [email protected]:${{ github.repository_owner }}/rgbds-www.git
fi
git push origin master
+249 -187
View File
@@ -3,57 +3,102 @@ on:
- push - push
- pull_request - pull_request
env:
# Force colored output (see https://bixense.com/clicolors/ and https://force-color.org/)
TERM: xterm-256color
CLICOLOR: 1
CLICOLOR_FORCE: 1
CMAKE_COLOR_DIAGNOSTICS: ON # Tells CMake-generated build systems to have colored output.
CMAKE_CONFIG_TYPE: Debug # `cmake --build` now implies `--config Debug`.
CTEST_PARALLEL_LEVEL: "" # `ctest` now implies `--parallel` (default number of logical CPUs).
CTEST_NO_TESTS_ACTION: error # Make CTest fail if it cannot find any tests. (That should never happen.)
CTEST_OUTPUT_ON_FAILURE: ON # CTest reports test program output on failure.
GIT_CONFIG_COUNT: 1
GIT_CONFIG_KEY_0: color.ui
GIT_CONFIG_VALUE_0: always # Colorful output is helpful even though CI is not considered to be a terminal.
# We instruct CMake to download and build third-party projects outside of our source tree,
# otherwise they can trigger `-Werror=dev` (from the `develop` preset).
DEPS_ROOT_DIR: ~/_deps # Note that this needs to be used in a position where Bash will trigger tilde expansion!
# We use bash syntax across OSes for consistency unless otherwise specified
defaults:
run:
shell: bash
# We use Ninja as the generator for CMake below because it provides synchronous output by default,
# and only shows full command lines when they fail.
jobs: jobs:
unix: unix:
strategy: strategy:
matrix: matrix:
os: [ubuntu-22.04, macos-14] os: [ubuntu-22.04, macos-15-intel, macos-26]
cxx: [g++, clang++] cxx: [g++, clang++]
buildsys: [make, cmake] buildsys: [make, cmake]
exclude: exclude: # Don't use `g++` on macOS; it's just an alias to `clang++`.
# Don't use `g++` on macOS; it's just an alias to `clang++`. - { os: macos-15-intel, cxx: g++ }
- os: macos-14 - { os: macos-26, cxx: g++ }
cxx: g++ include: # Use `g++-10`, the earliest GCC version we support, on the earliest Ubuntu runner.
- { os: ubuntu-22.04, cxx: g++-10, buildsys: make }
- { os: ubuntu-22.04, cxx: g++-10, buildsys: cmake }
fail-fast: false fail-fast: false
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v7
- name: Install deps - name: Install deps
shell: bash
run: | run: |
./.github/scripts/install_deps.sh ${{ matrix.os }} .github/scripts/install-deps.sh ${{ matrix.os }} ${{ matrix.cxx }}
- name: Build & install using Make - name: Build using Make
if: matrix.buildsys == 'make' if: matrix.buildsys == 'make'
run: | run: |
make develop -kj Q= CXX=${{ matrix.cxx }} make develop -k -j "$(getconf _NPROCESSORS_ONLN)" Q= CXX=${{ matrix.cxx }}
sudo make install -j Q= - name: Install using Make
- name: Build & install using CMake if: matrix.buildsys == 'make'
run: |
sudo make install Q=
type rgbasm rgblink rgbfix rgbgfx
man -w 1 rgbasm rgblink rgbfix rgbgfx
- name: Build using CMake
if: matrix.buildsys == 'cmake'
# Since GitHub's runners are basically kitchen sinks,
# the Mono framework exposes a libpng 1.4.x header, breaking everything.
# Searching frameworks last makes Homebrew's libpng be discovered first, which works.
# Note that since this is specific to our CI environment, the workaround is
# better applied here than in our CMakeLists, where it could affect and break someone else.
run: |
CXX=${{ matrix.cxx }} cmake -B build -G Ninja --preset develop -DCMAKE_FIND_FRAMEWORK=LAST
cmake --build build -- -k 0
- name: Install using CMake
if: matrix.buildsys == 'cmake' if: matrix.buildsys == 'cmake'
run: | run: |
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} -DSANITIZERS=ON -DMORE_WARNINGS=ON
cmake --build build -j --verbose
sudo cmake --install build --verbose sudo cmake --install build --verbose
type rgbasm rgblink rgbfix rgbgfx
man -w 1 rgbasm rgblink rgbfix rgbgfx
- name: Package binaries - name: Package binaries
run: | run: |
mkdir bins mkdir bins
cp rgb{asm,link,fix,gfx} bins cp rgb{asm,link,fix,gfx} bins
- name: Upload binaries - name: Upload binaries
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v7
with: with:
name: rgbds-canary-${{ matrix.os }}-${{ matrix.cxx }}-${{ matrix.buildsys }} name: rgbds-canary-${{ matrix.os }}-${{ matrix.cxx }}-${{ matrix.buildsys }}
path: bins path: bins
if-no-files-found: error
- name: Compute test dependency cache params - name: Compute test dependency cache params
id: test-deps-cache-params id: test-deps-cache-params
shell: bash
run: | run: |
paths=$(test/fetch-test-deps.sh --get-paths) paths=$(test/external/fetch-repos.sh --get-paths)
hash=$(test/fetch-test-deps.sh --get-hash) hash=$(test/external/fetch-repos.sh --get-hash)
tee -a <<<"paths=\"${paths//,/\\n}\"" $GITHUB_OUTPUT tee -a <<<"paths=\"${paths//,/\\n}\"" $GITHUB_OUTPUT
tee -a <<<"hash=${hash%-}" $GITHUB_OUTPUT tee -a <<<"hash=${hash%-}" $GITHUB_OUTPUT
- name: Check test dependency repositories cache - name: Check test dependency repositories cache
id: test-deps-cache id: test-deps-cache
uses: actions/cache@v4 uses: actions/cache@v5
with: with:
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }} path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
key: ${{ matrix.os }}-${{ steps.test-deps-cache-params.outputs.hash }} key: ${{ matrix.os }}-${{ steps.test-deps-cache-params.outputs.hash }}
@@ -61,211 +106,179 @@ jobs:
if: steps.test-deps-cache.outputs.cache-hit != 'true' if: steps.test-deps-cache.outputs.cache-hit != 'true'
continue-on-error: true continue-on-error: true
run: | run: |
test/fetch-test-deps.sh test/external/fetch-repos.sh
- name: Install test dependency dependencies - name: Run tests using our script
shell: bash if: matrix.buildsys == 'make'
run: | run: |
test/fetch-test-deps.sh --get-deps ${{ matrix.os }} CXX=${{ matrix.cxx }} test/run-tests.sh --os ${{ matrix.os }} --jobs "$(getconf _NPROCESSORS_ONLN)"
- name: Run tests - name: Run tests using CTest
shell: bash if: matrix.buildsys == 'cmake'
run: | run: |
CXX=${{ matrix.cxx }} test/run-tests.sh --os ${{ matrix.os }} ctest --test-dir build --schedule-random
macos-static: macos-static:
runs-on: macos-14 runs-on: macos-26
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v7
- name: Install deps - name: Install deps
shell: bash
run: | run: |
./.github/scripts/install_deps.sh macos .github/scripts/install-deps.sh macos lld
- name: Build libpng - name: Cache library deps
uses: actions/cache@v5
with:
path: ${{ env.DEPS_ROOT_DIR }}/*-tmp/
key: dep-srcs-${{ hashFiles('cmake/deps.cmake') }}
enableCrossOsArchive: true
- name: Build
run: | run: |
./.github/scripts/build_libpng.sh cmake -B build -G Ninja --preset macos-static -DFETCHCONTENT_BASE_DIR="${{ env.DEPS_ROOT_DIR }}"
- name: Build & install cmake --build build -- -k 0
env:
LDFLAGS: -fuse-ld=lld # cmake/macos-static.cmake comments explain why we use lld.
- name: Install
run: | run: |
make -kj CXXFLAGS="-O3 -flto -DNDEBUG -mmacosx-version-min=10.9 -arch x86_64 -arch arm64" PNGCFLAGS="-I libpng-staging/include" PNGLDLIBS="libpng-staging/lib/libpng.a -lz" Q= sudo cmake --install build --verbose
type rgbasm rgblink rgbfix rgbgfx
man -w 1 rgbasm rgblink rgbfix rgbgfx
- name: Package binaries - name: Package binaries
run: | run: |
mkdir bins mkdir bins
cp rgb{asm,link,fix,gfx} bins cp rgb{asm,link,fix,gfx} bins
- name: Upload binaries - name: Upload binaries
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v7
with: with:
name: rgbds-canary-macos-static name: rgbds-canary-macos-static
path: bins path: bins
if-no-files-found: error
- name: Compute test dependency cache params - name: Compute test dependency cache params
id: test-deps-cache-params id: test-deps-cache-params
shell: bash
run: | run: |
paths=$(test/fetch-test-deps.sh --get-paths) paths=$(test/external/fetch-repos.sh --get-paths)
hash=$(test/fetch-test-deps.sh --get-hash) hash=$(test/external/fetch-repos.sh --get-hash)
tee -a <<<"paths=\"${paths//,/\\n}\"" $GITHUB_OUTPUT tee -a <<<"paths=\"${paths//,/\\n}\"" $GITHUB_OUTPUT
tee -a <<<"hash=${hash%-}" $GITHUB_OUTPUT tee -a <<<"hash=${hash%-}" $GITHUB_OUTPUT
- name: Check test dependency repositories cache - name: Check test dependency repositories cache
id: test-deps-cache id: test-deps-cache
uses: actions/cache@v4 uses: actions/cache@v5
with: with:
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }} path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
key: ${{ matrix.os }}-${{ steps.test-deps-cache-params.outputs.hash }} key: macos-26-${{ steps.test-deps-cache-params.outputs.hash }}
- name: Fetch test dependency repositories - name: Fetch test dependency repositories
if: steps.test-deps-cache.outputs.cache-hit != 'true' if: steps.test-deps-cache.outputs.cache-hit != 'true'
continue-on-error: true continue-on-error: true
run: | run: |
test/fetch-test-deps.sh test/external/fetch-repos.sh
- name: Install test dependency dependencies
shell: bash
run: |
test/fetch-test-deps.sh --get-deps macos
- name: Run tests - name: Run tests
shell: bash
run: | run: |
test/run-tests.sh --os macos ctest --test-dir build --schedule-random
windows: windows:
strategy: strategy:
matrix: matrix:
bits: [32, 64] bits: [32, 64]
os: [windows-2022, windows-2025] os: [windows-2022, windows-2025]
include:
- bits: 32
arch: x86
platform: Win32
- bits: 64
arch: x86_x64
platform: x64
fail-fast: false fail-fast: false
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v7
- name: Install deps - name: Install deps
run: .github/scripts/get_win_deps.ps1 run: |
- name: Check libraries cache bash .github/scripts/install-deps.sh ${{ matrix.os }}
id: cache - name: Cache library deps
uses: actions/cache@v4 uses: actions/cache@v5
with: with:
path: | path: ${{ env.DEPS_ROOT_DIR }}/*-tmp/
zbuild key: dep-srcs-${{ hashFiles('cmake/deps.cmake') }}
pngbuild enableCrossOsArchive: true
key: ${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles('zlib/**', 'libpng/**') }} - name: Build
- name: Build zlib shell: cmd
if: steps.cache.outputs.cache-hit != 'true' run: | # ASan seems to be broken on Windows, so we disable it.
shell: bash call .github\scripts\msvc-env.bat ${{ matrix.bits }}
run: | # BUILD_SHARED_LIBS causes the output DLL to be correctly called `zlib1.dll` cmake -B build -G Ninja --preset develop -DFETCHCONTENT_BASE_DIR="${{ env.DEPS_ROOT_DIR }}" ^
cmake -S zlib -B zbuild -A ${{ matrix.platform }} -Wno-dev -DCMAKE_INSTALL_PREFIX=install_dir -DBUILD_SHARED_LIBS=ON -DSANITIZERS=OFF -DCMAKE_C_FLAGS="/nologo"
cmake --build zbuild --config Release -j cmake --build build -- -k 0
- name: Install zlib - name: Install
shell: cmd
run: | run: |
cmake --install zbuild cmake --install build --verbose
- name: Build libpng where rgbasm rgblink rgbfix rgbgfx
if: steps.cache.outputs.cache-hit != 'true'
shell: bash
run: |
cmake -S libpng -B pngbuild -A ${{ matrix.platform }} -Wno-dev -DCMAKE_INSTALL_PREFIX=install_dir -DPNG_SHARED=ON -DPNG_STATIC=OFF -DPNG_TESTS=OFF
cmake --build pngbuild --config Release -j
- name: Install libpng
run: |
cmake --install pngbuild
- name: Build Windows binaries
shell: bash
run: |
cmake -S . -B build -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release -j --verbose
cmake --install build --verbose --prefix install_dir
- name: Package binaries - name: Package binaries
shell: bash working-directory: build
run: | run: |
mkdir bins cpack -DCPACK_PACKAGE_FILE_NAME=rgbds-win${{ matrix.bits }} -G ZIP --verbose
cp install_dir/bin/{rgbasm.exe,rgblink.exe,rgbfix.exe,rgbgfx.exe,zlib1.dll,libpng16.dll} bins
- name: Upload Windows binaries - name: Upload Windows binaries
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v7
with: with:
name: rgbds-canary-w${{ matrix.bits }}-${{ matrix.os }} name: rgbds-canary-w${{ matrix.bits }}-${{ matrix.os }}
path: bins path: build/rgbds-win${{ matrix.bits }}.zip
if-no-files-found: error
- name: Compute test dependency cache params - name: Compute test dependency cache params
id: test-deps-cache-params id: test-deps-cache-params
shell: bash
run: | run: |
paths=$(test/fetch-test-deps.sh --get-paths) paths=$(test/external/fetch-repos.sh --get-paths)
hash=$(test/fetch-test-deps.sh --get-hash) hash=$(test/external/fetch-repos.sh --get-hash)
tee -a <<<"paths=\"${paths//,/\\n}\"" $GITHUB_OUTPUT tee -a <<<"paths=\"${paths//,/\\n}\"" $GITHUB_OUTPUT
tee -a <<<"hash=${hash%-}" $GITHUB_OUTPUT tee -a <<<"hash=${hash%-}" $GITHUB_OUTPUT
- name: Check test dependency repositories cache - name: Check test dependency repositories cache
id: test-deps-cache id: test-deps-cache
uses: actions/cache@v4 uses: actions/cache@v5
with: with:
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }} path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
key: ${{ matrix.os }}-${{ matrix.bits }}-${{ steps.test-deps-cache-params.outputs.hash }} key: ${{ matrix.os }}-${{ matrix.bits }}-${{ steps.test-deps-cache-params.outputs.hash }}
- name: Fetch test dependency repositories - name: Fetch test dependency repositories
if: steps.test-deps-cache.outputs.cache-hit != 'true' if: steps.test-deps-cache.outputs.cache-hit != 'true'
shell: bash
continue-on-error: true continue-on-error: true
run: | run: |
test/fetch-test-deps.sh test/external/fetch-repos.sh
- name: Install test dependency dependencies - name: Run tests using CTest
shell: bash
run: | run: |
test/fetch-test-deps.sh --get-deps ${{ matrix.os }} ctest --test-dir build --schedule-random
- name: Run tests
shell: bash
run: |
cp bins/* .
cp bins/*.dll test/gfx
test/run-tests.sh --os ${{ matrix.os }}
windows-mingw-build: windows-mingw-build:
strategy: strategy:
matrix: matrix:
bits: [32, 64] bits: [32, 64]
include:
- bits: 32
arch: i686
triplet: i686-w64-mingw32
- bits: 64
arch: x86-64
triplet: x86_64-w64-mingw32
fail-fast: false fail-fast: false
runs-on: ubuntu-22.04 runs-on: ubuntu-latest
env:
DIST_DIR: win${{ matrix.bits }}
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v7
- name: Install deps - name: Install deps
shell: bash
run: | run: |
./.github/scripts/install_deps.sh ubuntu .github/scripts/install-deps.sh ubuntu mingw${{ matrix.bits }}
- name: Install MinGW - name: Cache library deps
run: | # dpkg-dev is apparently required for pkg-config for cross-building uses: actions/cache@v5
sudo apt-get install g++-mingw-w64-${{ matrix.arch }}-win32 mingw-w64-tools libz-mingw-w64-dev dpkg-dev with:
- name: Install libpng dev headers for MinGW path: ${{ env.DEPS_ROOT_DIR }}/*-tmp/
run: | key: dep-srcs-${{ hashFiles('cmake/deps.cmake') }}
./.github/scripts/mingw-w64-libpng-dev.sh ${{ matrix.triplet }} enableCrossOsArchive: true
- name: Cross-build Windows binaries - name: Cross-build Windows binaries
run: | # MinGW doesn't support `--preset develop` sanitizers ASan or UBSan, so we disable them.
dll_search_dir=$(printf '%s\n' /usr/lib/gcc/*-w64-mingw32/*-win32 | tee -a /dev/stderr)
cmake -B build -G Ninja --preset develop -DFETCHCONTENT_BASE_DIR="${{ env.DEPS_ROOT_DIR }}" \
-DSANITIZERS=OFF -DDLL_SEARCH_DIRS="$dll_search_dir" \
--toolchain cmake/toolchain-mingw${{ matrix.bits }}.cmake
cmake --build build -- -k 0
- name: Package Windows binaries
run: | run: |
make mingw${{ matrix.bits }} -kj Q= cmake --install build --prefix . --verbose --component binaries
- name: Package binaries cmake --install build --prefix . --verbose --component shared-libs
run: | # DLL dependencies can be figured out using e.g. Dependency Walker or objdump -p
mkdir bins
mv -v rgb{asm,link,fix,gfx}.exe bins/
cp -v /usr/${{ matrix.triplet }}/lib/zlib1.dll bins
cp -v /usr/${{ matrix.triplet }}/bin/libpng16-16.dll bins
cp -v /usr/lib/gcc/${{ matrix.triplet }}/10-win32/lib{ssp-0,stdc++-6}.dll bins
[ "${{ matrix.bits }}" -ne 32 ] || cp -v /usr/lib/gcc/${{ matrix.triplet }}/10-win32/libgcc_s_dw2-1.dll bins
- name: Upload Windows binaries - name: Upload Windows binaries
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v7
with: with:
name: rgbds-canary-mingw-win${{ matrix.bits }} name: rgbds-canary-mingw-win${{ matrix.bits }}
path: bins path: bin
if-no-files-found: error
- name: Upload Windows test binaries - name: Upload Windows test binaries
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v7
with: with:
name: testing-programs-mingw-win${{ matrix.bits }} name: testing-programs-mingw-win${{ matrix.bits }}
path: | path: |
test/gfx/randtilegen.exe test/gfx/randtilegen.exe
test/gfx/rgbgfx_test.exe test/gfx/rgbgfx_test.exe
if-no-files-found: error
windows-mingw-testing: windows-mingw-testing:
needs: windows-mingw-build needs: windows-mingw-build
@@ -277,113 +290,162 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v7
- name: Install test deps
run: |
.github/scripts/install-deps.sh windowsmingw
- name: Retrieve binaries - name: Retrieve binaries
uses: actions/download-artifact@v4 uses: actions/download-artifact@v8
with: with:
name: rgbds-canary-mingw-win${{ matrix.bits }} name: rgbds-canary-mingw-win${{ matrix.bits }}
path: bins path: bins
- name: Retrieve test binaries - name: Retrieve test binaries
uses: actions/download-artifact@v4 uses: actions/download-artifact@v8
with: with:
name: testing-programs-mingw-win${{ matrix.bits }} name: testing-programs-mingw-win${{ matrix.bits }}
path: test/gfx path: test/gfx
- name: Extract binaries - name: Extract binaries
shell: bash
run: | run: |
cp bins/* . cp bins/* .
cp bins/*.dll test/gfx cp bins/*.dll test/gfx
- name: Compute test dependency cache params - name: Compute test dependency cache params
id: test-deps-cache-params id: test-deps-cache-params
shell: bash
run: | run: |
paths=$(test/fetch-test-deps.sh --get-paths) paths=$(test/external/fetch-repos.sh --get-paths)
hash=$(test/fetch-test-deps.sh --get-hash) hash=$(test/external/fetch-repos.sh --get-hash)
tee -a <<<"paths=\"${paths//,/\\n}\"" $GITHUB_OUTPUT tee -a <<<"paths=\"${paths//,/\\n}\"" $GITHUB_OUTPUT
tee -a <<<"hash=${hash%-}" $GITHUB_OUTPUT tee -a <<<"hash=${hash%-}" $GITHUB_OUTPUT
- name: Check test dependency repositories cache - name: Check test dependency repositories cache
id: test-deps-cache id: test-deps-cache
uses: actions/cache@v4 uses: actions/cache@v5
with: with:
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }} path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
key: mingw-${{ matrix.bits }}-${{ steps.test-deps-cache-params.outputs.hash }} key: mingw-${{ matrix.bits }}-${{ steps.test-deps-cache-params.outputs.hash }}
- name: Fetch test dependency repositories - name: Fetch test dependency repositories
if: steps.test-deps-cache.outputs.cache-hit != 'true' if: steps.test-deps-cache.outputs.cache-hit != 'true'
shell: bash
continue-on-error: true continue-on-error: true
run: | run: |
test/fetch-test-deps.sh test/external/fetch-repos.sh
- name: Install test dependency dependencies
shell: bash
run: |
test/fetch-test-deps.sh --get-deps ${{ matrix.os }}
- name: Run tests - name: Run tests
shell: bash
run: | run: |
test/run-tests.sh --os ${{ matrix.os }} test/run-tests.sh --os ${{ matrix.os }} --jobs "$(getconf _NPROCESSORS_ONLN)"
cygwin: cygwin:
strategy: strategy:
matrix: matrix:
bits: [32, 64] arch: [x86, x86_64]
include:
- bits: 32
arch: x86
- bits: 64
arch: x86_64
fail-fast: false fail-fast: false
runs-on: windows-2022 runs-on: windows-2022
timeout-minutes: 30 timeout-minutes: 30
env:
CYGWIN_NOWINPATH: 1
CHERE_INVOKING: 1
defaults:
run:
shell: C:\cygwin\bin\bash.exe -leo igncr '{0}'
steps: steps:
- name: Save Windows git location for the PATH
shell: pwsh
run: | # The `cygwin/cygwin-install-action` step will override the PATH with Cygwin's binaries
"ORIGINAL_GIT_DIR=$(Split-Path (Get-Command git).Source)" >> $env:GITHUB_ENV
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v7
with:
fetch-depth: 0
fetch-tags: true
- name: Setup Cygwin - name: Setup Cygwin
uses: cygwin/cygwin-install-action@v4 uses: cygwin/cygwin-install-action@v6
with: with:
platform: ${{ matrix.arch }} platform: ${{ matrix.arch }}
packages: >- packages: >-
bison bison
cmake
gcc-g++ gcc-g++
git git
libpng-devel libpng-devel
make make
ninja
pkg-config pkg-config
- name: Build & install using Make python3
shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -o igncr '{0}' python-imaging
run: | # Cygwin does not support `make develop` sanitizers ASan or UBSan - name: Build using Make
make -kj Q= if: matrix.arch == 'x86'
make install -j Q= run: | # Cygwin doesn't support `make develop` sanitizers ASan or UBSan, so we don't use it.
- name: Run tests make -k -j "$(getconf _NPROCESSORS_ONLN)" Q=
shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -o igncr '{0}' - name: Install using Make
if: matrix.arch == 'x86'
run: | run: |
test/run-tests.sh --only-internal make install Q=
type rgbasm rgblink rgbfix rgbgfx
man -w 1 rgbasm rgblink rgbfix rgbgfx
- name: Build using CMake
if: matrix.arch == 'x86_64'
run: | # Cygwin doesn't support `--preset develop` sanitizers ASan or UBSan, so we disable them.
cmake -B build -G Ninja --preset develop -DSANITIZERS=OFF
cmake --build build -- -k 0
- name: Install using CMake
if: matrix.arch == 'x86_64'
run: |
cmake --install build --verbose
type rgbasm rgblink rgbfix rgbgfx
man -w 1 rgbasm rgblink rgbfix rgbgfx
- name: Compute test dependency cache params
id: test-deps-cache-params
run: |
paths=$(test/external/fetch-repos.sh --get-paths)
hash=$(test/external/fetch-repos.sh --get-hash)
tee -a <<<"paths=\"${paths//,/\\n}\"" $GITHUB_OUTPUT
tee -a <<<"hash=${hash%-}" $GITHUB_OUTPUT
- name: Check test dependency repositories cache
id: test-deps-cache
uses: actions/cache@v5
with:
path: ${{ fromJSON(steps.test-deps-cache-params.outputs.paths) }}
key: cygwin-${{ matrix.bits }}-${{ steps.test-deps-cache-params.outputs.hash }}
- name: Fetch test dependency repositories
if: steps.test-deps-cache.outputs.cache-hit != 'true'
continue-on-error: true
run: |
test/external/fetch-repos.sh
- name: Run tests using our script
if: matrix.arch == 'x86'
run: | # 32-bit Cygwin's `git` 2.38 doesn't support `git clone --revision`, so we don't fetch/run external tests.
test/run-tests.sh --os cygwin --only-internal --jobs "$(getconf _NPROCESSORS_ONLN)"
- name: Run tests using CTest
if: matrix.arch == 'x86_64'
run: |
ctest --test-dir build --schedule-random
- name: Use Windows git location in the PATH
shell: pwsh
run: | # Prevents the `actions/checkout` post-job cleanup from using Cygwin's git binary
"$env:ORIGINAL_GIT_DIR" >> $env:GITHUB_PATH
freebsd: freebsd:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 30 timeout-minutes: 30
steps: steps:
- name: Checkout repo - name: Checkout repo
uses: actions/checkout@v4 uses: actions/checkout@v7
with:
fetch-depth: 0
fetch-tags: true
- name: Build & test using CMake on FreeBSD - name: Build & test using CMake on FreeBSD
uses: vmactions/freebsd-vm@v1 uses: vmactions/freebsd-vm@v1
with: with:
release: "14.3" envs: >-
TERM CLICOLOR CLICOLOR_FORCE
CMAKE_COLOR_DIAGNOSTICS CMAKE_CONFIG_TYPE
CTEST_PARALLEL_LEVEL
CTEST_NO_TESTS_ACTION CTEST_OUTPUT_ON_FAILURE
GIT_CONFIG_COUNT GIT_CONFIG_KEY_0 GIT_CONFIG_VALUE_0
release: "15.1"
usesh: true usesh: true
copyback: false
prepare: | prepare: |
pkg install -y \ .github/scripts/install-deps.sh freebsd
bash \ run: |
bison \ : # Build
cmake \ cmake -B build -G Ninja --preset develop
git \ cmake --build build -- -k 0
png : # Install
run: | # FreeBSD `c++` compiler does not support `make develop` sanitizers ASan or UBSan
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=c++ -DUSE_EXTERNAL_TESTS=OFF -DOS=bsd
cmake --build build -j4 --verbose
cmake --install build --verbose cmake --install build --verbose
cmake --build build --target test type rgbasm rgblink rgbfix rgbgfx
man -w 1 rgbasm rgblink rgbfix rgbgfx
: # Run tests
: # Leak detection is not supported on FreeBSD, so we disable it.
. .github/scripts/freebsd-env.sh
ASAN_OPTIONS=detect_leaks=0 ctest --test-dir build --schedule-random
+24 -34
View File
@@ -4,57 +4,47 @@ on:
branches: branches:
- master - master
paths: paths:
- man/gbz80.7 - man/*
- man/rgbds.5 workflow_dispatch:
- man/rgbds.7
- man/rgbasm.1 env:
- man/rgbasm.5 # Force colored output (see https://bixense.com/clicolors/ and https://force-color.org/)
- man/rgbasm-old.5 TERM: xterm-256color
- man/rgblink.1 CLICOLOR: 1
- man/rgblink.5 CLICOLOR_FORCE: 1
- man/rgbfix.1
- man/rgbgfx.1
jobs: jobs:
build: build:
if: github.repository_owner == 'gbdev' if: github.repository_owner == 'gbdev'
runs-on: ubuntu-latest runs-on: ubuntu-slim
steps: steps:
- name: Checkout rgbds@master - name: Checkout rgbds
uses: actions/checkout@v4 uses: actions/checkout@v7
with: with:
repository: gbdev/rgbds
ref: master
path: rgbds path: rgbds
- name: Checkout rgbds-www@master sparse-checkout: man # Avoid checking out other files, we don't use them here.
uses: actions/checkout@v4 - name: Checkout rgbds-www
uses: actions/checkout@v7
with: with:
repository: gbdev/rgbds-www
ref: master
path: rgbds-www path: rgbds-www
repository: ${{ github.repository_owner }}/rgbds-www
ref: master # Need to check out the branch to be able to commit on and push it.
ssh-key: !!str ${{ secrets.SSH_KEY_SECRET }}
- name: Install groff and mandoc - name: Install groff and mandoc
run: | run: |
sudo apt-get -qq update sudo apt install -Uyq groff mandoc -o APT::Color=yes -o Acquire::Queue-Mode=access
sudo apt-get install -yq groff mandoc
- name: Update pages - name: Update pages
working-directory: rgbds/man working-directory: rgbds/man
run: | run: |
../../rgbds-www/maintainer/man_to_html.sh master * ../../rgbds-www/maintainer/man_to_html.sh master *
- name: Push new pages - name: Push new pages
working-directory: rgbds-www working-directory: rgbds-www
env:
GIT_AUTHOR_NAME: !!str ${{ github.actor }}
GIT_AUTHOR_EMAIL: !!str ${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com
GIT_COMMITTER_NAME: github-actions[bot]
GIT_COMMITTER_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com
run: | run: |
mkdir -p -m 700 ~/.ssh
echo "${{ secrets.SSH_KEY_SECRET }}" > ~/.ssh/id_ed25519
chmod 0600 ~/.ssh/id_ed25519
eval $(ssh-agent -s)
ssh-add ~/.ssh/id_ed25519
git config --global user.name "GitHub Action"
git config --global user.email "[email protected]"
git add -A git add -A
git commit -m "Update RGBDS master documentation" git commit -m "Update RGBDS master documentation"
if git remote | grep -q origin; then git push
git remote set-url origin [email protected]:gbdev/rgbds-www.git
else
git remote add origin [email protected]:gbdev/rgbds-www.git
fi
git push origin master
+4 -1
View File
@@ -2,6 +2,7 @@
/rgblink /rgblink
/rgbfix /rgbfix
/rgbgfx /rgbgfx
/install.sh
/rgbshim.sh /rgbshim.sh
/coverage/ /coverage/
*.o *.o
@@ -10,9 +11,11 @@
*.gcno *.gcno
*.gcda *.gcda
*.gcov *.gcov
/compile_flags.txt
CMakeCache.txt CMakeCache.txt
CMakeFiles/ CMakeFiles/
cmake_install.cmake cmake_install.cmake
build/ CMakeUserPresets.json
build*/
*.dSYM/ *.dSYM/
callgrind.out.* callgrind.out.*
+191 -94
View File
@@ -1,122 +1,199 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
# 3.9 required for LTO checks # - 3.24 is required for `FetchContent`'s `find_package` integration.
# 3.17 optional for CMAKE_CTEST_ARGUMENTS # Older versions *may* work, but we can't test them; compat patches are welcome.
cmake_minimum_required(VERSION 3.9..3.17 FATAL_ERROR) # - 3.17 is required for `CHECK_*` messages to display properly, but is not essential.
# - 3.9 is required for LTO checks.
cmake_minimum_required(VERSION 3.24...4.3 FATAL_ERROR)
# Read the project version from the header (the canonical source of truth).
file(STRINGS "include/version.hpp" version_defines REGEX "^[ \t]*#define[ \t]+PACKAGE_VERSION_")
foreach(line IN LISTS version_defines)
# We want the `CMAKE_MATCH_n` variables, so we just need to run *some* regex op.
string(REGEX MATCH "PACKAGE_(VERSION_[^ \t]+)[ \t]+([0-9]+)" dummy "${line}")
set("${CMAKE_MATCH_1}" "${CMAKE_MATCH_2}")
endforeach()
project(rgbds project(rgbds
LANGUAGES CXX) VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}"
LANGUAGES CXX
include(CTest) DESCRIPTION "Game Boy assembly toolchain"
HOMEPAGE_URL "https://rgbds.gbdev.io")
# get real path of source and binary directories if(DEFINED VERSION_RC)
get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH) string(APPEND CMAKE_PROJECT_VERSION "-rc${VERSION_RC}")
get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH)
# reject in-source builds, may conflict with Makefile
if(srcdir STREQUAL bindir)
message("RGBDS should not be built in the source directory.")
message("Instead, create a separate build directory and specify to CMake the path to the source directory.")
message(FATAL_ERROR "Terminating configuration")
endif() endif()
option(SANITIZERS "Build with sanitizers enabled" OFF) # Ignored on MSVC # Reject in-source builds, as they may conflict with the Makefile.
option(MORE_WARNINGS "Turn on more warnings" OFF) # Ignored on MSVC get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH)
get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH)
if(srcdir STREQUAL bindir)
message(FATAL_ERROR "RGBDS should not be built in the source directory.
Instead, create a separate build directory and specify to CMake the path to the source directory.")
endif()
if(MSVC) include(CTest) # Note: CTest only functions properly if included from the top-level CMakeLists.
# MSVC's own standard library triggers warning C5105, include(GNUInstallDirs)
# "macro expansion producing 'defined' has undefined behavior". include(CMakeDependentOption)
# Warning C5030 is about unknown attributes (`[[gnu::ATTR]]`), none of ours being load-bearing.
# Warning C4996 is about using POSIX names, which we want to do for portability.
# We also opt into the C++20-conformant preprocessor.
add_compile_options(/MP /wd5105 /wd5030 /wd4996 /Zc:preprocessor)
add_definitions(/D_CRT_SECURE_NO_WARNINGS)
if(SANITIZERS) ## Compiler switches that should apply to our deps too.
set(SAN_FLAGS /fsanitize=address)
add_compile_options(${SAN_FLAGS}) option(SANITIZERS "Build with sanitizers enabled" OFF)
add_link_options(${SAN_FLAGS}) cmake_dependent_option(MORE_WARNINGS "Turn on more warnings" OFF "NOT MSVC" OFF)
endif()
else() if(NOT DEFINED CMAKE_MSVC_RUNTIME_LIBRARY) # Allow this to be overridden from the CLI easily.
add_compile_options(-Wall -pedantic) # We link against the Microsoft C++ runtime libs statically, except the debug-mode one.
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") # This is because Microsoft's license terms (through some indirection) allow us to redistribute
# C++20 allows macros to take zero variadic arguments, but Clang (aka AppleClang on macOS) # the runtime lib code *except* the debug-mode libs'.
# does not recognize this yet. # Since we want to remain able to not think too hard about debug executables, we link against
add_compile_options(-Wno-gnu-zero-variadic-macro-arguments) # the debug libs dynamically, shifting the burden of obtaining them onto the recipient.
endif() set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:DebugDLL>")
if(SANITIZERS) endif()
if(SANITIZERS)
if(MSVC)
message(STATUS "ASan enabled")
add_compile_options(/fsanitize=address) # Note that this shouldn't be passed to the linker.
else() # We assume a GNU-like compiler.
message(STATUS "ASan and UBSan enabled")
set(SAN_FLAGS -fsanitize=address -fsanitize=undefined set(SAN_FLAGS -fsanitize=address -fsanitize=undefined
-fsanitize=float-divide-by-zero) -fsanitize=float-divide-by-zero)
add_compile_options(${SAN_FLAGS}) add_compile_options(${SAN_FLAGS})
add_link_options(${SAN_FLAGS}) add_link_options(${SAN_FLAGS})
add_definitions(-D_GLIBCXX_ASSERTIONS) add_definitions(-D_GLIBCXX_ASSERTIONS -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG)
# A non-zero optimization level is desired in debug mode, but allow overriding it nonetheless # A non-zero optimization level is desired even in debug mode (especially for Clang),
set(CMAKE_CXX_FLAGS_DEBUG "-g -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls ${CMAKE_CXX_FLAGS_DEBUG}" # and the two codegen flags improve the sanitizers' backtraces, but we want the user to
CACHE STRING "" FORCE) # be able to override these easily so we put them first.
string(PREPEND CMAKE_CXX_FLAGS_DEBUG "-Og -fno-omit-frame-pointer -fno-optimize-sibling-calls ")
endif() endif()
endif()
message(CHECK_START "Checking if LTO is supported")
include(CheckIPOSupported)
check_ipo_supported(RESULT enable_lto)
if(enable_lto)
message(CHECK_PASS "yes (enabled only in release modes)")
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO ON)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL ON)
else()
message(CHECK_FAIL "no")
endif()
# Use versioning consistent with Makefile:
# the git revision is used but uses the fallback in an archive.
message(CHECK_START "Determining RGBDS version from Git history")
list(APPEND CMAKE_MESSAGE_INDENT " ")
set(GIT_REV "") # This fallback is important!
find_package(Git)
list(POP_BACK CMAKE_MESSAGE_INDENT)
if(NOT Git_FOUND)
message(CHECK_FAIL "Git not found")
else()
execute_process(COMMAND "${GIT_EXECUTABLE}" --git-dir=.git -c safe.directory='*'
describe --tags --dirty --always --match "v[0-9]*"
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
OUTPUT_VARIABLE GIT_REV OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_VARIABLE git_err ERROR_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE result)
if(NOT result EQUAL 0)
# Note that this happens e.g. when building from a tarball, so it shouldn't fail the build!
message(CHECK_FAIL "error ${result} from Git:")
list(APPEND CMAKE_MESSAGE_INDENT " ")
message("${git_err}")
list(POP_BACK CMAKE_MESSAGE_INDENT)
else()
message(CHECK_PASS "${GIT_REV}")
if(NOT "${GIT_REV}" MATCHES "^v[0-9]+\\.[0-9]+\\.[0-9]+(-rc[0-9]+)?")
# Can't find an ancestor tag! (That passes `--match`, anyway.)
message(WARNING "No `v*` Git tag reachable; falling back")
elseif(NOT CMAKE_MATCH_0 STREQUAL "v${CMAKE_PROJECT_VERSION}")
message(SEND_ERROR "\
Version mismatch! Git says ${CMAKE_MATCH_0},
version.hpp says v${CMAKE_PROJECT_VERSION}!")
endif()
endif()
endif()
## Dependencies.
include(FetchContent)
include(cmake/deps.cmake)
# Be sure to make zlib available before libpng, otherwise libpng
# will attempt to `find_package(ZLIB)` without going through `FetchContent`.
if(NOT DEFINED ZLIB_BUILD_TESTING) # Unless overridden (e.g. in the cache), we don't care about zlib's tests.
set(ZLIB_BUILD_TESTING OFF)
endif()
FetchContent_MakeAvailable(ZLIB)
if(NOT DEFINED ZLIB_INCLUDE_DIRS)
set(ZLIB_INCLUDE_DIRS "${zlib_BINARY_DIR};${zlib_SOURCE_DIR}") # libpng's `genout` script relies on this variable to be set.
endif()
# Statically building zlib (from source) generates a target named differently.
if(NOT TARGET ZLIB::ZLIB AND TARGET zlibstatic)
add_library(ZLIB::ZLIB ALIAS zlibstatic)
endif()
if(NOT DEFINED PNG_TESTS) # Unless overridden (e.g. in the cache), we don't care about libpng's tests.
set(PNG_TESTS OFF)
endif()
FetchContent_MakeAvailable(PNG)
if(NOT TARGET PNG::PNG)
if(PNG_SHARED)
add_library(PNG::PNG ALIAS png_shared)
else()
add_library(PNG::PNG ALIAS png_static)
endif()
endif()
## The actual stuff.
# Any compiler options that shouldn't apply to our dependencies go here.
include_directories("include")
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)
if(MSVC)
add_compile_options(
/wd5030 # Warning C5030 is about unknown attributes (`[[gnu::ATTR]]`), none of ours being load-bearing.
/wd4996 # Warning C4996 is about using POSIX names, which we want to do for portability.
/Zc:preprocessor # Opt into the C++20-conformant preprocessor.
)
add_definitions(/D_CRT_SECURE_NO_WARNINGS)
else()
add_compile_options(-Wall -pedantic -Wno-unknown-warning-option -fno-exceptions -fno-rtti)
if(MORE_WARNINGS) if(MORE_WARNINGS)
add_compile_options(-Werror -Wextra add_compile_options(-Wextra
-Walloc-zero -Wcast-align -Wcast-qual -Wduplicated-branches -Wduplicated-cond -Walloc-zero -Wcast-align -Wcast-qual -Wduplicated-branches -Wduplicated-cond
-Wfloat-equal -Wlogical-op -Wnull-dereference -Wold-style-cast -Wshift-overflow=2 -Wfloat-equal -Wlogical-op -Wnull-dereference -Wold-style-cast -Wshift-overflow=2
-Wstringop-overflow=4 -Wtrampolines -Wundef -Wuninitialized -Wunused -Wshadow -Wstringop-overflow=4 -Wtrampolines -Wundef -Wuninitialized -Wunused -Wshadow
-Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1 -Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1
-Wno-format-nonliteral -Wno-strict-overflow -Wno-format-nonliteral -Wno-strict-overflow
-Wno-unused-but-set-variable # bison's `yynerrs_` is incremented but unused
-Wno-type-limits -Wno-tautological-constant-out-of-range-compare -Wno-type-limits -Wno-tautological-constant-out-of-range-compare
-Wvla # MSVC does not support VLAs -Wvla) # MSVC does not support VLAs
-Wno-unknown-warning-option) # Clang shouldn't diagnose unknown warnings endif()
add_compile_options(-Wno-unused-but-set-variable # bison's `yynerrs_` is incremented but unused
# C++20 allows macros to take zero variadic arguments.
# Some versions of Clang don't recognize this, and treat them as a GNU extension.
-Wno-gnu-zero-variadic-macro-arguments)
if(MINGW)
# MinGW warns about format specifiers like `%z` and `%j`, which are missing on Windows XP and earlier.
# We rely on runtimes more modern than that, so we can ignore those warnings.
# If this is a problem for you: use Microsoft's equivalents, or define `__USE_MINGW_ANSI_STDIO=1`.
# https://sourceforge.net/p/mingw-w64/wiki2/gnu%20printf/
add_compile_options(-Wno-error=format -Wno-error=format-extra-args)
endif() endif()
endif() endif()
# Use versioning consistent with Makefile
# the git revision is used but uses the fallback in an archive
find_program(GIT git)
if(GIT)
execute_process(COMMAND ${GIT} --git-dir=.git -c safe.directory='*' describe --tags --dirty --always
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_REV OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET)
message(STATUS "RGBDS version: ${GIT_REV}")
else(GIT)
message(STATUS "Cannot determine RGBDS version (Git not installed), falling back")
endif(GIT)
find_package(PkgConfig)
if(MSVC OR NOT PKG_CONFIG_FOUND)
# fallback to find_package
# cmake's FindPNG is very fragile; it breaks when multiple versions are installed
# this is most evident on macOS but can occur on Linux too
find_package(PNG REQUIRED)
else()
pkg_check_modules(LIBPNG REQUIRED libpng)
endif()
include_directories("${PROJECT_SOURCE_DIR}/include")
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)
add_subdirectory(src) add_subdirectory(src)
set(CMAKE_CTEST_ARGUMENTS "--verbose") if(BUILD_TESTING) # This option is defined implicitly by `include(CTest)`.
add_subdirectory(test) add_subdirectory(test)
# By default, build in Release mode; Debug mode must be explicitly requested
# (You may want to augment it with the options above)
if(CMAKE_BUILD_TYPE STREQUAL "Release")
message(CHECK_START "Checking if LTO is supported")
include(CheckIPOSupported)
check_ipo_supported(RESULT enable_lto)
if(enable_lto)
message(CHECK_PASS "yes")
set_property(TARGET rgbasm rgblink rgbfix rgbgfx PROPERTY INTERPROCEDURAL_OPTIMIZATION ON)
else()
message(CHECK_FAIL "no")
endif()
endif() endif()
set(MANDIR "share/man")
set(man1 "man/rgbasm.1" set(man1 "man/rgbasm.1"
"man/rgbfix.1" "man/rgbfix.1"
"man/rgbgfx.1" "man/rgbgfx.1"
@@ -129,6 +206,26 @@ set(man7 "man/gbz80.7"
"man/rgbds.7") "man/rgbds.7")
foreach(SECTION "man1" "man5" "man7") foreach(SECTION "man1" "man5" "man7")
set(DEST "${MANDIR}/${SECTION}") install(FILES ${${SECTION}} DESTINATION "${CMAKE_INSTALL_MANDIR}/${SECTION}" COMPONENT man)
install(FILES ${${SECTION}} DESTINATION ${DEST})
endforeach() endforeach()
## Packaging.
# We only specify here the package-agnostic options;
# the rest is rather convention from our side, and thus more appropriate for presets or CLI flags.
## CPACK_PACKAGE_NAME: copied from `project()`
set(CPACK_PACKAGE_VENDOR "GBDev")
set(CPACK_PACKAGE_VERSION "${CMAKE_PROJECT_VERSION}") # The individual components are defined implicitly.
set(CPACK_PACKAGE_DESCRIPTION "An assembly toolchain for the Nintendo Game Boy and Game Boy Color") # Same as our repo's description.
## CPACK_PACKAGE_DESCRIPTION_SUMMARY: copied from `project()`
set(CPACK_PACKAGE_HOMEPAGE_URL "https://rgbds.gbdev.io")
## CPACK_PACKAGE_FILE_NAME: should be provided at runtime (`cpack -P`)
set(CPACK_PACKAGE_CHECKSUM SHA256)
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README.md")
set(CPACK_STRIP_FILES ON) # Only applies to binary packages, not sources.
set(CPACK_VERBATIM_VARIABLES ON)
set(CPACK_THREADS 0) # Use all available CPU cores.
set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY OFF)
include(CPack)
+46
View File
@@ -0,0 +1,46 @@
{
"version": 3,
"configurePresets": [
{
"name": "develop",
"description": "Debugging config for maintainers",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_EXPORT_COMPILE_COMMANDS": true,
"CMAKE_COMPILE_WARNING_AS_ERROR": true,
"CMAKE_LINK_WARNING_AS_ERROR": true,
"MORE_WARNINGS": true,
"SANITIZERS": true
},
"warnings": { "uninitialized": true },
"errors": { "dev": true }
},
{
"name": "develop-msbuild32",
"inherits": ["develop", "msbuild32"]
},
{
"name": "develop-msbuild64",
"inherits": ["develop", "msbuild64"]
},
{
"name": "msbuild32",
"description": "Flags for building 32-bit executables with MSBuild",
"architecture": "Win32"
},
{
"name": "msbuild64",
"description": "Flags for building 64-bit executables with MSBuild",
"architecture": "x64"
},
{
"name": "macos-static",
"description": "Flags for building an executable compatible with old macOS",
"cacheVariables": {
"CMAKE_PROJECT_TOP_LEVEL_INCLUDES": "cmake/macos-static.cmake"
}
}
]
}
+12 -8
View File
@@ -1,21 +1,25 @@
FROM debian:12-slim FROM debian:13-slim
LABEL org.opencontainers.image.source=https://github.com/gbdev/rgbds LABEL org.opencontainers.image.source=https://github.com/gbdev/rgbds
ARG version=1.0.0-rc2 ARG version=1.0.3
WORKDIR /rgbds WORKDIR /rgbds
COPY . . COPY . .
# Install dependencies
RUN apt-get update && \ RUN apt-get update && \
apt-get install sudo make cmake gcc build-essential -y apt-get install sudo make cmake gcc build-essential -y
RUN ./.github/scripts/install-deps.sh debian
# Install dependencies and compile RGBDS # Build RGBDS
RUN ./.github/scripts/install_deps.sh ubuntu-22.04 RUN make -j "$(getconf _NPROCESSORS_ONLN)" CXXFLAGS="-O3 -flto -DNDEBUG -static" PKG_CONFIG="pkg-config --static" Q=
RUN make -j CXXFLAGS="-O3 -flto -DNDEBUG -static" PKG_CONFIG="pkg-config --static" Q=
# Create an archive with the compiled executables and all the necessary to install it, # Create the install script
RUN make install.sh Q=
# Create an archive with the compiled executables, man pages, and install script,
# so it can be copied outside of the container and installed/used in another system # so it can be copied outside of the container and installed/used in another system
RUN tar caf rgbds-linux-x86_64.tar.xz --transform='s#.*/##' rgbasm rgblink rgbfix rgbgfx man/* .github/scripts/install.sh RUN tar caf rgbds-linux-x86_64.tar.xz --transform='s#.*/##' rgbasm rgblink rgbfix rgbgfx man/* install.sh
# Install RGBDS on the container so all the executables will be available in the PATH # Install RGBDS on the container so all the executables will be available in the PATH
RUN cp man/* . RUN cp man/* .
RUN ./.github/scripts/install.sh RUN make install Q=
+31 -38
View File
@@ -3,7 +3,7 @@
.SUFFIXES: .SUFFIXES:
.SUFFIXES: .cpp .y .o .SUFFIXES: .cpp .y .o
.PHONY: all clean install checkdiff develop debug profile coverage format tidy iwyu mingw32 mingw64 wine-shim dist .PHONY: all clean install develop debug profile coverage format tidy iwyu wine-shim dist
# User-defined variables # User-defined variables
@@ -15,6 +15,7 @@ SUFFIX :=
STRIP := -s STRIP := -s
BINMODE := 755 BINMODE := 755
MANMODE := 644 MANMODE := 644
MANSRC := man/
# Other variables # Other variables
@@ -26,31 +27,32 @@ PNGLDLIBS := `${PKG_CONFIG} --libs-only-l libpng`
# Note: if this comes up empty, `version.cpp` will automatically fall back to last release number # Note: if this comes up empty, `version.cpp` will automatically fall back to last release number
VERSION_STRING := `git --git-dir=.git -c safe.directory='*' describe --tags --dirty --always 2>/dev/null` VERSION_STRING := `git --git-dir=.git -c safe.directory='*' describe --tags --dirty --always 2>/dev/null`
WARNFLAGS := -Wall -pedantic -Wno-unknown-warning-option -Wno-gnu-zero-variadic-macro-arguments WARNFLAGS := -Wall -pedantic -Wno-unknown-warning-option \
-Wno-gnu-zero-variadic-macro-arguments -Wno-unused-but-set-variable
# Overridable CXXFLAGS # Overridable CXXFLAGS
CXXFLAGS ?= -O3 -flto -DNDEBUG CXXFLAGS ?= -O3 -flto -DNDEBUG
# Non-overridable CXXFLAGS # Non-overridable CXXFLAGS
REALCXXFLAGS := ${CXXFLAGS} ${WARNFLAGS} -std=c++2a -I include -fno-exceptions -fno-rtti REQUIREDCXXFLAGS := -std=c++20 -I include -fno-exceptions -fno-rtti
REALCXXFLAGS := ${CXXFLAGS} ${WARNFLAGS} ${REQUIREDCXXFLAGS}
# Overridable LDFLAGS # Overridable LDFLAGS
LDFLAGS ?= LDFLAGS ?=
# Non-overridable LDFLAGS # Non-overridable LDFLAGS
REALLDFLAGS := ${LDFLAGS} ${WARNFLAGS} -DBUILD_VERSION_STRING=\"${VERSION_STRING}\" REQUIREDLDFLAGS := -DBUILD_VERSION_STRING=\"${VERSION_STRING}\"
REALLDFLAGS := ${LDFLAGS} ${WARNFLAGS} ${REQUIREDLDFLAGS}
# Wrapper around bison that passes flags depending on what the version supports # Wrapper around bison that passes flags depending on what the version supports
BISON := src/bison.sh BISON := src/bison.sh
RM := rm -rf RM := rm -rf
# Used for checking pull requests
BASE_REF := origin/master
# Rules to build the RGBDS binaries # Rules to build the RGBDS binaries
all: rgbasm rgblink rgbfix rgbgfx all: rgbasm rgblink rgbfix rgbgfx
common_obj := \ common_obj := \
src/extern/getopt.o \ src/extern/getopt.o \
src/cli.o \
src/diagnostics.o \ src/diagnostics.o \
src/style.o \ src/style.o \
src/usage.o \ src/usage.o \
@@ -63,6 +65,7 @@ rgbasm_obj := \
src/asm/fixpoint.o \ src/asm/fixpoint.o \
src/asm/format.o \ src/asm/format.o \
src/asm/fstack.o \ src/asm/fstack.o \
src/asm/intern.o \
src/asm/lexer.o \ src/asm/lexer.o \
src/asm/macro.o \ src/asm/macro.o \
src/asm/main.o \ src/asm/main.o \
@@ -118,6 +121,7 @@ rgbgfx_obj := \
src/gfx/pal_packing.o \ src/gfx/pal_packing.o \
src/gfx/pal_sorting.o \ src/gfx/pal_sorting.o \
src/gfx/pal_spec.o \ src/gfx/pal_spec.o \
src/gfx/palette.o \
src/gfx/png.o \ src/gfx/png.o \
src/gfx/process.o \ src/gfx/process.o \
src/gfx/reverse.o \ src/gfx/reverse.o \
@@ -180,6 +184,11 @@ src/gfx/rgba.o: src/gfx/rgba.cpp
.cpp.o: .cpp.o:
$Q${CXX} ${REALCXXFLAGS} -c -o $@ $< $Q${CXX} ${REALCXXFLAGS} -c -o $@ $<
install.sh:
$Qecho '#!/usr/bin/env bash' > $@
$Q${MAKE} -s -n install MANSRC= >> $@
$Qchmod +x $@
# Target used to remove all files generated by other Makefile targets # Target used to remove all files generated by other Makefile targets
clean: clean:
$Q${RM} rgbasm rgbasm.exe $Q${RM} rgbasm rgbasm.exe
@@ -188,7 +197,7 @@ clean:
$Q${RM} rgbgfx rgbgfx.exe $Q${RM} rgbgfx rgbgfx.exe
$Qfind src/ -name "*.o" -exec rm {} \; $Qfind src/ -name "*.o" -exec rm {} \;
$Qfind . -type f \( -name "*.gcno" -o -name "*.gcda" -o -name "*.gcov" \) -exec rm {} \; $Qfind . -type f \( -name "*.gcno" -o -name "*.gcda" -o -name "*.gcov" \) -exec rm {} \;
$Q${RM} rgbshim.sh $Q${RM} install.sh rgbshim.sh
$Q${RM} src/asm/parser.cpp src/asm/parser.hpp src/asm/stack.hh $Q${RM} src/asm/parser.cpp src/asm/parser.hpp src/asm/stack.hh
$Q${RM} src/link/script.cpp src/link/script.hpp src/link/stack.hh $Q${RM} src/link/script.cpp src/link/script.hpp src/link/stack.hh
$Q${RM} test/gfx/randtilegen test/gfx/rgbgfx_test $Q${RM} test/gfx/randtilegen test/gfx/rgbgfx_test
@@ -200,13 +209,9 @@ install: all
$Qinstall ${STRIP} -m ${BINMODE} rgblink ${DESTDIR}${bindir}/rgblink${SUFFIX} $Qinstall ${STRIP} -m ${BINMODE} rgblink ${DESTDIR}${bindir}/rgblink${SUFFIX}
$Qinstall ${STRIP} -m ${BINMODE} rgbfix ${DESTDIR}${bindir}/rgbfix${SUFFIX} $Qinstall ${STRIP} -m ${BINMODE} rgbfix ${DESTDIR}${bindir}/rgbfix${SUFFIX}
$Qinstall ${STRIP} -m ${BINMODE} rgbgfx ${DESTDIR}${bindir}/rgbgfx${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} ${MANSRC}rgbasm.1 ${MANSRC}rgblink.1 ${MANSRC}rgbfix.1 ${MANSRC}rgbgfx.1 ${DESTDIR}${mandir}/man1/
$Qinstall -m ${MANMODE} man/rgbds.5 man/rgbasm.5 man/rgbasm-old.5 man/rgblink.5 ${DESTDIR}${mandir}/man5/ $Qinstall -m ${MANMODE} ${MANSRC}rgbds.5 ${MANSRC}rgbasm.5 ${MANSRC}rgbasm-old.5 ${MANSRC}rgblink.5 ${DESTDIR}${mandir}/man5/
$Qinstall -m ${MANMODE} man/rgbds.7 man/gbz80.7 ${DESTDIR}${mandir}/man7/ $Qinstall -m ${MANMODE} ${MANSRC}rgbds.7 ${MANSRC}gbz80.7 ${DESTDIR}${mandir}/man7/
# Target used to check for suspiciously missing changed files.
checkdiff:
$Qcontrib/checkdiff.bash `git merge-base HEAD ${BASE_REF}`
# Target used in development to prevent adding new issues to the source code. # Target used in development to prevent adding new issues to the source code.
# All warnings are treated as errors to block the compilation and make the # All warnings are treated as errors to block the compilation and make the
@@ -220,8 +225,8 @@ develop:
-Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1 \ -Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1 \
-Wno-format-nonliteral -Wno-strict-overflow -Wno-unused-but-set-variable \ -Wno-format-nonliteral -Wno-strict-overflow -Wno-unused-but-set-variable \
-Wno-type-limits -Wno-tautological-constant-out-of-range-compare -Wvla \ -Wno-type-limits -Wno-tautological-constant-out-of-range-compare -Wvla \
-D_GLIBCXX_ASSERTIONS -fsanitize=address -fsanitize=undefined \ -D_GLIBCXX_ASSERTIONS -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_DEBUG \
-fsanitize=float-divide-by-zero" \ -fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero" \
CXXFLAGS="-ggdb3 -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls" CXXFLAGS="-ggdb3 -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls"
# Target used in development to debug with gdb. # Target used in development to debug with gdb.
@@ -230,46 +235,35 @@ debug:
CXXFLAGS="-ggdb3 -O0 -fno-omit-frame-pointer -fno-optimize-sibling-calls" CXXFLAGS="-ggdb3 -O0 -fno-omit-frame-pointer -fno-optimize-sibling-calls"
# Target used in development to profile with callgrind. # Target used in development to profile with callgrind.
# Use `valgrind --tool=callgrind --dump-instr=yes --simulate-cache=yes --collect-jumps=yes ./rgbasm ...`.
profile: profile:
$Qenv ${MAKE} \ $Qenv ${MAKE} \
CXXFLAGS="-ggdb3 -O3 -fno-omit-frame-pointer -fno-optimize-sibling-calls" CXXFLAGS="-ggdb3 -O3 -fno-omit-frame-pointer -fno-optimize-sibling-calls"
# Target used in development to inspect code coverage with gcov. # Target used in development to inspect code coverage with gcov.
# Use `./contrib/coverage.bash`.
coverage: coverage:
$Qenv ${MAKE} \ $Qenv ${MAKE} \
CXXFLAGS="-ggdb3 -Og --coverage -fno-omit-frame-pointer -fno-optimize-sibling-calls" CXXFLAGS="-ggdb3 -Og --coverage -fno-omit-frame-pointer -fno-optimize-sibling-calls"
# Target used in development to format source code with clang-format. # Target used in development to format source code with clang-format.
format: format:
$Qclang-format -i $$(git ls-files '*.hpp' '*.cpp') $Qclang-format -i $$(git ls-files '*.[hc]pp')
# Target used in development to check code with clang-tidy. # Target used in development to check code with clang-tidy.
# Requires Bison-generated header files to exist. # Requires Bison-generated header files to exist.
tidy: src/asm/parser.hpp src/link/script.hpp tidy: src/asm/parser.hpp src/link/script.hpp compile_flags.txt
$Qclang-tidy -p . $$(git ls-files '*.hpp' '*.cpp') $Qclang-tidy -p . $$(git ls-files '*.[hc]pp')
compile_flags.txt:
$Qprintf '%s\n' ${REQUIREDCXXFLAGS} ${REQUIREDLDFLAGS} >$@
# Target used in development to remove unused `#include` headers. # Target used in development to remove unused `#include` headers.
iwyu: iwyu:
$Qenv ${MAKE} \ $Qenv ${MAKE} \
CXX="include-what-you-use" \ CXX="include-what-you-use" \
REALCXXFLAGS="-std=c++2a -I include" REALCXXFLAGS="-std=c++20 -I include"
# Targets for the project maintainer to easily create Windows exes.
# This is not for Windows users!
# If you're building on Windows with Cygwin or MinGW, just follow the Unix
# install instructions instead.
mingw32:
$Q${MAKE} all test/gfx/randtilegen test/gfx/rgbgfx_test \
CXX=i686-w64-mingw32-g++ \
CXXFLAGS="-O3 -flto -DNDEBUG -static-libgcc -static-libstdc++" \
PKG_CONFIG="PKG_CONFIG_SYSROOT_DIR=/usr/i686-w64-mingw32 pkg-config"
mingw64:
$Q${MAKE} all test/gfx/randtilegen test/gfx/rgbgfx_test \
CXX=x86_64-w64-mingw32-g++ \
PKG_CONFIG="PKG_CONFIG_SYSROOT_DIR=/usr/x86_64-w64-mingw32 pkg-config"
# Target used in development to conveniently invoke RGBDS binaries with Wine.
wine-shim: wine-shim:
$Qecho '#!/usr/bin/env bash' > rgbshim.sh $Qecho '#!/usr/bin/env bash' > rgbshim.sh
$Qecho 'WINEDEBUG=-all wine $$0.exe "$${@:1}"' >> rgbshim.sh $Qecho 'WINEDEBUG=-all wine $$0.exe "$${@:1}"' >> rgbshim.sh
@@ -281,7 +275,6 @@ wine-shim:
# Target for the project maintainer to produce distributable release tarballs # Target for the project maintainer to produce distributable release tarballs
# of the source code. # of the source code.
dist: dist:
$Qgit ls-files | sed s~^~$${PWD##*/}/~ \ $Qgit ls-files | sed s~^~$${PWD##*/}/~ \
| tar -czf rgbds-source.tar.gz -C .. -T - | tar -czf rgbds-source.tar.gz -C .. -T -
+30 -24
View File
@@ -1,33 +1,39 @@
# RGBDS # RGBDS
RGBDS (Rednex Game Boy Development System) is a free assembler/linker package [RGBDS](https://github.com/gbdev/rgbds) (Rednex Game Boy Development System) is
for the Game Boy and Game Boy Color. It consists of: a free assembler/linker package for the Game Boy and Game Boy Color.
- RGBASM (assembler) It consists of four tools:
- RGBLINK (linker)
- RGBFIX (checksum/header fixer)
- RGBGFX (PNG-to-Game Boy graphics converter)
This is a fork of the original RGBDS which aims to make the programs more like - **RGBASM:** assembler
other UNIX tools. - **RGBLINK:** linker
- **RGBFIX:** ROM checksum/header fixer
- **RGBGFX:** PNG-to-Game Boy graphics converter
This toolchain is maintained [on GitHub](https://github.com/gbdev/rgbds). ## Documentation
The documentation of this toolchain can be [viewed online](https://rgbds.gbdev.io/docs/), The [full documentation](https://rgbds.gbdev.io/docs/) is available online,
including its [basic usage and development history](https://rgbds.gbdev.io/docs/rgbds.7). including the [development history](https://rgbds.gbdev.io/docs/rgbds.7) and
It is generated from the man pages found in this repository. [version history](https://rgbds.gbdev.io/versions), generated from the man pages
The source code of the website itself is on GitHub as well under the repository in this repository.
[rgbds-www](https://github.com/gbdev/rgbds-www).
If you want to contribute or maintain RGBDS, read [CONTRIBUTING.md](CONTRIBUTING.md). The documentation website's own source code is available in the
If you have questions regarding the code, its organization, etc. you can find the maintainers [rgbds-www](https://github.com/gbdev/rgbds-www) repository.
[on the GBDev community channels](https://gbdev.io/chat) or via mail at `rgbds at gbdev dot io`.
## Installing RGBDS ## Community
The [installation procedure](https://rgbds.gbdev.io/install) is available If you have questions about the RGBDS code or its organization, you can contact
online for various platforms. [Building from source](https://rgbds.gbdev.io/install/source) the maintainers on the [GBDev Discord server](https://discord.gg/RjJKA8wrD4) or
is possible using `make` or `cmake`; follow the link for more detailed instructions. [other community channels](https://gbdev.io/chat), or via email at
`rgbds at gbdev dot io`.
If you want to help maintain RGBDS, please read [the contribution guide](/docs/CONTRIBUTING.md).
## Installing
The [platform-specific installation instructions](https://rgbds.gbdev.io/install/)
are available online. To [build from source](https://rgbds.gbdev.io/install/source)
using `make` or `cmake`, briefly:
```sh ```sh
make make
@@ -40,8 +46,8 @@ cmake --build build
cmake --install build cmake --install build
``` ```
Two parameters available when building are a prefix (e.g. to put the executables in a directory) Both build systems support a prefix (e.g. to install into a specific directory)
and a suffix (e.g. to append the version number or commit ID). and a suffix (e.g. to append the version number or commit ID):
```sh ```sh
make make
@@ -54,4 +60,4 @@ cmake --build build
cmake --install build --prefix install_dir cmake --install build --prefix install_dir
``` ```
(If you set a `SUFFIX`, it should include the `.exe` extension on Windows.) On Windows, any `SUFFIX` should include the `.exe` extension.
+25
View File
@@ -0,0 +1,25 @@
# This file declares the dependencies we use, using `FetchContent`.
# https://cmake.org/cmake.help/latest/guide/using-dependencies/index.html#downloading-and-building-from-source-with-fetchcontent
# These are kept in a separate file so that it can be hashed as a key for our CI's `actions/cache`.
FetchContent_Declare(PNG
URL https://download.sourceforge.net/libpng/libpng-1.6.58.tar.xz
URL_HASH SHA256=28eb403f51f0f7405249132cecfe82ea5c0ef97f1b32c5a65828814ae0d34775
EXCLUDE_FROM_ALL # We only install the runtime dependencies, and do so separately.
FIND_PACKAGE_ARGS 1.5.4)
set(PNG_TESTS OFF CACHE INTERNAL "") # We do not need libpng's tests or tools
set(PNG_TOOLS OFF CACHE INTERNAL "") # (and they can even cause compile errors!)
set(PNG_SHARED OFF CACHE INTERNAL "")
set(PNG_STATIC ON CACHE INTERNAL "")
FetchContent_Declare(ZLIB
URL https://www.zlib.net/zlib-1.3.2.tar.xz
URL_HASH SHA256=d7a0654783a4da529d1bb793b7ad9c3318020af77667bcae35f95d0e42a792f3
EXCLUDE_FROM_ALL # We only install the runtime dependencies, and do so separately.
# libpng documents requiring "zlib 1.0.4 or later (1.2.13 or later recommended for performance and security reasons)".
# We thus enforce 1.0.4, but note that the libpng source code mentions that "it may work with versions as old as zlib 0.95".
FIND_PACKAGE_ARGS 1.0.4)
set(ZLIB_BUILD_SHARED OFF CACHE INTERNAL "")
set(ZLIB_BUILD_STATIC ON CACHE INTERNAL "")
+33
View File
@@ -0,0 +1,33 @@
# This file is meant to be included at the project level,
# in order to generate executables compatible with old macOS versions.
# See our `macos-static` CMake preset for how it's meant to be used.
# Note that targeting old enough versions of Mac OS X on recent enough versions of macOS
# triggers some poorly-tested code paths within Apple's linker, which then crashes.
# This can be worked around by using LLVM's LLD linker and passing `-fuse-ld=lld` when linking.
# The `-mmacosx-version-min=10.4` flag ensures that the binary only uses APIs available on Mac OS X 10.4 Tiger.
# The `-arch` flags build a "fat binary" that works on both Apple architectures:
# older Intel x64 Macs and newer ARM "Apple Silicon" ones.
set(secret_sauce -mmacosx-version-min=10.4 "SHELL:-arch x86_64" "SHELL:-arch arm64") # Avoid `-arch` being dedup'd.
add_compile_options(${secret_sauce})
add_link_options(${secret_sauce})
set(PNG_HARDWARE_OPTIMIZATIONS OFF) # These do not play well with a dual-arch build.
# Mac OS X has always provided zlib, so we can safely link dynamically against it.
# However, libpng is *not* provided by default, so we link it statically, which requires downloading and building it from source.
set(PNG_SHARED OFF)
set(PNG_STATIC ON)
# If libpng is already available (e.g. via Homebrew), we ignore that and still build our own.
set(FETCHCONTENT_TRY_FIND_PACKAGE_MODE NEVER)
# But we still want to attempt linking against the system's zlib.
function(rgbds_provide_dependency method dep_name)
if(dep_name STREQUAL "ZLIB")
find_package(ZLIB)
if(ZLIB_FOUND)
FetchContent_SetPopulated(ZLIB)
endif()
endif()
endfunction(rgbds_provide_dependency)
cmake_language(SET_DEPENDENCY_PROVIDER rgbds_provide_dependency
SUPPORTED_METHODS FETCHCONTENT_MAKEAVAILABLE_SERIAL)
+22
View File
@@ -0,0 +1,22 @@
# From https://www.mingw-w64.org/build-systems/cmake/
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR i686)
# Specify the cross-compiler.
set(CMAKE_C_COMPILER i686-w64-mingw32-gcc)
set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++)
set(CMAKE_RC_COMPILER i686-w64-mingw32-windres)
# Specify the target environment.
set(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32)
# Search for programs in the build host directories.
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# Search for libraries and headers in the target directories.
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
# CMake determines how to examine dependencies based on the *host* system, leading to
# a "file unknown error" unless the target platform is explicitly specified.
set(CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM "windows+pe")
+22
View File
@@ -0,0 +1,22 @@
# From https://www.mingw-w64.org/build-systems/cmake/
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_PROCESSOR x86_64)
# Specify the cross-compiler.
set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres)
# Specify the target environment.
set(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32)
# Search for programs in the build host directories.
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# Search for libraries and headers in the target directories.
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
# CMake determines how to examine dependencies based on the *host* system, leading to
# a "file unknown error" unless the target platform is explicitly specified.
set(CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM "windows+pe")
-6
View File
@@ -1,6 +0,0 @@
-std=c++2a
-I
include
-fno-exceptions
-fno-rtti
-fno-caret-diagnostics
+1
View File
@@ -142,6 +142,7 @@ _rgblink_completions() {
mapfile -t COMPREPLY < <(compgen -W " mapfile -t COMPREPLY < <(compgen -W "
assert assert
div div
large-constant
obsolete obsolete
shift shift
shift-amount shift-amount
+57 -17
View File
@@ -1,17 +1,55 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -e set -eu
usage() {
cat <<"EOF"
Generates LCOV code coverage report for RGBDS.
Options:
-h, --help show this help message
-o, --open open the HTML report in the preferred application
--jobs <n> build RGBDS and external codebases with `make -j<n>`
--os <os> skip tests known to fail on <os> (e.g. `macos-14`)
EOF
}
# Parse options in pure Bash because macOS `getopt` is stuck
# in what util-linux `getopt` calls `GETOPT_COMPATIBLE` mode
make_args=()
runtests_args=()
open_report=false
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help)
usage
exit 0
;;
-o|--open)
open_report=true
;;
--jobs)
shift
make_args+=(-j "$1")
runtests_args+=(--jobs "$1")
;;
--os)
shift
runtests_args+=(--os "$1")
;;
*)
echo "$(basename "$0"): unknown option '$1'"
exit 1
;;
esac
shift
done
# Build RGBDS with gcov support # Build RGBDS with gcov support
make coverage -j make coverage "${make_args[@]}"
# Run the tests # Run the tests
pushd test pushd test
./fetch-test-deps.sh external/fetch-repos.sh
if [[ $# -eq 0 ]]; then ./run-tests.sh "${runtests_args[@]}"
./run-tests.sh
else
./run-tests.sh --os "$1"
fi
popd popd
# Generate coverage logs # Generate coverage logs
@@ -22,14 +60,16 @@ mkdir -p coverage
COVERAGE_INFO=coverage/coverage.info COVERAGE_INFO=coverage/coverage.info
lcov -c --no-external -d . -o "$COVERAGE_INFO" lcov -c --no-external -d . -o "$COVERAGE_INFO"
lcov -r "$COVERAGE_INFO" src/asm/parser.{hpp,cpp} src/link/script.{hpp,cpp} -o "$COVERAGE_INFO" lcov -r "$COVERAGE_INFO" src/asm/parser.{hpp,cpp} src/link/script.{hpp,cpp} -o "$COVERAGE_INFO"
genhtml --dark-mode -f -s -o coverage/ "$COVERAGE_INFO" genhtml --dark-mode --num-spaces 4 -f -s -o coverage/ "$COVERAGE_INFO"
# Check whether running from coverage.yml workflow if "$open_report"; then
if [ "$1" != "ubuntu-ci" ]; then # Open the report in the preferred web browser
# Open report in web browser if [ "$(uname)" == "Darwin" ]; then
if [ "$(uname)" == "Darwin" ]; then open coverage/index.html
open coverage/index.html else
else xdg-open coverage/index.html
xdg-open coverage/index.html fi
fi else
# Output the path to the report
echo "Generated LCOV report at $PWD/coverage/index.html"
fi fi
+1 -1
View File
@@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
if [[ $# -ne 2 ]]; then if [[ $# -ne 2 ]]; then
+1
View File
@@ -9,6 +9,7 @@ _rgblink_warnings() {
'assert:Warn when WARN-type asserts fail' 'assert:Warn when WARN-type asserts fail'
'div:Warn when dividing the smallest int by -1' 'div:Warn when dividing the smallest int by -1'
'large-constant:Warn on constants too large for a signed 32-bit int'
'obsolete:Warn when using deprecated features' 'obsolete:Warn when using deprecated features'
'shift:Warn when shifting negative values' 'shift:Warn when shifting negative values'
'shift-amount:Warn when a shift'\''s operand is negative or \> 32' 'shift-amount:Warn when a shift'\''s operand is negative or \> 32'
+36 -9
View File
@@ -28,11 +28,15 @@ rgbds/
│ │ └── ... │ │ └── ...
│ └── workflows/ │ └── workflows/
│ └── ... │ └── ...
├── cmake/
│ └── ...
├── contrib/ ├── contrib/
│ ├── bash_compl/ │ ├── bash_compl/
│ ├── zsh_compl/ │ ├── zsh_compl/
│ │ └── ... │ │ └── ...
│ └── ... │ └── ...
├── docs/
│ └── ...
├── include/ ├── include/
│ └── ... │ └── ...
├── man/ ├── man/
@@ -52,13 +56,15 @@ rgbds/
│ ├── bison.sh │ ├── bison.sh
│ └── ... │ └── ...
├── test/ ├── test/
│ ├── fetch-test-deps.sh
│ ├── run-tests.sh │ ├── run-tests.sh
│ ├── external/
│ │ ├── fetch-repos.sh
│ │ └── ...
│ └── ... │ └── ...
├── .clang-format ├── .clang-format
├── .clang-tidy ├── .clang-tidy
├── CMakeLists.txt ├── CMakeLists.txt
├── compile_flags.txt ├── CMakePresets.json
├── Dockerfile ├── Dockerfile
└── Makefile └── Makefile
``` ```
@@ -69,12 +75,20 @@ rgbds/
Scripts used by GitHub Actions workflow files. Scripts used by GitHub Actions workflow files.
* **`workflows/`:** * **`workflows/`:**
GitHub Actions CI workflow description files. Used for automated testing, deployment, etc. GitHub Actions CI workflow description files. Used for automated testing, deployment, etc.
- **`cmake/`**:
Files relevant to our CMake build system that are not required to be somewhere else (e.g. `CMakePresets.json` *has* to be at the root).
- **`contrib/`:** - **`contrib/`:**
Scripts and other resources which may be useful to RGBDS users and developers. Scripts and other resources which may be useful to RGBDS users and developers.
* **`bash_compl/`:** * **`bash_compl/`:**
Tab completion scripts for use with `bash`. Run them with `source` somewhere in your `.bashrc`, and they should auto-load when you open a shell. Tab completion scripts for use with `bash`. Run them with `source` somewhere in your `.bashrc`, and they should auto-load when you open a shell.
* **`zsh_compl/`:** * **`zsh_compl/`:**
Tab completion scripts for use with `zsh`. Put them somewhere in your `fpath`, and they should auto-load when you open a shell. Tab completion scripts for use with `zsh`. Put them somewhere in your `fpath`, and they should auto-load when you open a shell.
- **`docs/`:**
Documentation related to the development and maintenance of RGBDS itself.
(The directory's name, as well as some of its file's names, are
[recognized by GitHub](https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/setting-guidelines-for-repository-contributors)
and linked to in parts of its UI.)
Documentation for RGBDS itself exists as manual pages in `man/`.
- **`include/`:** - **`include/`:**
Header files for the respective source files in `src`. Header files for the respective source files in `src`.
- **`man/`:** - **`man/`:**
@@ -97,18 +111,23 @@ rgbds/
Script used to run the Bison parser generator with the latest flags that the user's version supports. Script used to run the Bison parser generator with the latest flags that the user's version supports.
- **`test/`:** - **`test/`:**
Testing framework used to verify that changes to the code don't break or modify the behavior of RGBDS. Testing framework used to verify that changes to the code don't break or modify the behavior of RGBDS.
* **`fetch-test-deps.sh`:** The `test.sh` scripts inside each of the subdirectories are the individual test drivers.
Script used to fetch dependencies for building external repositories. `fetch-test-deps.sh --help` describes its options.
* **`run-tests.sh`:** * **`run-tests.sh`:**
Script used to run tests, including internal test cases and external repositories. `run-tests.sh --help` describes its options. Script used to run tests, including internal test cases and external repositories. `run-tests.sh --help` describes its options.
* **`external/`:**
Directory for third-party repos making use of RGBDS, which get cloned here and built during testing.
- **`fetch-repos.sh`:**
Script used to clone the external repositories. `fetch-repos.sh --help` describes its options.
- **`*.cfg`:**
Each `.cfg` file defines the parameters to clone, build, and verify an external repository.
- **`.clang-format`:** - **`.clang-format`:**
Code style for automated C++ formatting with [`clang-format`](https://clang.llvm.org/docs/ClangFormat.html) (for which we define the shortcut `make format`). Code style for automated C++ formatting with [`clang-format`](https://clang.llvm.org/docs/ClangFormat.html) (for which we define the shortcut `make format`).
- **`.clang-tidy`:** - **`.clang-tidy`:**
Configuration for C++ static analysis with [`clang-tidy`](https://clang.llvm.org/extra/clang-tidy/) (for which we define the shortcut `make tidy`). Configuration for C++ static analysis with [`clang-tidy`](https://clang.llvm.org/extra/clang-tidy/) (for which we define the shortcut `make tidy`).
- **`CMakeLists.txt`:** - **`CMakeLists.txt`:**
Defines how to build RGBDS with CMake. Defines how to build RGBDS with CMake.
- **`compile_flags.txt`:** - **`CMakePresets.json`:**
Compiler flags for `clang-tidy`. Defines some [presets](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html) useful for working with our CMake.
- **`Dockerfile`:** - **`Dockerfile`:**
Defines how to build RGBDS with Docker (which we do in CI to provide a [container image](https://github.com/gbdev/rgbds/pkgs/container/rgbds)). Defines how to build RGBDS with Docker (which we do in CI to provide a [container image](https://github.com/gbdev/rgbds/pkgs/container/rgbds)).
- **`Makefile`:** - **`Makefile`:**
@@ -120,6 +139,9 @@ These files in the `src/` directory are shared across multiple programs: often a
- **`backtrace.cpp`:** - **`backtrace.cpp`:**
Generic printing of location backtraces for RGBASM and RGBLINK. Allows configuring backtrace styles with a command-line flag (conventionally `-B/--backtrace`). Renders warnings in yellow, errors in red, and locations in cyan. Generic printing of location backtraces for RGBASM and RGBLINK. Allows configuring backtrace styles with a command-line flag (conventionally `-B/--backtrace`). Renders warnings in yellow, errors in red, and locations in cyan.
- **`cli.cpp`:**
A function for parsing command-line options, including RGBDS-specific "at-files" (a filename containing more options, prepended with an "`@`").
This is the only file to use the extern/getopt.cpp variables and functions.
- **`diagnostics.cpp`:** - **`diagnostics.cpp`:**
Generic warning/error diagnostic support for all programs. Allows command-line flags (conventionally `-W`) to have `no-`, `error=`, or `no-error=` prefixes, and `=` level suffixes; allows "meta" flags to affect groups of individual flags; and counts how many total errors there have been. Every program has its own `warning.cpp` file that uses this. Generic warning/error diagnostic support for all programs. Allows command-line flags (conventionally `-W`) to have `no-`, `error=`, or `no-error=` prefixes, and `=` level suffixes; allows "meta" flags to affect groups of individual flags; and counts how many total errors there have been. Every program has its own `warning.cpp` file that uses this.
- **`linkdefs.cpp`:** - **`linkdefs.cpp`:**
@@ -162,10 +184,13 @@ These files have been copied ("vendored") from external authors and adapted for
- **`fstack.cpp`:** - **`fstack.cpp`:**
Functions and data related to "fstack" nodes (the contents of top-level or `INCLUDE`d files, macro expansions, or `REPT`/`FOR` loop iterations) and their "contexts" (metadata that is only relevant while a node's content is being lexed and parsed). Functions and data related to "fstack" nodes (the contents of top-level or `INCLUDE`d files, macro expansions, or `REPT`/`FOR` loop iterations) and their "contexts" (metadata that is only relevant while a node's content is being lexed and parsed).
This file *owns* the `Context`s in its `contextStack` collection. Each of those `Context`s *owns* its `LexerState`, and *refers* to its `FileStackNode`, `uniqueIDStr`, and `macroArgs`. Each `FileStackNode` also *references* its `parent`. This file *owns* the `Context`s in its `contextStack` collection. Each of those `Context`s *owns* its `LexerState`, and *refers* to its `FileStackNode`, `uniqueIDStr`, and `macroArgs`. Each `FileStackNode` also *references* its `parent`.
- **`intern.cpp`:**
[Interned strings](https://en.wikipedia.org/wiki/String_interning), which are used for the names of keywords, symbols (labels, constants, variables, string constants, macros, etc), and charmaps.
This file *owns* the strings in its `internedStrings` collection, which its `internedIndexes` collection *references* by view and index.
- **`lexer.cpp`:** - **`lexer.cpp`:**
Functions and data related to [lexing](https://en.wikipedia.org/wiki/Lexical_analysis) assembly source code into tokens, which can then be parsed. Functions and data related to [lexing](https://en.wikipedia.org/wiki/Lexical_analysis) assembly source code into tokens, which can then be parsed.
This file maintains static `lexerState` and `lexerStateEOL` pointers to `LexerState`s from the `Context`s in `fstack.cpp`. This file maintains static `lexerState` and `lexerStateEOL` pointers to `LexerState`s from the `Context`s in `fstack.cpp`.
Each `LexerState` *owns* its `content` and its `expansions`' content. Each `Expansion` (the contents of an `{interpolation}` or macro argument) in turn *owns* its `contents`. Each `LexerState` *owns* its `content` and its `expansionStack`'s content. Each `Expansion` (the contents of an `{interpolation}` or macro argument) in turn *owns* its `contents`.
The lexer and parser are interdependent: when the parser reaches certain tokens, it changes the lexer's mode, which affects how characters get lexed into tokens. For example, when the parser reaches a macro name, it changes the lexer to "raw" mode, which lexes the rest of the line as a sequence of string arguments to the macro. The lexer and parser are interdependent: when the parser reaches certain tokens, it changes the lexer's mode, which affects how characters get lexed into tokens. For example, when the parser reaches a macro name, it changes the lexer to "raw" mode, which lexes the rest of the line as a sequence of string arguments to the macro.
- **`macro.cpp`:** - **`macro.cpp`:**
`MacroArgs` methods related to macro arguments. Each `MacroArgs` *references* its arguments' contents. `MacroArgs` methods related to macro arguments. Each `MacroArgs` *references* its arguments' contents.
@@ -183,7 +208,7 @@ These files have been copied ("vendored") from external authors and adapted for
Grammar for the RGBASM assembly language, which Bison preprocesses into a [LALR(1) parser](https://en.wikipedia.org/wiki/LALR_parser). Grammar for the RGBASM assembly language, which Bison preprocesses into a [LALR(1) parser](https://en.wikipedia.org/wiki/LALR_parser).
The Bison-generated parser calls `yylex` (defined in `lexer.cpp`) to get the next token, and calls `yywrap` (defined in `fstack.cpp`) when the current context is out of tokens and returns `EOF`. The Bison-generated parser calls `yylex` (defined in `lexer.cpp`) to get the next token, and calls `yywrap` (defined in `fstack.cpp`) when the current context is out of tokens and returns `EOF`.
- **`rpn.cpp`:** - **`rpn.cpp`:**
`Expression` methods and data related to "[RPN](https://en.wikipedia.org/wiki/Reverse_Polish_notation)" expressions. When a numeric expression is parsed, if its value cannot be calculated at assembly time, it is built up into a buffer of RPN-encoded operations to do so at link time by RGBLINK. The valid RPN operations are defined in [man/rgbds.5](man/rgbds.5). `Expression` methods and data related to "[RPN](https://en.wikipedia.org/wiki/Reverse_Polish_notation)" expressions. When a numeric expression is parsed, if its value cannot be calculated at assembly time, it is built up into a buffer of RPN-encoded operations to do so at link time by RGBLINK. The valid RPN operations are defined in [man/rgbds.5](/man/rgbds.5).
- **`section.cpp`:** - **`section.cpp`:**
Functions and data related to `SECTION`s. Functions and data related to `SECTION`s.
This file *owns* the `Section`s in its `sections` collection. It also maintains various static pointers to those sections, including the `currentSection`, `currentLoadSection`, and `sectionStack` (which is affected by `PUSHS` and `POPS` directives). (Note that sections cannot be deleted.) This file *owns* the `Section`s in its `sections` collection. It also maintains various static pointers to those sections, including the `currentSection`, `currentLoadSection`, and `sectionStack` (which is affected by `PUSHS` and `POPS` directives). (Note that sections cannot be deleted.)
@@ -220,6 +245,8 @@ These files have been copied ("vendored") from external authors and adapted for
Functions for sorting colors within palettes, which works differently for grayscale, RGB, or indexed-color palettes. Functions for sorting colors within palettes, which works differently for grayscale, RGB, or indexed-color palettes.
- **`pal_spec.cpp`:** - **`pal_spec.cpp`:**
Functions for parsing various formats of palette specifications (from `-c/--colors`). Functions for parsing various formats of palette specifications (from `-c/--colors`).
- **`palette.cpp`:**
`Palette` methods for working with up to four GBC-native (RGB555) colors.
- **`png.cpp`:** - **`png.cpp`:**
`Png` methods for reading PNG image files, standardizing them to 8-bit RGBA pixels while also reading their indexed palette if there is one. `Png` methods for reading PNG image files, standardizing them to 8-bit RGBA pixels while also reading their indexed palette if there is one.
- **`process.cpp`:** - **`process.cpp`:**
@@ -256,7 +283,7 @@ These files have been copied ("vendored") from external authors and adapted for
Functions and data related to outputting ROM files (with `-o/--output`), symbol files (with `-n/--sym`), and map files (with `-m/--map`). Functions and data related to outputting ROM files (with `-o/--output`), symbol files (with `-n/--sym`), and map files (with `-m/--map`).
This file *references* some `Symbol`s and `Section`s, in collections that keep them sorted by address and name, which allows the symbol and map output to be in order. This file *references* some `Symbol`s and `Section`s, in collections that keep them sorted by address and name, which allows the symbol and map output to be in order.
- **`patch.cpp`:** - **`patch.cpp`:**
Functions and data related to "[RPN](https://en.wikipedia.org/wiki/Reverse_Polish_notation)" expression patches read from the object files, including the ones for `ASSERT` conditions. After sections have been assigned specific locations, the RPN patches can have their values calculated and applied to the ROM. The valid RPN operations are defined in [man/rgbds.5](man/rgbds.5). Functions and data related to "[RPN](https://en.wikipedia.org/wiki/Reverse_Polish_notation)" expression patches read from the object files, including the ones for `ASSERT` conditions. After sections have been assigned specific locations, the RPN patches can have their values calculated and applied to the ROM. The valid RPN operations are defined in [man/rgbds.5](/man/rgbds.5).
This file *owns* the `Assertion`s in its `assertions` collection, and the `RPNStackEntry`s in its `rpnStack` collection. This file *owns* the `Assertion`s in its `assertions` collection, and the `RPNStackEntry`s in its `rpnStack` collection.
- **`script.y`:** - **`script.y`:**
Grammar for the linker script language, which Bison preprocesses into a [LALR(1) parser](https://en.wikipedia.org/wiki/LALR_parser). Grammar for the linker script language, which Bison preprocesses into a [LALR(1) parser](https://en.wikipedia.org/wiki/LALR_parser).
+51 -37
View File
@@ -4,7 +4,7 @@ RGBDS was created in the late '90s and has received contributions from several
developers since then. It wouldn't have been possible to get to this point developers since then. It wouldn't have been possible to get to this point
without their work, and it is always open to the contributions of other people. without their work, and it is always open to the contributions of other people.
## Reporting Bugs ## Reporting bugs
Bug reports are essential to improve RGBDS and they are always welcome. If you Bug reports are essential to improve RGBDS and they are always welcome. If you
want to report a bug: want to report a bug:
@@ -35,6 +35,9 @@ implement it yourself, you can always contribute to RGBDS with your own code.
## Contributing code ## Contributing code
Thank you! Code contributions are the most appreciated.
You can find a description of the project's layout in [`ARCHITECTURE.md`](/ARCHITECTURE.md).
If you want to contribute with your own code, whether it is to fix a current If you want to contribute with your own code, whether it is to fix a current
issue or to add something that nobody had requested, you should first consider issue or to add something that nobody had requested, you should first consider
if your change is going to be small (and likely to be accepted as-is) or big if your change is going to be small (and likely to be accepted as-is) or big
@@ -62,20 +65,28 @@ years). If you are adding new files, you need to use the
target checks for additional warnings. Your patches shouldn't introduce any target checks for additional warnings. Your patches shouldn't introduce any
new warning (but it may be possible to remove some warning checks if it makes new warning (but it may be possible to remove some warning checks if it makes
the code much easier). the code much easier).
You can also use `cmake --preset develop` if you prefer.
5. Test your changes by running `./run-tests.sh` in the `test` directory. 5. Test your changes by running `./run-tests.sh` in the `test` directory.
(You must run `./fetch-test-deps.sh` first; if you forget to, the test suite `./run-tests.sh --help` will print its available options. (You must run
will fail and remind you mid-way.) `external/fetch-repos.sh` first; if you forget to, the test suite will fail and
5. Format your changes according to `clang-format`, which will reformat the remind you mid-way.)
coding style according to our standards defined in `.clang-format`.
6. Create a pull request against the branch `master`. You can also use `ctest --test-dir build` if you prefer.
7. Check the results of the GitHub Actions CI jobs for your pull request. The `ctest --test-dir build --print-labels` will print its available
[test labels](https://cmake.org/cmake/help/latest/manual/ctest.1.html#label-matching).
6. Format your changes according to `clang-format`, which will reformat the
coding style according to our standards defined in `.clang-format`. You can
use `make format` to format all the C++ files.
7. Create a pull request against the branch `master`.
8. Check the results of the GitHub Actions CI jobs for your pull request. The
"Code format checking" and "Regression testing" jobs should all succeed. "Code format checking" and "Regression testing" jobs should all succeed.
The "Diff completeness check" and "Static analysis" jobs should be manually The "Diff completeness check" job should be manually checked, as it may
checked, as they may output warnings which do not count as failure errors. output warnings which do not count as failure errors. The "Code coverage
The "Code coverage report" provides an report" provides an [LCOV](https://github.com/linux-test-project/lcov)-generated
[LCOV](https://github.com/linux-test-project/lcov)-generated report which report which can be downloaded and checked to see if your new code has full
can be downloaded and checked to see if your new code has full test coverage. test coverage.
8. Be prepared to get some comments about your code and to modify it. Tip: Use 9. Be prepared to get some comments about your code and to modify it. Tip: Use
`git rebase -i origin/master` to modify chains of commits. `git rebase -i origin/master` to modify chains of commits.
## Adding a test ## Adding a test
@@ -131,6 +142,10 @@ linked in various ways depending on the test.
These simply check that RGBLINK's output matches some expected output. These simply check that RGBLINK's output matches some expected output.
If a `.flags` file exists, its first line contains flags to pass to RGBLINK.
(There may be more lines, which will be ignored; they can serve as comments to
explain what the test is about.)
A `.out` file **must** exist, and RGBLINK's total output must match that file's A `.out` file **must** exist, and RGBLINK's total output must match that file's
contents. contents.
@@ -212,30 +227,21 @@ If a `.flags` file exists, it will be used as part of the RGBGFX invocation
Each `seed*.bin` file corresponds to one test. Each `seed*.bin` file corresponds to one test.
Each one is a binary RNG file which is passed to the `rgbgfx_test` program. Each one is a binary RNG file which is passed to the `rgbgfx_test` program.
### Downstream projects ### External projects
1. Make sure the downstream project supports Each `.cfg` file corresponds to one test.
<code>make <var>&lt;target&gt;</var> RGBDS=<var>&lt;path/to/RGBDS/&gt;</var></code>. Each one defines the parameters for a real external project that builds using RGBDS.
While the test suite supports any Make target name, only
[Make](//gnu.org/software/make) is currently supported, and the Makefile must 1. Ensure that the downstream project supports a `RGBDS` variable to build with
support a `RGBDS` variable to use a non-system RGBDS directory. a non-system copy of RGBDS in some directory, i.e.
<code>make <var>&lt;target&gt;</var> RGBDS=<var>&lt;path/to/rgbds/&gt;</var></code>.
Also, only projects hosted on GitHub are currently supported. Only [GNU Make](https://www.gnu.org/software/make/) is currently supported.
2. Add the project to `test/fetch-test-deps.sh`: add a new `action` line at the 2. Create the new `.cfg` file, following the existing ones.
bottom, following the existing pattern:
If an external project does not support `make RGBDS=`, or otherwise fails to
```sh build with current RGBDS, then you can edit the project to build correctly, and
action <owner> <repo> <date of last commit> <hash of last commit> create a patch with `git diff` to be committed as
``` <code>test/external/patches/<var>&lt;project&gt;</var>.patch</code>.
(The date is used to avoid fetching too much history when cloning the
repositories.)
3. Add the project to `test/run-tests.sh`: add a new `test_downstream` line at
the bottom, following the existing pattern:
```sh
test_downstream <owner> <repo> <makefile target> <build file> <sha1 hash of build file>
```
## Container images ## Container images
@@ -254,4 +260,12 @@ manually running:
# e.g. to build and tag as 'master' # e.g. to build and tag as 'master'
docker build . --tag ghcr.io/gbdev/rgbds:master docker build . --tag ghcr.io/gbdev/rgbds:master
docker push ghcr.io/gbdev/rgbds:master docker push ghcr.io/gbdev/rgbds:master
``` ```
## Publishing a new release
Please refer to [`RELEASE.md`](/RELEASE.md).
## Closing remarks
Feel free to add yourself to `CREDITS.md`!
View File
+8 -7
View File
@@ -6,23 +6,24 @@ GitHub.
1. Update the following files, then commit and push. 1. Update the following files, then commit and push.
You can use <code>git commit -m "Release <i>&lt;version&gt;</i>"</code> and `git push origin master`. You can use <code>git commit -m "Release <i>&lt;version&gt;</i>"</code> and `git push origin master`.
- [include/version.hpp](include/version.hpp): set appropriate values for `PACKAGE_VERSION_MAJOR`, - [include/version.hpp](/include/version.hpp): set appropriate values for `PACKAGE_VERSION_MAJOR`,
`PACKAGE_VERSION_MINOR`, `PACKAGE_VERSION_PATCH`, and `PACKAGE_VERSION_RC`. `PACKAGE_VERSION_MINOR`, `PACKAGE_VERSION_PATCH`, and `PACKAGE_VERSION_RC`.
**Only** define `PACKAGE_VERSION_RC` if you are publishing a release candidate! **Only** define `PACKAGE_VERSION_RC` if you are publishing a release candidate!
- [Dockerfile](Dockerfile): update `ARG version`. - [Dockerfile](/Dockerfile): update `ARG version`.
- [test/fetch-test-deps.sh](test/fetch-test-deps.sh): update test dependency commits - [test/external/\*.cfg](/test/external): update test dependency commits
(preferably, use the latest available). (preferably, use the latest available).
- [man/\*](man/): update dates and authors. - [man/\*](/man/): update dates and authors.
2. Create a Git tag formatted as <code>v<i>&lt;MAJOR&gt;</i>.<i>&lt;MINOR&gt;</i>.<i>&lt;PATCH&gt;</i></code>, 2. Create a Git tag formatted as <code>v<i>&lt;MAJOR&gt;</i>.<i>&lt;MINOR&gt;</i>.<i>&lt;PATCH&gt;</i></code>,
or <code>v<i>&lt;MAJOR&gt;</i>.<i>&lt;MINOR&gt;</i>.<i>&lt;PATCH&gt;</i>-rc<i>&lt;RC&gt;</i></code> or <code>v<i>&lt;MAJOR&gt;</i>.<i>&lt;MINOR&gt;</i>.<i>&lt;PATCH&gt;</i>-rc<i>&lt;RC&gt;</i></code>
for a release candidate. <code><i>MAJOR</i></code>, <code><i>MINOR</i></code>, for a release candidate. <code><i>MAJOR</i></code>, <code><i>MINOR</i></code>,
<code><i>PATCH</i></code>, and <code><i>RC</i></code> should match their values from <code><i>PATCH</i></code>, and <code><i>RC</i></code> should match their values from
[include/version.hpp](include/version.hpp). You can use <code>git tag <i>&lt;tag&gt;</i></code>. [include/version.hpp](/include/version.hpp). You can use <code>git tag <i>&lt;tag&gt;</i></code>.
3. Push the tag to GitHub. You can use <code>git push origin <i>&lt;tag&gt;</i></code>. 3. Push the tag to GitHub. You can use <code>git push origin <i>&lt;tag&gt;</i></code>.
GitHub Actions will run the [create-release-artifacts.yaml](.github/workflows/create-release-artifacts.yaml) GitHub Actions will run the
[create-release-artifacts.yml](/.github/workflows/create-release-artifacts.yml)
workflow to detect the tag starting with "`v[0-9]`" and automatically do the following: workflow to detect the tag starting with "`v[0-9]`" and automatically do the following:
1. Build 32-bit and 64-bit RGBDS binaries for Windows with `cmake`. 1. Build 32-bit and 64-bit RGBDS binaries for Windows with `cmake`.
@@ -38,7 +39,7 @@ GitHub.
procedure. You can use <code>git push --delete origin <i>&lt;tag&gt;</i></code> and procedure. You can use <code>git push --delete origin <i>&lt;tag&gt;</i></code> and
<code>git tag --delete <i>&lt;tag&gt;</i></code>. <code>git tag --delete <i>&lt;tag&gt;</i></code>.
4. GitHub Actions will run the [create-release-docs.yml](.github/workflows/create-release-docs.yml) 4. GitHub Actions will run the [create-release-docs.yml](/.github/workflows/create-release-docs.yml)
workflow to add the release documentation to [rgbds-www](https://github.com/gbdev/rgbds-www). workflow to add the release documentation to [rgbds-www](https://github.com/gbdev/rgbds-www).
This is not done automatically for prereleases, since we do not normally publish documentation This is not done automatically for prereleases, since we do not normally publish documentation
+4 -3
View File
@@ -13,7 +13,8 @@
#include "linkdefs.hpp" // AssertionType, RPNCommand #include "linkdefs.hpp" // AssertionType, RPNCommand
#include "asm/rpn.hpp" // Expression #include "asm/intern.hpp" // InternedStr
#include "asm/rpn.hpp" // Expression
struct AlignmentSpec { struct AlignmentSpec {
uint8_t alignment; uint8_t alignment;
@@ -54,8 +55,8 @@ std::string act_StringFormat(
std::string const &spec, std::vector<std::variant<uint32_t, std::string>> const &args std::string const &spec, std::vector<std::variant<uint32_t, std::string>> const &args
); );
std::string act_SectionName(std::string const &symName); std::string act_SectionName(InternedStr symName);
void act_CompoundAssignment(std::string const &symName, RPNCommand op, int32_t constValue); void act_CompoundAssignment(InternedStr symName, RPNCommand op, int32_t constValue);
#endif // RGBDS_ASM_ACTIONS_HPP #endif // RGBDS_ASM_ACTIONS_HPP
+5 -5
View File
@@ -10,14 +10,14 @@
#include <string_view> #include <string_view>
#include <vector> #include <vector>
#define DEFAULT_CHARMAP_NAME "main" #include "asm/intern.hpp"
void charmap_Init();
bool charmap_ForEach( bool charmap_ForEach(
void (*mapFunc)(std::string const &), void (*mapFunc)(InternedStr), void (*charFunc)(std::string const &, std::vector<int32_t>)
void (*charFunc)(std::string const &, std::vector<int32_t>)
); );
void charmap_New(std::string const &name, std::string const *baseName); void charmap_New(InternedStr name, InternedStr const *baseName);
void charmap_Set(std::string const &name); void charmap_Set(InternedStr name);
void charmap_Push(); void charmap_Push();
void charmap_Pop(); void charmap_Pop();
void charmap_CheckStack(); void charmap_CheckStack();
+5 -6
View File
@@ -15,6 +15,7 @@
#include "linkdefs.hpp" #include "linkdefs.hpp"
#include "asm/intern.hpp"
#include "asm/lexer.hpp" #include "asm/lexer.hpp"
struct FileStackNode { struct FileStackNode {
@@ -63,17 +64,15 @@ MacroArgs *fstk_GetCurrentMacroArgs();
void fstk_AddIncludePath(std::string const &path); void fstk_AddIncludePath(std::string const &path);
void fstk_AddPreIncludeFile(std::string const &path); void fstk_AddPreIncludeFile(std::string const &path);
std::optional<std::string> fstk_FindFile(std::string const &path); std::optional<std::string> fstk_FindFile(std::string const &path);
bool fstk_FileError(std::string const &path, char const *functionName); bool fstk_FileError(std::string const &path, char const *description);
bool fstk_FailedOnMissingInclude(); bool fstk_FailedOnMissingInclude();
bool yywrap(); bool yywrap();
bool fstk_RunInclude(std::string const &path, bool isQuiet); bool fstk_RunInclude(std::string const &path, bool isQuiet);
void fstk_RunMacro( void fstk_RunMacro(InternedStr macroName, std::shared_ptr<MacroArgs> macroArgs, bool isQuiet);
std::string const &macroName, std::shared_ptr<MacroArgs> macroArgs, bool isQuiet
);
void fstk_RunRept(uint32_t count, int32_t reptLineNo, ContentSpan const &span, bool isQuiet); void fstk_RunRept(uint32_t count, int32_t reptLineNo, ContentSpan const &span, bool isQuiet);
void fstk_RunFor( void fstk_RunFor(
std::string const &symName, InternedStr symName,
int32_t start, int32_t start,
int32_t stop, int32_t stop,
int32_t step, int32_t step,
@@ -84,6 +83,6 @@ void fstk_RunFor(
bool fstk_Break(); bool fstk_Break();
void fstk_NewRecursionDepth(size_t newDepth); void fstk_NewRecursionDepth(size_t newDepth);
void fstk_Init(std::string const &mainPath); bool fstk_Init(std::string const &mainPath);
#endif // RGBDS_ASM_FSTACK_HPP #endif // RGBDS_ASM_FSTACK_HPP
+34
View File
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT
#ifndef RGBDS_ASM_INTERN_HPP
#define RGBDS_ASM_INTERN_HPP
#include <stddef.h>
#include <string>
#include <string_view>
#include <utility> // hash
class InternedStr {
size_t index;
public:
constexpr InternedStr() : index(static_cast<size_t>(-1)) {}
explicit constexpr InternedStr(size_t index_) : index(index_) {}
std::string const &str() const;
char const *c_str() const { return str().c_str(); }
bool operator==(InternedStr const &rhs) const { return index == rhs.index; }
template<typename T>
friend struct std::hash;
};
template<>
struct std::hash<InternedStr> {
size_t operator()(InternedStr const &str) const { return std::hash<size_t>{}(str.index); }
};
InternedStr intern(std::string_view str);
#endif // RGBDS_ASM_INTERN_HPP
+10 -50
View File
@@ -9,18 +9,9 @@
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <string> #include <string>
#include <variant>
#include <vector> #include <vector>
#include "platform.hpp" // SSIZE_MAX #include "asm/intern.hpp"
// This value is a compromise between `LexerState` allocation performance when reading the entire
// file works, and buffering performance when it doesn't (e.g. when piping a file into RGBASM).
static constexpr size_t LEXER_BUF_SIZE = 64;
// The buffer needs to be large enough for the maximum `lexerState->peek()` lookahead distance
static_assert(LEXER_BUF_SIZE > 1, "Lexer buffer size is too small");
// This caps the size of buffer reads, and according to POSIX, passing more than SSIZE_MAX is UB
static_assert(LEXER_BUF_SIZE <= SSIZE_MAX, "Lexer buffer size is too large");
enum LexerMode { enum LexerMode {
LEXER_NORMAL, LEXER_NORMAL,
@@ -32,7 +23,7 @@ enum LexerMode {
}; };
struct Expansion { struct Expansion {
std::optional<std::string> name; std::optional<InternedStr> name;
std::shared_ptr<std::string> contents; std::shared_ptr<std::string> contents;
size_t offset; // Cursor into `contents` size_t offset; // Cursor into `contents`
@@ -45,34 +36,6 @@ struct ContentSpan {
size_t size; size_t size;
}; };
struct ViewedContent {
ContentSpan span; // Span of chars
size_t offset = 0; // Cursor into `span.ptr`
ViewedContent(ContentSpan const &span_) : span(span_) {}
ViewedContent(std::shared_ptr<char[]> ptr, size_t size) : span({.ptr = ptr, .size = size}) {}
std::shared_ptr<char[]> makeSharedContentPtr() const {
return std::shared_ptr<char[]>(span.ptr, &span.ptr[offset]);
}
};
struct BufferedContent {
int fd; // File from which to read chars
char buf[LEXER_BUF_SIZE] = {}; // Circular buffer of chars
size_t offset = 0; // Cursor into `buf`
size_t size = 0; // Number of "fresh" chars in `buf`
BufferedContent(int fd_) : fd(fd_) {}
~BufferedContent();
void advance(); // Increment `offset` circularly, decrement `size`
void refill(); // Read from `fd` to fill `buf`
private:
size_t readMore(size_t startIndex, size_t nbChars);
};
struct IfStackEntry { struct IfStackEntry {
bool ranIfBlock; // Whether an IF/ELIF/ELSE block ran already bool ranIfBlock; // Whether an IF/ELIF/ELSE block ran already
bool reachedElseBlock; // Whether an ELSE block ran already bool reachedElseBlock; // Whether an ELSE block ran already
@@ -87,31 +50,28 @@ struct LexerState {
int lastToken; int lastToken;
int nextToken; int nextToken;
std::deque<IfStackEntry> ifStack; std::deque<IfStackEntry> ifStack; // Front is the innermost `IF` block
bool capturing; // Whether the text being lexed should be captured bool capturing; // Whether the text being lexed should be captured
size_t captureSize; // Amount of text captured size_t captureSize; // Amount of text captured
std::shared_ptr<std::vector<char>> captureBuf; // Buffer to send the captured text to if set std::shared_ptr<std::vector<char>> captureBuf; // Buffer to send the captured text to if set
bool disableExpansions; bool enableExpansions;
size_t expansionScanDistance; // Max distance already scanned for expansions bool enableStringExpansions;
bool expandStrings; size_t expansionScanDistance; // Max distance already scanned for expansions
std::deque<Expansion> expansions; // Front is the innermost current expansion std::deque<Expansion> expansionStack; // Front is the innermost current expansion
std::variant<std::monostate, ViewedContent, BufferedContent> content; ContentSpan content; // Span of chars
size_t offset = 0; // Cursor into `content.ptr`
~LexerState(); ~LexerState();
int peekChar(); int peekChar();
int peekCharAhead(); int peekCharAhead();
std::shared_ptr<char[]> makeSharedCaptureBufPtr() const {
return std::shared_ptr<char[]>(captureBuf, captureBuf->data());
}
void setAsCurrentState(); void setAsCurrentState();
void setFileAsNextState(std::string const &filePath, bool updateStateNow); void setFileAsNextState(std::string const &filePath, bool updateStateNow);
void setViewAsNextState(char const *name, ContentSpan const &span, uint32_t lineNo_); void setViewAsNextState(char const *name, ContentSpan const &content_, uint32_t lineNo_);
void clear(uint32_t lineNo_); void clear(uint32_t lineNo_);
}; };
+1 -1
View File
@@ -13,7 +13,7 @@ struct MacroArgs {
std::vector<std::shared_ptr<std::string>> args; std::vector<std::shared_ptr<std::string>> args;
uint32_t nbArgs() const { return args.size() - shift; } uint32_t nbArgs() const { return args.size() - shift; }
std::shared_ptr<std::string> getArg(int32_t i) const; std::shared_ptr<std::string> getArg(int32_t num) const;
std::shared_ptr<std::string> getAllArgs() const; std::shared_ptr<std::string> getAllArgs() const;
void appendArg(std::shared_ptr<std::string> arg); void appendArg(std::shared_ptr<std::string> arg);
+9 -4
View File
@@ -3,10 +3,14 @@
#ifndef RGBDS_ASM_MAIN_HPP #ifndef RGBDS_ASM_MAIN_HPP
#define RGBDS_ASM_MAIN_HPP #define RGBDS_ASM_MAIN_HPP
#include <optional>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <string> #include <string>
#include "helpers.hpp" // assume
#include "util.hpp" // xfclose
enum MissingInclude { enum MissingInclude {
INC_ERROR, // A missing included file is an error that halts assembly INC_ERROR, // A missing included file is an error that halts assembly
GEN_EXIT, // A missing included file is assumed to be generated; exit normally GEN_EXIT, // A missing included file is assumed to be generated; exit normally
@@ -20,22 +24,23 @@ struct Options {
char binDigits[2] = {'0', '1'}; // -b char binDigits[2] = {'0', '1'}; // -b
char gfxDigits[4] = {'0', '1', '2', '3'}; // -g char gfxDigits[4] = {'0', '1', '2', '3'}; // -g
FILE *dependFile = nullptr; // -M FILE *dependFile = nullptr; // -M
std::string targetFileName; // -MQ, -MT std::optional<std::string> targetFileName{}; // -MQ, -MT
MissingInclude missingIncludeState = INC_ERROR; // -MC, -MG MissingInclude missingIncludeState = INC_ERROR; // -MC, -MG
bool generatePhonyDeps = false; // -MP bool generatePhonyDeps = false; // -MP
std::string objectFileName; // -o std::optional<std::string> objectFileName{}; // -o
uint8_t padByte = 0; // -p uint8_t padByte = 0; // -p
uint64_t maxErrors = 0; // -X uint64_t maxErrors = 0; // -X
~Options() { ~Options() {
if (dependFile) { if (dependFile) {
fclose(dependFile); xfclose(dependFile);
} }
} }
void printDep(std::string const &depName) { void printDep(std::string const &depName) {
if (dependFile) { if (dependFile) {
fprintf(dependFile, "%s: %s\n", targetFileName.c_str(), depName.c_str()); assume(targetFileName.has_value());
fprintf(dependFile, "%s: %s\n", targetFileName->c_str(), depName.c_str());
} }
} }
}; };
+6 -4
View File
@@ -10,16 +10,18 @@
#include "linkdefs.hpp" #include "linkdefs.hpp"
#include "asm/intern.hpp"
struct Symbol; struct Symbol;
struct RPNValue { struct RPNValue {
RPNCommand command; // The RPN_* command ID RPNCommand command; // The RPN_* command ID
std::variant<std::monostate, uint8_t, uint32_t, std::string> data; // Data after the ID, if any std::variant<std::monostate, uint8_t, uint32_t, InternedStr> data; // Data after the ID, if any
RPNValue(RPNCommand cmd); RPNValue(RPNCommand cmd);
RPNValue(RPNCommand cmd, uint8_t val); RPNValue(RPNCommand cmd, uint8_t val);
RPNValue(RPNCommand cmd, uint32_t val); RPNValue(RPNCommand cmd, uint32_t val);
RPNValue(RPNCommand cmd, std::string const &name); RPNValue(RPNCommand cmd, InternedStr name);
void appendEncoded(std::vector<uint8_t> &buffer) const; void appendEncoded(std::vector<uint8_t> &buffer) const;
}; };
@@ -40,8 +42,8 @@ struct Expression {
bool isDiffConstant(Symbol const *symName) const; bool isDiffConstant(Symbol const *symName) const;
void makeNumber(uint32_t value); void makeNumber(uint32_t value);
void makeSymbol(std::string const &symName); void makeSymbol(InternedStr symName);
void makeBankSymbol(std::string const &symName); void makeBankSymbol(InternedStr symName);
void makeBankSection(std::string const &sectName); void makeBankSection(std::string const &sectName);
void makeSizeOfSection(std::string const &sectName); void makeSizeOfSection(std::string const &sectName);
void makeStartOfSection(std::string const &sectName); void makeStartOfSection(std::string const &sectName);
+2 -1
View File
@@ -11,6 +11,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "intern.hpp"
#include "linkdefs.hpp" #include "linkdefs.hpp"
struct Expression; struct Expression;
@@ -107,6 +108,6 @@ void sect_PushSection();
void sect_PopSection(); void sect_PopSection();
void sect_CheckStack(); void sect_CheckStack();
std::string sect_PushSectionFragmentLiteral(); InternedStr sect_PushSectionFragmentLiteral();
#endif // RGBDS_ASM_SECTION_HPP #endif // RGBDS_ASM_SECTION_HPP
+21 -20
View File
@@ -10,6 +10,7 @@
#include <utility> #include <utility>
#include <variant> #include <variant>
#include "asm/intern.hpp"
#include "asm/lexer.hpp" #include "asm/lexer.hpp"
#include "asm/section.hpp" #include "asm/section.hpp"
@@ -26,7 +27,7 @@ struct Symbol; // Forward declaration for `sym_IsPC`
bool sym_IsPC(Symbol const *sym); // Forward declaration for `getSection` bool sym_IsPC(Symbol const *sym); // Forward declaration for `getSection`
struct Symbol { struct Symbol {
std::string name; InternedStr name;
SymbolType type; SymbolType type;
bool isBuiltin; bool isBuiltin;
bool isExported; // Not relevant for SYM_MACRO or SYM_EQUS bool isExported; // Not relevant for SYM_MACRO or SYM_EQUS
@@ -68,34 +69,34 @@ struct Symbol {
uint32_t getConstantValue() const; uint32_t getConstantValue() const;
}; };
bool sym_IsDotScope(InternedStr symName);
void sym_ForEach(void (*callback)(Symbol &)); void sym_ForEach(void (*callback)(Symbol &));
Symbol *sym_AddLocalLabel(std::string const &symName); Symbol *sym_AddLocalLabel(InternedStr symName);
Symbol *sym_AddLabel(std::string const &symName); Symbol *sym_AddLabel(InternedStr symName);
Symbol *sym_AddAnonLabel(); Symbol *sym_AddAnonLabel();
std::string sym_MakeAnonLabelName(uint32_t ofs, bool neg); InternedStr sym_MakeAnonLabelName(uint32_t ofs, bool neg);
void sym_Export(std::string const &symName); void sym_Export(InternedStr symName);
Symbol *sym_AddEqu(std::string const &symName, int32_t value); Symbol *sym_AddEqu(InternedStr symName, int32_t value);
Symbol *sym_RedefEqu(std::string const &symName, int32_t value); Symbol *sym_RedefEqu(InternedStr symName, int32_t value);
Symbol *sym_AddVar(std::string const &symName, int32_t value); Symbol *sym_AddVar(InternedStr symName, int32_t value);
int32_t sym_GetRSValue(); int32_t sym_GetRSValue();
void sym_SetRSValue(int32_t value); void sym_SetRSValue(int32_t value);
// Find a symbol by exact name, bypassing expansion checks // Find a symbol by exact name, bypassing expansion checks
Symbol *sym_FindExactSymbol(std::string const &symName); Symbol *sym_FindExactSymbol(InternedStr symName);
// Find a symbol, possibly scoped, by name // Find a symbol, possibly scoped, by name
Symbol *sym_FindScopedSymbol(std::string const &symName); Symbol *sym_FindScopedSymbol(InternedStr symName);
// Find a scoped symbol by name; do not return `@` or `_NARG` when they have no value // Find a scoped symbol by name; do not return `@` or `_NARG` when they have no value
Symbol *sym_FindScopedValidSymbol(std::string const &symName); Symbol *sym_FindScopedValidSymbol(InternedStr symName);
Symbol const *sym_GetPC(); Symbol const *sym_GetPC();
Symbol *sym_AddMacro( Symbol *sym_AddMacro(InternedStr symName, int32_t defLineNo, ContentSpan const &span, bool isQuiet);
std::string const &symName, int32_t defLineNo, ContentSpan const &span, bool isQuiet Symbol *sym_Ref(InternedStr symName);
); Symbol *sym_AddString(InternedStr symName, std::shared_ptr<std::string> value);
Symbol *sym_Ref(std::string const &symName); Symbol *sym_RedefString(InternedStr symName, std::shared_ptr<std::string> value);
Symbol *sym_AddString(std::string const &symName, std::shared_ptr<std::string> value); void sym_Purge(InternedStr symName);
Symbol *sym_RedefString(std::string const &symName, std::shared_ptr<std::string> value); bool sym_IsPurgedExact(InternedStr symName);
void sym_Purge(std::string const &symName); bool sym_IsPurgedScoped(InternedStr symName);
bool sym_IsPurgedExact(std::string const &symName);
bool sym_IsPurgedScoped(std::string const &symName);
void sym_Init(time_t now); void sym_Init(time_t now);
// Functions to save and restore the current label scopes. // Functions to save and restore the current label scopes.
+5 -1
View File
@@ -61,13 +61,17 @@ extern Diagnostics<WarningLevel, WarningID> warnings;
void warning(WarningID id, char const *fmt, ...); void warning(WarningID id, char const *fmt, ...);
// Used for errors that compromise the whole assembly process by affecting the // Used for errors that compromise the whole assembly process by affecting the
// following code, potencially making the assembler generate errors caused by // following code, potentially making the assembler generate errors caused by
// the first one and unrelated to the code that the assembler complains about. // the first one and unrelated to the code that the assembler complains about.
// It is also used when the assembler goes into an invalid state (for example, // It is also used when the assembler goes into an invalid state (for example,
// when it fails to allocate memory). // when it fails to allocate memory).
[[gnu::format(printf, 1, 2), noreturn]] [[gnu::format(printf, 1, 2), noreturn]]
void fatal(char const *fmt, ...); void fatal(char const *fmt, ...);
// Used for fatal errors that handle their own backtrace output.
[[noreturn]]
void fatalNoTrace(std::function<void()> callback);
// Used for errors that make it impossible to assemble correctly, but don't // Used for errors that make it impossible to assemble correctly, but don't
// affect the following code. The code will fail to assemble but the user will // affect the following code. The code will fail to assemble but the user will
// get a list of all errors at the end, making it easier to fix all of them at // get a list of all errors at the end, making it easier to fix all of them at
+14 -5
View File
@@ -9,8 +9,13 @@
#include <stdio.h> #include <stdio.h>
#include <vector> #include <vector>
#include "helpers.hpp" // InvocableR
#include "style.hpp" #include "style.hpp"
#define TRACE_SEPARATOR "<-"
#define NODE_SEPARATOR "::"
#define REPT_NODE_PREFIX "REPT~"
struct Tracing { struct Tracing {
uint64_t depth = 0; uint64_t depth = 0;
bool collapse = false; bool collapse = false;
@@ -21,20 +26,24 @@ extern Tracing tracing;
bool trace_ParseTraceDepth(char const *arg); bool trace_ParseTraceDepth(char const *arg);
template<typename T, typename M, typename N> template<typename NodeT>
void trace_PrintBacktrace(std::vector<T> const &stack, M getName, N getLineNo) { void trace_PrintBacktrace(
std::vector<NodeT> const &stack,
InvocableR<char const *, NodeT const &> auto getName,
InvocableR<uint32_t, NodeT const &> auto getLineNo
) {
size_t n = stack.size(); size_t n = stack.size();
if (n == 0) { if (n == 0) {
return; // LCOV_EXCL_LINE return; // LCOV_EXCL_LINE
} }
auto printLocation = [&](size_t i) { auto printLocation = [&](size_t i) {
T const &item = stack[n - i - 1]; NodeT const &item = stack[n - i - 1];
style_Reset(stderr); style_Reset(stderr);
if (!tracing.collapse) { if (!tracing.collapse) {
fputs(" ", stderr); // Just three spaces; the fourth will be printed next fputs(" ", stderr); // Just three spaces; the fourth will be printed next
} }
fprintf(stderr, " %s ", i == 0 ? "at" : "<-"); fprintf(stderr, " %s ", i == 0 ? "at" : TRACE_SEPARATOR);
style_Set(stderr, STYLE_CYAN, true); style_Set(stderr, STYLE_CYAN, true);
fputs(getName(item), stderr); fputs(getName(item), stderr);
style_Set(stderr, STYLE_CYAN, false); style_Set(stderr, STYLE_CYAN, false);
@@ -62,7 +71,7 @@ void trace_PrintBacktrace(std::vector<T> const &stack, M getName, N getLineNo) {
style_Reset(stderr); style_Reset(stderr);
if (tracing.collapse) { if (tracing.collapse) {
fputs(" <-", stderr); fputs(" " TRACE_SEPARATOR, stderr);
} else { } else {
fputs(" ", stderr); // Just three spaces; the fourth will be printed next fputs(" ", stderr); // Just three spaces; the fourth will be printed next
} }
+21
View File
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
#ifndef RGBDS_CLI_HPP
#define RGBDS_CLI_HPP
#include <stdarg.h>
#include <string>
#include "extern/getopt.hpp" // option
#include "usage.hpp"
void cli_ParseArgs(
int argc,
char *argv[],
char const *shortOpts,
option const *longOpts,
void (*parseArg)(int, char *),
Usage usage
);
#endif // RGBDS_CLI_HPP
+74 -24
View File
@@ -15,9 +15,20 @@
#include "helpers.hpp" #include "helpers.hpp"
#include "itertools.hpp" #include "itertools.hpp"
#include "style.hpp"
[[gnu::format(printf, 1, 2)]] [[gnu::format(printf, 1, 2)]]
void warnx(char const *fmt, ...); void warnx(char const *fmt, ...);
[[gnu::format(printf, 1, 0)]]
void vwarnx(char const *fmt, va_list args);
[[gnu::format(printf, 1, 2)]]
void errorx(char const *fmt, ...);
[[gnu::format(printf, 1, 0)]]
void verrorx(char const *fmt, va_list args);
[[gnu::format(printf, 1, 2)]]
void fatalx(char const *fmt, ...);
[[gnu::format(printf, 1, 0)]]
void vfatalx(char const *fmt, va_list args);
enum WarningAbled { WARNING_DEFAULT, WARNING_ENABLED, WARNING_DISABLED }; enum WarningAbled { WARNING_DEFAULT, WARNING_ENABLED, WARNING_DISABLED };
@@ -30,35 +41,35 @@ struct WarningState {
std::pair<WarningState, std::optional<uint32_t>> getInitialWarningState(std::string &flag); std::pair<WarningState, std::optional<uint32_t>> getInitialWarningState(std::string &flag);
template<typename L> template<Enum LevelEnumT>
struct WarningFlag { struct WarningFlag {
char const *name; char const *name;
L level; LevelEnumT level;
}; };
enum WarningBehavior { DISABLED, ENABLED, ERROR }; enum WarningBehavior { DISABLED, ENABLED, ERROR };
template<typename W> template<Enum WarningEnumT>
struct ParamWarning { struct ParamWarning {
W firstID; WarningEnumT firstID;
W lastID; WarningEnumT lastID;
uint8_t defaultLevel; uint8_t defaultLevel;
}; };
template<typename W> template<Enum WarningEnumT>
struct DiagnosticsState { struct DiagnosticsState {
WarningState flagStates[W::NB_WARNINGS]; WarningState flagStates[WarningEnumT::NB_WARNINGS];
WarningState metaStates[W::NB_WARNINGS]; WarningState metaStates[WarningEnumT::NB_WARNINGS];
bool warningsEnabled = true; bool warningsEnabled = true;
bool warningsAreErrors = false; bool warningsAreErrors = false;
}; };
template<typename L, typename W> template<Enum LevelEnumT, Enum WarningEnumT>
struct Diagnostics { struct Diagnostics {
std::vector<WarningFlag<L>> metaWarnings; std::vector<WarningFlag<LevelEnumT>> metaWarnings;
std::vector<WarningFlag<L>> warningFlags; std::vector<WarningFlag<LevelEnumT>> warningFlags;
std::vector<ParamWarning<W>> paramWarnings; std::vector<ParamWarning<WarningEnumT>> paramWarnings;
DiagnosticsState<W> state; DiagnosticsState<WarningEnumT> state;
uint64_t nbErrors; uint64_t nbErrors;
void incrementErrors() { void incrementErrors() {
@@ -67,12 +78,12 @@ struct Diagnostics {
} }
} }
WarningBehavior getWarningBehavior(W id) const; WarningBehavior getWarningBehavior(WarningEnumT id) const;
void processWarningFlag(char const *flag); void processWarningFlag(char const *flag);
}; };
template<typename L, typename W> template<Enum LevelEnumT, Enum WarningEnumT>
WarningBehavior Diagnostics<L, W>::getWarningBehavior(W id) const { WarningBehavior Diagnostics<LevelEnumT, WarningEnumT>::getWarningBehavior(WarningEnumT id) const {
// Check if warnings are globally disabled // Check if warnings are globally disabled
if (!state.warningsEnabled) { if (!state.warningsEnabled) {
return WarningBehavior::DISABLED; return WarningBehavior::DISABLED;
@@ -112,7 +123,7 @@ WarningBehavior Diagnostics<L, W>::getWarningBehavior(W id) const {
} }
// If no meta flag is specified, check the default state of this warning flag // If no meta flag is specified, check the default state of this warning flag
if (warningFlags[id].level == L::LEVEL_DEFAULT) { // enabled by default if (warningFlags[id].level == LevelEnumT::LEVEL_DEFAULT) { // enabled by default
return enabledBehavior; return enabledBehavior;
} }
@@ -120,8 +131,8 @@ WarningBehavior Diagnostics<L, W>::getWarningBehavior(W id) const {
return WarningBehavior::DISABLED; return WarningBehavior::DISABLED;
} }
template<typename L, typename W> template<Enum LevelEnumT, Enum WarningEnumT>
void Diagnostics<L, W>::processWarningFlag(char const *flag) { void Diagnostics<LevelEnumT, WarningEnumT>::processWarningFlag(char const *flag) {
std::string rootFlag = flag; std::string rootFlag = flag;
// Check for `-Werror` or `-Wno-error` to return early // Check for `-Werror` or `-Wno-error` to return early
@@ -140,8 +151,8 @@ void Diagnostics<L, W>::processWarningFlag(char const *flag) {
// Try to match the flag against a parametric warning // Try to match the flag against a parametric warning
// If there was an equals sign, it will have set `param`; if not, `param` will be 0, // If there was an equals sign, it will have set `param`; if not, `param` will be 0,
// which applies to all levels // which applies to all levels
for (ParamWarning<W> const &paramWarning : paramWarnings) { for (ParamWarning<WarningEnumT> const &paramWarning : paramWarnings) {
W baseID = paramWarning.firstID; WarningEnumT baseID = paramWarning.firstID;
uint8_t maxParam = paramWarning.lastID - baseID + 1; uint8_t maxParam = paramWarning.lastID - baseID + 1;
assume(paramWarning.defaultLevel <= maxParam); assume(paramWarning.defaultLevel <= maxParam);
@@ -183,13 +194,13 @@ void Diagnostics<L, W>::processWarningFlag(char const *flag) {
} }
// Try to match against a "meta" warning // Try to match against a "meta" warning
for (WarningFlag<L> const &metaWarning : metaWarnings) { for (WarningFlag<LevelEnumT> const &metaWarning : metaWarnings) {
if (rootFlag != metaWarning.name) { if (rootFlag != metaWarning.name) {
continue; continue;
} }
// Set each of the warning flags that meets this level // Set each of the warning flags that meets this level
for (W id : EnumSeq(W::NB_WARNINGS)) { for (WarningEnumT id : EnumSeq(WarningEnumT::NB_WARNINGS)) {
if (metaWarning.level >= warningFlags[id].level) { if (metaWarning.level >= warningFlags[id].level) {
state.metaStates[id].update(flagState); state.metaStates[id].update(flagState);
} }
@@ -198,7 +209,7 @@ void Diagnostics<L, W>::processWarningFlag(char const *flag) {
} }
// Try to match against a "normal" flag // Try to match against a "normal" flag
for (W id : EnumSeq(W::NB_PLAIN_WARNINGS)) { for (WarningEnumT id : EnumSeq(WarningEnumT::NB_PLAIN_WARNINGS)) {
if (rootFlag == warningFlags[id].name) { if (rootFlag == warningFlags[id].name) {
state.flagStates[id].update(flagState); state.flagStates[id].update(flagState);
return; return;
@@ -208,4 +219,43 @@ void Diagnostics<L, W>::processWarningFlag(char const *flag) {
warnx("Unknown warning flag \"%s\"", rootFlag.c_str()); warnx("Unknown warning flag \"%s\"", rootFlag.c_str());
} }
template<Enum LevelEnumT, Enum WarningEnumT>
[[gnu::format(printf, 3, 0)]]
WarningBehavior printDiagnostic(
Diagnostics<LevelEnumT, WarningEnumT> const &warnings,
WarningEnumT id,
char const *fmt,
va_list args
) {
char const *flag = warnings.warningFlags[id].name;
WarningBehavior behavior = warnings.getWarningBehavior(id);
switch (behavior) {
case WarningBehavior::DISABLED:
break;
case WarningBehavior::ENABLED:
style_Set(stderr, STYLE_YELLOW, true);
fputs("warning: ", stderr);
style_Reset(stderr);
vfprintf(stderr, fmt, args);
style_Set(stderr, STYLE_YELLOW, true);
fprintf(stderr, " [-W%s]\n", flag);
style_Reset(stderr);
break;
case WarningBehavior::ERROR:
style_Set(stderr, STYLE_RED, true);
fputs("error: ", stderr);
style_Reset(stderr);
vfprintf(stderr, fmt, args);
style_Set(stderr, STYLE_RED, true);
fprintf(stderr, " [-Werror=%s]\n", flag);
style_Reset(stderr);
break;
}
return behavior;
}
#endif // RGBDS_DIAGNOSTICS_HPP #endif // RGBDS_DIAGNOSTICS_HPP
+2 -4
View File
@@ -12,7 +12,7 @@ static constexpr int optional_argument = 2;
// clang-format on // clang-format on
extern char *musl_optarg; extern char *musl_optarg;
extern int musl_optind, musl_opterr, musl_optopt, musl_optreset; extern int musl_optind, musl_optopt;
struct option { struct option {
char const *name; char const *name;
@@ -21,8 +21,6 @@ struct option {
int val; int val;
}; };
int musl_getopt_long_only( int musl_getopt_long_only(int argc, char **argv, char const *optstring, option const *longopts);
int argc, char **argv, char const *optstring, option const *longopts, int *idx
);
#endif // RGBDS_EXTERN_GETOPT_HPP #endif // RGBDS_EXTERN_GETOPT_HPP
+6 -1
View File
@@ -8,6 +8,11 @@
#define UTF8_ACCEPT 0 #define UTF8_ACCEPT 0
#define UTF8_REJECT 12 #define UTF8_REJECT 12
uint32_t decode(uint32_t *state, uint32_t *codep, uint8_t byte); struct Utf8Decoder {
uint32_t state = UTF8_ACCEPT;
uint32_t codepoint = 0;
uint32_t update(uint8_t byte);
};
#endif // RGBDS_EXTERN_UTF8DECODER_HPP #endif // RGBDS_EXTERN_UTF8DECODER_HPP
+3 -8
View File
@@ -34,6 +34,9 @@ public:
} else { } else {
assume(mode & std::ios_base::out); assume(mode & std::ios_base::out);
_file.emplace<std::streambuf *>(std::cout.rdbuf()); _file.emplace<std::streambuf *>(std::cout.rdbuf());
if (setmode(STDOUT_FILENO, (mode & std::ios_base::binary) ? O_BINARY : O_TEXT) == -1) {
return nullptr;
}
} }
return this; return this;
} }
@@ -41,15 +44,7 @@ public:
return std::holds_alternative<std::filebuf>(_file) ? std::get<std::filebuf>(_file) return std::holds_alternative<std::filebuf>(_file) ? std::get<std::filebuf>(_file)
: *std::get<std::streambuf *>(_file); : *std::get<std::streambuf *>(_file);
} }
std::streambuf const &operator*() const {
// The non-`const` version does not perform any modifications, so it's okay.
return **const_cast<File *>(this);
}
std::streambuf *operator->() { return &**this; } std::streambuf *operator->() { return &**this; }
std::streambuf const *operator->() const {
// See the `operator*` equivalent.
return const_cast<File *>(this)->operator->();
}
char const *c_str(std::string const &path) const { char const *c_str(std::string const &path) const {
return std::holds_alternative<std::filebuf>(_file) ? path.c_str() return std::holds_alternative<std::filebuf>(_file) ? path.c_str()
+6 -4
View File
@@ -3,7 +3,9 @@
#ifndef RGBDS_FIX_MAIN_HPP #ifndef RGBDS_FIX_MAIN_HPP
#define RGBDS_FIX_MAIN_HPP #define RGBDS_FIX_MAIN_HPP
#include <optional>
#include <stdint.h> #include <stdint.h>
#include <string>
#include "fix/mbc.hpp" // UNSPECIFIED, MbcType #include "fix/mbc.hpp" // UNSPECIFIED, MbcType
@@ -28,19 +30,19 @@ struct Options {
uint16_t ramSize = UNSPECIFIED; // -r uint16_t ramSize = UNSPECIFIED; // -r
bool sgb = false; // -s bool sgb = false; // -s
char const *gameID = nullptr; // -i std::optional<std::string> gameID; // -i
uint8_t gameIDLen; uint8_t gameIDLen;
char const *newLicensee = nullptr; // -k std::optional<std::string> newLicensee; // -k
uint8_t newLicenseeLen; uint8_t newLicenseeLen;
char const *logoFilename = nullptr; // -L std::optional<std::string> logoFilename; // -L
uint8_t logo[48] = {}; uint8_t logo[48] = {};
MbcType cartridgeType = MBC_NONE; // -m MbcType cartridgeType = MBC_NONE; // -m
uint8_t tpp1Rev[2]; uint8_t tpp1Rev[2];
char const *title = nullptr; // -t std::optional<std::string> title; // -t
uint8_t titleLen; uint8_t titleLen;
}; };
+2 -6
View File
@@ -12,7 +12,7 @@ public:
static constexpr size_t capacity = 4; static constexpr size_t capacity = 4;
private: private:
// Up to 4 colors, sorted, and where SIZE_MAX means the slot is empty // Up to 4 colors, sorted, and where UINT16_MAX means the slot is empty
// (OK because it's not a valid color index) // (OK because it's not a valid color index)
// Sorting is done on the raw numerical values to lessen `compare`'s complexity // Sorting is done on the raw numerical values to lessen `compare`'s complexity
std::array<uint16_t, capacity> _colorIndices{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX}; std::array<uint16_t, capacity> _colorIndices{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX};
@@ -21,11 +21,7 @@ public:
// Adds the specified color to the set, or **silently drops it** if the set is full. // Adds the specified color to the set, or **silently drops it** if the set is full.
void add(uint16_t color); void add(uint16_t color);
enum ComparisonResult { enum ComparisonResult { INCOMPARABLE, SUBSET_OR_EQUAL, STRICT_SUPERSET };
NEITHER,
WE_BIGGER,
THEY_BIGGER = -1,
};
ComparisonResult compare(ColorSet const &other) const; ComparisonResult compare(ColorSet const &other) const;
size_t size() const; size_t size() const;
+23
View File
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: MIT
#ifndef RGBDS_GFX_FLIP_HPP
#define RGBDS_GFX_FLIP_HPP
#include <array>
#include <stdint.h>
// Flipping tends to happen fairly often, so take a bite out of dcache to speed it up
static std::array<uint16_t, 256> flipTable = ([]() constexpr {
std::array<uint16_t, 256> table{};
for (uint16_t i = 0; i < table.size(); ++i) {
// To flip all the bits, we'll flip both nibbles, then each nibble half, etc.
uint16_t byte = i;
byte = (byte & 0b0000'1111) << 4 | (byte & 0b1111'0000) >> 4;
byte = (byte & 0b0011'0011) << 2 | (byte & 0b1100'1100) >> 2;
byte = (byte & 0b0101'0101) << 1 | (byte & 0b1010'1010) >> 1;
table[i] = byte;
}
return table;
})();
#endif // RGBDS_GFX_FLIP_HPP
+1 -33
View File
@@ -5,12 +5,11 @@
#include <array> #include <array>
#include <optional> #include <optional>
#include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <string> #include <string>
#include <vector> #include <vector>
#include "helpers.hpp" #include "helpers.hpp" // assume
#include "gfx/rgba.hpp" #include "gfx/rgba.hpp"
@@ -69,35 +68,4 @@ struct Options {
extern Options options; extern Options options;
struct Palette {
// An array of 4 GBC-native (RGB555) colors
std::array<uint16_t, 4> colors{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX};
void addColor(uint16_t color);
uint8_t indexOf(uint16_t color) const;
uint16_t &operator[](size_t index) { return colors[index]; }
uint16_t const &operator[](size_t index) const { return colors[index]; }
decltype(colors)::iterator begin();
decltype(colors)::iterator end();
decltype(colors)::const_iterator begin() const;
decltype(colors)::const_iterator end() const;
uint8_t size() const;
};
// Flipping tends to happen fairly often, so take a bite out of dcache to speed it up
static std::array<uint16_t, 256> flipTable = ([]() constexpr {
std::array<uint16_t, 256> table{};
for (uint16_t i = 0; i < table.size(); ++i) {
// To flip all the bits, we'll flip both nibbles, then each nibble half, etc.
uint16_t byte = i;
byte = (byte & 0b0000'1111) << 4 | (byte & 0b1111'0000) >> 4;
byte = (byte & 0b0011'0011) << 2 | (byte & 0b1100'1100) >> 2;
byte = (byte & 0b0101'0101) << 1 | (byte & 0b1010'1010) >> 1;
table[i] = byte;
}
return table;
})();
#endif // RGBDS_GFX_MAIN_HPP #endif // RGBDS_GFX_MAIN_HPP
+27
View File
@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
#ifndef RGBDS_GFX_PALETTE_HPP
#define RGBDS_GFX_PALETTE_HPP
#include <array>
#include <stddef.h>
#include <stdint.h>
struct Palette {
// An array of 4 GBC-native (RGB555) colors
std::array<uint16_t, 4> colors{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX};
void addColor(uint16_t color);
uint8_t indexOf(uint16_t color) const;
uint16_t &operator[](size_t index) { return colors[index]; }
uint16_t const &operator[](size_t index) const { return colors[index]; }
decltype(colors)::iterator begin();
decltype(colors)::iterator end();
decltype(colors)::const_iterator begin() const;
decltype(colors)::const_iterator end() const;
uint8_t size() const;
};
#endif // RGBDS_GFX_PALETTE_HPP
+2 -1
View File
@@ -10,7 +10,8 @@
#include "gfx/rgba.hpp" #include "gfx/rgba.hpp"
struct Png { struct Png {
uint32_t width, height; uint32_t width = 0;
uint32_t height = 0;
std::vector<Rgba> pixels{}; std::vector<Rgba> pixels{};
std::vector<Rgba> palette{}; std::vector<Rgba> palette{};
+29 -8
View File
@@ -4,6 +4,9 @@
#define RGBDS_GFX_RGBA_HPP #define RGBDS_GFX_RGBA_HPP
#include <stdint.h> #include <stdint.h>
#include <string>
#include "helpers.hpp" // literal_strlen
struct Rgba { struct Rgba {
uint8_t red; uint8_t red;
@@ -17,18 +20,25 @@ struct Rgba {
explicit constexpr Rgba(uint32_t rgba = 0) explicit constexpr Rgba(uint32_t rgba = 0)
: red(rgba >> 24), green(rgba >> 16), blue(rgba >> 8), alpha(rgba) {} : red(rgba >> 24), green(rgba >> 16), blue(rgba >> 8), alpha(rgba) {}
// CGB colors are RGB555, so we use bit 15 to signify that the color is transparent instead
// Since the rest of the bits don't matter then, we return 0x8000 (1 << 15) exactly.
static constexpr uint16_t transparent = 0b1'00000'00000'00000;
static constexpr Rgba fromCGBColor(uint16_t color) { static constexpr Rgba fromCGBColor(uint16_t color) {
constexpr auto _5to8 = [](uint8_t c) -> uint8_t { return ((c & 0b11111) * 255 + 15) / 31; }; constexpr auto _5to8 = [](uint8_t channel) -> uint8_t {
channel &= 0b11111; // For caller's convenience
return channel << 3 | channel >> 2;
};
return { return {
_5to8(color), _5to8(color),
_5to8(color >> 5), _5to8(color >> 5),
_5to8(color >> 10), _5to8(color >> 10),
static_cast<uint8_t>(color & 0x8000 ? 0x00 : 0xFF), static_cast<uint8_t>(color & transparent ? 0x00 : 0xFF),
}; };
} }
// Returns this RGBA as a 32-bit number that can be printed in hex (`%08x`) to yield its CSS // Returns this RGBA as a 32-bit number that can be printed in hex (`#%08x`)
// representation // to yield its CSS representation (`#rrggbbaa`).
uint32_t toCSS() const { uint32_t toCSS() const {
constexpr auto shl = [](uint8_t val, unsigned shift) { constexpr auto shl = [](uint8_t val, unsigned shift) {
return static_cast<uint32_t>(val) << shift; return static_cast<uint32_t>(val) << shift;
@@ -38,10 +48,6 @@ struct Rgba {
bool operator==(Rgba const &rhs) const { return toCSS() == rhs.toCSS(); } bool operator==(Rgba const &rhs) const { return toCSS() == rhs.toCSS(); }
// CGB colors are RGB555, so we use bit 15 to signify that the color is transparent instead
// Since the rest of the bits don't matter then, we return 0x8000 exactly.
static constexpr uint16_t transparent = 0b1'00000'00000'00000;
static constexpr uint8_t transparency_threshold = 0x10; static constexpr uint8_t transparency_threshold = 0x10;
bool isTransparent() const { return alpha < transparency_threshold; } bool isTransparent() const { return alpha < transparency_threshold; }
static constexpr uint8_t opacity_threshold = 0xF0; static constexpr uint8_t opacity_threshold = 0xF0;
@@ -53,4 +59,19 @@ struct Rgba {
uint8_t grayIndex() const; uint8_t grayIndex() const;
}; };
// Returns a CGB color as a rendered string "GB:rr,gg,bb" or "transparent".
std::string toCGB(uint16_t color);
std::string listCGBColors(auto const &list) {
std::string buf;
for (uint16_t color : list) {
buf += toCGB(color) + "; ";
}
static constexpr size_t n = literal_strlen("; ");
if (buf.size() >= n) {
buf.erase(buf.size() - n, n);
}
return buf;
}
#endif // RGBDS_GFX_RGBA_HPP #endif // RGBDS_GFX_RGBA_HPP
+43 -22
View File
@@ -3,6 +3,8 @@
#ifndef RGBDS_HELPERS_HPP #ifndef RGBDS_HELPERS_HPP
#define RGBDS_HELPERS_HPP #define RGBDS_HELPERS_HPP
#include <type_traits>
// Ideally we'd use `std::unreachable`, but it has insufficient compiler support // Ideally we'd use `std::unreachable`, but it has insufficient compiler support
#ifdef __GNUC__ // GCC or compatible #ifdef __GNUC__ // GCC or compatible
#ifdef NDEBUG #ifdef NDEBUG
@@ -48,42 +50,40 @@ static inline void unreachable_() {
#pragma intrinsic(_BitScanReverse, _BitScanForward) #pragma intrinsic(_BitScanReverse, _BitScanForward)
static inline int ctz(unsigned int x) { static inline int ctz(unsigned int x) {
unsigned long cnt;
assume(x != 0); assume(x != 0);
_BitScanForward(&cnt, x); unsigned long count;
return cnt; _BitScanForward(&count, x);
return count;
} }
static inline int clz(unsigned int x) { static inline int clz(unsigned int x) {
unsigned long cnt;
assume(x != 0); assume(x != 0);
_BitScanReverse(&cnt, x); unsigned long count;
return 31 - cnt; _BitScanReverse(&count, x);
return 31 - count;
} }
#else #else
#include <limits.h> #include <limits.h>
static inline int ctz(unsigned int x) { static inline int ctz(unsigned int x) {
int cnt = 0; assume(x != 0);
int count = 0;
while (!(x & 1)) { while (!(x & 1)) {
x >>= 1; x >>= 1;
++cnt; ++count;
} }
return cnt; return count;
} }
static inline int clz(unsigned int x) { static inline int clz(unsigned int x) {
int cnt = 0; assume(x != 0);
int count = 0;
while (x <= UINT_MAX / 2) { while (x <= UINT_MAX / 2) {
x <<= 1; x <<= 1;
++cnt; ++count;
} }
return cnt; return count;
} }
#endif #endif
@@ -99,17 +99,38 @@ static inline int clz(unsigned int x) {
#define RANGE(s) std::begin(s), std::end(s) #define RANGE(s) std::begin(s), std::end(s)
#define RRANGE(s) std::rbegin(s), std::rend(s) #define RRANGE(s) std::rbegin(s), std::rend(s)
// Macros to print-format a `std::string_view` (like <inttype.h> macros)
#define PRI_SV ".*s"
#define PRI_SV_ARG(sv) static_cast<int>((sv).length()), (sv).data()
// MSVC does not inline `strlen()` or `.length()` of a constant string // MSVC does not inline `strlen()` or `.length()` of a constant string
template<int N> template<int SizeOfString>
static constexpr int literal_strlen(char const (&)[N]) { requires(SizeOfString > 0)
return N - 1; static constexpr int literal_strlen(char const (&)[SizeOfString]) {
return SizeOfString - 1; // Don't count the ending '\0'
} }
// Concept for template parameters that must be an `enum` type
template<typename EnumT>
concept Enum = std::is_enum_v<EnumT>;
// Concept for template parameters that must be a function returning `void`
template<typename ProcedureFnT, typename... Args>
concept Procedure = std::is_void_v<std::invoke_result_t<ProcedureFnT, Args...>>;
// Concept for template parameters that must be a function returning a particular type
template<typename FnT, typename ReturnT, typename... Args>
concept InvocableR = std::is_invocable_r_v<ReturnT, FnT, Args...>;
// Concept for template parameters that must match a particular type modulo cv-qualifiers
template<typename T, typename UnqualifiedT>
concept QualifiedEquivalent = std::is_same_v<std::remove_cvref_t<T>, UnqualifiedT>;
// For ad-hoc RAII in place of a `defer` statement or cross-platform `__attribute__((cleanup))` // For ad-hoc RAII in place of a `defer` statement or cross-platform `__attribute__((cleanup))`
template<typename T> template<Procedure DeferredFnT>
struct Defer { struct Defer {
T deferred; DeferredFnT deferred;
Defer(T func) : deferred(func) {} Defer(DeferredFnT func) : deferred(func) {}
~Defer() { deferred(); } ~Defer() { deferred(); }
}; };
+71 -33
View File
@@ -12,86 +12,118 @@
#include <unordered_map> #include <unordered_map>
#include <utility> #include <utility>
template<typename T> #include "helpers.hpp" // Enum, assume
// A wrapper around iterables to reverse their iteration order; used in `for`-each loops.
template<typename IterableT>
struct ReversedIterable {
IterableT &_iterable;
};
template<typename IterableT>
auto begin(ReversedIterable<IterableT> r) {
return std::rbegin(r._iterable);
}
template<typename IterableT>
auto end(ReversedIterable<IterableT> r) {
return std::rend(r._iterable);
}
template<typename IterableT>
ReversedIterable<IterableT> reversed(IterableT &&_iterable) {
return {_iterable};
}
// A map from `KeyT` keys to `ItemT` items, iterable in the order the items were inserted.
template<typename KeyT, typename ItemT>
class InsertionOrderedMap { class InsertionOrderedMap {
std::deque<T> list; std::deque<ItemT> list; // `deque` does not invalidate item references
std::unordered_map<std::string, size_t> map; // Indexes into `list` std::unordered_map<KeyT, size_t> map; // Indexes into `list`
public: public:
size_t size() const { return list.size(); } size_t size() const { return list.size(); }
bool empty() const { return list.empty(); } bool empty() const { return list.empty(); }
bool contains(std::string const &name) const { return map.find(name) != map.end(); } bool contains(KeyT const &key) const { return map.find(key) != map.end(); }
T &operator[](size_t i) { return list[i]; } ItemT &operator[](size_t i) { return list[i]; }
typename decltype(list)::iterator begin() { return list.begin(); } typename decltype(list)::iterator begin() { return list.begin(); }
typename decltype(list)::iterator end() { return list.end(); } typename decltype(list)::iterator end() { return list.end(); }
typename decltype(list)::const_iterator begin() const { return list.begin(); } typename decltype(list)::const_iterator begin() const { return list.begin(); }
typename decltype(list)::const_iterator end() const { return list.end(); } typename decltype(list)::const_iterator end() const { return list.end(); }
T &add(std::string const &name) { // Adding a key that already exists would make the previous value unreachable.
map[name] = list.size(); // `InsertionOrderedMap`s are only used for charmaps and sections, which each
// avoid `add`ing if already present, so we can `assume` this is not a concern.
ItemT &add(KeyT const &key) {
assume(!contains(key));
map[key] = list.size();
return list.emplace_back(); return list.emplace_back();
} }
T &add(std::string const &name, T &&value) { ItemT &add(KeyT const &key, ItemT &&value) {
map[name] = list.size(); assume(!contains(key));
map[key] = list.size();
list.emplace_back(std::move(value)); list.emplace_back(std::move(value));
return list.back(); return list.back();
} }
T &addAnonymous() { ItemT &addAnonymous() {
// Add the new item to the list, but do not update the map // Add the new item to the list, but do not update the map
return list.emplace_back(); return list.emplace_back();
} }
std::optional<size_t> findIndex(std::string const &name) const { std::optional<size_t> findIndex(KeyT const &key) const {
if (auto search = map.find(name); search != map.end()) { if (auto search = map.find(key); search != map.end()) {
return search->second; return search->second;
} }
return std::nullopt; return std::nullopt;
} }
}; };
template<typename T> // An iterable of `enum` values in the half-open range [start, stop).
template<Enum EnumT>
class EnumSeq { class EnumSeq {
T _start; EnumT _start;
T _stop; EnumT _stop;
class Iterator { class Iterator {
T _value; EnumT _value;
public: public:
explicit Iterator(T value) : _value(value) {} explicit Iterator(EnumT value) : _value(value) {}
Iterator &operator++() { Iterator &operator++() {
_value = static_cast<T>(_value + 1); _value = static_cast<EnumT>(_value + 1);
return *this; return *this;
} }
T operator*() const { return _value; } EnumT operator*() const { return _value; }
bool operator==(Iterator const &rhs) const { return _value == rhs._value; } bool operator==(Iterator const &rhs) const { return _value == rhs._value; }
}; };
public: public:
explicit EnumSeq(T stop) : _start(static_cast<T>(0)), _stop(stop) {} explicit EnumSeq(EnumT stop) : _start(static_cast<EnumT>(0)), _stop(stop) {}
explicit EnumSeq(T start, T stop) : _start(start), _stop(stop) {} explicit EnumSeq(EnumT start, EnumT stop) : _start(start), _stop(stop) {}
Iterator begin() { return Iterator(_start); } Iterator begin() { return Iterator(_start); }
Iterator end() { return Iterator(_stop); } Iterator end() { return Iterator(_stop); }
}; };
// Only needed inside `ZipContainer` below.
// This is not a fully generic implementation; its current use cases only require for-loop behavior. // This is not a fully generic implementation; its current use cases only require for-loop behavior.
// We also assume that all iterators have the same length. // We also assume that all iterators have the same length.
template<typename... Ts> template<typename... IteratorTs>
class ZipIterator { class ZipIterator {
std::tuple<Ts...> _iters; std::tuple<IteratorTs...> _iters;
public: public:
explicit ZipIterator(std::tuple<Ts...> &&iters) : _iters(iters) {} explicit ZipIterator(std::tuple<IteratorTs...> &&iters) : _iters(iters) {}
ZipIterator &operator++() { ZipIterator &operator++() {
std::apply([](auto &&...it) { (++it, ...); }, _iters); std::apply([](auto &&...it) { (++it, ...); }, _iters);
@@ -109,12 +141,14 @@ public:
} }
}; };
template<typename... Ts> // Only needed inside `zip` below.
template<typename... IterableTs>
class ZipContainer { class ZipContainer {
std::tuple<Ts...> _containers; std::tuple<IterableTs...> _containers;
public: public:
explicit ZipContainer(Ts &&...containers) : _containers(std::forward<Ts>(containers)...) {} explicit ZipContainer(IterableTs &&...containers)
: _containers(std::forward<IterableTs>(containers)...) {}
auto begin() { auto begin() {
return ZipIterator(std::apply( return ZipIterator(std::apply(
@@ -137,15 +171,19 @@ public:
} }
}; };
// Only needed inside `zip` below.
// Take ownership of objects and rvalue refs passed to us, but not lvalue refs // Take ownership of objects and rvalue refs passed to us, but not lvalue refs
template<typename T> template<typename IterableT>
using Holder = std:: using ZipHolder = std::conditional_t<
conditional_t<std::is_lvalue_reference_v<T>, T, std::remove_cv_t<std::remove_reference_t<T>>>; std::is_lvalue_reference_v<IterableT>,
IterableT,
std::remove_cvref_t<IterableT>>;
// Iterates over N containers at once, yielding tuples of N items at a time.
// Does the same number of iterations as the first container's iterator! // Does the same number of iterations as the first container's iterator!
template<typename... Ts> template<typename... IterableTs>
static constexpr auto zip(Ts &&...cs) { static constexpr auto zip(IterableTs &&...containers) {
return ZipContainer<Holder<Ts>...>(std::forward<Ts>(cs)...); return ZipContainer<ZipHolder<IterableTs>...>(std::forward<IterableTs>(containers)...);
} }
#endif // RGBDS_ITERTOOLS_HPP #endif // RGBDS_ITERTOOLS_HPP
+1 -1
View File
@@ -10,6 +10,6 @@ void lexer_TraceCurrent();
void lexer_IncludeFile(std::string &&path); void lexer_IncludeFile(std::string &&path);
void lexer_IncLineNo(); void lexer_IncLineNo();
bool lexer_Init(char const *linkerScriptName); bool lexer_Init(std::string const &linkerScriptName);
#endif // RGBDS_LINK_LEXER_HPP #endif // RGBDS_LINK_LEXER_HPP
+9 -7
View File
@@ -3,16 +3,18 @@
#ifndef RGBDS_LINK_MAIN_HPP #ifndef RGBDS_LINK_MAIN_HPP
#define RGBDS_LINK_MAIN_HPP #define RGBDS_LINK_MAIN_HPP
#include <optional>
#include <stdint.h> #include <stdint.h>
#include <string>
struct Options { struct Options {
bool isDmgMode; // -d bool isDmgMode; // -d
char const *mapFileName; // -m std::optional<std::string> mapFileName; // -m
bool noSymInMap; // -M bool noSymInMap; // -M
char const *symFileName; // -n std::optional<std::string> symFileName; // -n
char const *overlayFileName; // -O std::optional<std::string> overlayFileName; // -O
char const *outputFileName; // -o std::optional<std::string> outputFileName; // -o
uint8_t padValue; // -p uint8_t padValue; // -p
bool hasPadValue = false; bool hasPadValue = false;
// Setting these three to 0 disables the functionality // Setting these three to 0 disables the functionality
uint16_t scrambleROMX; // -S uint16_t scrambleROMX; // -S
+5 -2
View File
@@ -3,10 +3,13 @@
#ifndef RGBDS_LINK_OBJECT_HPP #ifndef RGBDS_LINK_OBJECT_HPP
#define RGBDS_LINK_OBJECT_HPP #define RGBDS_LINK_OBJECT_HPP
#include <stddef.h>
#include <string>
// Read an object (.o) file, and add its info to the data structures. // Read an object (.o) file, and add its info to the data structures.
void obj_ReadFile(char const *fileName, unsigned int fileID); void obj_ReadFile(std::string const &filePath, size_t fileID);
// Sets up object file reading // Sets up object file reading
void obj_Setup(unsigned int nbFiles); void obj_Setup(size_t nbFiles);
#endif // RGBDS_LINK_OBJECT_HPP #endif // RGBDS_LINK_OBJECT_HPP
+34
View File
@@ -8,6 +8,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "helpers.hpp" // QualifiedEquivalent
#include "linkdefs.hpp" #include "linkdefs.hpp"
struct FileStackNode; struct FileStackNode;
@@ -49,6 +50,39 @@ struct Section {
std::vector<Symbol> *fileSymbols; std::vector<Symbol> *fileSymbols;
std::vector<Symbol *> symbols; std::vector<Symbol *> symbols;
std::unique_ptr<Section> nextPiece; // The next fragment or union "piece" of this section std::unique_ptr<Section> nextPiece; // The next fragment or union "piece" of this section
private:
// Template class for both const and non-const iterators over the "pieces" of this section
template<QualifiedEquivalent<Section> SectionT>
class PiecesIterable {
SectionT *_firstPiece;
class Iterator {
SectionT *_piece;
public:
explicit Iterator(SectionT *piece) : _piece(piece) {}
Iterator &operator++() {
_piece = _piece->nextPiece.get();
return *this;
}
SectionT &operator*() const { return *_piece; }
bool operator==(Iterator const &rhs) const { return _piece == rhs._piece; }
};
public:
explicit PiecesIterable(SectionT *firstPiece) : _firstPiece(firstPiece) {}
Iterator begin() { return Iterator(_firstPiece); }
Iterator end() { return Iterator(nullptr); }
};
public:
PiecesIterable<Section> pieces() { return PiecesIterable(this); }
PiecesIterable<Section const> pieces() const { return PiecesIterable(this); }
}; };
// Execute a callback for each section currently registered. // Execute a callback for each section currently registered.
+8 -5
View File
@@ -22,11 +22,12 @@ enum WarningLevel {
}; };
enum WarningID { enum WarningID {
WARNING_ASSERT, // Assertions WARNING_ASSERT, // Assertions
WARNING_DIV, // Undefined division behavior WARNING_DIV, // Undefined division behavior
WARNING_OBSOLETE, // Obsolete/deprecated things WARNING_LARGE_CONSTANT, // Constants too large
WARNING_SHIFT, // Undefined `SHIFT` behavior WARNING_OBSOLETE, // Obsolete/deprecated things
WARNING_SHIFT_AMOUNT, // Strange `SHIFT` amount WARNING_SHIFT, // Undefined `SHIFT` behavior
WARNING_SHIFT_AMOUNT, // Strange `SHIFT` amount
NB_PLAIN_WARNINGS, NB_PLAIN_WARNINGS,
@@ -47,6 +48,8 @@ void warning(FileStackNode const *src, uint32_t lineNo, WarningID id, char const
void warning(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...); void warning(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...);
[[gnu::format(printf, 1, 2)]] [[gnu::format(printf, 1, 2)]]
void warning(char const *fmt, ...); void warning(char const *fmt, ...);
[[gnu::format(printf, 2, 3)]]
void scriptWarning(WarningID id, char const *fmt, ...);
[[gnu::format(printf, 3, 4)]] [[gnu::format(printf, 3, 4)]]
void error(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...); void error(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...);
+1 -1
View File
@@ -120,7 +120,7 @@ enum SectionModifier { SECTION_NORMAL, SECTION_UNION, SECTION_FRAGMENT };
extern char const * const sectionModNames[]; extern char const * const sectionModNames[];
enum ExportLevel { SYMTYPE_LOCAL, SYMTYPE_IMPORT, SYMTYPE_EXPORT }; enum ExportLevel { SYMTYPE_LOCAL, SYMTYPE_IMPORT, SYMTYPE_EXPORT, SYMTYPE_INVALID };
enum PatchType { enum PatchType {
PATCHTYPE_BYTE, PATCHTYPE_BYTE,
+8 -2
View File
@@ -55,8 +55,14 @@
#define setmode(fd, mode) (0) #define setmode(fd, mode) (0)
#endif #endif
// MingGW and Cygwin need POSIX functions which are not standard C explicitly enabled, // Windows has 32-bit `long`, which limits `fseek` and `ftell` to 2 GiB
#if defined(__MINGW32__) || defined(__CYGWIN__) #if defined(_MSC_VER) || defined(__MINGW32__)
#define fseek _fseeki64
#define ftell _ftelli64
#endif
// MingGW and Cygwin may need POSIX functions which are not standard C explicitly enabled
#if (defined(__MINGW32__) || defined(__CYGWIN__)) && !defined(_POSIX_C_SOURCE)
#define _POSIX_C_SOURCE 200809L #define _POSIX_C_SOURCE 200809L
#endif #endif
+2
View File
@@ -13,6 +13,8 @@ struct Usage {
std::vector<std::string> flags; std::vector<std::string> flags;
std::vector<std::pair<std::vector<std::string>, std::vector<std::string>>> options; std::vector<std::pair<std::vector<std::string>, std::vector<std::string>>> options;
void printVersion(bool error) const;
[[noreturn]] [[noreturn]]
void printAndExit(int code) const; void printAndExit(int code) const;
+56 -14
View File
@@ -4,12 +4,12 @@
#define RGBDS_UTIL_HPP #define RGBDS_UTIL_HPP
#include <algorithm> #include <algorithm>
#include <ctype.h> // toupper
#include <numeric> #include <numeric>
#include <optional> #include <optional>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <string> #include <stdio.h>
#include <string_view>
#include <unordered_map> #include <unordered_map>
#include "helpers.hpp" #include "helpers.hpp"
@@ -22,6 +22,14 @@ enum NumberBase {
BASE_16 = 16, BASE_16 = 16,
}; };
// File-closing functions that will not close standard streams
int xfclose(FILE *file);
int xclose(int fd);
// Measure file size with `fseek` and `ftell` idiom
std::optional<uint64_t> seekSize(FILE *file);
// Locale-independent character class functions
bool isNewline(int c); bool isNewline(int c);
bool isBlankSpace(int c); bool isBlankSpace(int c);
bool isWhitespace(int c); bool isWhitespace(int c);
@@ -29,38 +37,72 @@ bool isPrintable(int c);
bool isUpper(int c); bool isUpper(int c);
bool isLower(int c); bool isLower(int c);
bool isLetter(int c); bool isLetter(int c);
bool isDigit(int c);
bool isBinDigit(int c);
bool isOctDigit(int c);
bool isHexDigit(int c);
bool isAlphanumeric(int c); bool isAlphanumeric(int c);
// Locale-independent character transform functions
char toLower(char c);
char toUpper(char c);
bool startsIdentifier(int c); bool startsIdentifier(int c);
bool continuesIdentifier(int c); bool continuesIdentifier(int c);
uint8_t parseHexDigit(int c); template<uint32_t Base>
inline constexpr bool ValidBaseV = Base > 0 && Base <= 36;
template<uint32_t Base>
requires ValidBaseV<Base>
bool isDigit(int c) {
if constexpr (Base <= 10) {
return c >= '0' && c < static_cast<int>('0' + Base);
} else {
return isDigit<10>(c) || (c >= 'A' && c < static_cast<int>('A' + Base - 10))
|| (c >= 'a' && c < static_cast<int>('a' + Base - 10));
}
}
template<uint32_t Base>
requires ValidBaseV<Base>
uint8_t parseDigit(int c) {
assume(isDigit<Base>(c));
if constexpr (Base <= 10) {
return c - '0';
} else {
// Check digit ranges from greatest to least ('a'-'z', then 'A'-'Z', then '0'-'9')
if (c >= 'a') {
return c - 'a' + 10;
} else if (c >= 'A') {
return c - 'A' + 10;
} else {
return parseDigit<10>(c);
}
}
}
std::optional<uint64_t> parseNumber(char const *&str, NumberBase base = BASE_AUTO); std::optional<uint64_t> parseNumber(char const *&str, NumberBase base = BASE_AUTO);
std::optional<uint64_t> parseWholeNumber(char const *str, NumberBase base = BASE_AUTO); std::optional<uint64_t> parseWholeNumber(char const *str, NumberBase base = BASE_AUTO);
char const *printChar(int c); char const *printChar(int c);
struct Uppercase { struct Uppercase {
using is_transparent = void;
// FNV-1a hash of an uppercased string // FNV-1a hash of an uppercased string
constexpr size_t operator()(std::string const &str) const { constexpr size_t operator()(std::string_view str) const {
return std::accumulate(RANGE(str), 0x811C9DC5, [](size_t hash, char c) { return std::accumulate(RANGE(str), size_t(0x811C9DC5), [](size_t hash, char c) {
return (hash ^ toupper(c)) * 16777619; return (hash ^ toUpper(c)) * 16777619;
}); });
} }
// Compare two strings without case-sensitivity (by converting to uppercase) // Compare two strings without case-sensitivity (by converting to uppercase)
constexpr bool operator()(std::string const &str1, std::string const &str2) const { constexpr bool operator()(std::string_view str1, std::string_view str2) const {
return std::equal(RANGE(str1), RANGE(str2), [](char c1, char c2) { return std::equal(RANGE(str1), RANGE(str2), [](char c1, char c2) {
return toupper(c1) == toupper(c2); return toUpper(c1) == toUpper(c2);
}); });
} }
}; };
template<typename T> // An unordered map from case-insensitive `std::string_view` keys to `ItemT` items
using UpperMap = std::unordered_map<std::string, T, Uppercase, Uppercase>; template<typename ItemT>
using UpperMap = std::unordered_map<std::string_view, ItemT, Uppercase, Uppercase>;
#endif // RGBDS_UTIL_HPP #endif // RGBDS_UTIL_HPP
+14 -3
View File
@@ -3,17 +3,17 @@
#ifndef RGBDS_VERBOSITY_HPP #ifndef RGBDS_VERBOSITY_HPP
#define RGBDS_VERBOSITY_HPP #define RGBDS_VERBOSITY_HPP
#include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include "helpers.hpp" // Procedure
#include "style.hpp" #include "style.hpp"
// This macro does not evaluate its arguments unless the condition is true. // This macro does not evaluate its arguments unless the condition is true.
#define verbosePrint(level, ...) \ #define verbosePrint(level, ...) \
do { \ do { \
if (checkVerbosity(level)) { \ if (checkVerbosity(level)) { \
style_Set(stderr, STYLE_MAGENTA, false); \ printVerbosely(__VA_ARGS__); \
fprintf(stderr, __VA_ARGS__); \
style_Reset(stderr); \
} \ } \
} while (0) } while (0)
@@ -30,6 +30,17 @@ enum Verbosity {
void incrementVerbosity(); void incrementVerbosity();
bool checkVerbosity(Verbosity level); bool checkVerbosity(Verbosity level);
[[gnu::format(printf, 1, 2)]]
void printVerbosely(char const *fmt, ...);
void verboseDo(Verbosity level, Procedure<> auto callback) {
if (checkVerbosity(level)) {
style_Set(stderr, STYLE_MAGENTA, false);
callback();
style_Reset(stderr);
}
}
void printVVVVVVerbosity(); void printVVVVVVerbosity();
#endif // RGBDS_VERBOSITY_HPP #endif // RGBDS_VERBOSITY_HPP
+2 -2
View File
@@ -5,8 +5,8 @@
#define PACKAGE_VERSION_MAJOR 1 #define PACKAGE_VERSION_MAJOR 1
#define PACKAGE_VERSION_MINOR 0 #define PACKAGE_VERSION_MINOR 0
#define PACKAGE_VERSION_PATCH 0 #define PACKAGE_VERSION_PATCH 3
#define PACKAGE_VERSION_RC 2 // #define PACKAGE_VERSION_RC 1
char const *get_package_version_string(); char const *get_package_version_string();
+42 -9
View File
@@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd September 30, 2025 .Dd August 1, 2026
.Dt GBZ80 7 .Dt GBZ80 7
.Os .Os
.Sh NAME .Sh NAME
@@ -11,6 +11,11 @@ This is the list of instructions supported by
.Xr rgbasm 1 , .Xr rgbasm 1 ,
including a short description, the number of bytes needed to encode them and the number of CPU cycles at 1MHz (or 2MHz in GBC double speed mode) needed to complete them. including a short description, the number of bytes needed to encode them and the number of CPU cycles at 1MHz (or 2MHz in GBC double speed mode) needed to complete them.
.Pp .Pp
Instructons are documented according to the syntax accepted by
.Xr rgbasm 1 ,
which does not always match one-to-one with the way instructions are
.Lk https://gbdev.io/gb-opcodes/optables/ encoded .
.Pp
Note: All arithmetic and logic instructions that use register Note: All arithmetic and logic instructions that use register
.Sy A .Sy A
as a destination can omit the destination, since it is assumed to be register as a destination can omit the destination, since it is assumed to be register
@@ -861,11 +866,13 @@ Flags: None affected.
Relative Jump to address Relative Jump to address
.Ar n16 . .Ar n16 .
.Pp .Pp
The address is encoded as a signed 8-bit offset from the address immediately following the The target address
.Ic JR
instruction, so the target address
.Ar n16 .Ar n16
must be between is
.Em encoded
as a signed 8-bit offset from the address immediately following the
.Ic JR
instruction, so it must be between
.Sy -128 .Sy -128
and and
.Sy 127 .Sy 127
@@ -889,6 +896,18 @@ if condition
.Ar cc .Ar cc
is met. is met.
.Pp .Pp
The target address
.Ar n16
is
.Em encoded
as a signed 8-bit offset from the address immediately following the
.Ic JR
instruction, so it must be between
.Sy -128
and
.Sy 127
bytes away.
.Pp
Cycles: 3 taken / 2 untaken Cycles: 3 taken / 2 untaken
.Pp .Pp
Bytes: 2 Bytes: 2
@@ -992,8 +1011,15 @@ Flags: None affected.
Copy the value in register Copy the value in register
.Sy A .Sy A
into the byte at address into the byte at address
.Ar n16 , .Ar n16 .
provided the address is between .Pp
The destination address
.Ar n16
is
.Em encoded
as its 8-bit low byte and assumes a high byte of
.Ad $FF ,
so it must be between
.Ad $FF00 .Ad $FF00
and and
.Ad $FFFF . .Ad $FFFF .
@@ -1043,8 +1069,15 @@ Flags: None affected.
Copy the byte at address Copy the byte at address
.Ar n16 .Ar n16
into register into register
.Sy A , .Sy A .
provided the address is between .Pp
The source address
.Ar n16
is
.Em encoded
as its 8-bit low byte and assumes a high byte of
.Ad $FF ,
so it must be between
.Ad $FF00 .Ad $FF00
and and
.Ad $FFFF . .Ad $FFFF .
+45 -20
View File
@@ -2,7 +2,7 @@
.\" .\"
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd September 30, 2025 .Dd August 1, 2026
.Dt RGBASM-OLD 5 .Dt RGBASM-OLD 5
.Os .Os
.Sh NAME .Sh NAME
@@ -140,12 +140,55 @@ Deprecated in 0.9.0, removed in 1.0.0.
Instead, use a multi-value Instead, use a multi-value
.Ic CHARMAP , .Ic CHARMAP ,
or explicitly combine the values of individual characters. or explicitly combine the values of individual characters.
.Pp
For example, instead of a multi-character string like:
.Bd -literal -offset indent
CHARMAP "X", $12
CHARMAP "Y", $34
LD BC, "XY"
.Ed
.Pp
Either use a multi-value
.Ic CHARMAP :
.Bd -literal -offset indent
CHARMAP "X", $12
CHARMAP "Y", $34
CHARMAP "<XY>", 'X', 'Y'
LD BC, CHARVAL("<XY>", 0) << 8 | CHARVAL("<XY>", 1)
.Ed
.Pp
Or combine the individual characters:
.Bd -literal -offset indent
CHARMAP "X", $12
CHARMAP "Y", $34
LD BC, 'X' << 8 | 'Y'
.Ed
.Pp
Or use a single-value character:
.Bd -literal -offset indent
CHARMAP "X", $12
CHARMAP "Y", $34
CHARMAP "<XY>", 'X' << 8 | 'Y'
LD BC, '<XY>'
.Ed
.Ss Treating strings as numbers .Ss Treating strings as numbers
Deprecated in 1.0.0. Deprecated in 1.0.0.
.Pp .Pp
Instead, use character constants or the Instead, use character constants or the
.Ic CHARVAL .Ic CHARVAL
function. function.
.Pp
For example, instead of a string literal like
.Ql LD A, \&"R" ,
use a numeric character literal
.Ql LD A, 'R' .
.Pp
And instead of a string expression like
.Ql CP STRUPR("\e1") ,
use a numeric
.Ic CHARVAL
expression
.Ql CP CHARVAL(STRUPR("\e1")) .
.Ss rgbgfx -f/--fix and -F/--fix-and-save .Ss rgbgfx -f/--fix and -F/--fix-and-save
Removed in 0.6.0. Removed in 0.6.0.
.Pp .Pp
@@ -370,7 +413,7 @@ or
Deprecated in 1.0.0. Deprecated in 1.0.0.
.Pp .Pp
Instead, use Instead, use
.Dl -Wno-overwrite . .Ql -Wno-overwrite .
.Ss rgbgfx -h/--horizontal .Ss rgbgfx -h/--horizontal
Removed in 0.6.0. Removed in 0.6.0.
.Pp .Pp
@@ -461,24 +504,6 @@ Previously we had
.Pp .Pp
Instead, now we have Instead, now we have
.Ql p ** q ** r == p ** (q ** r) . .Ql p ** q ** r == p ** (q ** r) .
.Ss 8-bit and 5-bit color conversion
Changed in 1.0.0.
.Pp
RGBGFX takes 8-bit RGB colors as its PNG input, and outputs 5-bit GBC colors.
Its
.Ql -r/--reverse
mode does the opposite 5-bit to 8-bit conversion.
Instead of the previous inaccurate conversions, we now do accurate rounding to the nearest equivalent.
.Pp
Previously to convert an 8-bit color channel to 5-bit, we truncated it as
.Ql c >> 3 ;
and to reverse a 5-bit color channel to 8-bit, we extended it as
.Ql (c << 3) | (c >> 2) .
.Pp
Instead, now we round 8-bit to 5-bit as
.Ql (c * 31 + 127) / 255 ,
and round 5-bit to 8-bit as
.Ql (c * 255 + 15) / 31 .
.Sh BUGS .Sh BUGS
These are misfeatures that may have been possible by mistake. These are misfeatures that may have been possible by mistake.
They do not get deprecated, just fixed. They do not get deprecated, just fixed.
+81 -16
View File
@@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd September 30, 2025 .Dd August 1, 2026
.Dt RGBASM 1 .Dt RGBASM 1
.Os .Os
.Sh NAME .Sh NAME
@@ -36,14 +36,14 @@ The
program creates an RGB object file from an assembly source file. program creates an RGB object file from an assembly source file.
The object file format is documented in The object file format is documented in
.Xr rgbds 5 . .Xr rgbds 5 .
.Pp .Sh ARGUMENTS
The input .Nm
.Ar asmfile accepts the usual short and long options, such as
can be a path to a file, or .Fl V
.Cm \- and
to read from standard input. .Fl -version .
.Pp Options later in the command line override those set earlier, except for when duplicate options are considered an error.
Note that options can be abbreviated as long as the abbreviation is unambiguous: Options can be abbreviated as long as the abbreviation is unambiguous:
.Fl \-verb .Fl \-verb
is is
.Fl \-verbose , .Fl \-verbose ,
@@ -51,7 +51,52 @@ but
.Fl \-ver .Fl \-ver
is invalid because it could also be is invalid because it could also be
.Fl \-version . .Fl \-version .
The arguments are as follows: .Pp
Unless otherwise noted, passing
.Ql -
(a single dash) as a file name makes
.Nm
use standard input (for input files) or standard output (for output files).
To suppress this behavior, and open a file in the current directory actually called
.Ql - ,
pass
.Ql ./-
instead.
Using standard input or output for more than one file in a single command may produce unexpected results.
.Pp
.Nm
accepts decimal, hexadecimal, octal, and binary for numeric option arguments.
Decimal numbers are written as usual; hexadecimal numbers must be prefixed with either
.Ql $
or
.Ql 0x ;
octal numbers must be prefixed with either
.Ql &
or
.Ql 0o ;
and binary numbers must be prefixed with either
.Ql %
or
.Ql 0b .
(The prefixes
.Ql $
and
.Ql &
will likely need escaping or quoting to avoid being interpreted by the shell.)
Leading zeros (after the base prefix, if any) are accepted, and letters are not case-sensitive.
For example, all of these are equivalent:
.Ql 42 ,
.Ql 042 ,
.Ql 0x2A ,
.Ql 0X2A ,
.Ql 0x2a ,
.Ql &52 ,
.Ql 0o52 ,
.Ql 0O052 ,
.Ql 0b00101010 ,
.Ql 0B101010 .
.Pp
The following options are accepted:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Fl B Ar param , Fl \-backtrace Ar param .It Fl B Ar param , Fl \-backtrace Ar param
Configures how location backtraces are printed if warnings or errors occur. Configures how location backtraces are printed if warnings or errors occur.
@@ -132,6 +177,9 @@ or
The defaults are 0123. The defaults are 0123.
.It Fl h , Fl \-help .It Fl h , Fl \-help
Print help text for the program and exit. Print help text for the program and exit.
(Help text wraps to the value of the
.Dv COLUMNS
environment variable if that is defined as nonzero; or else to the console window width if output is to a TTY.)
.It Fl I Ar path , Fl \-include Ar path .It Fl I Ar path , Fl \-include Ar path
Add a new Add a new
.Dq include path ; .Dq include path ;
@@ -306,6 +354,23 @@ disables this behavior.
The default is 100 if The default is 100 if
.Nm .Nm
is printing errors to a terminal, and 0 otherwise. is printing errors to a terminal, and 0 otherwise.
.It @ Ns Ar at_file
Read more options and arguments from a file, as if its contents were given on the command line.
Arguments are separated by whitespace or newlines.
Lines starting with a hash sign
.Pq Ql #
are considered comments and ignored.
.Pp
No shell processing is performed, such as wildcard or variable expansion.
There is no support for escaping or quoting whitespace to be included in arguments.
The standard
.Ql --
to stop option processing also disables at-file processing.
Note that while
.Ql --
can be used
.Em inside
an at-file, it only disables option processing within that at-file, and processing continues in the parent scope.
.El .El
.Sh DIAGNOSTICS .Sh DIAGNOSTICS
Warnings are diagnostic messages that indicate possibly erroneous behavior that does not necessarily compromise the assembling process. Warnings are diagnostic messages that indicate possibly erroneous behavior that does not necessarily compromise the assembling process.
@@ -344,9 +409,9 @@ Enables literally every warning.
.Pp .Pp
The following warnings are actual warning flags; with each description, the corresponding warning flag is included. The following warnings are actual warning flags; with each description, the corresponding warning flag is included.
Note that each of these flags also has a negation (for example, Note that each of these flags also has a negation (for example,
.Fl Wcharmap-redef .Fl Wobsolete
enables the warning that enables the warning that
.Fl Wno-charmap-redef .Fl Wno-obsolete
disables; and disables; and
.Fl Wall .Fl Wall
enables every warning that enables every warning that
@@ -441,10 +506,10 @@ or
.Fl Wno-purge .Fl Wno-purge
disables this warning. disables this warning.
.Fl Wpurge=1 .Fl Wpurge=1
or just
.Fl Wpurge
warns when purging any exported symbol (regardless of type). warns when purging any exported symbol (regardless of type).
.Fl Wpurge=2 .Fl Wpurge=2
or just
.Fl Wpurge
also warns when purging any label (even if not exported). also warns when purging any label (even if not exported).
.It Fl Wshift .It Fl Wshift
Warn when shifting right a negative value. Warn when shifting right a negative value.
@@ -460,10 +525,10 @@ or
.Fl Wno-truncation .Fl Wno-truncation
disables this warning. disables this warning.
.Fl Wtruncation=1 .Fl Wtruncation=1
warns when an N-bit value is 2**N or greater, or less than -2**N.
.Fl Wtruncation=2
or just or just
.Fl Wtruncation .Fl Wtruncation
warns when an N-bit value is 2**N or greater, or less than -2**N.
.Fl Wtruncation=2
also warns when an N-bit value is less than -2**(N-1), which will not fit in two's complement encoding. also warns when an N-bit value is less than -2**(N-1), which will not fit in two's complement encoding.
.It Fl Wunmapped-char= .It Fl Wunmapped-char=
Warn when a character goes through charmap conversion but has no defined mapping. Warn when a character goes through charmap conversion but has no defined mapping.
+143 -102
View File
@@ -2,7 +2,7 @@
.\" .\"
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd September 30, 2025 .Dd August 1, 2026
.Dt RGBASM 5 .Dt RGBASM 5
.Os .Os
.Sh NAME .Sh NAME
@@ -27,10 +27,13 @@ but any program that processes RGBDS object files (described in
can be used in its place. can be used in its place.
.Sh SYNTAX .Sh SYNTAX
The syntax is line-based, just as in any other assembler. The syntax is line-based, just as in any other assembler.
Each line may have components in this order: Each line may have components in either of these orders:
.Pp .Bl -bullet -offset indent
.Dl Oo Ar directive Oc Oo ;\ Ns Ar comment Oc .It
.Dl Oo Ar label : Oc Oo Ar instruction Oo :: Ar instruction ... Oc Oc Oo ;\ Ns Ar comment Oc .Oo Ar label : Oc Oo Ar directive Oc Oo ;\ Ns Ar comment Oc
.It
.Oo Ar label : Oc Oo Ar instruction Oo :: Ar instruction ... Oc Oc Oo ;\ Ns Ar comment Oc
.El
.Pp .Pp
Directives are commands to the assembler itself, such as Directives are commands to the assembler itself, such as
.Ic PRINTLN , .Ic PRINTLN ,
@@ -41,6 +44,17 @@ or
Labels tie a name to a specific location within a section (see Labels tie a name to a specific location within a section (see
.Sx Labels .Sx Labels
below). below).
Labels are allowed before most directives, but not before
.Ic IF ,
.Ic ELIF ,
.Ic ELSE ,
.Ic ENDC ,
.Ic REPT ,
.Ic FOR ,
.Ic ENDR ,
.Ic MACRO ,
or
.Ic ENDM .
.Pp .Pp
Instructions are assembled into Game Boy opcodes. Instructions are assembled into Game Boy opcodes.
Multiple instructions on one line, as well as data directives (see Multiple instructions on one line, as well as data directives (see
@@ -84,8 +98,8 @@ as the opposite condition code; for example,
for for
.Ic z . .Ic z .
.Pp .Pp
All reserved keywords (directives, register names, etc.) are case-insensitive; All reserved keywords (directives, instructions, registers, built-in functions, etc.) are case-insensitive;
all identifiers (labels and other symbol names) are case-sensitive. all identifiers (labels, variables, etc) are case-sensitive.
.Pp .Pp
Comments are used to give humans information about the code, such as explanations. Comments are used to give humans information about the code, such as explanations.
The assembler The assembler
@@ -124,17 +138,17 @@ To do so, put a backslash at the end of the line:
world!"\ \ \ \ \ \ \ \ \ \ \ ;\ Any leading space is included world!"\ \ \ \ \ \ \ \ \ \ \ ;\ Any leading space is included
.Ed .Ed
.Ss Symbol interpolation .Ss Symbol interpolation
A funky feature is writing a symbol between Symbols with string or numeric values can be
.Ql {braces} , .Dq interpolated
called by writing them inside
.Dq symbol interpolation . .Ql {braces} .
This will paste the symbol's contents as if they were part of the source file. This will paste the symbol's contents as if they were part of the source file.
If it is a string symbol, its characters are simply inserted as-is. If it is a string symbol, its characters are simply inserted as-is.
If it is a numeric symbol, its value is converted to hexadecimal notation with a dollar sign If it is a numeric symbol, its value is converted to hexadecimal notation with a dollar sign
.Sq $ .Sq $
prepended. prepended.
.Pp .Pp
Symbol interpolations can be nested, too! Symbol interpolations can be nested, too.
.Bd -literal -offset indent .Bd -literal -offset indent
DEF topic EQUS "life, the universe, and \e"everything\e"" DEF topic EQUS "life, the universe, and \e"everything\e""
DEF meaning EQUS "answer" DEF meaning EQUS "answer"
@@ -145,27 +159,29 @@ PRINTLN "The {meaning} to {topic} is {{meaning}}"
PURGE topic, meaning, {meaning} PURGE topic, meaning, {meaning}
.Ed .Ed
.Pp .Pp
Symbols can be Symbols can be interpolated even in contexts that disable automatic expansion of string constants: that is,
.Em interpolated
even in the contexts that disable automatic
.Em expansion
of string constants:
.Ql name .Ql name
will be expanded in all of will be expanded in all of
.Ql DEF({name}) , .Ql DEF({name}) ,
.Ql DEF {name} EQU/=/EQUS/etc ... , .Ql DEF {name} EQU/=/EQUS/etc ... ,
.Ql REDEF {name} EQU/=/EQUS/etc ... ,
.Ql FOR {name}, ... ,
.Ql PURGE {name} , .Ql PURGE {name} ,
and and
.Ql MACRO {name} , .Ql MACRO {name} ,
but, for example, won't be in even though it won't be in
.Ql DEF(name) . .Ql DEF(name) ,
.Ql PURGE {name} ,
etc.
.Pp .Pp
It's possible to change the way symbols are printed by specifying a print format like so: It's possible to change the way symbols are printed by specifying a print format like so:
.Ql {fmt:symbol} . .Ql {fmt:symbol} .
The The
.Ql fmt .Ql fmt
specifier consists of these parts: specifier consists of parts, which must be in the following order:
.Ql <sign><exact><align><pad><width><frac><prec><type> . .Ql <sign><exact><align><pad><width><frac><prec><type> .
All the parts are optional except the required
.Ql <type> .
These parts are: These parts are:
.Bl -column "<exact>" .Bl -column "<exact>"
.It Sy Part Ta Sy Meaning .It Sy Part Ta Sy Meaning
@@ -175,14 +191,17 @@ or
.Ql \ . .Ql \ .
If specified, prints this character in front of non-negative numbers. If specified, prints this character in front of non-negative numbers.
.It Ql <exact> Ta May be .It Ql <exact> Ta May be
.Ql # . .Ql #
If specified, prints the value in an "exact" format: with a base prefix for non-decimal integer types .Pq only allowed for non-decimal types .
.Pq So $ Sc , So & Sc , or So % Sc ; If specified, prints the value in an "exact" format: with a base prefix
.Pq So $ Sc , So & Sc , or So % Sc
for non-decimal integer types
.Pq So x Sc / So X Sc , So o Sc , or So b Sc ;
with a with a
.Ql q .Ql q
precision suffix for fixed-point numbers; or with precision suffix for fixed-point numbers; or with
.Ql \e .Ql \e
escape characters for strings. escape characters (but no enclosing quotes) for strings.
.It Ql <align> Ta May be .It Ql <align> Ta May be
.Ql - . .Ql - .
If specified, aligns left instead of right. If specified, aligns left instead of right.
@@ -208,7 +227,7 @@ followed by zero
.Ql 0 .Ql 0
\[en] \[en]
.Ql 9 .Ql 9
prints zero fractional digits.) prints zero fractional digits and no decimal point.)
.It Ql <prec> Ta May be .It Ql <prec> Ta May be
.Ql q .Ql q
followed by one or more followed by one or more
@@ -222,13 +241,11 @@ option.
.It Ql <type> Ta Specifies the type of value. .It Ql <type> Ta Specifies the type of value.
.El .El
.Pp .Pp
All the format specifier parts are optional except the Valid types are:
.Ql <type> .
Valid print types are:
.Bl -column -offset indent "Type" "Lowercase hexadecimal" "Example" .Bl -column -offset indent "Type" "Lowercase hexadecimal" "Example"
.It Sy Type Ta Sy Format Ta Sy Example .It Sy Type Ta Sy Format Ta Sy Example
.It Ql d Ta Signed decimal Ta -42 .It Ql d Ta Signed decimal Ta -42
.It Ql u Ta Unsigned decimal Ta 42 .It Ql u Ta Unsigned decimal Ta 4294967254
.It Ql x Ta Lowercase hexadecimal Ta 2a .It Ql x Ta Lowercase hexadecimal Ta 2a
.It Ql X Ta Uppercase hexadecimal Ta 2A .It Ql X Ta Uppercase hexadecimal Ta 2A
.It Ql b Ta Binary Ta 101010 .It Ql b Ta Binary Ta 101010
@@ -264,9 +281,10 @@ would be more appropriate; see
.Sx String expressions .Sx String expressions
below. below.
.Sh EXPRESSIONS .Sh EXPRESSIONS
An expression can be composed of many things. There are two types of expressions: numeric and string.
.Pp
Numeric expressions are always evaluated using signed 32-bit math. Numeric expressions are always evaluated using signed 32-bit math.
Zero is considered to be the only "false" number, all non-zero numbers (including negative) are "true". In Boolean logic contexts, zero is considered to be the only "false" number, and all non-zero numbers (including negative) are "true".
.Pp .Pp
An expression is said to be "constant" if An expression is said to be "constant" if
.Nm .Nm
@@ -278,18 +296,21 @@ However, some operators can be constant even with non-constant operands, as expl
.Sx Operators .Sx Operators
below. below.
.Pp .Pp
The instructions in the macro-language generally require constant expressions. Directives generally require constant expressions: for example,
.Ss Numeric formats .Ic REPT
There are a number of numeric formats. requires the number of repetitions to be known at assembly time.
.Bl -column -offset indent "Precise fixed-point" "Possible prefixes" .Ss Numeric literals
.It Sy Format type Ta Sy Possible prefixes Ta Sy Accepted characters .Nm
supports a variety of numeric literals.
.Bl -column -offset indent "Precise fixed-point" "Prefixes" "Accepted characters"
.It Sy Format type Ta Sy Prefixes Ta Sy Accepted characters
.It Decimal Ta none Ta 0123456789 .It Decimal Ta none Ta 0123456789
.It Hexadecimal Ta Li $ , 0x , 0X Ta 0123456789ABCDEF .It Hexadecimal Ta Li $ , 0x , 0X Ta 0123456789ABCDEF
.It Octal Ta Li & , 0o , 0O Ta 01234567 .It Octal Ta Li & , 0o , 0O Ta 01234567
.It Binary Ta Li % , 0b , 0B Ta 01 .It Binary Ta Li % , 0b , 0B Ta 01
.It Fixed-point Ta none Ta 01234.56789 .It Fixed-point Ta none Ta 01234.56789
.It Precise fixed-point Ta none Ta 12.34q8 .It Precise fixed-point Ta none Ta 12.34q8
.It Character constant Ta none Ta 'ABYZ' .It Character constant Ta none Ta 'A'
.It Game Boy graphics Ta Li \` Ta 0123 .It Game Boy graphics Ta Li \` Ta 0123
.El .El
.Pp .Pp
@@ -311,15 +332,14 @@ for information on charmaps, and
.Sx String expressions .Sx String expressions
for information on escape characters allowed in character constants. for information on escape characters allowed in character constants.
.Pp .Pp
The last one, Game Boy graphics, is quite interesting and useful. The last one, Game Boy graphics, expects up to eight digits between 0 and 3, corresponding to pixels' two-bit shade values.
After the backtick, 8 digits between 0 and 3 are expected, corresponding to pixel values. The resulting numeric value is the two bytes of tile data which would produce that row of pixels.
The resulting value is the two bytes of tile data that would produce that row of pixels.
For example, For example,
.Sq \`01012323 .Sq \`01012323
is equivalent to is equivalent to
.Sq $0F55 . .Sq $0F55 .
.Pp .Pp
You can also use symbols, which are implicitly replaced with their value. In place of a numeric literal, you can also use a numeric symbol's name, which is implicitly replaced with its value.
.Ss Operators .Ss Operators
You can use these operators in numeric expressions (listed from highest to lowest precedence): You can use these operators in numeric expressions (listed from highest to lowest precedence):
.Bl -column -offset indent "!= == <= >= < >" .Bl -column -offset indent "!= == <= >= < >"
@@ -327,8 +347,8 @@ You can use these operators in numeric expressions (listed from highest to lowes
.It Li \&( \&) Ta Grouping .It Li \&( \&) Ta Grouping
.It Li FUNC() Ta Built-in function call .It Li FUNC() Ta Built-in function call
.It Li ** Ta Exponentiation .It Li ** Ta Exponentiation
.It Li + - ~ \&! Ta Unary plus, minus (negation), complement (bitwise negation), and Boolean negation .It Li + - ~ \&! Ta Unary plus, unary minus (negation), complement (bitwise negation), and Boolean negation
.It Li * / % Ta Multiplication, division, and modulo (remainder) .It Li * / % Ta Multiplication, division (rounding down), and modulo (remainder)
.It Li << >> >>> Ta Bit shifts (left, sign-extended right, zero-extended right) .It Li << >> >>> Ta Bit shifts (left, sign-extended right, zero-extended right)
.It Li & \&| ^ Ta Bitwise AND/OR/XOR .It Li & \&| ^ Ta Bitwise AND/OR/XOR
.It Li + - Ta Addition and subtraction .It Li + - Ta Addition and subtraction
@@ -396,7 +416,7 @@ with a non-zero constant as either operand will be constant 1, even if the other
returns 1 if the operand was 0, and 0 otherwise. returns 1 if the operand was 0, and 0 otherwise.
Even a non-constant operand with any non-zero bits will return 0. Even a non-constant operand with any non-zero bits will return 0.
.Ss Integer functions .Ss Integer functions
Besides operators, there are also some functions which have more specialized uses. Besides operators, there are also some functions which have more specialized uses:
.Bl -column "BITWIDTH(n)" .Bl -column "BITWIDTH(n)"
.It Sy Name Ta Sy Operation .It Sy Name Ta Sy Operation
.It Fn HIGH n Ta Equivalent to Ql Po Ns Ar n No & $FF00 Pc >> 8 . .It Fn HIGH n Ta Equivalent to Ql Po Ns Ar n No & $FF00 Pc >> 8 .
@@ -408,12 +428,13 @@ delim $$
.Ar n . .Ar n .
Some useful formulas: Some useful formulas:
.Ic BITWIDTH Ns ( Ar n Ns )\ \-\ 1 .Ic BITWIDTH Ns ( Ar n Ns )\ \-\ 1
equals $\[lf] log sub 2 ( n ) \[rf]$, equals $\[lf] log sub 2 ( n ) \[rf]$;
.Ic BITWIDTH Ns Pq Ar n Ns \ \-\ 1 .Ic BITWIDTH Ns Pq Ar n Ns \ \-\ 1
equals $\[lc] log sub 2 ( n ) \[rc]$, and equals $\[lc] log sub 2 ( n ) \[rc]$; and
.No 32\ \-\ Ns Ic BITWIDTH Ns Pq Ar n .No 32\ \-\ Ns Ic BITWIDTH Ns Pq Ar n
equals $roman clz ( n )$. equals $roman clz ( n )$, the count of leading zero bits in the binary representation of
.It Fn TZCOUNT n Ta Returns $roman ctz ( n )$, the count of trailing zero bits at the end of the binary representation of .Ar n .
.It Fn TZCOUNT n Ta Returns $roman ctz ( n )$, the count of trailing zero bits in the binary representation of
.Ar n . .Ar n .
.El .El
.EQ .EQ
@@ -434,21 +455,25 @@ command-line option, and/or by
An individual fixed-point literal can specify its own precision, overriding the current default, by appending a An individual fixed-point literal can specify its own precision, overriding the current default, by appending a
.Dq q .Dq q
followed by the number of fractional bits: for example, followed by the number of fractional bits: for example,
.Ql 1234.5q8 .Ql 789.25q8
is equal to $0004d2_80 is equal to $000315_40
.EQ .EQ
delim $$ delim $$
.EN .EN
($= 1234.5 * 2 sup 8$). ($= 789.25 * 2 sup 8$).
.Pp .Pp
Since fixed-point values are still just integers, you can use them in normal integer expressions. Since fixed-point values are still just integers, you can use them in normal integer expressions.
You can easily truncate a fixed-point number into an integer by shifting it right by the number of fractional bits. You can easily truncate a fixed-point number into an integer by shifting it right by the number of fractional bits, or by dividing it by 1.0.
It follows that you can convert an integer to a fixed-point number by shifting it left that same amount. It follows that you can convert an integer to a fixed-point number by shifting it left that same amount, or by multiplying it by 1.0.
For example,
.Ql 123.0 / 1.0 == 123 ,
and
.Ql 123 * 1.0 == 123.0 .
.Pp .Pp
Note that the current number of fractional bits can be computed as Note that the current number of fractional bits can be computed as
.Ic TZCOUNT Ns Pq 1.0 . .Ic TZCOUNT Ns Pq 1.0 .
.Pp .Pp
The following functions are designed to operate with fixed-point numbers: The following functions are designed to operate with fixed-point numbers (which must be known constant):
.Bl -column -offset indent "ATAN2(y, x)" .Bl -column -offset indent "ATAN2(y, x)"
.It Sy Name Ta Sy Operation .It Sy Name Ta Sy Operation
.It Fn DIV x y Ta Fixed-point division .It Fn DIV x y Ta Fixed-point division
@@ -456,7 +481,7 @@ The following functions are designed to operate with fixed-point numbers:
.It Fn FMOD x y Ta Fixed-point modulo .It Fn FMOD x y Ta Fixed-point modulo
.It Fn POW x y Ta $x sup y$ .It Fn POW x y Ta $x sup y$
.It Fn LOG x y Ta Logarithm of $x$ to the base $y$ .It Fn LOG x y Ta Logarithm of $x$ to the base $y$
.It Fn ROUND x Ta Round $x$ to the nearest integer .It Fn ROUND x Ta Round $x$ half away from zero to the nearest integer
.It Fn CEIL x Ta Round $x$ up to the nearest integer .It Fn CEIL x Ta Round $x$ up to the nearest integer
.It Fn FLOOR x Ta Round $x$ down to the nearest integer .It Fn FLOOR x Ta Round $x$ down to the nearest integer
.It Fn SIN x Ta Sine of $x$ .It Fn SIN x Ta Sine of $x$
@@ -507,9 +532,7 @@ will produce a nonsensical (but technically correct) result:
The The
.Ic FMOD .Ic FMOD
function function
is used to get the remainder of the corresponding fixed-point division, so that is used to get the remainder of the corresponding fixed-point division.
.Ql MUL(DIV(x, y), y) + FMOD(x, y) == x
is always true.
The result has the same sign as the The result has the same sign as the
.Em dividend ; .Em dividend ;
this is the opposite of how the integer modulo operator this is the opposite of how the integer modulo operator
@@ -532,14 +555,15 @@ These functions are useful for automatic generation of various tables.
For example: For example:
.Bd -literal -offset indent .Bd -literal -offset indent
; Generate a table of 128 sine values ; Generate a table of 128 sine values
; from sin(0.0) to sin(0.5) excluded, ; from sin(0.0) included to sin(0.5) excluded,
; with amplitude scaled from [-1.0, 1.0] to [0.0, 128.0]. ; with amplitude scaled from [-1.0, 1.0] to [0.0, 128.0],
; then divided by 1.0 to round down to integer values.
FOR angle, 0.0, 0.5, 0.5 / 128 FOR angle, 0.0, 0.5, 0.5 / 128
db MUL(SIN(angle) + 1.0, 128.0 / 2) >> 16 db MUL(SIN(angle) + 1.0, 128.0 / 2) / 1.0
ENDR ENDR
.Ed .Ed
.Ss String expressions .Ss String expressions
The most basic string expression is any number of characters contained in double quotes The most basic string expression is a string literal: any number of characters contained in double quotes
.Pq Ql \&"for instance" . .Pq Ql \&"for instance" .
The backslash character The backslash character
.Ql \e .Ql \e
@@ -560,14 +584,14 @@ There are a number of escape sequences you can use within a string:
.It Ql \e0 Ta Null Pq ASCII $00 .It Ql \e0 Ta Null Pq ASCII $00
.El .El
.Pp .Pp
Multi-line strings are contained in triple quotes Multi-line string literals are contained in triple quotes
.Pq Ql \&"\&"\&"for instance""" . .Pq Ql \&"\&"\&"for instance""" .
Escape sequences work the same way in multi-line strings; however, literal newline characters will be included as-is, without needing to escape them with Escape sequences work the same way in multi-line strings; however, literal newline characters will be included as-is, without needing to escape them with
.Ql \er .Ql \er
or or
.Ql \en . .Ql \en .
.Pp .Pp
Raw strings are prefixed by a hash Raw string literals are prefixed by a hash
.Sq # . .Sq # .
Inside them, backslashes and braces are treated like regular characters, so they will not be expanded as macro arguments, interpolated symbols, or escape sequences. Inside them, backslashes and braces are treated like regular characters, so they will not be expanded as macro arguments, interpolated symbols, or escape sequences.
For example, the raw string For example, the raw string
@@ -599,7 +623,7 @@ and
is equivalent to is equivalent to
.Ql STRCMP("str", \&"ing") != 0 . .Ql STRCMP("str", \&"ing") != 0 .
.Pp .Pp
The following functions operate on string expressions, and return strings themselves. The following functions operate on string expressions, and return strings themselves:
.Bl -column "STRSLICE(str, start, stop)" .Bl -column "STRSLICE(str, start, stop)"
.It Sy Name Ta Sy Operation .It Sy Name Ta Sy Operation
.It Fn STRCAT strs... Ta Concatenates Ar strs . .It Fn STRCAT strs... Ta Concatenates Ar strs .
@@ -610,7 +634,7 @@ in uppercase.
.Pq Ql A-Z .Pq Ql A-Z
in lowercase. in lowercase.
.It Fn STRSLICE str start stop Ta Returns a substring of Ar str No starting at Ar start No and ending at Ar stop No (exclusive). If Ar stop No is not specified, the substring continues to the end of Ar str . .It Fn STRSLICE str start stop Ta Returns a substring of Ar str No starting at Ar start No and ending at Ar stop No (exclusive). If Ar stop No is not specified, the substring continues to the end of Ar str .
.It Fn STRRPL str old new Ta Returns Ar str No with each non-overlapping occurrence of the substring Ar old No replaced with Ar new . .It Fn STRRPL str old new Ta Returns Ar str No with each occurrence of the substring Ar old No replaced with Ar new .
.It Fn STRFMT fmt args... Ta Returns the string Ar fmt No with each .It Fn STRFMT fmt args... Ta Returns the string Ar fmt No with each
.Ql %spec .Ql %spec
pattern replaced by interpolating the format pattern replaced by interpolating the format
@@ -620,11 +644,15 @@ with its corresponding argument in
.Ar args .Ar args
.Pq So %% Sc is replaced by the So % Sc character . .Pq So %% Sc is replaced by the So % Sc character .
.It Fn STRCHAR str idx Ta Returns the substring of Ar str No for the charmap entry at Ar idx No with the current charmap . Pq Ar idx No counts charmap entries, not characters. .It Fn STRCHAR str idx Ta Returns the substring of Ar str No for the charmap entry at Ar idx No with the current charmap . Pq Ar idx No counts charmap entries, not characters.
.El
.Pp
The following functions take varying operands, and return strings:
.Bl -column "READFILE(name, max)"
.It Fn REVCHAR vals... Ta Returns the string that is mapped to Ar vals No with the current charmap. If there is no unique charmap entry for Ar vals Ns , an error occurs. .It Fn REVCHAR vals... Ta Returns the string that is mapped to Ar vals No with the current charmap. If there is no unique charmap entry for Ar vals Ns , an error occurs.
.It Fn READFILE name max Ta Returns the contents of the file Ar name No as a string. Reads up to Ar max No bytes, or the entire contents if Ar max No is not specified. If the file isn't found in the current directory, the include-path list passed to Xr rgbasm 1 Ap s Fl I No option on the command line will be searched. .It Fn READFILE name max Ta Returns the contents of the file Ar name No as a string. Reads up to Ar max No bytes, or the entire contents if Ar max No is not specified. If the file isn't found in the current directory, the include-path list passed to Xr rgbasm 1 Ap s Fl I No option on the command line will be searched.
.El .El
.Pp .Pp
The following functions operate on string expressions, but return integers. The following functions operate on string expressions, but return integers:
.Bl -column "STRRFIND(str, sub)" .Bl -column "STRRFIND(str, sub)"
.It Sy Name Ta Sy Operation .It Sy Name Ta Sy Operation
.It Fn STRLEN str Ta Returns the number of characters in Ar str . .It Fn STRLEN str Ta Returns the number of characters in Ar str .
@@ -668,7 +696,8 @@ and
being equivalent to being equivalent to
.Ql dw 50, 53, $20ac . .Ql dw 50, 53, $20ac .
.Pp .Pp
Any characters in a string without defined mappings will be copied directly, using the source file's encoding of characters to bytes. Character mappings are matched greedily, so the longest applicable one will be mapped in a string.
Any characters in the string without defined mappings will be copied directly, using the source file's encoding of characters to bytes.
.Pp .Pp
It is possible to create multiple character maps and then switch between them as desired. It is possible to create multiple character maps and then switch between them as desired.
This can be used to encode debug information in ASCII and use a different encoding for other purposes, for example. This can be used to encode debug information in ASCII and use a different encoding for other purposes, for example.
@@ -716,9 +745,14 @@ The result may be constant if
is able to compute it. is able to compute it.
.It Fn SECTION symbol Ta Returns the name of the section that .It Fn SECTION symbol Ta Returns the name of the section that
.Ar symbol .Ar symbol
is in. is defined in.
.Ar symbol .Ar symbol
must have been defined already. must have been defined already.
If
.Ar arg
is the symbol
.Ic @ ,
this function returns the name of the current section.
.It Fn SIZEOF arg Ta If .It Fn SIZEOF arg Ta If
.Ar arg .Ar arg
is a string, this function returns the size of the section named is a string, this function returns the size of the section named
@@ -741,7 +775,7 @@ The result is not constant, since only RGBLINK can compute its value.
.El .El
.Sh SECTIONS .Sh SECTIONS
Before you can start writing code, you must define a section. Before you can start writing code, you must define a section.
This tells the assembler what kind of information follows and, if it is code, where to put it. This tells the assembler what kind of information follows and where to put it.
.Pp .Pp
.Dl SECTION Ar name , type .Dl SECTION Ar name , type
.Dl SECTION Ar name , type , options .Dl SECTION Ar name , type , options
@@ -749,9 +783,9 @@ This tells the assembler what kind of information follows and, if it is code, wh
.Dl SECTION Ar name , type Ns Bo Ar addr Bc , Ar options .Dl SECTION Ar name , type Ns Bo Ar addr Bc , Ar options
.Pp .Pp
.Ar name .Ar name
is a string enclosed in double quotes, and can be a new name or the name of an existing section. is a string enclosed in double quotes, which is the name of the section.
If the type doesn't match, an error occurs. If the type doesn't match, an error occurs.
All other sections must have a unique name, even in different source files, or the linker will treat it as an error. Each section must have a unique name, even across different source files, or the linker will treat it as an error.
.Pp .Pp
Possible section Possible section
.Ar type Ns s .Ar type Ns s
@@ -776,8 +810,6 @@ can range from
.Ad $4000 .Ad $4000
to to
.Ad $7FFF . .Ad $7FFF .
.Ar bank
can range from 1 to 511.
Becomes an alias for Becomes an alias for
.Ic ROM0 .Ic ROM0
if tiny ROM mode is enabled in the linker. if tiny ROM mode is enabled in the linker.
@@ -797,8 +829,6 @@ can range from
.Ad $A000 .Ad $A000
to to
.Ad $BFFF . .Ad $BFFF .
.Ar bank
can range from 0 to 15.
.It Ic WRAM0 .It Ic WRAM0
A general-purpose RAM section. A general-purpose RAM section.
.Ar addr .Ar addr
@@ -839,12 +869,18 @@ to
.Ad $FFFE . .Ad $FFFE .
.El .El
.Pp .Pp
Since RGBDS produces ROMs, code and data can only be placed in RGBDS produces ROMs, which means that code and data can only be placed in
.Ic ROM0 .Ic ROM0
and and
.Ic ROMX .Ic ROMX
sections. sections.
To put some in RAM, have it stored in ROM, and copy it to RAM. The other RAM section types are for statically allocated labels.
If you need code or data in RAM, you will need to copy it from ROM to RAM yourself.
See
.Sx RAM code
for an example of how to conveniently do that with a
.Ic LOAD
block.
.Pp .Pp
.Ar option Ns s are comma-separated and may include: .Ar option Ns s are comma-separated and may include:
.Bl -tag -width Ds .Bl -tag -width Ds
@@ -901,7 +937,7 @@ creating it if it doesn't already exist.
It can end up in any ROM bank. It can end up in any ROM bank.
Code and data may follow. Code and data may follow.
.It .It
If it is needed, the the base address of the section can be specified: If it is needed, the base address of the section can be specified:
.Bd -literal -offset indent .Bd -literal -offset indent
SECTION "Cool Stuff", ROMX[$4567] SECTION "Cool Stuff", ROMX[$4567]
.Ed .Ed
@@ -956,13 +992,12 @@ Function:
ld [wAnswer], a ld [wAnswer], a
.Ed .Ed
.Ss RAM code .Ss RAM code
Sometimes you want to have some code in RAM. Sometimes you want to have some code (or data) in RAM, e.g. for self-modifying code.
But then you can't simply put it in a RAM section, you have to store it in ROM and copy it to RAM at some point. But you can't just put it directly in a RAM section; you have to store it in ROM and copy it to RAM at some point.
.Pp This means that the code will be executed at a different address range than where it's defined, which can be inconvenient for references to labels within that code.
This means the code (or data) will not be stored in the place it gets executed. This situation is what
Luckily,
.Ic LOAD .Ic LOAD
blocks are the perfect solution to that. blocks are designed for.
Here's an example of how to use them: Here's an example of how to use them:
.Bd -literal -offset indent .Bd -literal -offset indent
SECTION "LOAD example", ROMX SECTION "LOAD example", ROMX
@@ -1047,7 +1082,7 @@ However, a
.Ic UNION .Ic UNION
only works within a single file, so it can't be used e.g. to define temporary variables across several files, all of which use the same statically allocated memory. only works within a single file, so it can't be used e.g. to define temporary variables across several files, all of which use the same statically allocated memory.
Unionized sections solve this problem. Unionized sections solve this problem.
To declare an unionized section, add a To declare a unionized section, add a
.Ic UNION .Ic UNION
keyword after the keyword after the
.Ic SECTION .Ic SECTION
@@ -1085,9 +1120,9 @@ Different declarations of the same unionized section are not appended, but inste
.Sx Allocating overlapping spaces in RAM . .Sx Allocating overlapping spaces in RAM .
Similarly, the size of an unionized section is the largest of all its declarations. Similarly, the size of an unionized section is the largest of all its declarations.
.Ss Section fragments .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. Section fragments are sections with a small twist: when several fragments with the same name are encountered, they are concatenated into one section instead of producing an error, even across multiple object files.
This works within the same file (paralleling the behavior "plain" sections has in previous versions), but also across object files. This works within the same file (paralleling the behavior "plain" sections has in previous versions), but also across object files.
To declare an section fragment, add a To declare a section fragment, add a
.Ic FRAGMENT .Ic FRAGMENT
keyword after the keyword after the
.Ic SECTION .Ic SECTION
@@ -1556,6 +1591,8 @@ in the C programming language.
This expansion is disabled in a few contexts: This expansion is disabled in a few contexts:
.Ql DEF(name) , .Ql DEF(name) ,
.Ql DEF name EQU/=/EQUS/etc ... , .Ql DEF name EQU/=/EQUS/etc ... ,
.Ql REDEF name EQU/=/EQUS/etc ... ,
.Ql FOR name, ... ,
.Ql PURGE name , .Ql PURGE name ,
and and
.Ql MACRO name .Ql MACRO name
@@ -1705,21 +1742,24 @@ error: Undefined symbol "LabelA"
at c.asm(2) at c.asm(2)
Linking failed with 1 error Linking failed with 1 error
.Ed .Ed
.Pp
Note also that only exported symbols will appear in symbol and map files produced by
.Xr rgblink 1 .
.Ss Purging symbols .Ss Purging symbols
.Ic PURGE .Ic PURGE
allows you to completely remove a symbol from the symbol table, as if it had never been defined. allows you to completely remove a symbol from the symbol table, as if it had never been defined.
.Bd -literal -offset indent
DEF value EQU 42
PURGE value
DEF value EQUS "I'm a string now"
ASSERT DEF(value)
PURGE value
ASSERT !DEF(value)
.Ed
.Pp
Be Be
.Em very .Em very
careful when purging symbols, especially labels, because it could result in unpredictable errors if something depends on the missing symbol (for example, expressions the linker needs to calculate). careful when purging symbols that have been referenced in section data, or that have been exported, because it could result in unpredictable errors if something depends on the missing symbol (for example, expressions the linker needs to calculate).
.Bd -literal -offset indent Purging labels at all is
DEF Kamikaze EQUS "I don't want to live anymore" .Em not
AOLer: DB "Me too lol" recommended.
PURGE Kamikaze, AOLer
ASSERT !DEF(Kamikaze) && !DEF(AOLer)
.Ed
.Pp .Pp
String constants are not expanded within the symbol names. String constants are not expanded within the symbol names.
.Ss Predeclared symbols .Ss Predeclared symbols
@@ -1729,6 +1769,7 @@ The following symbols are defined by the assembler:
.It Dv @ Ta Ic EQU Ta PC value (essentially, the current memory address) .It Dv @ Ta Ic EQU Ta PC value (essentially, the current memory address)
.It Dv . Ta Ic EQUS Ta The current global label scope .It Dv . Ta Ic EQUS Ta The current global label scope
.It Dv .. Ta Ic EQUS Ta The current local label scope .It Dv .. Ta Ic EQUS Ta The current local label scope
.It Dv __SCOPE__ Ta Ic EQUS Ta The innermost current label scope level (empty, ".", or "..")
.It Dv _RS Ta Ic = Ta _RS Counter .It Dv _RS Ta Ic = Ta _RS Counter
.It Dv _NARG Ta Ic EQU Ta Number of arguments passed to macro, updated by Ic SHIFT .It Dv _NARG Ta Ic EQU Ta Number of arguments passed to macro, updated by Ic SHIFT
.It Dv __ISO_8601_LOCAL__ Ta Ic EQUS Ta ISO 8601 timestamp (local) .It Dv __ISO_8601_LOCAL__ Ta Ic EQUS Ta ISO 8601 timestamp (local)
@@ -1929,7 +1970,7 @@ and
.Sq wBonus . .Sq wBonus .
Thus, keep in mind that Thus, keep in mind that
.Ql ld [wHealth], a .Ql ld [wHealth], a
assembles to the exact same thing as assembles to the exact same instruction as
.Ql ld [wName], a . .Ql ld [wName], a .
.Pp .Pp
This whole union's total size is 20 bytes, the size of the largest block (the first one, containing This whole union's total size is 20 bytes, the size of the largest block (the first one, containing
@@ -2532,9 +2573,9 @@ The stack's number of entries is limited only by the amount of memory in your ma
.Ic PUSHO .Ic PUSHO
can also take a comma-separated list of options, to push the current set and apply the argument set at the same time: can also take a comma-separated list of options, to push the current set and apply the argument set at the same time:
.Bd -literal -offset indent .Bd -literal -offset indent
PUSHO b.X, g.oOX PUSHO b.#, g.oO#
DB %..XXXX.. DB %..####..
DW `..ooOOXX DW `..ooOO##
POPO POPO
.Ed .Ed
.Ss Excluding locations from backtraces .Ss Excluding locations from backtraces
+4 -6
View File
@@ -1,18 +1,16 @@
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd September 30, 2025 .Dd August 1, 2026
.Dt RGBDS 5 .Dt RGBDS 5
.Os .Os
.Sh NAME .Sh NAME
.Nm rgbds .Nm rgbds
.Nd object file format documentation .Nd object file format documentation
.Sh DESCRIPTION .Sh DESCRIPTION
This is the description of the object files used by This is the description of the RGB object file format that is output by
.Xr rgbasm 1 .Xr rgbasm 1
and and read by
.Xr rgblink 1 . .Xr rgblink 1 .
.Em Please note that the specification is not stable yet.
RGBDS is still in active development, and some new features require adding more information to the object file, or modifying some fields, both of which break compatibility with older versions.
.Sh FILE STRUCTURE .Sh FILE STRUCTURE
The following types are used: The following types are used:
.Pp .Pp
@@ -94,7 +92,7 @@ If the node is not a REPT node...
.Pp .Pp
.Bl -tag -width Ds -compact .Bl -tag -width Ds -compact
.It Cm STRING Ar Name .It Cm STRING Ar Name
The node's name: either a file name, or the macro's name prefixes by its definition's file name The node's name: either a file name, or the macro's name prefixed by its definition's file name
.Pq e.g. Ql src/includes/defines.asm::error . .Pq e.g. Ql src/includes/defines.asm::error .
.El .El
.It Cm ELSE .It Cm ELSE
+5 -1
View File
@@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd September 30, 2025 .Dd August 1, 2026
.Dt RGBDS 7 .Dt RGBDS 7
.Os .Os
.Sh NAME .Sh NAME
@@ -74,4 +74,8 @@ organization.
2022-05-17: The 2022-05-17: The
.Lk https://rgbds.gbdev.io rgbds.gbdev.io .Lk https://rgbds.gbdev.io rgbds.gbdev.io
website for RGBDS documentation and downloads is published. website for RGBDS documentation and downloads is published.
.It
2025-10-31: RGBDS reaches version 1.0.0 and starts adhering to
.Lk https://semver.org/ semantic versioning
("semver").
.El .El
+84 -20
View File
@@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd September 30, 2025 .Dd August 1, 2026
.Dt RGBFIX 1 .Dt RGBFIX 1
.Os .Os
.Sh NAME .Sh NAME
@@ -39,23 +39,67 @@ Developers are advised to fill those fields with 0x00 bytes in their source code
.Nm , .Nm ,
and to have already populated whichever fields they don't specify using and to have already populated whichever fields they don't specify using
.Nm . .Nm .
.Pp .Sh ARGUMENTS
The input .Nm
.Ar file accepts the usual short and long options, such as
can be a path to a file, or .Fl V
.Cm \- and
to read from standard input. .Fl -version .
.Pp Options later in the command line override those set earlier, except for when duplicate options are considered an error.
Note that options can be abbreviated as long as the abbreviation is unambiguous: Options can be abbreviated as long as the abbreviation is unambiguous:
.Fl \-verb
is
.Fl \-verbose ,
but
.Fl \-ver .Fl \-ver
is
.Fl \-version ,
but
.Fl \-v
is invalid because it could also be is invalid because it could also be
.Fl \-version . .Fl \-validate .
Options later in the command line override those set earlier. .Pp
Accepted options are as follows: Unless otherwise noted, passing
.Ql -
(a single dash) as a file name makes
.Nm
use standard input (for input files) or standard output (for output files).
To suppress this behavior, and open a file in the current directory actually called
.Ql - ,
pass
.Ql ./-
instead.
Using standard input or output for more than one file in a single command may produce unexpected results.
.Pp
.Nm
accepts decimal, hexadecimal, octal, and binary for numeric option arguments.
Decimal numbers are written as usual; hexadecimal numbers must be prefixed with either
.Ql $
or
.Ql 0x ;
octal numbers must be prefixed with either
.Ql &
or
.Ql 0o ;
and binary numbers must be prefixed with either
.Ql %
or
.Ql 0b .
(The prefixes
.Ql $
and
.Ql &
will likely need escaping or quoting to avoid being interpreted by the shell.)
Leading zeros (after the base prefix, if any) are accepted, and letters are not case-sensitive.
For example, all of these are equivalent:
.Ql 42 ,
.Ql 042 ,
.Ql 0x2A ,
.Ql 0X2A ,
.Ql 0x2a ,
.Ql &52 ,
.Ql 0o52 ,
.Ql 0O052 ,
.Ql 0b00101010 ,
.Ql 0B101010 .
.Pp
The following options are accepted:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Fl C , Fl \-color-only .It Fl C , Fl \-color-only
Set the Game Boy Color\(enonly flag Set the Game Boy Color\(enonly flag
@@ -108,6 +152,9 @@ Trash the global checksum.
.El .El
.It Fl h , Fl \-help .It Fl h , Fl \-help
Print help text for the program and exit. Print help text for the program and exit.
(Help text wraps to the value of the
.Dv COLUMNS
environment variable if that is defined as nonzero; or else to the console window width if output is to a TTY.)
.It Fl i Ar game_id , Fl \-game-id Ar game_id .It Fl i Ar game_id , Fl \-game-id Ar game_id
Set the game ID string Set the game ID string
.Pq Ad 0x13F Ns \(en Ns Ad 0x142 .Pq Ad 0x13F Ns \(en Ns Ad 0x142
@@ -199,6 +246,23 @@ See the
section for a list of warnings. section for a list of warnings.
.It Fl w .It Fl w
Disable all warning output, even when turned into errors. Disable all warning output, even when turned into errors.
.It @ Ns Ar at_file
Read more options and arguments from a file, as if its contents were given on the command line.
Arguments are separated by whitespace or newlines.
Lines starting with a hash sign
.Pq Ql #
are considered comments and ignored.
.Pp
No shell processing is performed, such as wildcard or variable expansion.
There is no support for escaping or quoting whitespace to be included in arguments.
The standard
.Ql --
to stop option processing also disables at-file processing.
Note that while
.Ql --
can be used
.Em inside
an at-file, it only disables option processing within that at-file, and processing continues in the parent scope.
.El .El
.Sh DIAGNOSTICS .Sh DIAGNOSTICS
Warnings are diagnostic messages that indicate possibly erroneous behavior that does not necessarily compromise the header-fixing process. Warnings are diagnostic messages that indicate possibly erroneous behavior that does not necessarily compromise the header-fixing process.
@@ -212,7 +276,7 @@ to prevent turning all warnings into errors.
.It Fl Werror= .It Fl Werror=
Make the specified warning or meta warning into an error. Make the specified warning or meta warning into an error.
A warning's name is appended A warning's name is appended
.Pq example: Fl Werror=overwrite , .Pq example: Fl Werror=obsolete ,
and this warning is implicitly enabled and turned into an error. and this warning is implicitly enabled and turned into an error.
This can be negated as This can be negated as
.Fl Wno-error= .Fl Wno-error=
@@ -234,10 +298,10 @@ Enables literally every warning.
.El .El
.Pp .Pp
The following warnings are actual warning flags; with each description, the corresponding warning flag is included. The following warnings are actual warning flags; with each description, the corresponding warning flag is included.
Note that each of these flag also has a negation (for example, Note that each of these flags also has a negation (for example,
.Fl Wtruncation .Fl Wobsolete
enables the warning that enables the warning that
.Fl Wno-truncation .Fl Wno-obsolete
disables; and disables; and
.Fl Wall .Fl Wall
enables every warning that enables every warning that
+92 -76
View File
@@ -2,7 +2,7 @@
.\" .\"
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd September 30, 2025 .Dd August 1, 2026
.Dt RGBGFX 1 .Dt RGBGFX 1
.Os .Os
.Sh NAME .Sh NAME
@@ -13,6 +13,7 @@
.Op Fl CmhOuVwXYZ .Op Fl CmhOuVwXYZ
.Op Fl v Op Fl v No ... .Op Fl v Op Fl v No ...
.Op Fl a Ar attrmap | Fl A .Op Fl a Ar attrmap | Fl A
.Op Fl B Ar color
.Op Fl b Ar base_ids .Op Fl b Ar base_ids
.Op Fl c Ar pal_spec .Op Fl c Ar pal_spec
.Op Fl \-color Ar when .Op Fl \-color Ar when
@@ -43,7 +44,13 @@ is to divide the input PNG into 8\[tmu]8 pixel
convert each of those squares into 1bpp or 2bpp tile data, and save all of the tile data in a file. convert each of those squares into 1bpp or 2bpp tile data, and save all of the tile data in a file.
It also has options to generate a tile map, attribute map, and/or palette set as well; more on that and how the conversion process can be tweaked below. It also has options to generate a tile map, attribute map, and/or palette set as well; more on that and how the conversion process can be tweaked below.
.Sh ARGUMENTS .Sh ARGUMENTS
Note that options can be abbreviated as long as the abbreviation is unambiguous: .Nm
accepts the usual short and long options, such as
.Fl V
and
.Fl -version .
Options later in the command line override those set earlier, except for when duplicate options are considered an error.
Options can be abbreviated as long as the abbreviation is unambiguous:
.Fl \-verb .Fl \-verb
is is
.Fl \-verbose , .Fl \-verbose ,
@@ -52,26 +59,6 @@ but
is invalid because it could also be is invalid because it could also be
.Fl \-version . .Fl \-version .
.Pp .Pp
.Nm
accepts decimal, binary, and hexadecimal numbers in option arguments.
Decimal numbers are written as usual; binary numbers must be prefixed with either
.Ql %
or
.Ql 0b ,
and hexadecimal numbers must be prefixed with either
.Ql $
(which will likely need escaping or quoting to avoid being interpreted by the shell), or
.Ql 0x .
Leading zeros (after the base prefix, if any) are accepted, and letters are not case-sensitive.
All of these are equivalent:
.Ql 42 ,
.Ql 042 ,
.Ql 0b00101010 ,
.Ql 0B101010 ,
.Ql 0x2A ,
.Ql 0X2A ,
.Ql 0x2a .
.Pp
Unless otherwise noted, passing Unless otherwise noted, passing
.Ql - .Ql -
(a single dash) as a file name makes (a single dash) as a file name makes
@@ -82,7 +69,39 @@ To suppress this behavior, and open a file in the current directory actually cal
pass pass
.Ql ./- .Ql ./-
instead. instead.
Using standard input or output more than once in a single command will likely produce unexpected results. Using standard input or output for more than one file in a single command may produce unexpected results.
.Pp
.Nm
accepts decimal, hexadecimal, octal, and binary for numeric option arguments.
Decimal numbers are written as usual; hexadecimal numbers must be prefixed with either
.Ql $
or
.Ql 0x ;
octal numbers must be prefixed with either
.Ql &
or
.Ql 0o ;
and binary numbers must be prefixed with either
.Ql %
or
.Ql 0b .
(The prefixes
.Ql $
and
.Ql &
will likely need escaping or quoting to avoid being interpreted by the shell.)
Leading zeros (after the base prefix, if any) are accepted, and letters are not case-sensitive.
For example, all of these are equivalent:
.Ql 42 ,
.Ql 042 ,
.Ql 0x2A ,
.Ql 0X2A ,
.Ql 0x2a ,
.Ql &52 ,
.Ql 0o52 ,
.Ql 0O052 ,
.Ql 0b00101010 ,
.Ql 0B101010 .
.Pp .Pp
The following options are accepted: The following options are accepted:
.Bl -tag -width Ds .Bl -tag -width Ds
@@ -125,9 +144,17 @@ Set the base IDs for tile map output.
should be one or two numbers between 0 and 255, separated by a comma; they are for bank 0 and bank 1 respectively. should be one or two numbers between 0 and 255, separated by a comma; they are for bank 0 and bank 1 respectively.
Both default to 0. Both default to 0.
.It Fl C , Fl \-color-curve .It Fl C , Fl \-color-curve
When generating palettes, use a color curve mimicking the Game Boy Color's screen. Modifies the color palettes
The resulting colors may look closer to the input image's .Pq whether they are generated from the input image or taken from an input palette specification
.Sy on hardware and accurate emulators . with a color curve mimicking the Game Boy Color's screen.
This adjusts the
.Em absolute
RGB color values so that the
.Em perceived
colors, when displayed on Game Boy Color hardware
.Pq or an emulator with an accurate display filter ,
will look like the original colors as displayed on a backlit computer screen.
Note that GBC displays can look very different depending on the ambient light and their exact hardware model, so this color curve is only a "best effort".
.It Fl c Ar pal_spec , Fl \-colors Ar pal_spec .It Fl c Ar pal_spec , Fl \-colors Ar pal_spec
Use the specified color palettes instead of having Use the specified color palettes instead of having
.Nm .Nm
@@ -230,6 +257,9 @@ Set the bit depth of the output tile data, in bits per pixel (bpp), either 1 or
This changes how tile data is output, and the maximum number of colors per palette (2 and 4 respectively). This changes how tile data is output, and the maximum number of colors per palette (2 and 4 respectively).
.It Fl h , Fl \-help .It Fl h , Fl \-help
Print help text for the program and exit. Print help text for the program and exit.
(Help text wraps to the value of the
.Dv COLUMNS
environment variable if that is defined as nonzero; or else to the console window width if output is to a TTY.)
.It Fl i Ar input_tiles , Fl \-input-tileset Ar input_tiles .It Fl i Ar input_tiles , Fl \-input-tileset Ar input_tiles
Use the specified input tiles in addition to having Use the specified input tiles in addition to having
.Nm .Nm
@@ -287,8 +317,10 @@ This is useful for example if the input image is a sheet of some sort, and you w
The default is to process the whole image as-is. The default is to process the whole image as-is.
.Pp .Pp
.Ar slice .Ar slice
must be two number pairs, separated by a colon. must be formatted as
The numbers must be separated by commas; space is allowed around all punctuation. .Ql Ar X , Ns Ar Y : Ns Ar W , Ns Ar H :
two comma-separated number pairs, separated by a colon.
Whitespace is allowed around all punctuation.
The first number pair specifies the X and Y coordinates of the top-left pixel that will be processed (anything above it or to its left will be ignored). The first number pair specifies the X and Y coordinates of the top-left pixel that will be processed (anything above it or to its left will be ignored).
The second number pair specifies how many tiles to process horizontally and vertically, respectively. The second number pair specifies how many tiles to process horizontally and vertically, respectively.
.Pp .Pp
@@ -469,69 +501,53 @@ Implies
.It Fl Z , Fl \-columns .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). 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. This primarily affects tile map and attribute map output, although it may also change generated tile data and palettes.
.It @ Ns Ar at_file
Read more options and arguments from a file, as if its contents were given on the command line.
Arguments are separated by whitespace or newlines.
Lines starting with a hash sign
.Pq Ql #
are considered comments and ignored.
.Pp
No shell processing is performed, such as wildcard or variable expansion.
There is no support for escaping or quoting whitespace to be included in arguments.
The standard
.Ql --
to stop option processing also disables at-file processing.
Note that while
.Ql --
can be used
.Em inside
an at-file, it only disables option processing within that at-file, and processing continues in the parent scope.
.Pp
See
.Sx At-files
below for an explanation of how this can be useful.
.El .El
.Ss At-files .Ss At-files
In a given project, many images are to be converted with different flags. In a given project, many images are to be converted with different flags.
The traditional way of solving this problem has been to specify the different flags for each image in the Makefile / build script; this can be inconvenient, as it centralizes all those flags away from the images they concern. The traditional way of solving this problem has been to specify the different flags for each image in the Makefile or build script; this can be inconvenient, as it centralizes all those flags away from the images they concern.
.Pp .Pp
To avoid these drawbacks, To avoid these drawbacks, you can use
.Nm
supports
.Dq at-files : .Dq at-files :
any command-line argument that begins with an at sign any command-line argument that begins with an at sign
.Pq Ql @ .Pq Ql @
is interpreted as one. is interpreted as one, as documented above.
The rest of the argument (without the @, that is) is interpreted as the path to a file, whose contents are interpreted as if given on the command line.
At-files can be stored right next to the corresponding image, for example: At-files can be stored right next to the corresponding image, for example:
.Pp .Pp
.Dl $ rgbgfx -o image.2bpp -t image.tilemap @image.flags image.png .Dl $ rgbgfx -o image.2bpp -t image.tilemap @image.flags image.png
.Pp .Pp
This will read additional flags from file This will read additional flags from the file
.Ql image.flags , .Ql image.flags ,
which could contains for example which could contain, for example,
.Ql -b 128 .Ql -b 128
to specify a base offset for the image's tiles. to specify a base offset for the image's tiles.
The above command could be generated from the following The above command could be generated from the following
.Xr make 1 .Xr make 1
rule, for example: rule:
.Bd -literal -offset indent .Bd -literal -offset indent
%.2bpp %.tilemap: %.flags %.png %.2bpp %.tilemap: %.flags %.png
rgbgfx -o $*.2bpp -t $*.tilemap @$*.flags $*.png rgbgfx -o $*.2bpp -t $*.tilemap @$*.flags $*.png
.Ed .Ed
.Pp
Since the contents of at-files are interpreted by
.Nm ,
.Sy no shell processing is performed ;
for example, shell variables are not expanded
.Ql ( $PWD ,
.Ql %WINDIR% ,
etc.).
In at-files, lines that are empty or contain only whitespace are ignored; lines that begin with a hash sign
.Pq Ql # ,
optionally preceded by whitespace, are considered comments and also ignored.
Each line can contain any number of arguments, which are separated by whitespace.
.Pq \&No quoting feature to prevent this is provided.
.Pp
Note that a leading
.Ql @
has no special meaning on option arguments, and that the standard
.Ql --
to stop option processing also disables at-file processing.
For example, the following command line reads command-line options from
.Ql tilesets/town.flags
then
.Ql tilesets.flags ,
but processes
.Ql @tilesets/town.png
as the input image and outputs tile data to
.Ql @tilesets/town.2bpp :
.Pp
.Dl $ rgbgfx -o @tilesets/town.2bpp @tilesets/town.flags @tilesets.flags -- @tilesets/town.png
.Pp
At-files can also specify the input image directly, and call for more at-files, both using the regular syntax.
Note that while
.Ql --
can be used in an at-file (with identical semantics), it is only effective inside of it\(emnormal option processing continues in the parent scope.
.Sh PALETTE SPECIFICATION FORMATS .Sh PALETTE SPECIFICATION FORMATS
The following formats are supported: The following formats are supported:
.Bl -tag -width Ds .Bl -tag -width Ds
@@ -769,7 +785,7 @@ to prevent turning all warnings into errors.
.It Fl Werror= .It Fl Werror=
Make the specified warning or meta warning into an error. Make the specified warning or meta warning into an error.
A warning's name is appended A warning's name is appended
.Pq example: Fl Werror=embedded , .Pq example: Fl Werror=obsolete ,
and this warning is implicitly enabled and turned into an error. and this warning is implicitly enabled and turned into an error.
This can be negated as This can be negated as
.Fl Wno-error= .Fl Wno-error=
@@ -791,10 +807,10 @@ Enables literally every warning.
.El .El
.Pp .Pp
The following warnings are actual warning flags; with each description, the corresponding warning flag is included. The following warnings are actual warning flags; with each description, the corresponding warning flag is included.
Note that each of these flag also has a negation (for example, Note that each of these flags also has a negation (for example,
.Fl Wtrim-nonempty .Fl Wobsolete
enables the warning that enables the warning that
.Fl Wno-trim-nonempty .Fl Wno-obsolete
disables; and disables; and
.Fl Wall .Fl Wall
enables every warning that enables every warning that
+92 -20
View File
@@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd September 30, 2025 .Dd August 1, 2026
.Dt RGBLINK 1 .Dt RGBLINK 1
.Os .Os
.Sh NAME .Sh NAME
@@ -48,14 +48,14 @@ Also, if your ROM is designed for a monochrome Game Boy, you can make sure that
option, which implies option, which implies
.Fl w .Fl w
but also prohibits the use of banked VRAM. but also prohibits the use of banked VRAM.
.Pp .Sh ARGUMENTS
The input .Nm
.Ar file accepts the usual short and long options, such as
can be a path to a file, or .Fl V
.Cm \- and
to read from standard input. .Fl -version .
.Pp Options later in the command line override those set earlier, except for when duplicate options are considered an error.
Note that options can be abbreviated as long as the abbreviation is unambiguous: Options can be abbreviated as long as the abbreviation is unambiguous:
.Fl \-verb .Fl \-verb
is is
.Fl \-verbose , .Fl \-verbose ,
@@ -63,7 +63,52 @@ but
.Fl \-ver .Fl \-ver
is invalid because it could also be is invalid because it could also be
.Fl \-version . .Fl \-version .
The arguments are as follows: .Pp
Unless otherwise noted, passing
.Ql -
(a single dash) as a file name makes
.Nm
use standard input (for input files) or standard output (for output files).
To suppress this behavior, and open a file in the current directory actually called
.Ql - ,
pass
.Ql ./-
instead.
Using standard input or output for more than one file in a single command may produce unexpected results.
.Pp
.Nm
accepts decimal, hexadecimal, octal, and binary for numeric option arguments.
Decimal numbers are written as usual; hexadecimal numbers must be prefixed with either
.Ql $
or
.Ql 0x ;
octal numbers must be prefixed with either
.Ql &
or
.Ql 0o ;
and binary numbers must be prefixed with either
.Ql %
or
.Ql 0b .
(The prefixes
.Ql $
and
.Ql &
will likely need escaping or quoting to avoid being interpreted by the shell.)
Leading zeros (after the base prefix, if any) are accepted, and letters are not case-sensitive.
For example, all of these are equivalent:
.Ql 42 ,
.Ql 042 ,
.Ql 0x2A ,
.Ql 0X2A ,
.Ql 0x2a ,
.Ql &52 ,
.Ql 0o52 ,
.Ql 0O052 ,
.Ql 0b00101010 ,
.Ql 0B101010 .
.Pp
The following options are accepted:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Fl B Ar param , Fl \-backtrace Ar param .It Fl B Ar param , Fl \-backtrace Ar param
Configures how location backtraces are printed if warnings or errors occur. Configures how location backtraces are printed if warnings or errors occur.
@@ -107,6 +152,9 @@ This option automatically enables
.Fl w . .Fl w .
.It Fl h , Fl \-help .It Fl h , Fl \-help
Print help text for the program and exit. Print help text for the program and exit.
(Help text wraps to the value of the
.Dv COLUMNS
environment variable if that is defined as nonzero; or else to the console window width if output is to a TTY.)
.It Fl l Ar linker_script , Fl \-linkerscript Ar linker_script .It Fl l Ar linker_script , Fl \-linkerscript Ar linker_script
Specify a linker script file that tells the linker how sections must be placed in the ROM. Specify a linker script file that tells the linker how sections must be placed in the ROM.
The attributes assigned in the linker script must be consistent with any assigned in the code. The attributes assigned in the linker script must be consistent with any assigned in the code.
@@ -190,27 +238,49 @@ You can use this to make binary files that are not a ROM.
When making a ROM, note that not using this is not a replacement for When making a ROM, note that not using this is not a replacement for
.Xr rgbfix 1 Ap s Fl p .Xr rgbfix 1 Ap s Fl p
option! option!
.It @ Ns Ar at_file
Read more options and arguments from a file, as if its contents were given on the command line.
Arguments are separated by whitespace or newlines.
Lines starting with a hash sign
.Pq Ql #
are considered comments and ignored.
.Pp
No shell processing is performed, such as wildcard or variable expansion.
There is no support for escaping or quoting whitespace to be included in arguments.
The standard
.Ql --
to stop option processing also disables at-file processing.
Note that while
.Ql --
can be used
.Em inside
an at-file, it only disables option processing within that at-file, and processing continues in the parent scope.
.El .El
.Ss Scrambling algorithm .Ss Scrambling algorithm
The default section placement algorithm tries to place sections into as few banks as possible. The default section placement algorithm tries to place sections into as few banks as possible.
(It turns out that section placement is an NP-complete problem known as "bin packing", so (It turns out that section placement is an NP-complete problem known as "bin packing", so
.Nm .Nm
does not attempt to find the optimal solution, but instead uses a "first-fit" heuristic to find a good one in a reasonable amount of time. does not attempt to find the optimal solution, but instead uses a "first-fit" heuristic to find a good one in a reasonable amount of time.)
There are no guarantees about where this algorithm will place sections, apart from the bank, address, and alignment constraints manually specified for the sections.) There are no guarantees about where this algorithm will place sections, apart from the bank, address, and alignment constraints manually specified for the sections.
.Pp .Pp
.Dq Scrambling .Dq Scrambling
instead places sections into a given pool of banks, trying to minimize the number of sections sharing a given bank. instead places sections into a given pool of banks, trying to minimize the number of sections sharing a given bank.
This is useful to catch broken bank assumptions, such as expecting two different sections to land in the same bank (that is not guaranteed unless both are manually assigned the same bank number). This is useful to catch broken bank assumptions, such as expecting two different sections to land in the same bank (that is not guaranteed unless both are manually assigned the same bank number).
There are still no guarantees about where this algorithm will place sections, apart from the bank pool size and any manually specified section constraints.
.Pp .Pp
A scrambling spec is a comma-separated list of region specs. A scrambling spec is a comma-separated list of region specs.
A trailing comma is allowed, as well as whitespace between all specs and their components. A trailing comma is allowed, as well as whitespace between all specs and their components.
Each region spec has the following form: Each region spec has the following form:
.D1 Ar region Ns Op = Ns Ar size .D1 Ar region Ns Op = Ns Ar size
.Ar region .Ar region
specifies the section type, and
.Ar size
specifies the number of banks in the pool for that section type.
.Ar region
must be one of the following (case-insensitive), while must be one of the following (case-insensitive), while
.Ar size .Ar size
must be a positive decimal integer between 1 and the corresponding maximum. must be a positive decimal integer between 1 and the corresponding maximum.
Certain regions allow omitting the size, in which case it defaults to its max value. Certain regions allow omitting the size, in which case it defaults to its maximum value.
.Bl -column "Region name" "Max value" "Size optional" .Bl -column "Region name" "Max value" "Size optional"
Region name Ta Max size Ta Size optional Region name Ta Max size Ta Size optional
.Cm romx Ta 65535 Ta \&No .Cm romx Ta 65535 Ta \&No
@@ -223,14 +293,14 @@ A
of 0 disables scrambling for that region. of 0 disables scrambling for that region.
.Pp .Pp
For example, For example,
.Ql romx=64,wramx=4 .Ql romx=64,wramx,sram=4
will scramble will scramble
.Ic ROMX .Ic ROMX
sections among ROM banks 1 to 64, sections among ROM banks 1 to 64,
.Ic WRAMX .Ic WRAMX
sections among RAM banks 1 to 4, and will not scramble sections among RAM banks 1 to 7, and
.Ic SRAM .Ic SRAM
sections. sections among SRAM banks 0 to 3.
.Pp .Pp
Later region specs override earlier ones; for example, Later region specs override earlier ones; for example,
.Ql romx=42, Romx=0 .Ql romx=42, Romx=0
@@ -258,7 +328,7 @@ to prevent turning all warnings into errors.
.It Fl Werror= .It Fl Werror=
Make the specified warning or meta warning into an error. Make the specified warning or meta warning into an error.
A warning's name is appended A warning's name is appended
.Pq example: Fl Werror=assert , .Pq example: Fl Werror=obsolete ,
and this warning is implicitly enabled and turned into an error. and this warning is implicitly enabled and turned into an error.
This can be negated as This can be negated as
.Fl Wno-error= .Fl Wno-error=
@@ -280,7 +350,7 @@ Enables literally every warning.
.El .El
.Pp .Pp
The following warnings are actual warning flags; with each description, the corresponding warning flag is included. The following warnings are actual warning flags; with each description, the corresponding warning flag is included.
Note that each of these flag also has a negation (for example, Note that each of these flags also has a negation (for example,
.Fl Wobsolete .Fl Wobsolete
enables the warning that enables the warning that
.Fl Wno-obsolete .Fl Wno-obsolete
@@ -307,6 +377,8 @@ for
Warn when dividing the smallest negative integer (-2**31) by -1, which yields itself due to integer overflow. Warn when dividing the smallest negative integer (-2**31) by -1, which yields itself due to integer overflow.
This warning is enabled by This warning is enabled by
.Fl Wall . .Fl Wall .
.It Fl Wno-large-constant
Warn when a constant too large to fit in a signed 32-bit integer is encountered.
.It Fl Wno-obsolete .It Fl Wno-obsolete
Warn when obsolete features are encountered, which have been deprecated and may later be removed. Warn when obsolete features are encountered, which have been deprecated and may later be removed.
.It Fl Wshift .It Fl Wshift
@@ -327,10 +399,10 @@ or
.Fl Wno-truncation .Fl Wno-truncation
disables this warning. disables this warning.
.Fl Wtruncation=1 .Fl Wtruncation=1
warns when an N-bit value is 2**N or greater, or less than -2**N.
.Fl Wtruncation=2
or just or just
.Fl Wtruncation .Fl Wtruncation
warns when an N-bit value is 2**N or greater, or less than -2**N.
.Fl Wtruncation=2
also warns when an N-bit value is less than -2**(N-1), which will not fit in two's complement encoding. also warns when an N-bit value is less than -2**(N-1), which will not fit in two's complement encoding.
.El .El
.Sh EXAMPLES .Sh EXAMPLES
+1 -1
View File
@@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd September 30, 2025 .Dd August 1, 2026
.Dt RGBLINK 5 .Dt RGBLINK 5
.Os .Os
.Sh NAME .Sh NAME
-2
View File
@@ -1,2 +0,0 @@
# Generated by CMake
/.version.cpp
+80 -54
View File
@@ -1,47 +1,44 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
configure_file(version.cpp _version.cpp ESCAPE_QUOTES) add_library(common OBJECT
set(common_src
"extern/getopt.cpp" "extern/getopt.cpp"
"cli.cpp"
"diagnostics.cpp" "diagnostics.cpp"
"style.cpp" "style.cpp"
"usage.cpp" "usage.cpp"
"util.cpp" "util.cpp"
"_version.cpp" "version.cpp"
) )
target_compile_definitions(common PRIVATE "BUILD_VERSION_STRING=\"${GIT_REV}\"")
find_package(BISON 3.0.0 REQUIRED) find_package(BISON 3.0.0 REQUIRED)
# BISON 4.0 deprecates passing this BISON_FLAGS string to `bison_target`'s `COMPILE_FLAGS`,
# in favor of passing a list to `OPTIONS`, but that would require dropping support for CMake <4.0.
set(BISON_FLAGS "-Wall -Dlr.type=ielr") set(BISON_FLAGS "-Wall -Dlr.type=ielr")
# Set some optimization flags on versions that support them # Set some flags on versions that support them.
if(BISON_VERSION VERSION_GREATER_EQUAL "3.5") if(BISON_VERSION VERSION_GREATER_EQUAL "3.5")
set(BISON_FLAGS "${BISON_FLAGS} -Dparse.lac=full -Dapi.token.raw=true") set(BISON_FLAGS "${BISON_FLAGS} -Dparse.lac=full -Dapi.token.raw=true -Wdangling-alias")
endif() endif()
if(BISON_VERSION VERSION_GREATER_EQUAL "3.6") if(BISON_VERSION VERSION_GREATER_EQUAL "3.6")
set(BISON_FLAGS "${BISON_FLAGS} -Dparse.error=detailed") set(BISON_FLAGS "${BISON_FLAGS} -Dparse.error=detailed")
else() else()
set(BISON_FLAGS "${BISON_FLAGS} -Dparse.error=verbose") set(BISON_FLAGS "${BISON_FLAGS} -Dparse.error=verbose")
endif() endif()
if(BISON_VERSION VERSION_GREATER_EQUAL "3.7")
set(BISON_FLAGS "${BISON_FLAGS} -Wcounterexamples")
endif()
BISON_TARGET(ASM_PARSER "asm/parser.y" bison_target(asm_parser "asm/parser.y"
"${PROJECT_SOURCE_DIR}/src/asm/parser.cpp" "${CMAKE_CURRENT_BINARY_DIR}/parser.cpp"
COMPILE_FLAGS "${BISON_FLAGS}" COMPILE_FLAGS "${BISON_FLAGS}")
DEFINES_FILE "${PROJECT_SOURCE_DIR}/src/asm/parser.hpp" add_executable(rgbasm $<TARGET_OBJECTS:common>
) "${BISON_asm_parser_OUTPUT_SOURCE}"
BISON_TARGET(LINKER_SCRIPT_PARSER "link/script.y"
"${PROJECT_SOURCE_DIR}/src/link/script.cpp"
COMPILE_FLAGS "${BISON_FLAGS}"
DEFINES_FILE "${PROJECT_SOURCE_DIR}/src/link/script.hpp"
)
set(rgbasm_src
"${BISON_ASM_PARSER_OUTPUT_SOURCE}"
"asm/actions.cpp" "asm/actions.cpp"
"asm/charmap.cpp" "asm/charmap.cpp"
"asm/fixpoint.cpp" "asm/fixpoint.cpp"
"asm/format.cpp" "asm/format.cpp"
"asm/fstack.cpp" "asm/fstack.cpp"
"asm/intern.cpp"
"asm/lexer.cpp" "asm/lexer.cpp"
"asm/macro.cpp" "asm/macro.cpp"
"asm/main.cpp" "asm/main.cpp"
@@ -56,10 +53,15 @@ set(rgbasm_src
"linkdefs.cpp" "linkdefs.cpp"
"opmath.cpp" "opmath.cpp"
"verbosity.cpp" "verbosity.cpp"
) )
cmake_path(GET BISON_asm_parser_OUTPUT_HEADER PARENT_PATH parser_header_dir)
target_include_directories(rgbasm PRIVATE "${parser_header_dir}")
set(rgblink_src bison_target(linker_script_parser "link/script.y"
"${BISON_LINKER_SCRIPT_PARSER_OUTPUT_SOURCE}" "${CMAKE_CURRENT_BINARY_DIR}/script.cpp"
COMPILE_FLAGS "${BISON_FLAGS}")
add_executable(rgblink $<TARGET_OBJECTS:common>
"${BISON_linker_script_parser_OUTPUT_SOURCE}"
"link/assign.cpp" "link/assign.cpp"
"link/fstack.cpp" "link/fstack.cpp"
"link/lexer.cpp" "link/lexer.cpp"
@@ -77,53 +79,77 @@ set(rgblink_src
"linkdefs.cpp" "linkdefs.cpp"
"opmath.cpp" "opmath.cpp"
"verbosity.cpp" "verbosity.cpp"
) )
cmake_path(GET BISON_linker_script_parser_OUTPUT_HEADER PARENT_PATH parser_header_dir)
target_include_directories(rgblink PRIVATE "${parser_header_dir}")
set(rgbfix_src add_executable(rgbfix $<TARGET_OBJECTS:common>
"fix/fix.cpp" "fix/fix.cpp"
"fix/main.cpp" "fix/main.cpp"
"fix/mbc.cpp" "fix/mbc.cpp"
"fix/warning.cpp" "fix/warning.cpp"
) )
set(rgbgfx_src add_executable(rgbgfx $<TARGET_OBJECTS:common>
"gfx/color_set.cpp" "gfx/color_set.cpp"
"gfx/main.cpp" "gfx/main.cpp"
"gfx/pal_packing.cpp" "gfx/pal_packing.cpp"
"gfx/pal_sorting.cpp" "gfx/pal_sorting.cpp"
"gfx/pal_spec.cpp" "gfx/pal_spec.cpp"
"gfx/palette.cpp"
"gfx/png.cpp" "gfx/png.cpp"
"gfx/process.cpp" "gfx/process.cpp"
"gfx/reverse.cpp" "gfx/reverse.cpp"
"gfx/rgba.cpp" "gfx/rgba.cpp"
"gfx/warning.cpp" "gfx/warning.cpp"
"verbosity.cpp" "verbosity.cpp"
) )
foreach(PROG "asm" "fix" "gfx" "link") install(TARGETS rgbasm rgblink rgbfix rgbgfx RUNTIME COMPONENT binaries)
add_executable(rgb${PROG} # Tests expect the binaries to end up in-source; this is more acceptable than the entire artifact dir.
${rgb${PROG}_src} set_target_properties(rgbasm rgblink rgbfix rgbgfx PROPERTIES
${common_src} # The generator expression (even if a no-op) stops muti-config generators using a of "per-configuration subdirectory".
) RUNTIME_OUTPUT_DIRECTORY $<1:${CMAKE_CURRENT_SOURCE_DIR}/..>)
install(TARGETS rgb${PROG} RUNTIME DESTINATION bin) target_link_libraries(rgbgfx PRIVATE PNG::PNG)
# Required to run tests # Copy the DLLs in the output directory so the program can be run for testing without having to `install`.
set_target_properties(rgb${PROG} PROPERTIES # From https://cmake.org/cmake/help/v4.3/manual/cmake-generator-expressions.7.html#genex:TARGET_RUNTIME_DLLS.
# hack for MSVC: no-op generator expression to stop generation of "per-configuration subdirectory" add_custom_command(TARGET rgbgfx POST_BUILD COMMENT "Copying rgbgfx's DLLs"
RUNTIME_OUTPUT_DIRECTORY $<1:${CMAKE_SOURCE_DIR}>) # It would be nice to symlink instead, but that only supports one file at a time.
endforeach() COMMAND ${CMAKE_COMMAND} -E copy -t $<TARGET_FILE_DIR:rgbgfx> $<TARGET_RUNTIME_DLLS:rgbgfx>
COMMAND_EXPAND_LISTS VERBATIM)
if(LIBPNG_FOUND) # pkg-config if(WIN32)
target_include_directories(rgbgfx PRIVATE ${LIBPNG_INCLUDE_DIRS}) # CMake 3.27 is required for `$<INSTALL_PREFIX>`, but is only needed when cross-compiling for MinGW.
target_link_directories(rgbgfx PRIVATE ${LIBPNG_LIBRARY_DIRS}) if(CMAKE_VERSION VERSION_LESS "3.27")
target_link_libraries(rgbgfx PRIVATE ${LIBPNG_LIBRARIES}) message(WARNING "CMake 3.27 or higher is required for `\$<INSTALL_PREFIX>` inside `install(CODE)`")
else() endif()
target_compile_definitions(rgbgfx PRIVATE ${PNG_DEFINITIONS}) # On Windows, you don't link against DLLs directly, but against an import library for that DLL.
target_include_directories(rgbgfx PRIVATE ${PNG_INCLUDE_DIRS}) # This leads to, sometimes, the DLL being stored in a directory far away from the import library itself!
target_link_libraries(rgbgfx PRIVATE ${PNG_LIBRARIES}) set(DLL_SEARCH_DIRS CACHE PATH "List of directories in which DLLs will be searched")
endif() cmake_path(CONVERT "${DLL_SEARCH_DIRS}" TO_CMAKE_PATH_LIST search_dirs NORMALIZE)
# TODO: we've only tested this with MinGW, but it may not work with other uses of the variable.
include(CheckLibraryExists) # Anyone who knows how to handle this better, feel free to submit a patch!
check_library_exists("m" "sin" "" HAS_LIBM) if(DEFINED CMAKE_FIND_ROOT_PATH)
if(HAS_LIBM) list(APPEND search_dirs "${CMAKE_FIND_ROOT_PATH}/bin")
target_link_libraries(rgbasm PRIVATE "m") endif()
# Escape the search paths in a form that makes them suitable for splatting into the CMake install script.
list(TRANSFORM search_dirs REPLACE "([\\$\"])" "\\\\1") # Neutralize escapes and variable refs...
list(TRANSFORM search_dirs REPLACE "^(.+)\$" "\"\\1\"") # ...and quote each argument.
list(JOIN search_dirs " " search_dirs_splat)
# This is a modified version of the install code generated by
# `install(TARGETS rgbgfx RUNTIME_DEPENDENCIES)`.
# Unfortunately, that `install(TARGETS)` is outright not supported when cross-compiling,
# and working around that has CMake fail to resolve the DLL locations with MinGW,
# due to not specifying any `DIRECTORIES` and the system search path being ineffective.
install(CODE "\
file(GET_RUNTIME_DEPENDENCIES
RESOLVED_DEPENDENCIES_VAR rgbgfx_deps
EXECUTABLES \"$<TARGET_FILE:rgbgfx>\"
DIRECTORIES ${search_dirs_splat} \"$<TARGET_FILE_DIR:ZLIB::ZLIB>\" \"$<TARGET_FILE_DIR:PNG::PNG>\"
PRE_EXCLUDE_REGEXES \"^kernel32\\\\.dll$\" \"^msvcrt.?.?\\\\.dll\" \"^api-ms-win-.*\\\\.dll$\")
foreach(rgbgfx_dep IN LISTS rgbgfx_deps)
file(INSTALL DESTINATION \"$<INSTALL_PREFIX>/${CMAKE_INSTALL_BINDIR}\" TYPE SHARED_LIBRARY FILES \${rgbgfx_dep}
FOLLOW_SYMLINK_CHAIN)
endforeach()"
COMPONENT shared-libs EXCLUDE_FROM_ALL) # Most platforms install those separately.
endif() endif()
+30 -32
View File
@@ -18,10 +18,12 @@
#include "extern/utf8decoder.hpp" #include "extern/utf8decoder.hpp"
#include "helpers.hpp" #include "helpers.hpp"
#include "linkdefs.hpp" #include "linkdefs.hpp"
#include "util.hpp" // xfclose, seekSize
#include "asm/charmap.hpp" #include "asm/charmap.hpp"
#include "asm/format.hpp" #include "asm/format.hpp"
#include "asm/fstack.hpp" #include "asm/fstack.hpp"
#include "asm/intern.hpp"
#include "asm/lexer.hpp" #include "asm/lexer.hpp"
#include "asm/output.hpp" #include "asm/output.hpp"
#include "asm/rpn.hpp" // Expression #include "asm/rpn.hpp" // Expression
@@ -139,23 +141,22 @@ std::optional<std::string> act_ReadFile(std::string const &name, uint32_t maxLen
file = fopen(fullPath->c_str(), "rb"); file = fopen(fullPath->c_str(), "rb");
} }
if (!file) { if (!file) {
if (fstk_FileError(name, "READFILE")) { if (fstk_FileError(name, "`READFILE`")) {
// If `fstk_FileError` returned true due to `-MG`, we should abort due to a // If `fstk_FileError` returned true due to `-MG`, we should abort due to a
// missing file, so return `std::nullopt`, which tells the caller to `YYACCEPT` // missing file, so return `std::nullopt`, which tells the caller to `YYACCEPT`
return std::nullopt; return std::nullopt;
} }
return ""; return "";
} }
Defer closeFile{[&] { fclose(file); }}; Defer closeFile{[&] { xfclose(file); }};
size_t readSize = maxLen; size_t readSize = maxLen;
if (fseek(file, 0, SEEK_END) == 0) { if (std::optional<uint64_t> fileSize = seekSize(file); fileSize.has_value()) {
// If the file is seekable and shorter than the max length, // If the file is seekable and shorter than the max length,
// just read as many bytes as there are // just read as many bytes as there are
if (long fileSize = ftell(file); static_cast<size_t>(fileSize) < readSize) { if (*fileSize < readSize) {
readSize = fileSize; readSize = *fileSize;
} }
fseek(file, 0, SEEK_SET);
// LCOV_EXCL_START // LCOV_EXCL_START
} else if (errno != ESPIPE) { } else if (errno != ESPIPE) {
error( error(
@@ -278,19 +279,18 @@ static void errorInvalidUTF8Byte(uint8_t byte, char const *functionName) {
size_t act_StringLen(std::string const &str, bool printErrors) { size_t act_StringLen(std::string const &str, bool printErrors) {
size_t len = 0; size_t len = 0;
uint32_t state = UTF8_ACCEPT; Utf8Decoder decoder;
uint32_t codepoint = 0;
for (char c : str) { for (char c : str) {
uint8_t byte = static_cast<uint8_t>(c); uint8_t byte = static_cast<uint8_t>(c);
switch (decode(&state, &codepoint, byte)) { switch (decoder.update(byte)) {
case UTF8_REJECT: case UTF8_REJECT:
if (printErrors) { if (printErrors) {
errorInvalidUTF8Byte(byte, "STRLEN"); errorInvalidUTF8Byte(byte, "STRLEN");
} }
state = UTF8_ACCEPT; decoder.state = UTF8_ACCEPT;
// fallthrough [[fallthrough]];
case UTF8_ACCEPT: case UTF8_ACCEPT:
++len; ++len;
break; break;
@@ -298,7 +298,7 @@ size_t act_StringLen(std::string const &str, bool printErrors) {
} }
// Check for partial code point. // Check for partial code point.
if (state != UTF8_ACCEPT) { if (decoder.state != UTF8_ACCEPT) {
if (printErrors) { if (printErrors) {
error("STRLEN: Incomplete UTF-8 character"); error("STRLEN: Incomplete UTF-8 character");
} }
@@ -316,17 +316,16 @@ std::string
size_t strLen = str.length(); size_t strLen = str.length();
size_t index = 0; size_t index = 0;
uint32_t state = UTF8_ACCEPT; Utf8Decoder decoder;
uint32_t codepoint = 0;
uint32_t curIdx = 0; uint32_t curIdx = 0;
// Advance to starting index in source string. // Advance to starting index in source string.
while (index < strLen && curIdx < start) { while (index < strLen && curIdx < start) {
switch (decode(&state, &codepoint, str[index])) { switch (decoder.update(str[index])) {
case UTF8_REJECT: case UTF8_REJECT:
errorInvalidUTF8Byte(str[index], "STRSLICE"); errorInvalidUTF8Byte(str[index], "STRSLICE");
state = UTF8_ACCEPT; decoder.state = UTF8_ACCEPT;
// fallthrough [[fallthrough]];
case UTF8_ACCEPT: case UTF8_ACCEPT:
++curIdx; ++curIdx;
break; break;
@@ -348,11 +347,11 @@ std::string
// Advance to ending index in source string. // Advance to ending index in source string.
while (index < strLen && curIdx < stop) { while (index < strLen && curIdx < stop) {
switch (decode(&state, &codepoint, str[index])) { switch (decoder.update(str[index])) {
case UTF8_REJECT: case UTF8_REJECT:
errorInvalidUTF8Byte(str[index], "STRSLICE"); errorInvalidUTF8Byte(str[index], "STRSLICE");
state = UTF8_ACCEPT; decoder.state = UTF8_ACCEPT;
// fallthrough [[fallthrough]];
case UTF8_ACCEPT: case UTF8_ACCEPT:
++curIdx; ++curIdx;
break; break;
@@ -361,7 +360,7 @@ std::string
} }
// Check for partial code point. // Check for partial code point.
if (state != UTF8_ACCEPT) { if (decoder.state != UTF8_ACCEPT) {
error("STRSLICE: Incomplete UTF-8 character"); error("STRSLICE: Incomplete UTF-8 character");
++curIdx; ++curIdx;
} }
@@ -386,17 +385,16 @@ std::string act_StringSub(std::string const &str, int32_t negPos, std::optional<
size_t strLen = str.length(); size_t strLen = str.length();
size_t index = 0; size_t index = 0;
uint32_t state = UTF8_ACCEPT; Utf8Decoder decoder;
uint32_t codepoint = 0;
uint32_t curPos = 1; uint32_t curPos = 1;
// Advance to starting position in source string. // Advance to starting position in source string.
while (index < strLen && curPos < pos) { while (index < strLen && curPos < pos) {
switch (decode(&state, &codepoint, str[index])) { switch (decoder.update(str[index])) {
case UTF8_REJECT: case UTF8_REJECT:
errorInvalidUTF8Byte(str[index], "STRSUB"); errorInvalidUTF8Byte(str[index], "STRSUB");
state = UTF8_ACCEPT; decoder.state = UTF8_ACCEPT;
// fallthrough [[fallthrough]];
case UTF8_ACCEPT: case UTF8_ACCEPT:
++curPos; ++curPos;
break; break;
@@ -417,11 +415,11 @@ std::string act_StringSub(std::string const &str, int32_t negPos, std::optional<
// Compute the result length in bytes. // Compute the result length in bytes.
while (index < strLen && curLen < len) { while (index < strLen && curLen < len) {
switch (decode(&state, &codepoint, str[index])) { switch (decoder.update(str[index])) {
case UTF8_REJECT: case UTF8_REJECT:
errorInvalidUTF8Byte(str[index], "STRSUB"); errorInvalidUTF8Byte(str[index], "STRSUB");
state = UTF8_ACCEPT; decoder.state = UTF8_ACCEPT;
// fallthrough [[fallthrough]];
case UTF8_ACCEPT: case UTF8_ACCEPT:
++curLen; ++curLen;
break; break;
@@ -430,7 +428,7 @@ std::string act_StringSub(std::string const &str, int32_t negPos, std::optional<
} }
// Check for partial code point. // Check for partial code point.
if (state != UTF8_ACCEPT) { if (decoder.state != UTF8_ACCEPT) {
error("STRSUB: Incomplete UTF-8 character"); error("STRSUB: Incomplete UTF-8 character");
++curLen; ++curLen;
} }
@@ -607,7 +605,7 @@ std::string act_StringFormat(
return str; return str;
} }
std::string act_SectionName(std::string const &symName) { std::string act_SectionName(InternedStr symName) {
Symbol *sym = sym_FindScopedValidSymbol(symName); Symbol *sym = sym_FindScopedValidSymbol(symName);
if (!sym) { if (!sym) {
if (sym_IsPurgedScoped(symName)) { if (sym_IsPurgedScoped(symName)) {
@@ -625,7 +623,7 @@ std::string act_SectionName(std::string const &symName) {
return section->name; return section->name;
} }
void act_CompoundAssignment(std::string const &symName, RPNCommand op, int32_t constValue) { void act_CompoundAssignment(InternedStr symName, RPNCommand op, int32_t constValue) {
Expression oldExpr, constExpr, newExpr; Expression oldExpr, constExpr, newExpr;
oldExpr.makeSymbol(symName); oldExpr.makeSymbol(symName);
constExpr.makeNumber(constValue); constExpr.makeNumber(constValue);

Some files were not shown because too many files have changed in this diff Show More