Compare commits

..

255 Commits

Author SHA1 Message Date
Rangi42
d1829ed923 Release v0.9.4 2025-07-31 07:41:49 -04:00
Rangi42
6d53bc4121 Simplify toggling of enabling/disabling expansions 2025-07-31 07:19:04 -04:00
Rangi42
32b5ef5095 Expand the test for {interpolation} after two double quotes 2025-07-31 07:08:05 -04:00
Rangi42
a36f2b3b7d Add test for {interpolation} after two double quotes
This is an edge case of how the lexer changes behavior when
reading string literals versus reading unquoted source code.
Three double quotes would begin a multi-line string literal,
but just two are still ambiguous, so it has to `peek()` to
see whether it's inside a multi-line string literal or
outside an empty regular string.
2025-07-31 03:31:16 -04:00
Rangi42
cd4be6aa07 Update test dependencies 2025-07-30 14:49:11 -04:00
Rangi42
499edaecd0 Exclude verboseOutputAssignments from LCOV coverage testing 2025-07-29 14:53:30 -04:00
Rangi
225490163e Fix RGBGFX "decanting" on "components" (color sets sharing colors) (#1768) 2025-07-29 14:52:20 -04:00
Rangi42
d388a60daa Reduce deep nesting in gfx/pal_packing.cpp 2025-07-29 14:02:51 -04:00
Rangi42
6d05de9d4d Remove a little no-longer-necessary header boilerplate 2025-07-29 11:22:31 -04:00
Rangi42
5d6e571338 Mention palette filenames in rgbgfx -c error messages 2025-07-29 06:44:33 -04:00
Rangi42
308d488833 Use UpperMap for rgblink -S scramble spec matching
This also makes invalid RGBLINK CLI options into fatal errors
like the other programs
2025-07-29 03:41:31 -04:00
Rangi42
cecbf0aa0e Add test case for some macro+interpolation behavior 2025-07-29 01:34:55 -04:00
Rangi42
d21e6669ce Unrefactor peek() to use tail recursion instead of a loop
Profiling RGBDS on building pret/pokecrystal reveals commit
cfe1f60e47 as a clear outlier
among the past few hundred commits for reducing performance
(an 8% increase on my machine from ~5 to ~5.4 seconds).
2025-07-29 00:24:51 -04:00
Rangi42
2341d1ee50 Replace platform-specific mmap with reading the entire .asm file 2025-07-28 20:50:48 -04:00
Rangi42
53949761a7 Factor out our manual std::filesystem::path.replace_extension replacement into its own function 2025-07-28 13:11:47 -04:00
Rangi42
f7eb986313 Encapsulate access to sectionList and currentSection 2025-07-28 11:47:37 -04:00
Rangi42
75aed1afd5 Factor out an UpperMap for case-insensitive matching 2025-07-28 00:00:24 -04:00
Rangi42
d16751f56a Avoid hard-coding a redundant "FATAL:" in RGBLINK 2025-07-27 20:07:53 -04:00
Rangi42
8b1a5244f7 Avoid hard-coding a redundant "FATAL:" in RGBFIX 2025-07-27 19:35:08 -04:00
Rangi42
b2747dfbd8 Factor out common usage-help code 2025-07-27 19:20:04 -04:00
Rangi42
16e16cdf51 Split up the linkerscript lexer and layout actions 2025-07-27 13:38:20 -04:00
Rangi42
a353637a90 Split RGBLINK linkerscript parser functions into their own file 2025-07-27 11:31:25 -04:00
Rangi42
f3cbfcecf4 Split RGBASM parser action functions into their own file 2025-07-26 16:20:25 -04:00
Rangi42
3bc8b1ff7c Handle unknown MBCs, since raw numeric values are accepted 2025-07-26 14:15:05 -04:00
Rangi42
aa46c79db6 Use unordered_map lookups instead of manual switches for MbcType data 2025-07-26 13:30:56 -04:00
Rangi42
92acb6e547 Implement a single nbErrors counter inside generic diagnostic code 2025-07-26 12:54:50 -04:00
Rangi42
ac632d9223 RGBFIX returns 1 if there was a -Werror before processing any files 2025-07-26 12:29:57 -04:00
Rangi
0df5b7b86d Implement warning diagnostic flags for RGBFIX (#1766)
* Implement warning diagnostic flags for RGBFIX

* `-m/--mbc-type help` prints to stdout

* Support `-m list` as well as `-m help`

* Make invalid `rgbfix -l` characters a fatal error, like other invalid CLI arguments

* Refactor fix/main.cpp into multiple files
2025-07-26 12:11:52 -04:00
Rangi42
87c10988ed Add test case for block comments after line continuations 2025-07-25 07:49:25 -04:00
Rangi42
d6a28a6259 Prefer pre-increment/decrement operators in for loops 2025-07-24 18:08:17 -04:00
Rangi42
c6d0e8de63 Improve some error messages 2025-07-24 11:19:04 -04:00
Rangi42
ded4ef4072 Avoid non-ASCII characters when not necessary 2025-07-23 21:18:21 -04:00
Rangi42
1849a35e61 Rename proto-palettes to color sets (copied from rsgbds) 2025-07-23 21:13:46 -04:00
Rangi42
18e35053fa Replace vectors with unordered_sets 2025-07-23 18:17:39 -04:00
Rangi
7e151f16c3 Factor out a single PNG-reading function to encapsulate the libpng API (#1765) 2025-07-23 15:53:33 -04:00
Rangi42
2ce4cdbff6 Reduce deep nesting some more, including larger refactors to assign.cpp 2025-07-22 19:38:49 -04:00
Rangi42
eea532ded1 Make sectionMap not extern 2025-07-21 20:20:04 -04:00
Rangi42
c83b87e0a0 Make all non-extern globals static 2025-07-21 20:14:09 -04:00
Rangi42
8d268e8a8a Initialize maxRecursionDepth with other options 2025-07-21 19:58:37 -04:00
Rangi42
ee0f311c10 Make nbErrors not extern 2025-07-21 19:49:08 -04:00
Rangi42
61730be6ce Make failedOnMissingInclude not extern 2025-07-21 19:40:01 -04:00
Rangi42
5f333d9753 More refactoring around extern variables 2025-07-21 19:22:10 -04:00
Rangi42
d1493a9f96 Group extern RGBASM variables in an Options struct 2025-07-21 19:02:21 -04:00
Rangi42
d652212857 Group extern RGBLINK variables in an Options struct, like RGBGFX 2025-07-21 18:12:40 -04:00
Rangi42
0cd60ea1e6 Use a patch_AddAssertion function instead of extern assertions 2025-07-21 17:49:41 -04:00
Rangi42
a0e23ee911 Remove unnecessary extern from nbSectionsToAssign 2025-07-21 17:27:43 -04:00
Rangi
ad81c74cda Support PNG-format palette spec files (#1764) 2025-07-21 11:33:16 -04:00
Rangi42
9ef32e405c Combine two places that did REPT chain printing 2025-07-20 15:16:53 -04:00
Rangi42
89ca6a325c Increase parser test coverage 2025-07-20 13:52:29 -04:00
Rangi42
9e0e7ef9a1 Improve RGBASM test coverage 2025-07-20 13:13:45 -04:00
Rangi42
2dc948fefb Miscellaneous refactoring 2025-07-20 12:28:59 -04:00
Rangi42
e3a5290dad Restore blue-painting of macro arg expansions to prevent recursion 2025-07-20 11:53:36 -04:00
Rangi42
cfe1f60e47 Refactor peek() to use a loop instead of tail recursion 2025-07-20 11:38:26 -04:00
Rangi42
0eed237517 Refactor peeking at macro args to be like peeking at interpolations 2025-07-20 11:21:44 -04:00
Rangi42
68ffb01cac Use nextChar() for shiftChar() and then peek() 2025-07-20 11:15:06 -04:00
Rangi42
169ac61e14 Rename nextChar to bumpChar in the RGBASM lexer for symmetry with std::filebuf in the RGBLINK lexer 2025-07-20 11:02:02 -04:00
Rangi42
0681110647 Refactor peek() some more 2025-07-20 10:59:35 -04:00
Rangi42
8d1b111692 Small lexer refactors, commenting when tail recursion occurs 2025-07-20 00:14:13 -04:00
Rangi
2935942667 Allow multiple preinclude files (#1763) 2025-07-19 17:07:15 -04:00
Rangi42
9a4593e823 Reduce nesting depth some more 2025-07-19 16:18:35 -04:00
Rangi42
250e08043b Fix -W parameter parsing 2025-07-19 16:18:35 -04:00
Rangi42
14f5e16ae8 Prefer pre-inc/dec unless post-inc/dec are necessary 2025-07-19 16:18:35 -04:00
Rangi42
bf69043a1d Reduce deep nesting some more 2025-07-19 16:18:30 -04:00
Rangi42
7086b8aeff Document that -MG and -MC count READFILE 2025-07-18 20:32:08 -04:00
Rangi
53c39d01d4 Implement READFILE function (#1759) 2025-07-18 18:27:52 -04:00
Rangi42
4a2f9fc744 A little refactoring 2025-07-18 14:17:23 -04:00
Rangi42
e7d63f5f6b Refactor code that handles when included files are missing
- Single unified routine for erroring out or handling missing dependencies
- Single three-state enum instead of two Booleans for missing dependencies
  (this causes `-MC` to imply `-MG` instead of needing `-MG -MC`)
- Functions than can miss included files return a Boolean for whether the
  parser should `YYACCEPT` and exit
2025-07-18 14:03:23 -04:00
Rangi42
b80b30fba1 Ensure that INCBIN parameters are non-negative 2025-07-18 12:44:27 -04:00
Rangi42
8e84850679 Run clang-tidy with make tidy 2025-07-18 10:10:34 -04:00
Rangi42
e31256c0d4 Remove rgbasm-only -lm from Makefile 2025-07-18 08:22:40 -04:00
Rangi42
9a9fd6603c Replace test comments with assertions 2025-07-18 07:47:40 -04:00
Rangi42
e99ff5ac45 Use more concrete types instead of auto 2025-07-17 23:52:09 -04:00
Rangi42
60cec85638 Use separate caches for zlib and libpng on Windows 2022 vs 2025
Windows 2025 no longer has a D:/ drive
<https://github.com/actions/runner-images/issues/12416>
which affects where the cached files go.

This can result in one of two errors in the "Install zlib" step
after restoring from cache:

- file cannot create directory: D:/a/rgbds/rgbds/install_dir/lib.
  Maybe need administrative privileges.
- file INSTALL cannot find "C:/a/rgbds/rgbds/zbuild/Release/zlib1.dll":
  File exists.

Separate caches per OS should solve this problem.
2025-07-17 21:47:45 -04:00
Rangi42
39f2ed1339 Fix order of sentences 2025-07-17 20:14:50 -04:00
Rangi
4c8724899b Support SIZEOF(reg) to distinguish 8- and 16-bit registers (#1758) 2025-07-17 15:49:28 -04:00
Rangi
0c96234532 Use concrete types instead of auto when convenient and not redundant (#1757) 2025-07-17 14:59:51 -04:00
Rangi
9dddd87893 Run all CMake commands in bash (#1755) 2025-07-17 13:39:37 -04:00
Rangi
5eb093f13e Implement warning diagnostic flags for RGBLINK (#1754) 2025-07-17 12:54:29 -04:00
Rangi
529989bde5 Update libpng to 1.6.50 (#1750)
* Update libpng to 1.6.50

* Don't patch MinGW libpng APNG support

* Use current libpng repo URL
2025-07-16 19:00:51 -04:00
Rangi
776e37980b Add gb-starter-kit to test suite, excluding it on Windows, macOS, and BSD (#1753) 2025-07-16 18:17:01 -04:00
Rangi
7f24d46d44 Support more syntax in linkerscripts (#1752)
* No need to use `locale`s

* Implement octal numbers, `_` digit separators, and `0x/0b/0o` prefixes in linkerscripts

* Refactor some functions out of `yylex`

* Support `\0` in linkerscripts
2025-07-16 15:00:02 -04:00
Rangi42
cf6e5fec63 Fix unterminated strings in linkerscripts consuming their newline 2025-07-16 09:59:48 -04:00
Rangi42
d8fc25ee43 Add TODO comment for SDAS XL4 support 2025-07-15 22:07:11 -04:00
Rangi42
a0eccceb01 Remove unplanned TODO comments 2025-07-15 21:53:55 -04:00
Rangi42
2720224890 Refactor parsing of ld hl, sp + e8 2025-07-15 21:52:22 -04:00
Rangi42
8bebab1db0 Abbreviate RGBLINK errput that includes a src+lineNo 2025-07-15 18:37:28 -04:00
Rangi42
ee29579d3e Reduce some more deep nesting 2025-07-15 17:41:32 -04:00
Rangi42
5aec36350b Document more deprecated/removed features
Fixes #1748
2025-07-15 16:32:03 -04:00
Rangi
1fecf80659 Implement 'character' literals (#1747) 2025-07-15 13:08:50 -04:00
Rangi
b6d77fbb9e Implement BYTELEN and STRBYTE (#1744) 2025-07-14 21:46:35 -04:00
Rangi
8a19c5c30a Fix string function behavior with NUL characters (#1746) 2025-07-14 21:43:32 -04:00
Rangi42
0149122cd0 Note how to print lexed token names for future reference 2025-07-14 10:25:47 -04:00
Rangi42
35335aadbe Add test for labels when expecting symbols 2025-07-14 00:10:14 -04:00
Rangi42
80df858ee3 Clarify comment 2025-07-14 00:02:25 -04:00
Rangi42
eafc32fd68 Simplify switch with one case to if 2025-07-13 23:48:01 -04:00
Rangi42
2adeda0318 Use std::tie for sort comparator functions 2025-07-13 14:47:10 -04:00
Rangi42
21a6d35b8b Simplify readString 2025-07-13 13:26:59 -04:00
Rangi42
ce78280af3 Simplify appendCharInLiteral 2025-07-13 13:22:40 -04:00
Rangi42
041b86b8dd Use Defer instead of relying on a "Don't return before this" comment 2025-07-13 12:52:45 -04:00
Rangi
611b0041c4 Exclude Bison-generated files from coverage report, and use dark theme (#1742) 2025-07-12 10:11:23 -04:00
Rangi42
ddb2acb652 Reduce more nesting depth, and fix an error message 2025-07-12 08:17:26 -04:00
Rangi42
da133baf17 Reduce nesting depth in lexer.cpp 2025-07-12 07:54:16 -04:00
Rangi42
d32b1912ed Use verbosePrint in rgbasm as well as rgblink 2025-07-12 01:38:19 -04:00
Rangi42
82513e5255 Simplify appendCharInLiteral 2025-07-11 23:52:52 -04:00
Rangi42
f2708ce967 Consolidate readString and appendStringLiteral 2025-07-11 23:43:36 -04:00
Rangi42
01a5c94c7e Factor out common code from readString and appendStringLiteral 2025-07-11 22:37:28 -04:00
Rangi42
e7a05b1db8 Refactor for better formatting 2025-07-11 10:59:03 -04:00
Rangi
510a4aa99d Add RGBASM -MC flag to continue -MG after missing dependency files (#1687) 2025-07-10 13:25:36 -04:00
Rangi
3f4e8396aa Implement warning diagnostic flags for RGBGFX (#1738) 2025-07-10 09:58:40 -04:00
Rangi42
276a200590 A few miscellaneous edits 2025-07-10 00:33:03 -04:00
Rangi
a40109e4e4 Update the UTF-8 decoder (#1741) 2025-07-09 23:13:30 -04:00
Rangi
34cf959c9d Refactor to reduce nesting depth some more (#1740) 2025-07-09 22:46:40 -04:00
Rangi42
bf6875f160 Reduce nesting depth in diagnostics.cpp 2025-07-09 19:37:55 -04:00
Rangi
44f5b47bf0 Reduce nesting depth in some functions (#1739) 2025-07-09 16:20:33 -04:00
Rangi
41ab5dff5a Implement [[ fragment literals ]] (#1614)
This feature is referred to as "code/data literals" in ASMotor,
and simply as "literals" in some older assemblers like MIDAS
for the PDP-10. RGBASM already had the "section fragments"
feature for keeping disparate contents together when linked,
so these worked naturally as "fragment literals".
2025-07-09 12:13:01 -04:00
Rangi
5e43ece578 Remove errx and errors.hpp (#1737) 2025-07-09 11:04:23 -04:00
Rangi
9acba4b412 Generate a coverage report in CI (#1736) 2025-07-08 21:39:11 -04:00
Rangi42
8c50839109 Miscellaneous updates 2025-07-08 17:45:13 -04:00
Rangi42
6736d2ec66 Ignore errors to allow lcov 2.3 to work 2025-07-08 17:02:28 -04:00
Rangi
6869e4807c Make file.hpp independent of gfx (#1733) 2025-07-08 15:16:16 -04:00
Rangi
5de05e2e4b Replace DefaultInitVec with std::vector (#1732) 2025-07-08 14:55:28 -04:00
Rangi
fda54fd0c3 Replace Either with std::variant (#1731) 2025-07-08 13:59:03 -04:00
Rangi
35962dedc4 Refactor warnings and errors (#1728)
* Remove `err` and `warn`, keep `errx` and `warnx`, using them in RGBGFX too

* Separate RGBGFX and RGBLINK warnings/errors from main options

* Separate `report` function into `error` and `fatal` messages

* Implicit newlines for most RGBASM errors
2025-07-08 12:58:23 -04:00
Rangi
991b74dd0d Fix segfault with invalid RGBLINK scramble spec (#1730) 2025-07-08 12:34:54 -04:00
Rangi
1a77667409 Fix UBSan error with overflowing exponent operator (#1727) 2025-07-07 19:08:26 -04:00
Rangi
c9765ec158 Factor out program-independent warning diagnostic code (#1725) 2025-07-07 18:34:34 -04:00
Rangi42
202c91471c Fix alignment compatibility with current lower alignment 2025-07-07 22:43:19 +02:00
Rangi
e14f68d1d7 Improve error message for align failure (#1721) 2025-07-06 08:36:11 -04:00
Rangi42
185a3b29e6 Implement base palette ID 2025-07-06 12:57:20 +02:00
Rangi42
d7b1569ee6 Document % changing from remainder to modulo in 0.5.0 2025-07-04 23:02:45 +02:00
Rangi42
468f1cd912 "The windows-2019 runner image is being deprecated" 2025-07-02 19:43:11 +02:00
Rangi42
965288de38 Fix out-of-bounds image slices 2025-07-02 19:43:11 +02:00
Antonio Vivace
008920f533 ci: tag rgbds:latest before pushing it 2025-07-01 01:29:57 +02:00
Rangi42
20ed6a52ee Clarify release procedure checklist 2025-06-30 15:52:42 -04:00
Rangi42
8b85875b67 Release v0.9.3 2025-06-30 15:08:04 -04:00
Rangi
7054d81650 Implement grayscale DMG palette specs (#1709) 2025-06-30 14:53:05 -04:00
Rangi
5942117ac3 Avoid generating phony dependencies for files that don't exist (#1708) 2025-06-29 16:42:24 -04:00
Rangi42
e7a3b9d90e Format rgbgfx -vvvvvv string visually 2025-06-21 14:16:48 -04:00
Rangi
b13d623ad4 Encode reversed PNG images as grayscale or indexed when possible (#1703) 2025-06-19 09:48:27 -04:00
Rangi
37bf9fae01 Only define parse.lac for Bison 3.5 or greater (#1702) 2025-06-14 17:01:16 -04:00
Rangi42
612cf3b7dd Fix some formatting 2025-06-12 17:27:08 -04:00
Rangi
089e366ddc Implement CHARVAL function (#1701) 2025-06-12 17:21:12 -04:00
Rangi
fa9e29e4ce Implement ++ operator for string concatenation (#1698) 2025-06-12 22:52:00 +02:00
Antonio Vivace
fa3d83a3d1 ci container build: when pushing a version-tagged build, overwrite 'latest' as well 2025-06-08 18:21:58 +02:00
Rangi
804db4e073 Handle missing newline at EOF for linkerscript INCLUDEd files (#1691) 2025-05-22 10:55:58 +02:00
Rangi
5d998ef483 Restrict custom binary and graphics digits (#1693)
* Restrict custom binary and graphics digits

* Update documentation

* Fix build error
2025-05-22 10:52:51 +02:00
Rangi
126b1e5726 Reuse startsIdentifier and continuesIdentifier functions (#1695) 2025-05-19 15:31:26 -04:00
Rangi
4f2400c15b Hint to {interpolate} names when EQUS expanding does not occur (#1692) 2025-05-18 17:53:34 +02:00
Antonio Vivace
063d284cbf Dockerfile: explain commands 2025-05-16 18:48:08 +02:00
Antonio Vivace
205bf5a11d Dockerfile: install the compiled tools after the compilation (#1690) 2025-05-16 18:48:08 +02:00
Rangi
41c94aa448 Omit the version number from distrbuted release archive filenames (#1685) 2025-05-06 13:28:54 +02:00
Rangi
d413870e6d .sym file sorting accounts for local labels' parents' addresses and names (#1684) 2025-05-05 13:57:25 -04:00
Rangi
e95ac6fb06 Recover from errors even inside REPT/FOR loops (#1683) 2025-05-04 17:51:53 -04:00
Rangi
e1ae92709c Fix STRSLICE with no stop index argument (#1682) 2025-05-04 16:56:25 -04:00
Rangi42
1715f85d50 Release v0.9.2 2025-05-04 10:04:11 -04:00
Rangi42
c2de0a991a Update test dependency 2025-05-03 12:43:57 -04:00
Rangi
2e6e1ccf06 Show specific messages for some more invalid instructions, not just "syntax error" (#1679) 2025-05-03 12:31:00 -04:00
Rangi42
081f48404c Remove a TODO comment about overlapping proto-palettes
The original algorithm makes a simplifying assumption that one
proto-palette does not fully contain another, and we have no
particular reason to violate this condition.
2025-05-03 10:19:33 -04:00
Rangi42
bdac0ce053 Remove unplanned TODO comments in src/gfx/pal_spec.cpp
ACT palette files support a transparent color index, but RGBGFX
cannot apply *one* such transparent color; it would need every
palette's first color to be transparent. Also ACT files tend to
say the first color is transparent anyway, which the user may
not have intended.

ACO palette files can specify version 2 color data, but it's
required to come after version 1 data, and the colors themselves
already exist in the earlier v1 data; v2 just adds UTF-16 names.

Thus, we do not need to be handling these data in those formats.
2025-05-02 21:29:14 -04:00
Rangi
122d91509f Clear some more TODO comments (#1677) 2025-05-02 21:06:34 -04:00
Rangi
7c6f778ae7 Take care of miscellaneous commented TODOs (#1676) 2025-05-02 16:44:12 -04:00
Eldred Habert
8cf6c5423a Implement --background-color (#1508)
Co-authored-by: Rangi42 <sylvie.oukaour+rangi42@gmail.com>
2025-05-01 23:39:52 -04:00
Rangi
56f7222230 Don't output anonymous labels in map files (#1674) 2025-05-01 19:19:25 +02:00
Rangi
e45b9625ca Group sequences of garbage characters (#1672) 2025-04-30 23:31:41 -04:00
Rangi42
e0a8eb8aff Update test dependencies 2025-04-24 09:52:08 -04:00
Rangi
2a5b9b5f98 Fix two RGBGFX bugs (#1671)
* Fix two RGBGFX bugs

* Fix clang-format idempotence

* Update src/gfx/rgba.cpp

Co-authored-by: Eldred Habert <me@eldred.fr>

---------

Co-authored-by: Eldred Habert <me@eldred.fr>
2025-04-24 15:39:14 +02:00
Rangi42
a72843748f Avoid using indirect C++ types 2025-04-23 00:53:20 -04:00
Rangi42
762e2311d2 Add test case for FOR loop variable reusing an existing one 2025-04-22 15:10:50 -04:00
Rangi
0b7cda9e0c Allow negative values to count macro arguments from the end (#1670) 2025-04-20 00:37:50 -04:00
Rangi42
df83bc31d2 Consistently use PRId* not PRIi* 2025-04-19 23:44:34 -04:00
John Millikin
bc8d99d915 Add -o / --output option to rgbfix to write separate output files (#1666) 2025-04-19 23:17:11 -04:00
Rangi42
c841672059 Don't use tabs for alignment 2025-03-31 19:13:46 -04:00
Rangi42
75b605797d Fix rgblink(5) man page syntax error
Copied from https://salsa.debian.org/twolife/rgbds/-/blob/640a5293/debian/patches/groff.patch
2025-03-07 10:37:20 -05:00
Rangi42
00d0ae840d Avoid use of goto in nextLine 2025-02-27 14:28:17 -05:00
Rangi42
2cdbb145da Avoid use of goto in shiftChar 2025-02-27 14:17:01 -05:00
Rangi42
d8192560b0 Avoid use of goto in FormatSpec::useCharacter 2025-02-27 13:45:13 -05:00
Rangi42
9b395f3bf1 Fix double negative 2025-02-23 13:36:55 -05:00
Rangi
0150eb4bf3 Exclude more lines from test coverage (#1663)
These fall into a few categories:
- `_unreachable()`
- Verbose print messages
- Errors that should never practically occur (alloc/read/write failure,
  more than UINT32_MAX anonymous labels, etc)
2025-02-17 04:56:10 -05:00
Rangi
632342b254 Use LCOV_EXCL comments to exclude some lines from test coverage (#1662) 2025-02-16 13:56:55 -05:00
Rangi
c9060c7f2d Increase test coverage (#1661) 2025-02-16 09:29:16 -05:00
Rangi
993879a2ed Derive operator!= from operator== (#1660) 2025-02-15 12:37:42 +01:00
Rangi
62309d5c87 Define operator!= in terms of operator== (#1659) 2025-02-15 11:34:06 +01:00
Rangi
b2e865ee2a Disable EQUS expansion for raw symbols (by parsing them as strings) (#1648) 2025-02-15 10:44:51 +01:00
Rangi
3feb75f84f Implement new string functions (#1655)
`STRFIND`, `STRRFIND`, `STRCHAR`, `STRSLICE`, `CHARCMP`, `CHARSIZE`, and `REVCHAR`
2025-02-14 23:09:45 +01:00
Rangi42
ad4d9da4cf Remove unnecessary default constructor definitions 2025-02-14 18:58:34 +01:00
Rangi42
1489854932 Use more const references when possible 2025-02-14 18:58:34 +01:00
Rangi
2aef09c8d9 Allow the bit/res/set bit index to be determined at link time (#1654)
This increments the object file revision number from 11 to 12
since it adds a new `RPN_BIT_INDEX` command.
2025-02-12 17:14:10 +01:00
Rangi42
48412e9c56 Some miscellaneous refactoring and copy-editing 2025-02-10 16:51:51 +01:00
Rangi
177a3abfac Fix bug where macro names can be treated as numeric symbols (#1653) 2025-02-08 23:03:21 +01:00
Rangi
4c916b8da8 Parser refers to "symbol"s, "label"s, and "local label"s, not "identifier"s (#1652)
This better matches how the lexed tokens are discussed in rgbasm(5)
2025-02-06 18:01:33 +01:00
Rangi42
d9d381cb62 Refactor the parser to have fewer *_no_str intermediate rules 2025-02-04 18:59:11 +01:00
Rangi
fbde24ee17 Add contrib/checkformat.bash to check for clang-formatting (#1646) 2025-02-04 10:40:38 +01:00
Rangi
91310c9eb6 Update the post-release checklist to mention rgbds-live (#1647) 2025-02-04 09:59:03 +01:00
Rangi42
81ea4ee920 Release 0.9.1 2025-02-02 20:16:54 +01:00
Rangi
29ece2940d Mention ASMotor's continued development (#1643) 2025-02-01 21:39:19 +01:00
Rangi
03452c6d4f Allow git describe to get the version for FreeBSD and Cygwin in CI (#1640)
* Specify `safe.directory`
* Fetch tags
* Fetch all commits
2025-01-29 19:57:15 -05:00
Rangi
b35e9d86fb Remove redundant @-style doc comment tags (#1641) 2025-01-29 19:56:28 -05:00
Rangi
e20347e38c Add more RGBLINK tests (#1639) 2025-01-29 12:53:44 -05:00
Rangi
f61019dd68 Add more RGBLINK test coverage (#1637) 2025-01-29 11:41:08 -05:00
Rangi
c19ddc80f0 Fix failing assertion with backslash at EOF in macro arguments (#1634)
`Expansion::advance()` can increase its offset beyond the size,
so I don't think this assumption was valid in the first place;
`BufferedContent::advance()` should be able to as well.
2025-01-28 21:51:11 -05:00
Rangi
a59867cd78 Consistently use LF line endings in expected .out and .err output (#1635)
Test scripts compare files as text
2025-01-28 21:24:40 -05:00
Rangi
375adc6804 Fix STRLEN and STRSUB on incomplete UTF-8 (#1633) 2025-01-28 13:13:35 -05:00
Rangi
44caffe04a Fix CHARLEN and CHARSUB on invalid UTF-8 (#1630) 2025-01-28 02:01:18 -05:00
Rangi42
d54619a453 Remove colNo column tracking from lexer
This was added as part of 71f88717 just for debug and fstack trace
output, but we no longer output it anyway.
2025-01-28 01:12:18 -05:00
Rangi42
e49291b7cf Refactor readUTF8Char into charmap_ConvertNext 2025-01-28 00:07:08 -05:00
Rangi42
34a9c8e083 Add some more string test cases 2025-01-28 00:02:25 -05:00
Rangi42
c4b456b166 Remove unused fix_PrecisionFactor function 2025-01-27 23:04:11 -05:00
Rangi42
79401cce8b Add braces inside #define macro bodies 2025-01-27 20:12:12 -05:00
Rangi42
4e44958d26 Add braces to Bison .y files 2025-01-27 20:12:12 -05:00
Rangi42
cae31005f8 Always use braces with InsertBraces: true in .clang-format 2025-01-27 20:12:12 -05:00
Rangi42
25c9f8f383 Add more rules to .clang-format 2025-01-27 20:12:12 -05:00
Rangi42
01c9106b59 Include windows.h before other Win32 header files 2025-01-27 20:12:12 -05:00
Rangi42
192fc808c8 Run clang-format on some Bison .y file contents 2025-01-27 20:12:12 -05:00
Rangi42
9c8e327ae2 Zero-initialize trimmedTile array 2025-01-27 20:12:12 -05:00
Rangi42
9ebd2a7e8e Fix clang-format of sectionTypeInfo array 2025-01-27 20:12:12 -05:00
Rangi42
b8b60207f5 Use // line comments not /* block comments 2025-01-27 20:12:12 -05:00
Rangi42
c5e59f40fd Get rid of unnecessary extern "C" blocks 2025-01-27 20:12:12 -05:00
Rangi42
a354af3d08 Reformat source files with clang-format 19.1.7 2025-01-27 20:12:12 -05:00
Rangi
20c18256ed Avoid errors after missing INCLUDE with -MG (#1627) 2025-01-25 12:38:17 -05:00
Rangi42
890528812e Prefer C++ constructs to C-style sizeof-based macros 2025-01-24 18:56:41 -05:00
Rangi42
84f59e14ed Rename Z80 prefix to SM83 2025-01-24 12:11:46 -05:00
Rangi42
91d7ce5e09 Add test case for expansions changing context 2025-01-21 21:47:22 -05:00
Rangi
d9654b752f Support -h/--help for all programs (#1620) 2025-01-21 21:24:17 -05:00
Rangi42
157826bf82 Support fetch-test-deps.sh --get-deps debian
Also use `apt-get` instead of `pip` to install
Pillow for libbet
2025-01-21 14:40:09 -05:00
Rangi42
a5e36f924f Update help and error messages in run-tests.sh 2025-01-21 14:10:36 -05:00
robbi-blechdose
82f7bdb480 Allow running external tests against installed rgbds (#1621) 2025-01-21 13:43:31 -05:00
Rangi42
056190413e Add test to cover RGBLINK behavior for patch overflow 2025-01-20 14:42:58 -05:00
Rangi
c2db23aef0 Run internal tests in FreeBSD (#1616) 2025-01-20 14:08:48 -05:00
Rangi
2426068409 Undeprecate ld [$ff00+c] (#1619) 2025-01-20 14:05:15 -05:00
Rangi
147a5c9bf3 Document more obsolete syntax (#1618) 2025-01-18 23:50:20 -05:00
Rangi
6ae3f040b8 Correct the DAA documentation (#1617) 2025-01-17 23:04:03 -05:00
Rangi
e561f63db3 Run internal tests in Cygwin (#1592) 2025-01-17 18:31:37 -05:00
Rangi
af9de812ec Update libpng to 1.6.45 (#1615) 2025-01-17 14:41:38 -05:00
Rangi42
edc9e07a2d Move all common error checks together inside mergeSections 2025-01-17 02:18:40 -05:00
Rangi
382ad17969 Don't output sections in reverse order (#1613) 2025-01-17 01:28:17 -05:00
Rangi42
fac5e35d24 Prefer empty braces to semicolons for empty loop bodies 2025-01-17 00:20:33 -05:00
Rangi42
a85d6b3b57 Remove unused readMagic function 2025-01-17 00:09:47 -05:00
Rangi42
f23a14afc7 Remove unnecessary semicolons after closing braces 2025-01-17 00:01:06 -05:00
Rangi42
f63167dd0f Use const reference 2025-01-16 23:45:43 -05:00
Rangi42
0ee4ba95b3 Replace old-style cast in Windows-only code with static_cast 2025-01-16 23:41:12 -05:00
Rangi
727c1f5b50 Update Dockerfile to use Debian 12 slim (#1599) 2025-01-04 14:54:30 -05:00
Rangi
d829fd2ffe Remove the 99999 macro arg limit (#1597) 2025-01-04 04:04:12 -05:00
Rangi
b13c0f2f8e Use a constant for 0x8001 (#1596) 2025-01-04 04:03:40 -05:00
Rangi42
d9773424e4 RGBDS_OBJECT_VERSION_STRING is a literal 2025-01-04 03:53:59 -05:00
Rangi42
4e2464a69d Replace some #define with constexpr 2025-01-04 03:53:59 -05:00
Rangi42
a5f12f66bb Define the default -recursion depth in main.cpp with other default values 2025-01-04 03:53:59 -05:00
Rangi
73ad431b8d Fix the node type for "file" nodes in object files (#1593) 2025-01-03 17:20:06 +01:00
Rangi42
d88feee1c0 Update test dependencies 2024-12-31 13:07:46 -05:00
Rangi42
5963dc9e0e Only define __asan_default_options in make develop builds
`NDEBUG` is not defined in `develop`, `debug`, `profile`, and `coverage`
builds.
`__SANITIZE_ADDRESS__` is defined in `develop` builds.
2024-12-31 11:01:26 -05:00
Rangi
8363f25d47 Enable more sanitizers in make develop (#1588)
- `-fsanitize=undefined` encompasses multiple checks we were specifying

- "detect_leaks=1" for `__asan_default_options` checks for memory leaks
  (except for with macOS clang++, which does not support LSan)

- `-fsanitize=float-divide-by-zero` is an extra UBSan check
  (and reveals a UB bug to fix with fixed-point `DIV` and `LOG`)
2024-12-31 10:02:20 +01:00
Rangi
72b2a4d7c0 Use if constexpr to guarantee compile-time simplification (#1590) 2024-12-30 23:44:12 -05:00
Rangi
06daf2a9b5 Include <signal.h> in rgbgfx_test.cpp (#1589) 2024-12-30 23:22:14 +01:00
Rangi
ad95d2e06f Allow deduplicating tiles with neither an input nor output tileset (#1585) 2024-12-30 18:58:07 +01:00
Rangi
5197e6b79f Run gcc static analysis in CI (#1587) 2024-12-30 09:57:41 -05:00
Rangi42
b99ce3845e Fix RGBFIX writing bytes when one syscall is not sufficient 2024-12-30 11:25:20 +01:00
672 changed files with 13475 additions and 9572 deletions

View File

@@ -24,6 +24,7 @@ AttributeMacros:
BinPackArguments: false
BinPackParameters: false
BitFieldColonSpacing: Both
BreakAfterAttributes: Always
BreakBeforeBinaryOperators: NonAssignment
BreakBeforeBraces: Attach
BreakBeforeConceptDeclarations: true
@@ -54,12 +55,14 @@ IncludeCategories:
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: false
IndentExternBlock: NoIndent
IndentExternBlock: Indent
IndentGotoLabels: false
IndentPPDirectives: BeforeHash
IndentRequires: true
IndentWidth: 4
IndentWrappedFunctionNames: true
InsertBraces: true
InsertNewlineAtEOF: true
# Only support for Javascript as of clang-format 14...
# InsertTrailingCommas: Wrapped
KeepEmptyLinesAtTheStartOfBlocks: false
@@ -71,6 +74,8 @@ PPIndentWidth: -1
PointerAlignment: Right
QualifierAlignment: Right
ReflowComments: true
RemoveParentheses: ReturnStatement
RemoveSemicolon: true
SortIncludes: CaseSensitive
SortUsingDeclarations: true
SpaceAfterCStyleCast: false

6
.clang-tidy Normal file
View File

@@ -0,0 +1,6 @@
---
Checks: ''
WarningsAsErrors: ''
HeaderFilterRegex: ''
FormatStyle: none
SystemHeaders: false

View File

@@ -1,13 +1,13 @@
#!/bin/bash
set -euo pipefail
pngver=1.6.43
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)" != 6a5ca0652392a2d7c9db2ae5b40210843c0bbc081cbd410825ab00cc59f14a6c ]; then
if [ "$(sha2 -q -256 libpng-$pngver.tar.xz)" != 4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 ]; then
sha2 -256 libpng-$pngver.tar.xz
echo Checksum mismatch! Aborting. >&2
exit 1

View File

@@ -16,8 +16,8 @@ function getlibrary ([string] $URI, [string] $filename, [string] $hash, [string]
}
getlibrary 'https://www.zlib.net/zlib131.zip' 'zlib.zip' '72af66d44fcc14c22013b46b814d5d2514673dda3d115e64b690c1ad636e7b17' .
getlibrary 'https://github.com/glennrp/libpng/archive/refs/tags/v1.6.43.zip' 'libpng.zip' '5e18474a26814ae479e02ca6432da32d19dc6e615551d140c954a68d63b3f192' .
getlibrary 'https://github.com/lexxmark/winflexbison/releases/download/v2.5.25/win_flex_bison-2.5.25.zip' 'winflexbison.zip' '8d324b62be33604b2c45ad1dd34ab93d722534448f55a16ca7292de32b6ac135' install_dir
getlibrary 'https://github.com/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.43 libpng
Move-Item libpng-1.6.50 libpng

View File

@@ -1,22 +1,17 @@
#!/bin/bash
set -euo pipefail
pngver=1.6.43
pngver=1.6.50
arch="$1"
## Grab sources and check them
wget http://downloads.sourceforge.net/project/libpng/libpng16/$pngver/libpng-$pngver.tar.xz
wget http://downloads.sourceforge.net/project/apng/libpng/libpng16/libpng-$pngver-apng.patch.gz
sha256sum -c .github/scripts/mingw-w64-libpng-dev.sha256sums
echo 4df396518620a7aa3651443e87d1b2862e4e88cad135a8b93423e01706232307 libpng-$pngver.tar.xz | sha256sum -c -
## Extract sources and patch them
tar -xf libpng-$pngver.tar.xz
gunzip libpng-$pngver-apng.patch.gz
# Patch in apng support
env -C libpng-$pngver patch -p0 ../libpng-$pngver-apng.patch
## Start building!

View File

@@ -1,2 +0,0 @@
d6bd2a3f43f17020918a4c1bd81c1a78111b6f759af9c1d3c754f704a1bf0429 libpng-1.6.43-apng.patch.gz
6a5ca0652392a2d7c9db2ae5b40210843c0bbc081cbd410825ab00cc59f14a6c libpng-1.6.43.tar.xz

18
.github/workflows/analysis.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
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=

View File

@@ -37,7 +37,9 @@ jobs:
TAG_NAME=${GITHUB_REF#refs/tags/}
sed -i "2i LABEL org.opencontainers.image.description=\"RGBDS container image for the release version $TAG_NAME\"" Dockerfile
docker build . --tag ghcr.io/gbdev/rgbds:$TAG_NAME
docker tag ghcr.io/gbdev/rgbds:$TAG_NAME ghcr.io/gbdev/rgbds:latest
docker push ghcr.io/gbdev/rgbds:$TAG_NAME
docker push ghcr.io/gbdev/rgbds:latest
- name: Delete untagged container images
if: github.repository_owner == 'gbdev'

View File

@@ -1,4 +1,4 @@
name: Code coverage checking
name: Diff completeness check
on: pull_request
jobs:
@@ -11,7 +11,7 @@ jobs:
cd rgbds
git remote add upstream "${{ github.event.pull_request.base.repo.clone_url }}"
git fetch upstream
- name: Checkdiff
- name: Check diff
working-directory: rgbds
run: |
make checkdiff "BASE_REF=${{ github.event.pull_request.base.sha }}" Q= | tee log

12
.github/workflows/checkformat.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
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

34
.github/workflows/coverage.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: Code coverage report
on:
- push
- pull_request
jobs:
coverage:
runs-on: ubuntu-24.04
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Install deps
shell: bash
run: |
./.github/scripts/install_deps.sh ubuntu
- 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
run: |
contrib/coverage.bash ubuntu-ci
- name: Upload coverage report
uses: actions/upload-artifact@v4
with:
name: coverage-report
# Workaround for keeping the top-level coverage/ directory
# https://github.com/actions/upload-artifact/issues/174
path: |
coverage
dummy-file-to-keep-directory-structure.txt

View File

@@ -61,12 +61,12 @@ jobs:
cmake --install build --verbose --prefix install_dir --strip
- name: Package binaries
run: |
Compress-Archive -LiteralPath @("install_dir/bin/rgbasm.exe", "install_dir/bin/rgblink.exe", "install_dir/bin/rgbfix.exe", "install_dir/bin/rgbgfx.exe", "install_dir/bin/zlib1.dll", "install_dir/bin/libpng16.dll") "rgbds-${{ env.version }}-win${{ matrix.bits }}.zip"
Compress-Archive -LiteralPath @("install_dir/bin/rgbasm.exe", "install_dir/bin/rgblink.exe", "install_dir/bin/rgbfix.exe", "install_dir/bin/rgbgfx.exe", "install_dir/bin/zlib1.dll", "install_dir/bin/libpng16.dll") "rgbds-win${{ matrix.bits }}.zip"
- name: Upload Windows binaries
uses: actions/upload-artifact@v4
with:
name: win${{ matrix.bits }}
path: rgbds-${{ env.version }}-win${{ matrix.bits }}.zip
path: rgbds-win${{ matrix.bits }}.zip
macos:
runs-on: macos-14
@@ -92,15 +92,15 @@ jobs:
strip rgb{asm,link,fix,gfx}
- name: Package binaries
run: |
zip --junk-paths rgbds-${{ env.version }}-macos.zip rgb{asm,link,fix,gfx} man/* .github/scripts/install.sh
zip --junk-paths rgbds-macos.zip rgb{asm,link,fix,gfx} man/* .github/scripts/install.sh
- name: Upload macOS binaries
uses: actions/upload-artifact@v4
with:
name: macos
path: rgbds-${{ env.version }}-macos.zip
path: rgbds-macos.zip
linux:
runs-on: ubuntu-20.04 # Oldest supported, for best glibc compatibility.
runs-on: ubuntu-22.04 # Oldest supported, for best glibc compatibility.
steps:
- name: Get version from tag
shell: bash
@@ -112,19 +112,19 @@ jobs:
- name: Install deps
shell: bash
run: |
./.github/scripts/install_deps.sh ubuntu-20.04
./.github/scripts/install_deps.sh ubuntu-22.04
- name: Build binaries
run: |
make -kj WARNFLAGS="-Wall -Wextra -pedantic -static" PKG_CONFIG="pkg-config --static" Q=
strip rgb{asm,link,fix,gfx}
- name: Package binaries
run: |
tar caf rgbds-${{ env.version }}-linux-x86_64.tar.xz --transform='s#.*/##' rgb{asm,link,fix,gfx} man/* .github/scripts/install.sh
tar caf rgbds-linux-x86_64.tar.xz --transform='s#.*/##' rgb{asm,link,fix,gfx} man/* .github/scripts/install.sh
- name: Upload Linux binaries
uses: actions/upload-artifact@v4
with:
name: linux
path: rgbds-${{ env.version }}-linux-x86_64.tar.xz
path: rgbds-linux-x86_64.tar.xz
release:
runs-on: ubuntu-latest
@@ -155,11 +155,11 @@ jobs:
draft: true # Don't publish the release quite yet...
prerelease: ${{ contains(github.ref, '-rc') }}
files: |
win32/rgbds-${{ env.version }}-win32.zip
win64/rgbds-${{ env.version }}-win64.zip
macos/rgbds-${{ env.version }}-macos.zip
linux/rgbds-${{ env.version }}-linux-x86_64.tar.xz
rgbds-${{ env.version }}.tar.gz
win32/rgbds-win32.zip
win64/rgbds-win64.zip
macos/rgbds-macos.zip
linux/rgbds-linux-x86_64.tar.xz
rgbds-source.tar.gz
fail_on_unmatched_files: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -7,7 +7,7 @@ jobs:
unix:
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-22.04, macos-14]
os: [ubuntu-22.04, macos-14]
cxx: [g++, clang++]
buildsys: [make, cmake]
exclude:
@@ -69,7 +69,7 @@ jobs:
- name: Run tests
shell: bash
run: |
CXX=${{ matrix.cxx }} test/run-tests.sh
CXX=${{ matrix.cxx }} test/run-tests.sh --os ${{ matrix.os }}
macos-static:
runs-on: macos-14
@@ -121,13 +121,13 @@ jobs:
- name: Run tests
shell: bash
run: |
test/run-tests.sh
test/run-tests.sh --os macos
windows:
strategy:
matrix:
bits: [32, 64]
os: [windows-2019, windows-2022]
os: [windows-2022, windows-2025]
include:
- bits: 32
arch: x86
@@ -149,9 +149,10 @@ jobs:
path: |
zbuild
pngbuild
key: ${{ matrix.arch }}-${{ hashFiles('zlib/**', 'libpng/**') }}
key: ${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles('zlib/**', 'libpng/**') }}
- name: Build zlib
if: steps.cache.outputs.cache-hit != 'true'
shell: bash
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
@@ -212,7 +213,7 @@ jobs:
run: |
cp bins/* .
cp bins/*.dll test/gfx
test/run-tests.sh
test/run-tests.sh --os ${{ matrix.os }}
windows-mingw-build:
strategy:
@@ -270,7 +271,7 @@ jobs:
needs: windows-mingw-build
strategy:
matrix:
os: [windows-2019, windows-2022]
os: [windows-2022, windows-2025]
bits: [32, 64]
fail-fast: false
runs-on: ${{ matrix.os }}
@@ -319,4 +320,70 @@ jobs:
- name: Run tests
shell: bash
run: |
test/run-tests.sh
test/run-tests.sh --os ${{ matrix.os }}
cygwin:
strategy:
matrix:
bits: [32, 64]
include:
- bits: 32
arch: x86
- bits: 64
arch: x86_64
fail-fast: false
runs-on: windows-2022
timeout-minutes: 30
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Setup Cygwin
uses: cygwin/cygwin-install-action@v4
with:
platform: ${{ matrix.arch }}
packages: >-
bison
gcc-g++
git
libpng-devel
make
pkg-config
- name: Build & install using Make
shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -o igncr '{0}'
run: | # Cygwin does not support `make develop` sanitizers ASan or UBSan
make -kj Q=
make install -j Q=
- name: Run tests
shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -o igncr '{0}'
run: |
test/run-tests.sh --only-internal
freebsd:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout repo
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Build & test using CMake on FreeBSD
uses: vmactions/freebsd-vm@v1
with:
release: "15.0"
usesh: true
prepare: |
pkg install -y \
bash \
bison \
cmake \
git \
png
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 --build build --target test

1
.gitignore vendored
View File

@@ -14,4 +14,5 @@ CMakeCache.txt
CMakeFiles/
cmake_install.cmake
build/
*.dSYM/
callgrind.out.*

View File

@@ -45,25 +45,21 @@ else()
add_compile_options(-Wno-gnu-zero-variadic-macro-arguments)
endif()
if(SANITIZERS)
set(SAN_FLAGS -fsanitize=shift -fsanitize=integer-divide-by-zero
-fsanitize=unreachable -fsanitize=vla-bound
-fsanitize=signed-integer-overflow -fsanitize=bounds
-fsanitize=object-size -fsanitize=bool -fsanitize=enum
-fsanitize=alignment -fsanitize=null -fsanitize=address)
set(SAN_FLAGS -fsanitize=address -fsanitize=undefined
-fsanitize=float-divide-by-zero)
add_compile_options(${SAN_FLAGS})
add_link_options(${SAN_FLAGS})
add_definitions(-D_GLIBCXX_ASSERTIONS)
# A non-zero optimization level is desired in debug mode, but allow overriding it nonetheless
# TODO: this overrides anything previously set... that's a bit sloppy!
set(CMAKE_CXX_FLAGS_DEBUG "-g -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE STRING "" FORCE)
set(CMAKE_CXX_FLAGS_DEBUG "-g -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls ${CMAKE_CXX_FLAGS_DEBUG}"
CACHE STRING "" FORCE)
endif()
if(MORE_WARNINGS)
add_compile_options(-Werror -Wextra
-Walloc-zero -Wcast-align -Wcast-qual -Wduplicated-branches -Wduplicated-cond
-Wfloat-equal -Wlogical-op -Wnull-dereference -Wold-style-cast -Wshift-overflow=2
-Wstringop-overflow=4 -Wundef -Wuninitialized -Wunused
-Wshadow # TODO: -Wshadow=compatible-local?
-Wstringop-overflow=4 -Wtrampolines -Wundef -Wuninitialized -Wunused -Wshadow
-Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1
-Wno-format-nonliteral -Wno-strict-overflow
-Wno-unused-but-set-variable # bison's `yynerrs_` is incremented but unused
@@ -78,7 +74,7 @@ endif()
find_program(GIT git)
if(GIT)
execute_process(COMMAND ${GIT} --git-dir=.git describe --tags --dirty --always
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)

View File

@@ -97,7 +97,7 @@ All tests begin by assembling the `.asm` file into an object file, which will be
These simply check that RGBLINK's output matches some expected output.
A `.out` file **must** exist, and RGBLINK's output must match that file's contents.
A `.out` file **must** exist, and RGBLINK's total output must match that file's contents.
Additionally, if a `.out.bin` file exists, the `.gb` file generated by RGBLINK must match it.
@@ -106,14 +106,14 @@ Additionally, if a `.out.bin` file exists, the `.gb` file generated by RGBLINK m
These allow applying various linker scripts to the same object file.
If one or more `.link` files exist, whose names start the same as the `.asm` file, then each of those files correspond to one test.
Each `.link` linker script **must** be accompanied by a `.out` file, and RGBLINK's output must match that file's contents when passed the corresponding linker script.
Each `.link` linker script **must** be accompanied by a `.out` file, and RGBLINK's total output must match that file's contents when passed the corresponding linker script.
#### Variant tests
These allow testing RGBLINK's `-d`, `-t`, and `-w` flags.
If one or more <code>-<var>&lt;flag&gt;</var>.out</code> or <code>-no-<var>&lt;flag&gt;</var>.out</code> files exist, then each of them corresponds to one test.
The object file will be linked with and without said flag, respectively; and in each case, RGBLINK's output must match the `.out` file's contents.
The object file will be linked with and without said flag, respectively; and in each case, RGBLINK's total output must match the `.out` file's contents.
### RGBFIX
@@ -123,8 +123,11 @@ Each one is a text file whose first line contains flags to pass to RGBFIX.
RGBFIX will be invoked on the `.bin` file if it exists, or else on default-input.bin.
If no `.out` file exist, RGBFIX is not expected to output anything.
If one *does* exist, RGBFIX's output **must** match the `.out` file's contents.
If no `.err` file exists, RGBFIX is simply expected to be able to process the file normally.
If one *does* exist, RGBFIX's return status is ignored, but its output **must** match the `.err` file's contents.
If one *does* exist, RGBFIX's return status is ignored, but its error output **must** match the `.err` file's contents.
Additionally, if a `.gb` file exists, the output of RGBFIX must match the `.gb`.

View File

@@ -1,6 +1,6 @@
FROM debian:11-slim
FROM debian:12-slim
LABEL org.opencontainers.image.source=https://github.com/gbdev/rgbds
ARG version=0.9.0
ARG version=0.9.4
WORKDIR /rgbds
COPY . .
@@ -8,7 +8,14 @@ COPY . .
RUN apt-get update && \
apt-get install sudo make cmake gcc build-essential -y
RUN ./.github/scripts/install_deps.sh ubuntu-20.04
# Install dependencies and compile RGBDS
RUN ./.github/scripts/install_deps.sh ubuntu-22.04
RUN make -j CXXFLAGS="-O3 -flto -DNDEBUG -static" PKG_CONFIG="pkg-config --static" Q=
RUN tar caf rgbds-${version}-linux-x86_64.tar.xz --transform='s#.*/##' rgbasm rgblink rgbfix rgbgfx man/* .github/scripts/install.sh
# Create an archive with the compiled executables and all the necessary to install it,
# so it can be copied outside of the container and installed/used in another system
RUN tar caf rgbds-linux-x86_64.tar.xz --transform='s#.*/##' rgbasm rgblink rgbfix rgbgfx man/* .github/scripts/install.sh
# Install RGBDS on the container so all the executables will be available in the PATH
RUN cp man/* .
RUN ./.github/scripts/install.sh

View File

@@ -1,6 +1,6 @@
The MIT License
Copyright (c) 1997-2024, Carsten Sørensen and RGBDS contributors.
Copyright (c) 1996-2025, Carsten Sørensen and RGBDS contributors.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to

119
Makefile
View File

@@ -3,53 +3,60 @@
.SUFFIXES:
.SUFFIXES: .cpp .y .o
.PHONY: all clean install checkdiff develop debug profile coverage iwyu mingw32 mingw64 wine-shim dist
.PHONY: all clean install checkdiff develop debug profile coverage tidy iwyu mingw32 mingw64 wine-shim dist
# User-defined variables
Q := @
PREFIX := /usr/local
bindir := ${PREFIX}/bin
mandir := ${PREFIX}/share/man
SUFFIX :=
STRIP := -s
BINMODE := 755
MANMODE := 644
Q := @
PREFIX := /usr/local
bindir := ${PREFIX}/bin
mandir := ${PREFIX}/share/man
SUFFIX :=
STRIP := -s
BINMODE := 755
MANMODE := 644
# Other variables
PKG_CONFIG := pkg-config
PNGCFLAGS := `${PKG_CONFIG} --cflags libpng`
PNGLDFLAGS := `${PKG_CONFIG} --libs-only-L libpng`
PNGLDLIBS := `${PKG_CONFIG} --libs-only-l libpng`
PKG_CONFIG := pkg-config
PNGCFLAGS := `${PKG_CONFIG} --cflags libpng`
PNGLDFLAGS := `${PKG_CONFIG} --libs-only-L libpng`
PNGLDLIBS := `${PKG_CONFIG} --libs-only-l libpng`
# Note: if this comes up empty, `version.cpp` will automatically fall back to last release number
VERSION_STRING := `git --git-dir=.git 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
# Overridable CXXFLAGS
CXXFLAGS ?= -O3 -flto -DNDEBUG
CXXFLAGS ?= -O3 -flto -DNDEBUG
# Non-overridable CXXFLAGS
REALCXXFLAGS := ${CXXFLAGS} ${WARNFLAGS} -std=c++2a -I include -fno-exceptions -fno-rtti
REALCXXFLAGS := ${CXXFLAGS} ${WARNFLAGS} -std=c++2a -I include -fno-exceptions -fno-rtti
# Overridable LDFLAGS
LDFLAGS ?=
LDFLAGS ?=
# Non-overridable LDFLAGS
REALLDFLAGS := ${LDFLAGS} ${WARNFLAGS} -DBUILD_VERSION_STRING=\"${VERSION_STRING}\"
REALLDFLAGS := ${LDFLAGS} ${WARNFLAGS} -DBUILD_VERSION_STRING=\"${VERSION_STRING}\"
# 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
BASE_REF := origin/master
# Rules to build the RGBDS binaries
all: rgbasm rgblink rgbfix rgbgfx
common_obj := \
src/extern/getopt.o \
src/diagnostics.o \
src/usage.o
rgbasm_obj := \
${common_obj} \
src/asm/actions.o \
src/asm/charmap.o \
src/asm/fixpoint.o \
src/asm/format.o \
@@ -64,9 +71,7 @@ rgbasm_obj := \
src/asm/section.o \
src/asm/symbol.o \
src/asm/warning.o \
src/extern/getopt.o \
src/extern/utf8decoder.o \
src/error.o \
src/linkdefs.o \
src/opmath.o \
src/util.o
@@ -74,7 +79,10 @@ rgbasm_obj := \
src/asm/lexer.o src/asm/main.o: src/asm/parser.hpp
rgblink_obj := \
${common_obj} \
src/link/assign.o \
src/link/lexer.o \
src/link/layout.o \
src/link/main.o \
src/link/object.o \
src/link/output.o \
@@ -83,34 +91,36 @@ rgblink_obj := \
src/link/sdas_obj.o \
src/link/section.o \
src/link/symbol.o \
src/extern/getopt.o \
src/link/warning.o \
src/extern/utf8decoder.o \
src/error.o \
src/linkdefs.o \
src/opmath.o \
src/util.o
src/link/main.o: src/link/script.hpp
src/link/lexer.o src/link/main.o: src/link/script.hpp
rgbfix_obj := \
${common_obj} \
src/fix/main.o \
src/extern/getopt.o \
src/error.o
src/fix/mbc.o \
src/fix/warning.o
rgbgfx_obj := \
${common_obj} \
src/gfx/color_set.o \
src/gfx/main.o \
src/gfx/pal_packing.o \
src/gfx/pal_sorting.o \
src/gfx/pal_spec.o \
src/gfx/png.o \
src/gfx/process.o \
src/gfx/proto_palette.o \
src/gfx/reverse.o \
src/gfx/rgba.o \
src/extern/getopt.o \
src/error.o
src/gfx/warning.o \
src/util.o
rgbasm: ${rgbasm_obj}
$Q${CXX} ${REALLDFLAGS} -o $@ ${rgbasm_obj} ${REALCXXFLAGS} src/version.cpp -lm
$Q${CXX} ${REALLDFLAGS} -o $@ ${rgbasm_obj} ${REALCXXFLAGS} src/version.cpp
rgblink: ${rgblink_obj}
$Q${CXX} ${REALLDFLAGS} -o $@ ${rgblink_obj} ${REALCXXFLAGS} src/version.cpp
@@ -142,6 +152,8 @@ src/link/script.hpp: src/link/script.cpp
$Qtouch $@
# Only RGBGFX uses libpng (POSIX make doesn't support pattern rules to cover all these)
src/gfx/color_set.o: src/gfx/color_set.cpp
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
src/gfx/main.o: src/gfx/main.cpp
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
src/gfx/pal_packing.o: src/gfx/pal_packing.cpp
@@ -150,9 +162,9 @@ src/gfx/pal_sorting.o: src/gfx/pal_sorting.cpp
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
src/gfx/pal_spec.o: src/gfx/pal_spec.cpp
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
src/gfx/process.o: src/gfx/process.cpp
src/gfx/png.o: src/gfx/png.cpp
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
src/gfx/proto_palette.o: src/gfx/proto_palette.cpp
src/gfx/process.o: src/gfx/process.cpp
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
src/gfx/reverse.o: src/gfx/reverse.cpp
$Q${CXX} ${REALCXXFLAGS} ${PNGCFLAGS} -c -o $@ $<
@@ -163,7 +175,6 @@ src/gfx/rgba.o: src/gfx/rgba.cpp
$Q${CXX} ${REALCXXFLAGS} -c -o $@ $<
# Target used to remove all files generated by other Makefile targets
clean:
$Q${RM} rgbasm rgbasm.exe
$Q${RM} rgblink rgblink.exe
@@ -177,7 +188,6 @@ clean:
$Q${RM} test/gfx/randtilegen test/gfx/rgbgfx_test
# Target used to install the binaries and man pages.
install: all
$Qinstall -d ${DESTDIR}${bindir}/ ${DESTDIR}${mandir}/man1/ ${DESTDIR}${mandir}/man5/ ${DESTDIR}${mandir}/man7/
$Qinstall ${STRIP} -m ${BINMODE} rgbasm ${DESTDIR}${bindir}/rgbasm${SUFFIX}
@@ -189,51 +199,46 @@ install: all
$Qinstall -m ${MANMODE} man/rgbds.7 man/gbz80.7 ${DESTDIR}${mandir}/man7/
# Target used to check for suspiciously missing changed files.
checkdiff:
$Qcontrib/checkdiff.bash `git merge-base HEAD ${BASE_REF}`
# This target is used during development in order to prevent adding new issues
# to the source code. All warnings are treated as errors in order to block the
# compilation and make the continous integration infrastructure return failure.
# 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
# continous integration infrastructure return failure.
# The rationale for some of the flags is documented in the CMakeLists.
develop:
$Q${MAKE} WARNFLAGS="${WARNFLAGS} -Werror -Wextra \
-Walloc-zero -Wcast-align -Wcast-qual -Wduplicated-branches -Wduplicated-cond \
-Wfloat-equal -Wlogical-op -Wnull-dereference -Wold-style-cast -Wshift-overflow=2 \
-Wstringop-overflow=4 -Wundef -Wuninitialized -Wunused -Wshadow \
-Wstringop-overflow=4 -Wtrampolines -Wundef -Wuninitialized -Wunused -Wshadow \
-Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1 \
-Wno-format-nonliteral -Wno-strict-overflow -Wno-unused-but-set-variable \
-Wno-type-limits -Wno-tautological-constant-out-of-range-compare -Wvla \
-D_GLIBCXX_ASSERTIONS \
-fsanitize=shift -fsanitize=integer-divide-by-zero \
-fsanitize=unreachable -fsanitize=vla-bound \
-fsanitize=signed-integer-overflow -fsanitize=bounds \
-fsanitize=object-size -fsanitize=bool -fsanitize=enum \
-fsanitize=alignment -fsanitize=null -fsanitize=address" \
-D_GLIBCXX_ASSERTIONS -fsanitize=address -fsanitize=undefined \
-fsanitize=float-divide-by-zero" \
CXXFLAGS="-ggdb3 -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls"
# This target is used during development in order to more easily debug with gdb.
# Target used in development to debug with gdb.
debug:
$Qenv ${MAKE} \
CXXFLAGS="-ggdb3 -O0 -fno-omit-frame-pointer -fno-optimize-sibling-calls"
# This target is used during development in order to more easily profile with callgrind.
# Target used in development to profile with callgrind.
profile:
$Qenv ${MAKE} \
CXXFLAGS="-ggdb3 -O3 -fno-omit-frame-pointer -fno-optimize-sibling-calls"
# This target is used during development in order to inspect code coverage with gcov.
# Target used in development to inspect code coverage with gcov.
coverage:
$Qenv ${MAKE} \
CXXFLAGS="-ggdb3 -Og --coverage -fno-omit-frame-pointer -fno-optimize-sibling-calls"
# This target is used during development in order to remove unused `#include` headers.
# Target used in development to check code with clang-tidy.
# Requires Bison-generated header files to exist.
tidy: src/asm/parser.hpp src/link/script.hpp
$Qclang-tidy -p . $$(find src -name '*.cpp')
# Target used in development to remove unused `#include` headers.
iwyu:
$Qenv ${MAKE} \
CXX="include-what-you-use" \
@@ -269,4 +274,4 @@ wine-shim:
dist:
$Qgit ls-files | sed s~^~$${PWD##*/}/~ \
| tar -czf rgbds-`git describe --tags | cut -c 2-`.tar.gz -C .. -T -
| tar -czf rgbds-source.tar.gz -C .. -T -

View File

@@ -6,7 +6,7 @@ for the Game Boy and Game Boy Color. It consists of:
- RGBASM (assembler)
- RGBLINK (linker)
- RGBFIX (checksum/header fixer)
- RGBGFX (PNGtoGame Boy graphics converter)
- RGBGFX (PNG-to-Game Boy graphics converter)
This is a fork of the original RGBDS which aims to make the programs more like
other UNIX tools.
@@ -93,6 +93,7 @@ The RGBDS source code file structure is as follows:
│ └── run-tests.sh
├── .clang-format
├── CMakeLists.txt
├── compile_flags.txt
├── Dockerfile
├── Makefile
└── README.md
@@ -118,6 +119,8 @@ The RGBDS source code file structure is as follows:
modify the behavior of RGBDS.
- `.clang-format` - code style for automated C++ formatting with
[`clang-format`](https://clang.llvm.org/docs/ClangFormat.html).
- `compile_flags.txt` - compiler flags for C++ static analysis with
[`clang-tidy`](https://clang.llvm.org/extra/clang-tidy/).
- `Dockerfile` - defines how to build RGBDS with Docker.
## 3. History
@@ -137,9 +140,12 @@ The RGBDS source code file structure is as follows:
this version as [rgbds-linux](https://github.com/vegard/rgbds-linux).
- 2010-01-12: Anthony J. Bentley [forks](https://github.com/bentley) Nossum's
repository. The fork becomes the reference implementation of RGBDS.
- 2010-09-25: Sørensen continues development of
[ASMotor](https://github.com/asmotor/asmotor) to this day.
- 2015-01-18: stag019 begins implementing [RGBGFX](https://github.com/stag019/rgbgfx),
a PNGtoGame Boy graphics converter, for eventual integration into RGBDS.
- 2016-09-05: RGBGFX is [integrated](https://github.com/gbdev/rgbds/commit/c3c31138ddbd8680d4e67957e387f2816798a71b)
a PNG-to-Game Boy graphics converter, for eventual integration into RGBDS.
- 2016-09-05: RGBGFX is
[integrated](https://github.com/gbdev/rgbds/commit/c3c31138ddbd8680d4e67957e387f2816798a71b)
into Bentley's repository.
- 2017-02-23: Bentley's repository is moved to the [rednex](https://github.com/rednex)
organization.

View File

@@ -68,13 +68,16 @@ GitHub.
6. Click the "Publish release" button to publish it!
7. Update the `release` branch. You can use `git push origin release`.
7. Update the `release` branch. You can use `git push origin master:release`.
8. Update the following related projects.
- [rgbobj](https://github.com/gbdev/rgbobj) and [rgbds-obj](https://github.com/gbdev/rgbds-obj):
make sure that object files created by the latest RGBASM can be parsed and displayed.
If the object file revision has been updated, rgbobj will need a corresponding release.
- [rgbds-www](https://github.com/gbdev/rgbds-www): update
[src/pages/versions.mdx](https://github.com/gbdev/rgbds-www/blob/master/src/pages/versions.mdx)
to list the new release.
1. [rgbds-www](https://github.com/gbdev/rgbds-www): update
[src/pages/versions.mdx](https://github.com/gbdev/rgbds-www/blob/master/src/pages/versions.mdx)
to list the new release.
2. [rgbds-live](https://github.com/gbdev/rgbds-live): update the `rgbds` submodule (and
[patches/rgbds.patch](https://github.com/gbdev/rgbds-live/blob/master/patches/rgbds.patch)
if necessary) to use the new release.
3. [rgbobj](https://github.com/gbdev/rgbobj) and [rgbds-obj](https://github.com/gbdev/rgbds-obj):
make sure that object files created by the latest RGBASM can be parsed and displayed.
If the object file revision has been updated, `rgbobj` will need a corresponding release.

6
compile_flags.txt Normal file
View File

@@ -0,0 +1,6 @@
-std=c++2a
-I
include
-fno-exceptions
-fno-rtti
-fno-caret-diagnostics

View File

@@ -24,12 +24,13 @@ _rgbasm_completions() {
# Empty long opt = it doesn't exit
# See the `state` variable below for info about `state_after`
declare -A opts=(
[h]="help:normal"
[V]="version:normal"
[E]="export-all:normal"
[v]="verbose:normal"
[W]="warning:warning"
[w]=":normal"
[b]="binary-digits:unk"
[D]="define:unk"
[E]="export-all:normal"
[g]="gfx-chars:unk"
[I]="include:dir"
[M]="dependfile:glob-*.mk *.d"
@@ -39,7 +40,7 @@ _rgbasm_completions() {
[Q]="q-precision:unk"
[r]="recursion-depth:unk"
[s]="state:unk"
[W]="warning:warning"
[v]="verbose:normal"
[X]="max-errors:unk"
)
# Parse command-line up to current word
@@ -154,7 +155,7 @@ _rgbasm_completions() {
parse_short_opt "$cur_word"
if [[ "$state" = 'normal' ]]; then
mapfile -t COMPREPLY < <(compgen -W "${!opts[*]}" -P "$cur_word" ''; compgen -W '-MG -MP -MQ -MT' "$cur_word")
mapfile -t COMPREPLY < <(compgen -W "${!opts[*]}" -P "$cur_word" ''; compgen -W '-MC -MG -MP -MQ -MT' "$cur_word")
return 0
elif [[ "$optlen" = "${#cur_word}" && "$state" != "warning" ]]; then
# This short option group only awaits its argument!

View File

@@ -7,22 +7,26 @@ _rgbfix_completions() {
# Empty long opt = it doesn't exit
# See the `state` variable below for info about `state_after`
declare -A opts=(
[h]="help:normal"
[V]="version:normal"
[j]="non-japanese:normal"
[s]="sgb-compatible:normal"
[v]="validate:normal"
[W]="warning:warning"
[w]=":normal"
[C]="color-only:normal"
[c]="color-compatible:normal"
[f]="fix-spec:fix-spec"
[i]="game-id:unk"
[j]="non-japanese:normal"
[k]="new-licensee:unk"
[L]="custom-logo:glob-*.1bpp"
[l]="old-licensee:unk"
[m]="mbc-type:mbc"
[n]="rom-version:unk"
[o]="output:glob-*.gb *.gbc *.sgb"
[p]="pad-value:unk"
[r]="ram-size:unk"
[s]="sgb-compatible:normal"
[t]="title:unk"
[v]="validate:normal"
)
# Parse command-line up to current word
local opt_ena=true
@@ -138,6 +142,16 @@ _rgbfix_completions() {
case "$state" in
unk) # Return with no replies: no idea what to complete!
;;
warning)
mapfile -t COMPREPLY < <(compgen -W "
mbc
overwrite
sgb
truncation
all
everything
error" -P "${cur_word:0:$optlen}" -- "${cur_word:$optlen}")
;;
fix-spec)
COMPREPLY=( "${cur_word}"{l,h,g,L,H,G} )
;;

View File

@@ -7,34 +7,38 @@ _rgbgfx_completions() {
# Empty long opt = it doesn't exit
# See the `state` variable below for info about `state_after`
declare -A opts=(
[h]="help:normal"
[V]="version:normal"
[C]="color-curve:normal"
[m]="mirror-tiles:normal"
[O]="group-outputs:normal"
[u]="unique-tiles:normal"
[v]="verbose:normal"
[X]="mirror-x:normal"
[Y]="mirror-y:normal"
[Z]="columns:normal"
[a]="attr-map:glob-*.attrmap"
[W]="warning:warning"
[w]=":normal"
[A]="auto-attr-map:normal"
[a]="attr-map:glob-*.attrmap"
[B]="background-color:unk"
[b]="base-tiles:unk"
[C]="color-curve:normal"
[c]="colors:unk"
[d]="depth:unk"
[i]="input-tileset:glob-*.2bpp"
[L]="slice:unk"
[m]="mirror-tiles:normal"
[N]="nb-tiles:unk"
[n]="nb-palettes:unk"
[O]="group-outputs:normal"
[o]="output:glob-*.2bpp"
[p]="palette:glob-*.pal"
[P]="auto-palette:normal"
[q]="palette-map:glob-*.palmap"
[p]="palette:glob-*.pal"
[Q]="auto-palette-map:normal"
[q]="palette-map:glob-*.palmap"
[r]="reverse:unk"
[s]="palette-size:unk"
[t]="tilemap:glob-*.tilemap"
[T]="auto-tilemap:normal"
[t]="tilemap:glob-*.tilemap"
[u]="unique-tiles:normal"
[v]="verbose:normal"
[X]="mirror-x:normal"
[x]="trim-end:unk"
[Y]="mirror-y:normal"
[Z]="columns:normal"
)
# Parse command-line up to current word
local opt_ena=true
@@ -150,6 +154,14 @@ _rgbgfx_completions() {
case "$state" in
unk) # Return with no replies: no idea what to complete!
;;
warning)
mapfile -t COMPREPLY < <(compgen -W "
embedded
trim-nonempty
all
everything
error" -P "${cur_word:0:$optlen}" -- "${cur_word:$optlen}")
;;
normal) # Acts like a glob...
state="glob-*.png"
;&

View File

@@ -7,19 +7,21 @@ _rgblink_completions() {
# Empty long opt = it doesn't exit
# See the `state` variable below for info about `state_after`
declare -A opts=(
[h]="help:normal"
[V]="version:normal"
[d]="dmg:normal"
[t]="tiny:normal"
[v]="verbose:normal"
[w]="wramx:normal"
[x]="nopad:normal"
[l]="linkerscript:glob-*"
[W]="warning:warning"
[M]="no-sym-in-map:normal"
[d]="dmg:normal"
[l]="linkerscript:glob-*"
[m]="map:glob-*.map"
[n]="sym:glob-*.sym"
[O]="overlay:glob-*.gb *.gbc *.sgb"
[o]="output:glob-*.gb *.gbc *.sgb"
[p]="pad:unk"
[t]="tiny:normal"
[v]="verbose:normal"
[w]="wramx:normal"
[x]="nopad:normal"
)
# Parse command-line up to current word
local opt_ena=true
@@ -135,6 +137,18 @@ _rgblink_completions() {
case "$state" in
unk) # Return with no replies: no idea what to complete!
;;
warning)
mapfile -t COMPREPLY < <(compgen -W "
assert
div
obsolete
shift
shift-amount
truncation
all
everything
error" -P "${cur_word:0:$optlen}" -- "${cur_word:$optlen}")
;;
normal) # Acts like a glob...
state="glob-*.o *.obj"
;&

View File

@@ -26,11 +26,23 @@ dependency include/linkdefs.hpp man/rgbds.5 \
dependency src/asm/parser.y man/rgbasm.5 \
"Was the rgbasm grammar changed?"
dependency src/asm/actions.cpp man/rgbasm.5 \
"Was the rgbasm grammar changed?"
dependency src/link/script.y man/rgblink.5 \
"Was the linker script grammar changed?"
dependency include/asm/warning.hpp man/rgbasm.1 \
dependency src/link/layout.cpp man/rgblink.5 \
"Was the linker script grammar changed?"
dependency include/asm/warning.hpp man/rgbasm.1 \
"Were the rgbasm warnings changed?"
dependency include/link/warning.hpp man/rgblink.1 \
"Were the rgblink warnings changed?"
dependency include/fix/warning.hpp man/rgbfix.1 \
"Were the rgbfix warnings changed?"
dependency include/gfx/warning.hpp man/rgbgfx.1 \
"Were the rgbgfx warnings changed?"
dependency src/asm/object.cpp include/linkdefs.hpp \
"Should the object file revision be bumped?"

15
contrib/checkformat.bash Executable file
View File

@@ -0,0 +1,15 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: MIT
clang-format --version
find . -type f \( -iname '*.hpp' -o -iname '*.cpp' \) -exec clang-format -i {} +
if ! git diff-index --quiet HEAD --; then
echo 'Unformatted files:'
git diff-index --name-only HEAD --
echo
git diff HEAD --
exit 1
fi

View File

@@ -7,20 +7,29 @@ make coverage -j
# Run the tests
pushd test
./fetch-test-deps.sh
./run-tests.sh
if [[ $# -eq 0 ]]; then
./run-tests.sh
else
./run-tests.sh --os "$1"
fi
popd
# Generate coverage logs
gcov src/**/*.cpp
mkdir -p coverage
# Generate coverage report
lcov -c --no-external -d . -o coverage/coverage.info
genhtml -f -s -o coverage/ coverage/coverage.info
# Generate coverage report, excluding Bison-generated files
COVERAGE_INFO=coverage/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"
genhtml --dark-mode -f -s -o coverage/ "$COVERAGE_INFO"
# Open report in web browser
if [ "$(uname)" == "Darwin" ]; then
open coverage/index.html
else
xdg-open coverage/index.html
# Check whether running from coverage.yml workflow
if [ "$1" != "ubuntu-ci" ]; then
# Open report in web browser
if [ "$(uname)" == "Darwin" ]; then
open coverage/index.html
else
xdg-open coverage/index.html
fi
fi

View File

@@ -32,14 +32,15 @@ diff <(xxd "$1") <(xxd "$2") | while read -r LINE; do
# Ignore comment lines, only pick matching bank
# (The bank regex ignores comments already, make `cut` and `tr` process less lines)
grep -Ei "$(printf "^%02x:" $BANK)" "$SYMFILE" |
sed "s/$(printf "^%02x:" $BANK)/0x/g" |
cut -d ';' -f 1 |
tr -d "\r" |
sort -g |
while read -r SYMADDR SYM; do
SYMADDR=$((0x${SYMADDR#*:}))
SYMADDR=$(($SYMADDR))
if [[ $SYMADDR -le $ADDR ]]; then
printf " (%s+0x%x)\n" "$SYM" $((ADDR - SYMADDR))
fi
# TODO: assumes sorted sym files
done | tail -n 1
fi)
printf "%02x:%04x %s\n" $BANK $ADDR "$EXTRA"

View File

@@ -23,21 +23,20 @@ _rgbasm_warnings() {
'obsolete:Warn when using deprecated features'
'purge:Warn when purging exported symbols or labels'
'shift:Warn when shifting negative values'
'shift-amount:Warn when a shift'\''s operand it negative or \> 32'
'shift-amount:Warn when a shift'\''s operand is negative or \> 32'
'truncation:Warn when implicit truncation loses bits'
'unmapped-char:Warn on unmapped character'
'unmatched-directive:Warn on unmatched directive pair'
'unterminated-load:Warn on LOAD without ENDL'
'user:Warn when executing the WARN built-in'
)
# TODO: handle `no-` and `error=` somehow?
# TODO: handle `=0|1|2` levels for `numeric-string`, `purge`, `truncation`, and `unmapped-char`?
_describe warning warnings
}
local args=(
# Arguments are listed here in the same order as in the manual, except for the version
'(- : * options)'{-V,--version}'[Print version number]'
# Arguments are listed here in the same order as in the manual, except for the version and help
'(- : * options)'{-V,--version}'[Print version number and exit]'
'(- : * options)'{-h,--help}'[Print help text and exit]'
'(-E --export-all)'{-E,--export-all}'[Export all symbols]'
'(-v --verbose)'{-v,--verbose}'[Print additional messages regarding progression]'
@@ -47,9 +46,10 @@ local args=(
'*'{-D,--define}'+[Define a string symbol]:name + value (default 1):'
'(-g --gfx-chars)'{-g,--gfx-chars}'+[Change chars for gfx constants]:chars spec:'
'(-I --include)'{-I,--include}'+[Add an include directory]:include path:_files -/'
'(-M --dependfile)'{-M,--dependfile}"+[Write deps in make format]:output file:_files -g '*.{d,mk}'"
-MG'[Assume missing files should be generated]'
-MP'[Add phony targets to all deps]'
'(-M --dependfile)'{-M,--dependfile}"+[Write dependencies in Makefile format]:output file:_files -g '*.{d,mk}'"
-MC'[Continue after missing dependencies]'
-MG'[Assume missing dependencies should be generated]'
-MP'[Add phony targets to all dependencies]'
'*'-MT"+[Add a target to the rules]:target:_files -g '*.{d,mk,o}'"
'*'-MQ"+[Add a target to the rules]:target:_files -g '*.{d,mk,o}'"
'(-o --output)'{-o,--output}'+[Output file]:output file:_files'

View File

@@ -34,9 +34,25 @@ _mbc_names() {
_describe "MBC name" mbc_names
}
_rgbfix_warnings() {
local warnings=(
'error:Turn all warnings into errors'
'all:Enable most warning messages'
'everything:Enable literally everything'
'mbc:Warn about issues with MBC specs'
'overwrite:Warn when overwriting non-zero bytes'
'sgb:Warn when SGB flag conflicts with old licensee code'
'truncation:Warn when values are truncated to fit'
)
_describe warning warnings
}
local args=(
# Arguments are listed here in the same order as in the manual, except for the version
'(- : * options)'{-V,--version}'[Print version number]'
# Arguments are listed here in the same order as in the manual, except for the version and help
'(- : * options)'{-V,--version}'[Print version number and exit]'
'(- : * options)'{-h,--help}'[Print help text and exit]'
'(-C --color-only -c --color-compatible)'{-C,--color-only}'[Mark ROM as GBC-only]'
'(-C --color-only -c --color-compatible)'{-c,--color-compatible}'[Mark ROM as GBC-compatible]'
@@ -44,6 +60,7 @@ local args=(
'(-O --overwrite)'{-O,--overwrite}'[Allow overwriting non-zero bytes]'
'(-s --sgb-compatible)'{-s,--sgb-compatible}'[Set the SGB flag]'
'(-f --fix-spec -v --validate)'{-v,--validate}'[Shorthand for -f lhg]'
-w'[Disable all warnings]'
'(-f --fix-spec -v --validate)'{-f,--fix-spec}'+[Fix or trash some header values]:fix spec:'
'(-i --game-id)'{-i,--game-id}'+[Set game ID string]:4-char game ID:'
@@ -52,9 +69,11 @@ local args=(
'(-L --logo)'{-L,--logo}'+[Set custom logo]:1bpp image:'
'(-m --mbc-type)'{-m,--mbc-type}"+[Set MBC flags]:mbc name:_mbc_names"
'(-n --rom-version)'{-n,--rom-version}'+[Set ROM version]:rom version byte:'
'(-o --output)'{-o,--output}"+[Output file]:output file:_files -g '*.{gb,sgb,gbc}'"
'(-p --pad-value)'{-p,--pad-value}'+[Pad to next valid size using this byte as padding]:padding byte:'
'(-r --ram-size)'{-r,--ram-size}'+[Set RAM size]:ram size byte:'
'(-t --title)'{-t,--title}'+[Set title string]:11-char title string:'
'(-W --warning)'{-W,--warning}'+[Toggle warning flags]:warning flag:_rgbfix_warnings'
'*'":ROM files:_files -g '*.{gb,sgb,gbc}'"
)

View File

@@ -9,9 +9,23 @@ _depths() {
_describe 'bit depth' depths
}
_rgbgfx_warnings() {
local warnings=(
'error:Turn all warnings into errors'
'all:Enable most warning messages'
'everything:Enable literally everything'
'embedded:Warn when using embedded PLTE without "-c embedded"'
'trim-nonempty:Warn when "-x" trims nonempty tiles'
)
_describe warning warnings
}
local args=(
# Arguments are listed here in the same order as in the manual, except for the version
'(- : * options)'{-V,--version}'[Print version number]'
# Arguments are listed here in the same order as in the manual, except for the version and help
'(- : * options)'{-V,--version}'[Print version number and exit]'
'(- : * options)'{-h,--help}'[Print help text and exit]'
'(-a --attr-map -A --auto-attr-map)'{-A,--auto-attr-map}'[Shortcut for -a <file>.attrmap]'
'(-C --color-curve)'{-C,--color-curve}'[Generate palettes using GBC color curve]'
@@ -21,12 +35,14 @@ local args=(
'(-q --palette-map -Q --auto-palette-map)'{-Q,--auto-palette-map}'[Shortcut for -p <file>.palmap]'
'(-t --tilemap -T --auto-tilemap)'{-T,--auto-tilemap}'[Shortcut for -t <file>.tilemap]'
'(-u --unique-tiles)'{-u,--unique-tiles}'[Eliminate redundant tiles]'
{-v,--verbose}'[Enable verbose output]'
'(-v --verbose)'{-v,--verbose}'[Enable verbose output]'
-w'[Disable all warnings]'
'(-X --mirror-x)'{-X,--mirror-x}'[Eliminate horizontally mirrored tiles from output]'
'(-Y --mirror-y)'{-Y,--mirror-y}'[Eliminate vertically mirrored tiles from output]'
'(-Z --columns)'{-Z,--columns}'[Read the image in column-major order]'
'(-a --attr-map -A --auto-attr-map)'{-a,--attr-map}'+[Generate a map of tile attributes (mirroring)]:attrmap file:_files'
'(-B --background-color)'{-B,--background-color}'+[Ignore tiles containing only specified color]:color:'
'(-b --base-tiles)'{-b,--base-tiles}'+[Base tile IDs for tile map output]:base tile IDs:'
'(-c --colors)'{-c,--colors}'+[Specify color palettes]:palette spec:'
'(-d --depth)'{-d,--depth}'+[Set bit depth]:bit depth:_depths'
@@ -40,6 +56,7 @@ local args=(
'(-r --reverse)'{-r,--reverse}'+[Yield an image from binary data]:image width (in tiles):'
'(-s --palette-size)'{-s,--palette-size}'+[Limit palette size]:palette size:'
'(-t --tilemap -T --auto-tilemap)'{-t,--tilemap}'+[Generate a map of tile indices]:tilemap file:_files'
'(-W --warning)'{-W,--warning}'+[Toggle warning flags]:warning flag:_rgbgfx_warnings'
'(-x --trim-end)'{-x,--trim-end}'+[Trim end of output by this many tiles]:tile count:'
":input png file:_files -g '*.png'"

View File

@@ -1,8 +1,26 @@
#compdef rgblink
_rgblink_warnings() {
local warnings=(
'error:Turn all warnings into errors'
'all:Enable most warning messages'
'everything:Enable literally everything'
'assert:Warn when WARN-type asserts fail'
'div:Warn when dividing the smallest int by -1'
'obsolete:Warn when using deprecated features'
'shift:Warn when shifting negative values'
'shift-amount:Warn when a shift'\''s operand is negative or \> 32'
'truncation:Warn when implicit truncation loses bits'
)
_describe warning warnings
}
local args=(
# Arguments are listed here in the same order as in the manual, except for the version
'(- : * options)'{-V,--version}'[Print version number]'
# Arguments are listed here in the same order as in the manual, except for the version and help
'(- : * options)'{-V,--version}'[Print version number and exit]'
'(- : * options)'{-h,--help}'[Print help text and exit]'
'(-d --dmg)'{-d,--dmg}'[Enable DMG mode (-w + no VRAM banking)]'
'(-t --tiny)'{-t,--tiny}'[Enable tiny mode, disabling ROM banking]'
@@ -18,6 +36,7 @@ local args=(
'(-o --output)'{-o,--output}"+[Write ROM image to this file]:rom file:_files -g '*.{gb,sgb,gbc}'"
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
'(-S --scramble)'{-s,--scramble}'+[Activate scrambling]:scramble spec'
'(-W --warning)'{-W,--warning}'+[Toggle warning flags]:warning flag:_rgblink_warnings'
'*'":object files:_files -g '*.o'"
)

42
include/asm/actions.hpp Normal file
View File

@@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT
#ifndef RGBDS_ASM_ACTIONS_HPP
#define RGBDS_ASM_ACTIONS_HPP
#include <optional>
#include <stdint.h>
#include <string>
#include <string_view>
#include <variant>
#include <vector>
#include "asm/output.hpp" // AssertionType
#include "asm/rpn.hpp" // RPNCommand
std::optional<std::string> act_ReadFile(std::string const &name, uint32_t maxLen);
uint32_t act_StringToNum(std::vector<int32_t> const &str);
size_t act_StringLen(std::string const &str, bool printErrors);
std::string act_StringSlice(std::string const &str, uint32_t start, uint32_t stop);
std::string act_StringSub(std::string const &str, uint32_t pos, uint32_t len);
size_t act_CharLen(std::string const &str);
std::string act_StringChar(std::string const &str, uint32_t idx);
std::string act_CharSub(std::string const &str, uint32_t pos);
int32_t act_CharCmp(std::string_view str1, std::string_view str2);
uint32_t act_AdjustNegativeIndex(int32_t idx, size_t len, char const *functionName);
uint32_t act_AdjustNegativePos(int32_t pos, size_t len, char const *functionName);
std::string act_StringReplace(std::string_view str, std::string const &old, std::string const &rep);
std::string act_StringFormat(
std::string const &spec, std::vector<std::variant<uint32_t, std::string>> const &args
);
void act_CompoundAssignment(std::string const &symName, RPNCommand op, int32_t constValue);
void act_FailAssert(AssertionType type);
void act_FailAssertMsg(AssertionType type, std::string const &message);
#endif // RGBDS_ASM_ACTIONS_HPP

View File

@@ -1,8 +1,9 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_ASM_CHARMAP_HPP
#define RGBDS_ASM_CHARMAP_HPP
#include <optional>
#include <stdint.h>
#include <string>
#include <string_view>
@@ -20,8 +21,11 @@ void charmap_Push();
void charmap_Pop();
void charmap_CheckStack();
void charmap_Add(std::string const &mapping, std::vector<int32_t> &&value);
bool charmap_HasChar(std::string const &input);
bool charmap_HasChar(std::string const &mapping);
size_t charmap_CharSize(std::string const &mapping);
std::optional<int32_t> charmap_CharValue(std::string const &mapping, size_t idx);
std::vector<int32_t> charmap_Convert(std::string const &input);
size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output);
std::string charmap_Reverse(std::vector<int32_t> const &value, bool &unique);
#endif // RGBDS_ASM_CHARMAP_HPP

View File

@@ -1,14 +1,10 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_ASM_FIXPOINT_HPP
#define RGBDS_ASM_FIXPOINT_HPP
#include <stdint.h>
extern uint8_t fixPrecision;
uint8_t fix_Precision();
double fix_PrecisionFactor();
int32_t fix_Sin(int32_t i, int32_t q);
int32_t fix_Cos(int32_t i, int32_t q);
int32_t fix_Tan(int32_t i, int32_t q);

View File

@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_ASM_FORMAT_HPP
#define RGBDS_ASM_FORMAT_HPP

View File

@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
// Contains some assembler-wide defines and externs
@@ -10,16 +10,16 @@
#include <stdint.h>
#include <stdio.h>
#include <string>
#include <variant>
#include <vector>
#include "either.hpp"
#include "linkdefs.hpp"
#include "asm/lexer.hpp"
struct FileStackNode {
FileStackNodeType type;
Either<
std::variant<
std::vector<uint32_t>, // NODE_REPT
std::string // NODE_FILE, NODE_MACRO
>
@@ -34,37 +34,34 @@ struct FileStackNode {
uint32_t ID = UINT32_MAX;
// REPT iteration counts since last named node, in reverse depth order
std::vector<uint32_t> &iters() { return data.get<std::vector<uint32_t>>(); }
std::vector<uint32_t> const &iters() const { return data.get<std::vector<uint32_t>>(); }
std::vector<uint32_t> &iters() { return std::get<std::vector<uint32_t>>(data); }
std::vector<uint32_t> const &iters() const { return std::get<std::vector<uint32_t>>(data); }
// File name for files, file::macro name for macros
std::string &name() { return data.get<std::string>(); }
std::string const &name() const { return data.get<std::string>(); }
std::string &name() { return std::get<std::string>(data); }
std::string const &name() const { return std::get<std::string>(data); }
FileStackNode(FileStackNodeType type_, Either<std::vector<uint32_t>, std::string> data_)
: type(type_), data(data_){};
FileStackNode(FileStackNodeType type_, std::variant<std::vector<uint32_t>, std::string> data_)
: type(type_), data(data_) {}
std::string const &dump(uint32_t curLineNo) const;
// If true, entering this context generates a new unique ID.
bool generatesUniqueID() const { return type == NODE_REPT || type == NODE_MACRO; }
std::string reptChain() const;
};
#define DEFAULT_MAX_DEPTH 64
extern size_t maxRecursionDepth;
struct MacroArgs;
void fstk_DumpCurrent();
bool fstk_DumpCurrent();
std::shared_ptr<FileStackNode> fstk_GetFileStack();
std::shared_ptr<std::string> fstk_GetUniqueIDStr();
MacroArgs *fstk_GetCurrentMacroArgs();
void fstk_AddIncludePath(std::string const &path);
void fstk_SetPreIncludeFile(std::string const &path);
void fstk_AddPreIncludeFile(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_FailedOnMissingInclude();
bool yywrap();
void fstk_RunInclude(std::string const &path, bool updateStateNow);
bool fstk_RunInclude(std::string const &path);
void fstk_RunMacro(std::string const &macroName, std::shared_ptr<MacroArgs> macroArgs);
void fstk_RunRept(uint32_t count, int32_t reptLineNo, ContentSpan const &span);
void fstk_RunFor(
@@ -75,10 +72,9 @@ void fstk_RunFor(
int32_t reptLineNo,
ContentSpan const &span
);
void fstk_StopRept();
bool fstk_Break();
void fstk_NewRecursionDepth(size_t newDepth);
void fstk_Init(std::string const &mainPath, size_t maxDepth);
void fstk_Init(std::string const &mainPath);
#endif // RGBDS_ASM_FSTACK_HPP

View File

@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_ASM_LEXER_HPP
#define RGBDS_ASM_LEXER_HPP
@@ -8,14 +8,14 @@
#include <optional>
#include <stdint.h>
#include <string>
#include <variant>
#include <vector>
#include "either.hpp"
#include "platform.hpp" // SSIZE_MAX
// This value is a compromise between `LexerState` allocation performance when `mmap` works, and
// buffering performance when it doesn't/can't (e.g. when piping a file into RGBASM).
#define LEXER_BUF_SIZE 64
// 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
@@ -83,8 +83,8 @@ struct LexerState {
LexerMode mode;
bool atLineStart;
uint32_t lineNo;
uint32_t colNo;
int lastToken;
int nextToken;
std::deque<IfStackEntry> ifStack;
@@ -92,13 +92,12 @@ struct LexerState {
size_t captureSize; // Amount of text captured
std::shared_ptr<std::vector<char>> captureBuf; // Buffer to send the captured text to if set
bool disableMacroArgs;
bool disableInterpolation;
size_t macroArgScanDistance; // Max distance already scanned for macro args
bool disableExpansions;
size_t expansionScanDistance; // Max distance already scanned for expansions
bool expandStrings;
std::deque<Expansion> expansions; // Front is the innermost current expansion
Either<ViewedContent, BufferedContent> content;
std::variant<std::monostate, ViewedContent, BufferedContent> content;
~LexerState();
@@ -110,30 +109,17 @@ struct LexerState {
}
void setAsCurrentState();
bool 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 clear(uint32_t lineNo_);
};
extern char binDigits[2];
extern char gfxDigits[4];
static inline void lexer_SetBinDigits(char const digits[2]) {
binDigits[0] = digits[0];
binDigits[1] = digits[1];
}
static inline void lexer_SetGfxDigits(char const digits[4]) {
gfxDigits[0] = digits[0];
gfxDigits[1] = digits[1];
gfxDigits[2] = digits[2];
gfxDigits[3] = digits[3];
}
void lexer_SetBinDigits(char const digits[2]);
void lexer_SetGfxDigits(char const digits[4]);
bool lexer_AtTopLevel();
void lexer_RestartRept(uint32_t lineNo);
void lexer_Init();
void lexer_SetMode(LexerMode mode);
void lexer_ToggleStringExpansion(bool enable);
@@ -147,7 +133,6 @@ void lexer_ReachELSEBlock();
void lexer_CheckRecursionDepth();
uint32_t lexer_GetLineNo();
uint32_t lexer_GetColNo();
void lexer_DumpStringExpansions();
struct Capture {

View File

@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_ASM_MACRO_HPP
#define RGBDS_ASM_MACRO_HPP
@@ -9,11 +9,11 @@
#include <vector>
struct MacroArgs {
unsigned int shift;
uint32_t shift;
std::vector<std::shared_ptr<std::string>> args;
uint32_t nbArgs() const { return args.size() - shift; }
std::shared_ptr<std::string> getArg(uint32_t i) const;
std::shared_ptr<std::string> getArg(int32_t i) const;
std::shared_ptr<std::string> getAllArgs() const;
void appendArg(std::shared_ptr<std::string> arg);

View File

@@ -1,18 +1,52 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_ASM_MAIN_HPP
#define RGBDS_ASM_MAIN_HPP
#include <stdint.h>
#include <stdio.h>
#include <string>
extern bool verbose;
extern bool warnings; // True to enable warnings, false to disable them.
enum MissingInclude {
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_CONTINUE, // A missing included file is assumed to be generated; continue assembling
};
extern FILE *dependFile;
extern std::string targetFileName;
extern bool generatedMissingIncludes;
extern bool failedOnMissingInclude;
extern bool generatePhonyDeps;
struct Options {
uint8_t fixPrecision = 16; // -Q
size_t maxRecursionDepth = 64; // -r
char binDigits[2] = {'0', '1'}; // -b
char gfxDigits[4] = {'0', '1', '2', '3'}; // -g
bool verbose = false; // -v
FILE *dependFile = nullptr; // -M
std::string targetFileName; // -MQ, -MT
MissingInclude missingIncludeState = INC_ERROR; // -MC, -MG
bool generatePhonyDeps = false; // -MP
std::string objectFileName; // -o
uint8_t padByte = 0; // -p
uint64_t maxErrors = 0; // -X
~Options() {
if (dependFile) {
fclose(dependFile);
}
}
void printDep(std::string const &depName) {
if (dependFile) {
fprintf(dependFile, "%s: %s\n", targetFileName.c_str(), depName.c_str());
}
}
};
extern Options options;
#define verbosePrint(...) \
do { \
if (options.verbose) { \
fprintf(stderr, __VA_ARGS__); \
} \
} while (0)
#endif // RGBDS_ASM_MAIN_HPP

View File

@@ -1,14 +1,14 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_ASM_OPT_HPP
#define RGBDS_ASM_OPT_HPP
#include <stdint.h>
void opt_B(char const chars[2]);
void opt_G(char const chars[4]);
void opt_B(char const binDigits[2]);
void opt_G(char const gfxDigits[4]);
void opt_P(uint8_t padByte);
void opt_Q(uint8_t precision);
void opt_Q(uint8_t fixPrecision);
void opt_W(char const *flag);
void opt_Parse(char const *option);

View File

@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_ASM_OUTPUT_HPP
#define RGBDS_ASM_OUTPUT_HPP
@@ -14,19 +14,9 @@
struct Expression;
struct FileStackNode;
enum StateFeature {
STATE_EQU,
STATE_VAR,
STATE_EQUS,
STATE_CHAR,
STATE_MACRO,
NB_STATE_FEATURES
};
extern std::string objectFileName;
enum StateFeature { STATE_EQU, STATE_VAR, STATE_EQUS, STATE_CHAR, STATE_MACRO, NB_STATE_FEATURES };
void out_RegisterNode(std::shared_ptr<FileStackNode> node);
void out_SetFileName(std::string const &name);
void out_CreatePatch(uint32_t type, Expression const &expr, uint32_t ofs, uint32_t pcShift);
void out_CreateAssert(
AssertionType type, Expression const &expr, std::string const &message, uint32_t ofs

View File

@@ -1,19 +1,19 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_ASM_RPN_HPP
#define RGBDS_ASM_RPN_HPP
#include <stdint.h>
#include <string>
#include <variant>
#include <vector>
#include "either.hpp"
#include "linkdefs.hpp"
struct Symbol;
struct Expression {
Either<
std::variant<
int32_t, // If the expression's value is known, it's here
std::string // Why the expression is not known, if it isn't
>
@@ -22,21 +22,8 @@ struct Expression {
std::vector<uint8_t> rpn{}; // Bytes serializing the RPN expression
uint32_t rpnPatchSize = 0; // Size the expression will take in the object file
Expression() = default;
Expression(Expression &&) = default;
#ifdef _MSC_VER
// MSVC and WinFlexBison won't build without this...
Expression(Expression const &) = default;
#endif
Expression &operator=(Expression &&) = default;
bool isKnown() const {
return data.holds<int32_t>();
}
int32_t value() const {
return data.get<int32_t>();
}
bool isKnown() const { return std::holds_alternative<int32_t>(data); }
int32_t value() const { return std::get<int32_t>(data); }
int32_t getConstVal() const;
Symbol const *symbolOf() const;
@@ -55,6 +42,7 @@ struct Expression {
bool makeCheckHRAM();
void makeCheckRST();
void makeCheckBitIndex(uint8_t mask);
void checkNBit(uint8_t n) const;

View File

@@ -1,10 +1,11 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_ASM_SECTION_HPP
#define RGBDS_ASM_SECTION_HPP
#include <deque>
#include <memory>
#include <optional>
#include <stdint.h>
#include <string>
#include <unordered_map>
@@ -12,8 +13,6 @@
#include "linkdefs.hpp"
extern uint8_t fillByte;
struct Expression;
struct FileStackNode;
struct Section;
@@ -42,6 +41,7 @@ struct Section {
std::deque<Patch> patches;
std::vector<uint8_t> data;
uint32_t getID() const; // ID of the section in the object file (`UINT32_MAX` if none)
bool isSizeKnown() const;
};
@@ -51,9 +51,8 @@ struct SectionSpec {
uint16_t alignOfs;
};
extern std::deque<Section> sectionList;
extern std::unordered_map<std::string, size_t> sectionMap; // Indexes into `sectionList`
extern Section *currentSection;
size_t sect_CountSections();
void sect_ForEach(void (*callback)(Section &));
Section *sect_FindSectionByName(std::string const &name);
void sect_NewSection(
@@ -76,6 +75,10 @@ void sect_CheckLoadClosed();
Section *sect_GetSymbolSection();
uint32_t sect_GetSymbolOffset();
uint32_t sect_GetOutputOffset();
std::optional<uint32_t> sect_GetOutputBank();
Patch *sect_AddOutputPatch();
uint32_t sect_GetAlignBytes(uint8_t alignment, uint16_t offset);
void sect_AlignPC(uint8_t alignment, uint16_t offset);
@@ -87,21 +90,23 @@ void sect_EndUnion();
void sect_CheckUnionClosed();
void sect_ConstByte(uint8_t byte);
void sect_ByteString(std::vector<int32_t> const &string);
void sect_WordString(std::vector<int32_t> const &string);
void sect_LongString(std::vector<int32_t> const &string);
void sect_ByteString(std::vector<int32_t> const &str);
void sect_WordString(std::vector<int32_t> const &str);
void sect_LongString(std::vector<int32_t> const &str);
void sect_Skip(uint32_t skip, bool ds);
void sect_RelByte(Expression &expr, uint32_t pcShift);
void sect_RelBytes(uint32_t n, std::vector<Expression> &exprs);
void sect_RelWord(Expression &expr, uint32_t pcShift);
void sect_RelLong(Expression &expr, uint32_t pcShift);
void sect_PCRelByte(Expression &expr, uint32_t pcShift);
void sect_BinaryFile(std::string const &name, int32_t startPos);
void sect_BinaryFileSlice(std::string const &name, int32_t startPos, int32_t length);
void sect_RelByte(Expression const &expr, uint32_t pcShift);
void sect_RelBytes(uint32_t n, std::vector<Expression> const &exprs);
void sect_RelWord(Expression const &expr, uint32_t pcShift);
void sect_RelLong(Expression const &expr, uint32_t pcShift);
void sect_PCRelByte(Expression const &expr, uint32_t pcShift);
bool sect_BinaryFile(std::string const &name, uint32_t startPos);
bool sect_BinaryFileSlice(std::string const &name, uint32_t startPos, uint32_t length);
void sect_EndSection();
void sect_PushSection();
void sect_PopSection();
void sect_CheckStack();
std::string sect_PushSectionFragmentLiteral();
#endif // RGBDS_ASM_SECTION_HPP

View File

@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_ASM_SYMBOL_HPP
#define RGBDS_ASM_SYMBOL_HPP
@@ -24,8 +24,8 @@ enum SymbolType {
SYM_REF // Forward reference to a label
};
struct Symbol; // For the `sym_IsPC` forward declaration
bool sym_IsPC(Symbol const *sym); // For the inline `getSection` method
struct Symbol; // Forward declaration for `sym_IsPC`
bool sym_IsPC(Symbol const *sym); // Forward declaration for `getSection`
struct Symbol {
std::string name;
@@ -82,7 +82,6 @@ Symbol *sym_RedefEqu(std::string const &symName, int32_t value);
Symbol *sym_AddVar(std::string const &symName, int32_t value);
int32_t sym_GetRSValue();
void sym_SetRSValue(int32_t value);
uint32_t sym_GetConstantValue(std::string const &symName);
// Find a symbol by exact name, bypassing expansion checks
Symbol *sym_FindExactSymbol(std::string const &symName);
// Find a symbol, possibly scoped, by name

View File

@@ -1,9 +1,18 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_ASM_WARNING_HPP
#define RGBDS_ASM_WARNING_HPP
extern unsigned int nbErrors, maxErrors;
#include <functional>
#include "diagnostics.hpp"
enum WarningLevel {
LEVEL_DEFAULT, // Warnings that are enabled by default
LEVEL_ALL, // Warnings that probably indicate an error
LEVEL_EXTRA, // Warnings that are less likely to indicate an error
LEVEL_EVERYTHING, // Literally every warning
};
enum WarningID {
WARNING_ASSERT, // Assertions
@@ -43,46 +52,34 @@ enum WarningID {
NB_WARNINGS,
};
enum WarningAbled { WARNING_DEFAULT, WARNING_ENABLED, WARNING_DISABLED };
extern Diagnostics<WarningLevel, WarningID> warnings;
struct WarningState {
WarningAbled state;
WarningAbled error;
// Used to warn the user about problems that don't prevent the generation of
// valid code.
[[gnu::format(printf, 2, 3)]]
void warning(WarningID id, char const *fmt, ...);
void update(WarningState other);
};
// Used for errors that compromise the whole assembly process by affecting the
// following code, potencially making the assembler generate errors caused by
// 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,
// when it fails to allocate memory).
[[gnu::format(printf, 1, 2), noreturn]]
void fatal(char const *fmt, ...);
struct Diagnostics {
WarningState flagStates[NB_WARNINGS];
WarningState metaStates[NB_WARNINGS];
};
// 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
// get a list of all errors at the end, making it easier to fix all of them at
// once.
[[gnu::format(printf, 1, 2)]]
void error(char const *fmt, ...);
extern Diagnostics warningStates;
extern bool warningsAreErrors;
// 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
// get a list of all errors at the end, making it easier to fix all of them at
// once.
void error(std::function<void()> callback);
void processWarningFlag(char const *flag);
/*
* Used to warn the user about problems that don't prevent the generation of
* valid code.
*/
[[gnu::format(printf, 2, 3)]] void warning(WarningID id, char const *fmt, ...);
/*
* Used for errors that compromise the whole assembly process by affecting the
* following code, potencially making the assembler generate errors caused by
* 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,
* when it fails to allocate memory).
*/
[[gnu::format(printf, 1, 2), noreturn]] void fatalerror(char const *fmt, ...);
/*
* 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
* get a list of all errors at the end, making it easier to fix all of them at
* once.
*/
[[gnu::format(printf, 1, 2)]] void error(char const *fmt, ...);
void requireZeroErrors();
#endif // RGBDS_ASM_WARNING_HPP

View File

@@ -1,41 +0,0 @@
/* SPDX-License-Identifier: MIT */
#ifndef RGBDS_DEFAULT_INIT_ALLOC_HPP
#define RGBDS_DEFAULT_INIT_ALLOC_HPP
#include <memory>
#include <vector>
/*
* Allocator adaptor that interposes construct() calls to convert value-initialization
* (which is what you get with e.g. `vector::resize`) into default-initialization (which does not
* zero out non-class types).
* From
* https://stackoverflow.com/questions/21028299/is-this-behavior-of-vectorresizesize-type-n-under-c11-and-boost-container/21028912#21028912
*/
template<typename T, typename A = std::allocator<T>>
class default_init_allocator : public A {
using a_t = std::allocator_traits<A>;
public:
template<typename U>
struct rebind {
using other = default_init_allocator<U, typename a_t::template rebind_alloc<U>>;
};
using A::A; // Inherit the allocator's constructors
template<typename U>
void construct(U *ptr) noexcept(std::is_nothrow_default_constructible_v<U>) {
::new (static_cast<void *>(ptr)) U;
}
template<typename U, typename... Args>
void construct(U *ptr, Args &&...args) {
a_t::construct(static_cast<A &>(*this), ptr, std::forward<Args>(args)...);
}
};
template<typename T>
using DefaultInitVec = std::vector<T, default_init_allocator<T>>;
#endif // RGBDS_DEFAULT_INIT_ALLOC_HPP

212
include/diagnostics.hpp Normal file
View File

@@ -0,0 +1,212 @@
// SPDX-License-Identifier: MIT
#ifndef RGBDS_DIAGNOSTICS_HPP
#define RGBDS_DIAGNOSTICS_HPP
#include <inttypes.h>
#include <optional>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <vector>
#include "helpers.hpp"
#include "itertools.hpp"
[[gnu::format(printf, 1, 2)]]
void warnx(char const *fmt, ...);
enum WarningAbled { WARNING_DEFAULT, WARNING_ENABLED, WARNING_DISABLED };
struct WarningState {
WarningAbled state;
WarningAbled error;
void update(WarningState other);
};
std::pair<WarningState, std::optional<uint32_t>> getInitialWarningState(std::string &flag);
template<typename L>
struct WarningFlag {
char const *name;
L level;
};
enum WarningBehavior { DISABLED, ENABLED, ERROR };
template<typename W>
struct ParamWarning {
W firstID;
W lastID;
uint8_t defaultLevel;
};
template<typename W>
struct DiagnosticsState {
WarningState flagStates[W::NB_WARNINGS];
WarningState metaStates[W::NB_WARNINGS];
bool warningsEnabled = true;
bool warningsAreErrors = false;
};
template<typename L, typename W>
struct Diagnostics {
std::vector<WarningFlag<L>> metaWarnings;
std::vector<WarningFlag<L>> warningFlags;
std::vector<ParamWarning<W>> paramWarnings;
DiagnosticsState<W> state;
uint64_t nbErrors;
void incrementErrors() {
if (nbErrors != UINT64_MAX) {
++nbErrors;
}
}
WarningBehavior getWarningBehavior(W id) const;
std::string processWarningFlag(char const *flag);
};
template<typename L, typename W>
WarningBehavior Diagnostics<L, W>::getWarningBehavior(W id) const {
// Check if warnings are globally disabled
if (!state.warningsEnabled) {
return WarningBehavior::DISABLED;
}
// Get the state of this warning flag
WarningState const &flagState = state.flagStates[id];
WarningState const &metaState = state.metaStates[id];
// If subsequent checks determine that the warning flag is enabled, this checks whether it has
// -Werror without -Wno-error=<flag> or -Wno-error=<meta>, which makes it into an error
bool warningIsError = state.warningsAreErrors && flagState.error != WARNING_DISABLED
&& metaState.error != WARNING_DISABLED;
WarningBehavior enabledBehavior =
warningIsError ? WarningBehavior::ERROR : WarningBehavior::ENABLED;
// First, check the state of the specific warning flag
if (flagState.state == WARNING_DISABLED) { // -Wno-<flag>
return WarningBehavior::DISABLED;
}
if (flagState.error == WARNING_ENABLED) { // -Werror=<flag>
return WarningBehavior::ERROR;
}
if (flagState.state == WARNING_ENABLED) { // -W<flag>
return enabledBehavior;
}
// If no flag is specified, check the state of the "meta" flags that affect this warning flag
if (metaState.state == WARNING_DISABLED) { // -Wno-<meta>
return WarningBehavior::DISABLED;
}
if (metaState.error == WARNING_ENABLED) { // -Werror=<meta>
return WarningBehavior::ERROR;
}
if (metaState.state == WARNING_ENABLED) { // -W<meta>
return enabledBehavior;
}
// If no meta flag is specified, check the default state of this warning flag
if (warningFlags[id].level == L::LEVEL_DEFAULT) { // enabled by default
return enabledBehavior;
}
// No flag enables this warning, explicitly or implicitly
return WarningBehavior::DISABLED;
}
template<typename L, typename W>
std::string Diagnostics<L, W>::processWarningFlag(char const *flag) {
std::string rootFlag = flag;
// Check for `-Werror` or `-Wno-error` to return early
if (rootFlag == "error") {
// `-Werror` promotes warnings to errors
state.warningsAreErrors = true;
return rootFlag;
} else if (rootFlag == "no-error") {
// `-Wno-error` disables promotion of warnings to errors
state.warningsAreErrors = false;
return rootFlag;
}
auto [flagState, param] = getInitialWarningState(rootFlag);
// 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,
// which applies to all levels
for (ParamWarning<W> const &paramWarning : paramWarnings) {
W baseID = paramWarning.firstID;
uint8_t maxParam = paramWarning.lastID - baseID + 1;
assume(paramWarning.defaultLevel <= maxParam);
if (rootFlag != warningFlags[baseID].name) {
continue;
}
// If making the warning an error but param is 0, set to the maximum
// This accommodates `-Werror=<flag>`, but also `-Werror=<flag>=0`, which is
// thus filtered out by the caller.
// A param of 0 makes sense for disabling everything, but neither for
// enabling nor "erroring". Use the default for those.
if (!param.has_value() || *param == 0) {
param = paramWarning.defaultLevel;
} else if (*param > maxParam) {
warnx(
"Invalid warning flag parameter \"%s=%" PRIu32 "\"; capping at maximum %" PRIu8,
rootFlag.c_str(),
*param,
maxParam
);
*param = maxParam;
}
// Set the first <param> to enabled/error, and disable the rest
for (uint32_t ofs = 0; ofs < maxParam; ++ofs) {
if (WarningState &warning = state.flagStates[baseID + ofs]; ofs < *param) {
warning.update(flagState);
} else {
warning.state = WARNING_DISABLED;
}
}
return rootFlag;
}
if (param.has_value()) {
warnx("Unknown warning flag parameter \"%s=%" PRIu32 "\"", rootFlag.c_str(), *param);
return rootFlag;
}
// Try to match against a "meta" warning
for (WarningFlag<L> const &metaWarning : metaWarnings) {
if (rootFlag != metaWarning.name) {
continue;
}
// Set each of the warning flags that meets this level
for (W id : EnumSeq(W::NB_WARNINGS)) {
if (metaWarning.level >= warningFlags[id].level) {
state.metaStates[id].update(flagState);
}
}
return rootFlag;
}
// Try to match against a "normal" flag
for (W id : EnumSeq(W::NB_PLAIN_WARNINGS)) {
if (rootFlag == warningFlags[id].name) {
state.flagStates[id].update(flagState);
return rootFlag;
}
}
warnx("Unknown warning flag \"%s\"", rootFlag.c_str());
return rootFlag;
}
#endif // RGBDS_DIAGNOSTICS_HPP

View File

@@ -1,176 +0,0 @@
/* SPDX-License-Identifier: MIT */
#ifndef RGBDS_EITHER_HPP
#define RGBDS_EITHER_HPP
#include <type_traits>
#include <utility>
#include "helpers.hpp" // assume
template<typename T1, typename T2>
union Either {
typedef T1 type1;
typedef T2 type2;
private:
template<typename T, unsigned V>
struct Field {
constexpr static unsigned tag_value = V;
unsigned tag = tag_value;
T value;
Field() : value() {}
Field(T &value_) : value(value_) {}
Field(T const &value_) : value(value_) {}
Field(T &&value_) : value(std::move(value_)) {}
};
// The `_tag` unifies with the first `tag` member of each `struct`.
constexpr static unsigned nulltag = 0;
unsigned _tag = nulltag;
Field<T1, 1> _t1;
Field<T2, 2> _t2;
// Value accessors; the function parameters are dummies for overload resolution.
// Only used to implement `field()` below.
auto &pick(T1 *) { return _t1; }
auto const &pick(T1 *) const { return _t1; }
auto &pick(T2 *) { return _t2; }
auto const &pick(T2 *) const { return _t2; }
// Generic field accessors; for internal use only.
template<typename T>
auto &field() {
return pick(static_cast<T *>(nullptr));
}
template<typename T>
auto const &field() const {
return pick(static_cast<T *>(nullptr));
}
public:
// Equivalent of `std::monostate` for `std::variant`s.
Either() : _tag() {}
// These constructors cannot be generic over the value type, because that would prevent
// constructible values from being inferred, e.g. a `const char *` string literal for an
// `std::string` field value.
Either(T1 &value) : _t1(value) {}
Either(T2 &value) : _t2(value) {}
Either(T1 const &value) : _t1(value) {}
Either(T2 const &value) : _t2(value) {}
Either(T1 &&value) : _t1(std::move(value)) {}
Either(T2 &&value) : _t2(std::move(value)) {}
// Destructor manually calls the appropriate value destructor.
~Either() {
if (_tag == _t1.tag_value) {
_t1.value.~T1();
} else if (_tag == _t2.tag_value) {
_t2.value.~T2();
}
}
// Copy assignment operators for each possible value.
Either &operator=(T1 const &value) {
_t1.tag = _t1.tag_value;
new (&_t1.value) T1(value);
return *this;
}
Either &operator=(T2 const &value) {
_t2.tag = _t2.tag_value;
new (&_t2.value) T2(value);
return *this;
}
// Move assignment operators for each possible value.
Either &operator=(T1 &&value) {
_t1.tag = _t1.tag_value;
new (&_t1.value) T1(std::move(value));
return *this;
}
Either &operator=(T2 &&value) {
_t2.tag = _t2.tag_value;
new (&_t2.value) T2(std::move(value));
return *this;
}
// Copy assignment operator from another `Either`.
Either &operator=(Either other) {
if (other._tag == other._t1.tag_value) {
*this = other._t1.value;
} else if (other._tag == other._t2.tag_value) {
*this = other._t2.value;
} else {
_tag = nulltag;
}
return *this;
}
// Copy constructor from another `Either`; implemented in terms of value assignment operators.
Either(Either const &other) {
if (other._tag == other._t1.tag_value) {
*this = other._t1.value;
} else if (other._tag == other._t2.tag_value) {
*this = other._t2.value;
} else {
_tag = nulltag;
}
}
// Move constructor from another `Either`; implemented in terms of value assignment operators.
Either(Either &&other) {
if (other._tag == other._t1.tag_value) {
*this = std::move(other._t1.value);
} else if (other._tag == other._t2.tag_value) {
*this = std::move(other._t2.value);
} else {
_tag = nulltag;
}
}
// Equivalent of `.emplace<T>()` for `std::variant`s.
template<typename T, typename... Args>
void emplace(Args &&...args) {
this->~Either();
if constexpr (std::is_same_v<T, T1>) {
_t1.tag = _t1.tag_value;
new (&_t1.value) T1(std::forward<Args>(args)...);
} else if constexpr (std::is_same_v<T, T2>) {
_t2.tag = _t2.tag_value;
new (&_t2.value) T2(std::forward<Args>(args)...);
} else {
_tag = nulltag;
}
}
// Equivalent of `std::holds_alternative<std::monostate>()` for `std::variant`s.
bool empty() const { return _tag == nulltag; }
// Equivalent of `std::holds_alternative<T>()` for `std::variant`s.
template<typename T>
bool holds() const {
if constexpr (std::is_same_v<T, T1>) {
return _tag == _t1.tag_value;
} else if constexpr (std::is_same_v<T, T2>) {
return _tag == _t2.tag_value;
} else {
return false;
}
}
// Equivalent of `std::get<T>()` for `std::variant`s.
template<typename T>
auto &get() {
assume(holds<T>());
return field<T>().value;
}
template<typename T>
auto const &get() const {
assume(holds<T>());
return field<T>().value;
}
};
#endif // RGBDS_EITHER_HPP

View File

@@ -1,15 +0,0 @@
/* SPDX-License-Identifier: MIT */
#ifndef RGBDS_ERROR_HPP
#define RGBDS_ERROR_HPP
extern "C" {
[[gnu::format(printf, 1, 2)]] void warn(char const *fmt...);
[[gnu::format(printf, 1, 2)]] void warnx(char const *fmt, ...);
[[gnu::format(printf, 1, 2), noreturn]] void err(char const *fmt, ...);
[[gnu::format(printf, 1, 2), noreturn]] void errx(char const *fmt, ...);
}
#endif // RGBDS_ERROR_HPP

View File

@@ -1,11 +1,15 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
/* This implementation was taken from musl and modified for RGBDS */
// This implementation was taken from musl and modified for RGBDS
#ifndef RGBDS_EXTERN_GETOPT_HPP
#define RGBDS_EXTERN_GETOPT_HPP
extern "C" {
// clang-format off: vertically align values
static constexpr int no_argument = 0;
static constexpr int required_argument = 1;
static constexpr int optional_argument = 2;
// clang-format on
extern char *musl_optarg;
extern int musl_optind, musl_opterr, musl_optopt, musl_optreset;
@@ -21,10 +25,4 @@ int musl_getopt_long_only(
int argc, char **argv, char const *optstring, option const *longopts, int *idx
);
#define no_argument 0
#define required_argument 1
#define optional_argument 2
} // extern "C"
#endif // RGBDS_EXTERN_GETOPT_HPP

View File

@@ -1,10 +1,13 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_EXTERN_UTF8DECODER_HPP
#define RGBDS_EXTERN_UTF8DECODER_HPP
#include <stdint.h>
#define UTF8_ACCEPT 0
#define UTF8_REJECT 12
uint32_t decode(uint32_t *state, uint32_t *codep, uint8_t byte);
#endif // RGBDS_EXTERN_UTF8DECODER_HPP

View File

@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_FILE_HPP
#define RGBDS_FILE_HPP
@@ -10,38 +10,27 @@
#include <streambuf>
#include <string.h>
#include <string>
#include <variant>
#include "either.hpp"
#include "helpers.hpp" // assume
#include "platform.hpp"
#include "gfx/main.hpp"
class File {
// Construct a `std::streambuf *` by default, since it's probably lighter than a `filebuf`.
Either<std::streambuf *, std::filebuf> _file;
std::variant<std::streambuf *, std::filebuf> _file;
public:
File() {}
~File() { close(); }
File() : _file(nullptr) {}
/**
* This should only be called once, and before doing any `->` operations.
* Returns `nullptr` on error, and a non-null pointer otherwise.
*/
// This should only be called once, and before doing any `->` operations.
// Returns `nullptr` on error, and a non-null pointer otherwise.
File *open(std::string const &path, std::ios_base::openmode mode) {
if (path != "-") {
_file.emplace<std::filebuf>();
return _file.get<std::filebuf>().open(path, mode) ? this : nullptr;
return _file.emplace<std::filebuf>().open(path, mode) ? this : nullptr;
} else if (mode & std::ios_base::in) {
assume(!(mode & std::ios_base::out));
_file.emplace<std::streambuf *>(std::cin.rdbuf());
if (setmode(STDIN_FILENO, (mode & std::ios_base::binary) ? O_BINARY : O_TEXT) == -1) {
fatal(
"Failed to set stdin to %s mode: %s",
mode & std::ios_base::binary ? "binary" : "text",
strerror(errno)
);
return nullptr;
}
} else {
assume(mode & std::ios_base::out);
@@ -50,8 +39,8 @@ public:
return this;
}
std::streambuf &operator*() {
return _file.holds<std::filebuf>() ? _file.get<std::filebuf>()
: *_file.get<std::streambuf *>();
return std::holds_alternative<std::filebuf>(_file) ? std::get<std::filebuf>(_file)
: *std::get<std::streambuf *>(_file);
}
std::streambuf const &operator*() const {
// The non-`const` version does not perform any modifications, so it's okay.
@@ -63,24 +52,10 @@ public:
return const_cast<File *>(this)->operator->();
}
File *close() {
if (_file.holds<std::filebuf>()) {
// This is called by the destructor, and an explicit `close` shouldn't close twice.
std::filebuf fileBuf = std::move(_file.get<std::filebuf>());
_file.emplace<std::streambuf *>(nullptr);
if (fileBuf.close() != nullptr) {
return this;
}
} else if (_file.get<std::streambuf *>() != nullptr) {
return this;
}
return nullptr;
}
char const *c_str(std::string const &path) const {
return _file.holds<std::filebuf>() ? path.c_str()
: _file.get<std::streambuf *>() == std::cin.rdbuf() ? "<stdin>"
: "<stdout>";
return std::holds_alternative<std::filebuf>(_file) ? path.c_str()
: std::get<std::streambuf *>(_file) == std::cin.rdbuf() ? "<stdin>"
: "<stdout>";
}
};

79
include/fix/mbc.hpp Normal file
View File

@@ -0,0 +1,79 @@
// SPDX-License-Identifier: MIT
#ifndef RGBDS_FIX_MBC_HPP
#define RGBDS_FIX_MBC_HPP
#include <stdint.h>
#include <stdio.h>
constexpr uint16_t UNSPECIFIED = 0x200;
static_assert(UNSPECIFIED > 0xFF, "UNSPECIFIED should not be in byte range!");
enum MbcType {
ROM = 0x00,
ROM_RAM = 0x08,
ROM_RAM_BATTERY = 0x09,
MBC1 = 0x01,
MBC1_RAM = 0x02,
MBC1_RAM_BATTERY = 0x03,
MBC2 = 0x05,
MBC2_BATTERY = 0x06,
MMM01 = 0x0B,
MMM01_RAM = 0x0C,
MMM01_RAM_BATTERY = 0x0D,
MBC3 = 0x11,
MBC3_TIMER_BATTERY = 0x0F,
MBC3_TIMER_RAM_BATTERY = 0x10,
MBC3_RAM = 0x12,
MBC3_RAM_BATTERY = 0x13,
MBC5 = 0x19,
MBC5_RAM = 0x1A,
MBC5_RAM_BATTERY = 0x1B,
MBC5_RUMBLE = 0x1C,
MBC5_RUMBLE_RAM = 0x1D,
MBC5_RUMBLE_RAM_BATTERY = 0x1E,
MBC6 = 0x20,
MBC7_SENSOR_RUMBLE_RAM_BATTERY = 0x22,
POCKET_CAMERA = 0xFC,
BANDAI_TAMA5 = 0xFD,
HUC3 = 0xFE,
HUC1_RAM_BATTERY = 0xFF,
// "Extended" values (still valid, but not directly actionable)
// A high byte of 0x01 means TPP1, the low byte is the requested features
// This does not include SRAM, which is instead implied by a non-zero SRAM size
// Note: Multiple rumble speeds imply rumble
TPP1 = 0x100,
TPP1_RUMBLE = 0x101,
TPP1_MULTIRUMBLE_RUMBLE = 0x103,
TPP1_TIMER = 0x104,
TPP1_TIMER_RUMBLE = 0x105,
TPP1_TIMER_MULTIRUMBLE_RUMBLE = 0x107,
TPP1_BATTERY = 0x108,
TPP1_BATTERY_RUMBLE = 0x109,
TPP1_BATTERY_MULTIRUMBLE_RUMBLE = 0x10B,
TPP1_BATTERY_TIMER = 0x10C,
TPP1_BATTERY_TIMER_RUMBLE = 0x10D,
TPP1_BATTERY_TIMER_MULTIRUMBLE_RUMBLE = 0x10F,
// Error values
MBC_NONE = UNSPECIFIED, // No MBC specified, do not act on it
};
bool mbc_HasRAM(MbcType type);
char const *mbc_Name(MbcType type);
MbcType mbc_ParseName(char const *name, uint8_t &tpp1Major, uint8_t &tpp1Minor);
#endif // RGBDS_FIX_MBC_HPP

41
include/fix/warning.hpp Normal file
View File

@@ -0,0 +1,41 @@
// SPDX-License-Identifier: MIT
#ifndef RGBDS_FIX_WARNING_HPP
#define RGBDS_FIX_WARNING_HPP
#include "diagnostics.hpp"
enum WarningLevel {
LEVEL_DEFAULT, // Warnings that are enabled by default
LEVEL_ALL, // Warnings that probably indicate an error
LEVEL_EVERYTHING, // Literally every warning
};
enum WarningID {
WARNING_MBC, // Issues with MBC specs
WARNING_OVERWRITE, // Overwriting non-zero bytes
WARNING_SGB, // SGB flag conflicts with old licensee code
WARNING_TRUNCATION, // Truncating values to fit
NB_PLAIN_WARNINGS,
NB_WARNINGS = NB_PLAIN_WARNINGS,
};
extern Diagnostics<WarningLevel, WarningID> warnings;
// Warns the user about problems that don't prevent fixing the ROM header
[[gnu::format(printf, 2, 3)]]
void warning(WarningID id, char const *fmt, ...);
// Prints an error, and increments the error count
[[gnu::format(printf, 1, 2)]]
void error(char const *fmt, ...);
// Prints an error, and exits with failure
[[gnu::format(printf, 1, 2), noreturn]]
void fatal(char const *fmt, ...);
uint32_t checkErrors(char const *filename);
#endif // RGBDS_FIX_WARNING_HPP

View File

@@ -1,13 +1,13 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_GFX_PROTO_PALETTE_HPP
#define RGBDS_GFX_PROTO_PALETTE_HPP
#ifndef RGBDS_GFX_COLOR_SET_HPP
#define RGBDS_GFX_COLOR_SET_HPP
#include <array>
#include <stddef.h>
#include <stdint.h>
class ProtoPalette {
class ColorSet {
public:
static constexpr size_t capacity = 4;
@@ -26,7 +26,7 @@ public:
WE_BIGGER,
THEY_BIGGER = -1,
};
ComparisonResult compare(ProtoPalette const &other) const;
ComparisonResult compare(ColorSet const &other) const;
size_t size() const;
bool empty() const;
@@ -35,4 +35,4 @@ public:
decltype(_colorIndices)::const_iterator end() const;
};
#endif // RGBDS_GFX_PROTO_PALETTE_HPP
#endif // RGBDS_GFX_COLOR_SET_HPP

View File

@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_GFX_MAIN_HPP
#define RGBDS_GFX_MAIN_HPP
@@ -10,6 +10,8 @@
#include <utility>
#include <vector>
#include "helpers.hpp"
#include "gfx/rgba.hpp"
struct Options {
@@ -21,13 +23,16 @@ struct Options {
uint8_t verbosity = 0; // -v
std::string attrmap{}; // -a, -A
std::optional<Rgba> bgColor{}; // -B
std::array<uint8_t, 2> baseTileIDs{0, 0}; // -b
enum {
NO_SPEC,
EXPLICIT,
EMBEDDED,
DMG,
} palSpecType = NO_SPEC; // -c
std::vector<std::array<std::optional<Rgba>, 4>> palSpec{};
uint8_t palSpecDmg = 0;
uint8_t bitDepth = 2; // -d
std::string inputTileset{}; // -i
struct {
@@ -35,7 +40,10 @@ struct Options {
uint16_t top;
uint16_t width;
uint16_t height;
uint32_t right() const { return left + width * 8; }
uint32_t bottom() const { return top + height * 8; }
} inputSlice{0, 0, 0, 0}; // -L (margins in clockwise order, like CSS)
uint8_t basePalID = 0; // -l
std::array<uint16_t, 2> maxNbTiles{UINT16_MAX, 0}; // -N
uint16_t nbPalettes = 8; // -n
std::string output{}; // -o
@@ -48,6 +56,7 @@ struct Options {
std::string input{}; // positional arg
// clang-format off: vertically align values
static constexpr uint8_t VERB_NONE = 0; // Normal, no extra output
static constexpr uint8_t VERB_CFG = 1; // Print configuration after parsing options
static constexpr uint8_t VERB_LOG_ACT = 2; // Log actions before doing them
@@ -55,41 +64,24 @@ struct Options {
static constexpr uint8_t VERB_DEBUG = 4; // Internals are logged
static constexpr uint8_t VERB_TRACE = 5; // Step-by-step algorithm details
static constexpr uint8_t VERB_VVVVVV = 6; // What, can't I have a little fun?
[[gnu::format(printf, 3, 4)]] void verbosePrint(uint8_t level, char const *fmt, ...) const;
// clang-format on
[[gnu::format(printf, 3, 4)]]
void verbosePrint(uint8_t level, char const *fmt, ...) const;
mutable bool hasTransparentPixels = false;
uint8_t maxOpaqueColors() const { return nbColorsPerPal - hasTransparentPixels; }
uint16_t maxNbColors() const { return nbColorsPerPal * nbPalettes; }
uint8_t dmgColors[4] = {};
uint8_t dmgValue(uint8_t i) const {
assume(i < 4);
return (palSpecDmg >> (2 * i)) & 0b11;
}
};
extern Options options;
/*
* Prints the error count, and exits with failure
*/
[[noreturn]] void giveUp();
/*
* If any error has been emitted thus far, calls `giveUp()`.
*/
void requireZeroErrors();
/*
* Prints a warning, and does not change the error count
*/
[[gnu::format(printf, 1, 2)]] void warning(char const *fmt, ...);
/*
* Prints an error, and increments the error count
*/
[[gnu::format(printf, 1, 2)]] void error(char const *fmt, ...);
/*
* Prints an error, and increments the error count
* Does not take format arguments so `format_` and `-Wformat-security` won't complain about
* calling `errorMessage(msg)`.
*/
void errorMessage(char const *msg);
/*
* Prints a fatal error, increments the error count, and gives up
*/
[[gnu::format(printf, 1, 2), noreturn]] void fatal(char const *fmt, ...);
struct Palette {
// An array of 4 GBC-native (RGB555) colors
std::array<uint16_t, 4> colors{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX};
@@ -108,9 +100,9 @@ struct Palette {
};
// Flipping tends to happen fairly often, so take a bite out of dcache to speed it up
static constexpr auto flipTable = ([]() constexpr {
static std::array<uint16_t, 256> flipTable = ([]() constexpr {
std::array<uint16_t, 256> table{};
for (uint16_t i = 0; i < table.size(); i++) {
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;

View File

@@ -1,20 +1,16 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_GFX_PAL_PACKING_HPP
#define RGBDS_GFX_PAL_PACKING_HPP
#include <stddef.h>
#include <tuple>
#include <vector>
#include "defaultinitvec.hpp"
struct Palette;
class ProtoPalette;
class ColorSet;
/*
* Returns which palette each proto-palette maps to, and how many palettes are necessary
*/
std::tuple<DefaultInitVec<size_t>, size_t>
overloadAndRemove(std::vector<ProtoPalette> const &protoPalettes);
// Returns which palette each color set maps to, and how many palettes are necessary
std::tuple<std::vector<size_t>, size_t> overloadAndRemove(std::vector<ColorSet> const &colorSets);
#endif // RGBDS_GFX_PAL_PACKING_HPP

View File

@@ -1,26 +1,23 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_GFX_PAL_SORTING_HPP
#define RGBDS_GFX_PAL_SORTING_HPP
#include <array>
#include <optional>
#include <png.h>
#include <vector>
#include "gfx/rgba.hpp"
// Allow a slot for every possible CGB color, plus one for transparency
// 32 (1 << 5) per channel, times 3 RGB channels = 32768 CGB colors
static constexpr size_t NB_COLOR_SLOTS = (1 << (5 * 3)) + 1;
struct Palette;
void sortIndexed(
std::vector<Palette> &palettes,
int palSize,
png_color const *palRGB,
int palAlphaSize,
png_byte *palAlpha
);
void sortIndexed(std::vector<Palette> &palettes, std::vector<Rgba> const &embPal);
void sortGrayscale(
std::vector<Palette> &palettes, std::array<std::optional<Rgba>, 0x8001> const &colors
std::vector<Palette> &palettes, std::array<std::optional<Rgba>, NB_COLOR_SLOTS> const &colors
);
void sortRgb(std::vector<Palette> &palettes);

View File

@@ -1,9 +1,12 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_GFX_PAL_SPEC_HPP
#define RGBDS_GFX_PAL_SPEC_HPP
void parseInlinePalSpec(char const * const arg);
void parseInlinePalSpec(char const * const rawArg);
void parseExternalPalSpec(char const *arg);
void parseDmgPalSpec(char const * const rawArg);
void parseBackgroundPalSpec(char const *arg);
#endif // RGBDS_GFX_PAL_SPEC_HPP

21
include/gfx/png.hpp Normal file
View File

@@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
#ifndef RGBDS_GFX_PNG_HPP
#define RGBDS_GFX_PNG_HPP
#include <fstream>
#include <stdint.h>
#include <vector>
#include "gfx/rgba.hpp"
struct Png {
uint32_t width, height;
std::vector<Rgba> pixels{};
std::vector<Rgba> palette{};
Png() {}
Png(char const *filename, std::streambuf &file);
};
#endif // RGBDS_GFX_PNG_HPP

View File

@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_GFX_PROCESS_HPP
#define RGBDS_GFX_PROCESS_HPP

View File

@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_GFX_REVERSE_HPP
#define RGBDS_GFX_REVERSE_HPP

View File

@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_GFX_RGBA_HPP
#define RGBDS_GFX_RGBA_HPP
@@ -13,49 +13,43 @@ struct Rgba {
constexpr Rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
: red(r), green(g), blue(b), alpha(a) {}
/*
* Constructs the color from a "packed" RGBA representation (0xRRGGBBAA)
*/
// Constructs the color from a "packed" RGBA representation (0xRRGGBBAA)
explicit constexpr Rgba(uint32_t rgba = 0)
: red(rgba >> 24), green(rgba >> 16), blue(rgba >> 8), alpha(rgba) {}
static constexpr Rgba fromCGBColor(uint16_t cgbColor) {
constexpr auto _5to8 = [](uint8_t fiveBpp) -> uint8_t {
fiveBpp &= 0b11111; // For caller's convenience
return fiveBpp << 3 | fiveBpp >> 2;
static constexpr Rgba fromCGBColor(uint16_t color) {
constexpr auto _5to8 = [](uint8_t channel) -> uint8_t {
channel &= 0b11111; // For caller's convenience
return channel << 3 | channel >> 2;
};
return {
_5to8(cgbColor),
_5to8(cgbColor >> 5),
_5to8(cgbColor >> 10),
static_cast<uint8_t>(cgbColor & 0x8000 ? 0x00 : 0xFF),
_5to8(color),
_5to8(color >> 5),
_5to8(color >> 10),
static_cast<uint8_t>(color & 0x8000 ? 0x00 : 0xFF),
};
}
/*
* Returns this RGBA as a 32-bit number that can be printed in hex (`%08x`) to yield its CSS
* representation
*/
// Returns this RGBA as a 32-bit number that can be printed in hex (`%08x`) to yield its CSS
// representation
uint32_t toCSS() const {
auto shl = [](uint8_t val, unsigned shift) { return static_cast<uint32_t>(val) << shift; };
constexpr auto shl = [](uint8_t val, unsigned shift) {
return static_cast<uint32_t>(val) << shift;
};
return shl(red, 24) | shl(green, 16) | shl(blue, 8) | shl(alpha, 0);
}
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.
*/
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;
bool isTransparent() const { return alpha < transparency_threshold; }
static constexpr uint8_t opacity_threshold = 0xF0;
bool isOpaque() const { return alpha >= opacity_threshold; }
/*
* Computes the equivalent CGB color, respects the color curve depending on options
*/
// Computes the equivalent CGB color, respects the color curve depending on options
uint16_t cgbColor() const;
bool isGray() const { return red == green && green == blue; }

44
include/gfx/warning.hpp Normal file
View File

@@ -0,0 +1,44 @@
// SPDX-License-Identifier: MIT
#ifndef RGBDS_GFX_WARNING_HPP
#define RGBDS_GFX_WARNING_HPP
#include "diagnostics.hpp"
enum WarningLevel {
LEVEL_DEFAULT, // Warnings that are enabled by default
LEVEL_ALL, // Warnings that probably indicate an error
LEVEL_EVERYTHING, // Literally every warning
};
enum WarningID {
WARNING_EMBEDDED, // Using an embedded PNG palette without '-c embedded'
WARNING_TRIM_NONEMPTY, // '-x' trims nonempty tiles
NB_PLAIN_WARNINGS,
NB_WARNINGS = NB_PLAIN_WARNINGS,
};
extern Diagnostics<WarningLevel, WarningID> warnings;
// Warns the user about problems that don't prevent valid graphics conversion
[[gnu::format(printf, 2, 3)]]
void warning(WarningID id, char const *fmt, ...);
// Prints the error count, and exits with failure
[[noreturn]]
void giveUp();
// If any error has been emitted thus far, calls `giveUp()`
void requireZeroErrors();
// Prints an error, and increments the error count
[[gnu::format(printf, 1, 2)]]
void error(char const *fmt, ...);
// Prints a fatal error, increments the error count, and gives up
[[gnu::format(printf, 1, 2), noreturn]]
void fatal(char const *fmt, ...);
#endif // RGBDS_GFX_WARNING_HPP

View File

@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_HELPERS_HPP
#define RGBDS_HELPERS_HPP
@@ -14,7 +14,8 @@
#else
// This seems to generate similar code to __builtin_unreachable, despite different semantics
// Note that executing this is undefined behavior (declared [[noreturn]], but does return)
[[noreturn]] static inline void unreachable_() {
[[noreturn]]
static inline void unreachable_() {
}
#endif
@@ -23,11 +24,12 @@
#ifdef _MSC_VER
#define assume(x) __assume(x)
#else
// `[[gnu::assume()]]` for GCC or compatible also has insufficient support (GCC 13+ only)
// `[[gnu::assume()]]` for GCC or compatible also has insufficient support (GCC 13+ only)
#define assume(x) \
do { \
if (!(x)) \
if (!(x)) { \
unreachable_(); \
} \
} while (0)
#endif
#else
@@ -69,7 +71,7 @@ static inline int ctz(unsigned int x) {
while (!(x & 1)) {
x >>= 1;
cnt++;
++cnt;
}
return cnt;
}
@@ -79,7 +81,7 @@ static inline int clz(unsigned int x) {
while (x <= UINT_MAX / 2) {
x <<= 1;
cnt++;
++cnt;
}
return cnt;
}
@@ -93,15 +95,14 @@ static inline int clz(unsigned int x) {
#define CAT(x, y) x##y
#define EXPAND_AND_CAT(x, y) CAT(x, y)
// Obtaining the size of an array; `arr` must be an expression, not a type!
// (Having two instances of `arr` is OK because the contents of `sizeof` are not evaluated.)
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof *(arr))
// For lack of <ranges>, this adds some more brevity
#define RANGE(s) std::begin(s), std::end(s)
// MSVC does not inline `strlen()` or `.length()` of a constant string, so we use `sizeof`
#define QUOTEDSTRLEN(s) (sizeof(s) - 1)
// MSVC does not inline `strlen()` or `.length()` of a constant string
template<int N>
static constexpr int literal_strlen(char const (&)[N]) {
return N - 1;
}
// For ad-hoc RAII in place of a `defer` statement or cross-platform `__attribute__((cleanup))`
template<typename T>

View File

@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_ITERTOOLS_HPP
#define RGBDS_ITERTOOLS_HPP
@@ -22,10 +22,9 @@ class EnumSeq {
return *this;
}
auto operator*() const { return _value; }
T 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:
@@ -59,10 +58,6 @@ public:
bool operator==(ZipIterator const &rhs) const {
return std::get<0>(_iters) == std::get<0>(rhs._iters);
}
bool operator!=(ZipIterator const &rhs) const {
return std::get<0>(_iters) != std::get<0>(rhs._iters);
}
};
template<typename... Ts>

View File

@@ -1,12 +1,8 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_LINK_ASSIGN_HPP
#define RGBDS_LINK_ASSIGN_HPP
#include <stdint.h>
extern uint64_t nbSectionsToAssign;
// Assigns all sections a slice of the address space
void assign_AssignSections();

22
include/link/layout.hpp Normal file
View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT
#ifndef RGBDS_LINK_LAYOUT_HPP
#define RGBDS_LINK_LAYOUT_HPP
#include <stdint.h>
#include <string>
#include "linkdefs.hpp"
void layout_SetFloatingSectionType(SectionType type);
void layout_SetSectionType(SectionType type);
void layout_SetSectionType(SectionType type, uint32_t bank);
void layout_SetAddr(uint32_t addr);
void layout_MakeAddrFloating();
void layout_AlignTo(uint32_t alignment, uint32_t offset);
void layout_Pad(uint32_t length);
void layout_PlaceSection(std::string const &name, bool isOptional);
#endif // RGBDS_LINK_LAYOUT_HPP

17
include/link/lexer.hpp Normal file
View File

@@ -0,0 +1,17 @@
// SPDX-License-Identifier: MIT
#ifndef RGBDS_LINK_LEXER_HPP
#define RGBDS_LINK_LEXER_HPP
#include <stdarg.h>
#include <string>
[[gnu::format(printf, 1, 2)]]
void lexer_Error(char const *fmt, ...);
void lexer_IncludeFile(std::string &&path);
void lexer_IncLineNo();
bool lexer_Init(char const *linkerScriptName);
#endif // RGBDS_LINK_LEXER_HPP

View File

@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_LINK_MAIN_HPP
#define RGBDS_LINK_MAIN_HPP
@@ -6,39 +6,43 @@
#include <stdint.h>
#include <stdio.h>
#include <string>
#include <variant>
#include <vector>
#include "either.hpp"
#include "linkdefs.hpp"
// Variables related to CLI options
extern bool isDmgMode;
extern char const *linkerScriptName;
extern char const *mapFileName;
extern bool noSymInMap;
extern char const *symFileName;
extern char const *overlayFileName;
extern char const *outputFileName;
extern uint8_t padValue;
extern bool hasPadValue;
extern uint16_t scrambleROMX;
extern uint8_t scrambleWRAMX;
extern uint8_t scrambleSRAM;
extern bool is32kMode;
extern bool beVerbose;
extern bool isWRAM0Mode;
extern bool disablePadding;
struct Options {
bool isDmgMode; // -d
char const *mapFileName; // -m
bool noSymInMap; // -M
char const *symFileName; // -n
char const *overlayFileName; // -O
char const *outputFileName; // -o
uint8_t padValue; // -p
bool hasPadValue = false;
// Setting these three to 0 disables the functionality
uint16_t scrambleROMX; // -S
uint16_t scrambleWRAMX;
uint16_t scrambleSRAM;
bool is32kMode; // -t
bool beVerbose; // -v
bool isWRAM0Mode; // -w
bool disablePadding; // -x
};
extern Options options;
// Helper macro for printing verbose-mode messages
#define verbosePrint(...) \
do { \
if (beVerbose) \
if (options.beVerbose) { \
fprintf(stderr, __VA_ARGS__); \
} \
} while (0)
struct FileStackNode {
FileStackNodeType type;
Either<
std::variant<
std::monostate, // Default constructed; `.type` and `.data` must be set manually
std::vector<uint32_t>, // NODE_REPT
std::string // NODE_FILE, NODE_MACRO
>
@@ -49,20 +53,13 @@ struct FileStackNode {
uint32_t lineNo;
// REPT iteration counts since last named node, in reverse depth order
std::vector<uint32_t> &iters() { return data.get<std::vector<uint32_t>>(); }
std::vector<uint32_t> const &iters() const { return data.get<std::vector<uint32_t>>(); }
std::vector<uint32_t> &iters() { return std::get<std::vector<uint32_t>>(data); }
std::vector<uint32_t> const &iters() const { return std::get<std::vector<uint32_t>>(data); }
// File name for files, file::macro name for macros
std::string &name() { return data.get<std::string>(); }
std::string const &name() const { return data.get<std::string>(); }
std::string &name() { return std::get<std::string>(data); }
std::string const &name() const { return std::get<std::string>(data); }
std::string const &dump(uint32_t curLineNo) const;
};
[[gnu::format(printf, 3, 4)]] void
warning(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...);
[[gnu::format(printf, 3, 4)]] void
error(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...);
[[gnu::format(printf, 3, 4), noreturn]] void
fatal(FileStackNode const *where, uint32_t lineNo, char const *fmt, ...);
#endif // RGBDS_LINK_MAIN_HPP

View File

@@ -1,19 +1,12 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_LINK_OBJECT_HPP
#define RGBDS_LINK_OBJECT_HPP
/*
* Read an object (.o) file, and add its info to the data structures.
* @param fileName A path to the object file to be read
* @param i The ID of the file
*/
void obj_ReadFile(char const *fileName, unsigned int i);
// Read an object (.o) file, and add its info to the data structures.
void obj_ReadFile(char const *fileName, unsigned int fileID);
/*
* Sets up object file reading
* @param nbFiles The number of object files that will be read
*/
// Sets up object file reading
void obj_Setup(unsigned int nbFiles);
#endif // RGBDS_LINK_OBJECT_HPP

View File

@@ -1,26 +1,17 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_LINK_OUTPUT_HPP
#define RGBDS_LINK_OUTPUT_HPP
struct Section;
/*
* Registers a section for output.
* @param section The section to add
*/
// Registers a section for output.
void out_AddSection(Section const &section);
/*
* Finds an assigned section overlapping another one.
* @param section The section that is being overlapped
* @return A section overlapping it
*/
// Finds an assigned section overlapping another one.
Section const *out_OverlappingSection(Section const &section);
/*
* Writes all output (bin, sym, map) files.
*/
// Writes all output (bin, sym, map) files.
void out_WriteFiles();
#endif // RGBDS_LINK_OUTPUT_HPP

View File

@@ -1,17 +1,29 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_LINK_PATCH_HPP
#define RGBDS_LINK_PATCH_HPP
/*
* Checks all assertions
* @return true if assertion failed
*/
#include <string>
#include <vector>
#include "link/section.hpp"
struct Symbol;
struct Assertion {
Patch patch; // Also used for its `.type`
std::string message;
// This would be redundant with `patch.pcSection->fileSymbols`, but `section` is sometimes
// `nullptr`!
std::vector<Symbol> *fileSymbols;
};
Assertion &patch_AddAssertion();
// Checks all assertions
void patch_CheckAssertions();
/*
* Applies all SECTIONs' patches to them
*/
// Applies all SECTIONs' patches to them
void patch_ApplyPatches();
#endif // RGBDS_LINK_PATCH_HPP

View File

@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_LINK_SDAS_OBJ_HPP
#define RGBDS_LINK_SDAS_OBJ_HPP

View File

@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_LINK_SECTION_HPP
#define RGBDS_LINK_SECTION_HPP
@@ -56,38 +56,17 @@ struct Section {
std::unique_ptr<Section> nextu; // The next "component" of this unionized sect
};
struct Assertion {
Patch patch; // Also used for its `.type`
std::string message;
// This would be redundant with `.section->fileSymbols`, but `section` is sometimes `nullptr`!
std::vector<Symbol> *fileSymbols;
};
extern std::deque<Assertion> assertions;
/*
* Execute a callback for each section currently registered.
* This is to avoid exposing the data structure in which sections are stored.
* @param callback The function to call for each structure.
*/
// Execute a callback for each section currently registered.
// This is to avoid exposing the data structure in which sections are stored.
void sect_ForEach(void (*callback)(Section &));
/*
* Registers a section to be processed.
* @param section The section to register.
*/
// Registers a section to be processed.
void sect_AddSection(std::unique_ptr<Section> &&section);
/*
* Finds a section by its name.
* @param name The name of the section to look for
* @return A pointer to the section, or `nullptr` if it wasn't found
*/
// Finds a section by its name.
Section *sect_GetSection(std::string const &name);
/*
* Checks if all sections meet reasonable criteria, such as max size
*/
// Checks if all sections meet reasonable criteria, such as max size
void sect_DoSanityChecks();
#endif // RGBDS_LINK_SECTION_HPP

View File

@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_LINK_SYMBOL_HPP
#define RGBDS_LINK_SYMBOL_HPP
@@ -7,8 +7,8 @@
#include <stdint.h>
#include <string>
#include <variant>
#include "either.hpp"
#include "linkdefs.hpp"
struct FileStackNode;
@@ -27,25 +27,21 @@ struct Symbol {
ExportLevel type;
FileStackNode const *src;
int32_t lineNo;
Either<
std::variant<
int32_t, // Constants just have a numeric value
Label // Label values refer to an offset within a specific section
>
data;
Label &label() { return data.get<Label>(); }
Label const &label() const { return data.get<Label>(); }
Label &label() { return std::get<Label>(data); }
Label const &label() const { return std::get<Label>(data); }
};
void sym_ForEach(void (*callback)(Symbol &));
void sym_AddSymbol(Symbol &symbol);
/*
* Finds a symbol in all the defined symbols.
* @param name The name of the symbol to look for
* @return A pointer to the symbol, or `nullptr` if not found.
*/
// Finds a symbol in all the defined symbols.
Symbol *sym_GetSymbol(std::string const &name);
void sym_DumpLocalAliasedSymbols(std::string const &name);

61
include/link/warning.hpp Normal file
View File

@@ -0,0 +1,61 @@
// SPDX-License-Identifier: MIT
#ifndef RGBDS_LINK_WARNING_HPP
#define RGBDS_LINK_WARNING_HPP
#include <stdarg.h>
#include <stdint.h>
#include "diagnostics.hpp"
#define warningAt(where, ...) warning(where.src, where.lineNo, __VA_ARGS__)
#define errorAt(where, ...) error(where.src, where.lineNo, __VA_ARGS__)
#define fatalAt(where, ...) fatal(where.src, where.lineNo, __VA_ARGS__)
enum WarningLevel {
LEVEL_DEFAULT, // Warnings that are enabled by default
LEVEL_ALL, // Warnings that probably indicate an error
LEVEL_EVERYTHING, // Literally every warning
};
enum WarningID {
WARNING_ASSERT, // Assertions
WARNING_DIV, // Undefined division behavior
WARNING_OBSOLETE, // Obsolete/deprecated things
WARNING_SHIFT, // Undefined `SHIFT` behavior
WARNING_SHIFT_AMOUNT, // Strange `SHIFT` amount
WARNING_TRUNCATION, // Implicit truncation loses some bits
NB_PLAIN_WARNINGS,
NB_WARNINGS = NB_PLAIN_WARNINGS,
};
extern Diagnostics<WarningLevel, WarningID> warnings;
struct FileStackNode;
[[gnu::format(printf, 4, 5)]]
void warning(FileStackNode const *src, uint32_t lineNo, WarningID id, char const *fmt, ...);
[[gnu::format(printf, 3, 4)]]
void warning(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...);
[[gnu::format(printf, 1, 2)]]
void warning(char const *fmt, ...);
[[gnu::format(printf, 3, 4)]]
void error(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...);
[[gnu::format(printf, 1, 2)]]
void error(char const *fmt, ...);
[[gnu::format(printf, 1, 2)]]
void errorNoDump(char const *fmt, ...);
void scriptError(char const *name, uint32_t lineNo, char const *fmt, va_list args);
[[gnu::format(printf, 3, 4), noreturn]]
void fatal(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...);
[[gnu::format(printf, 1, 2), noreturn]]
void fatal(char const *fmt, ...);
void requireZeroErrors();
#endif // RGBDS_LINK_WARNING_HPP

View File

@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_LINKDEFS_HPP
#define RGBDS_LINKDEFS_HPP
@@ -9,7 +9,7 @@
#include "helpers.hpp" // assume
#define RGBDS_OBJECT_VERSION_STRING "RGB9"
#define RGBDS_OBJECT_REV 11U
#define RGBDS_OBJECT_REV 12U
enum AssertionType { ASSERT_WARN, ASSERT_ERROR, ASSERT_FATAL };
@@ -52,6 +52,7 @@ enum RPNCommand {
RPN_HRAM = 0x60,
RPN_RST = 0x61,
RPN_BIT_INDEX = 0x62,
RPN_HIGH = 0x70,
RPN_LOW = 0x71,
@@ -92,29 +93,19 @@ extern struct SectionTypeInfo {
uint32_t lastBank;
} sectionTypeInfo[SECTTYPE_INVALID];
/*
* Tells whether a section has data in its object file definition,
* depending on type.
* @param type The section's type
* @return `true` if the section's definition includes data
*/
// Tells whether a section has data in its object file definition,
// depending on type.
static inline bool sect_HasData(SectionType type) {
assume(type != SECTTYPE_INVALID);
return type == SECTTYPE_ROM0 || type == SECTTYPE_ROMX;
}
/*
* Computes a memory region's end address (last byte), eg. 0x7FFF
* @return The address of the last byte in that memory region
*/
// Returns a memory region's end address (last byte), e.g. 0x7FFF
static inline uint16_t endaddr(SectionType type) {
return sectionTypeInfo[type].startAddr + sectionTypeInfo[type].size - 1;
}
/*
* Computes a memory region's number of banks
* @return The number of banks, 1 for regions without banking
*/
// Returns a memory region's number of banks, or 1 for regions without banking
static inline uint32_t nbbanks(SectionType type) {
return sectionTypeInfo[type].lastBank - sectionTypeInfo[type].firstBank + 1;
}

View File

@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_OP_MATH_HPP
#define RGBDS_OP_MATH_HPP

View File

@@ -1,6 +1,4 @@
/* SPDX-License-Identifier: MIT */
// platform-specific hacks
// SPDX-License-Identifier: MIT
#ifndef RGBDS_PLATFORM_HPP
#define RGBDS_PLATFORM_HPP

21
include/usage.hpp Normal file
View File

@@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
#ifndef RGBDS_USAGE_HPP
#define RGBDS_USAGE_HPP
#include <stdarg.h>
class Usage {
char const *usage;
public:
Usage(char const *usage_) : usage(usage_) {}
[[noreturn]]
void printAndExit(int code) const;
[[gnu::format(printf, 2, 3), noreturn]]
void printAndExit(char const *fmt, ...) const;
};
#endif // RGBDS_USAGE_HPP

View File

@@ -1,17 +1,25 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_UTIL_HPP
#define RGBDS_UTIL_HPP
#include <stddef.h>
#include <stdint.h>
#include <vector>
#include <string>
#include <unordered_map>
#include "helpers.hpp"
bool startsIdentifier(int c);
bool continuesIdentifier(int c);
char const *printChar(int c);
/*
* @return The number of bytes read, or 0 if invalid data was found
*/
size_t readUTF8Char(std::vector<int32_t> *dest, char const *src);
struct Uppercase {
size_t operator()(std::string const &str) const;
bool operator()(std::string const &str1, std::string const &str2) const;
};
template<typename T>
using UpperMap = std::unordered_map<std::string, T, Uppercase, Uppercase>;
#endif // RGBDS_UTIL_HPP

View File

@@ -1,15 +1,12 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#ifndef RGBDS_VERSION_HPP
#define RGBDS_VERSION_HPP
extern "C" {
#define PACKAGE_VERSION_MAJOR 0
#define PACKAGE_VERSION_MINOR 9
#define PACKAGE_VERSION_PATCH 0
#define PACKAGE_VERSION_PATCH 4
char const *get_package_version_string();
}
#endif // RGBDS_VERSION_H

View File

@@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: MIT
.\"
.Dd December 25, 2024
.Dd July 31, 2025
.Dt GBZ80 7
.Os
.Sh NAME
@@ -615,9 +615,6 @@ to the adjustment.
.It
Subtract the adjustment from
.Sy A .
.It
Set the carry flag if borrow (i.e. if adjustment >
.Sy A ) .
.El
.It If the subtract flag Sy N No is not set:
.Bl -enum -compact
@@ -639,15 +636,13 @@ to the adjustment.
If the carry flag is set or
.Sy A
>
.Ad $9F ,
.Ad $99 ,
then add
.Ad $60
to the adjustment.
to the adjustment and set the carry flag.
.It
Add the adjustment to
.Sy A .
.It
Set the carry flag if overflow from bit 7.
.El
.El
.Pp

View File

@@ -2,7 +2,7 @@
.\"
.\" SPDX-License-Identifier: MIT
.\"
.Dd December 25, 2024
.Dd July 31, 2025
.Dt RGBASM-OLD 5
.Os
.Sh NAME
@@ -15,7 +15,7 @@ assembly language over its decades of evolution, along with their modern alterna
Its goal is to be a reference for backwards incompatibility, when upgrading an old assembly codebase to work with the latest RGBDS release.
It does
.Em not
attempt to list syntax bugs that were fixed, nor new reserved keywords that may conflict with old identifiers.
attempt to list every syntax bug that was ever fixed (with some notable exceptions), nor new reserved keywords that may conflict with old identifiers.
.Sh REMOVED
These are features which have been completely removed, without any direct alternatives.
Usually these features were limiting the addition of other features, or had awkward limits on their own intended effects.
@@ -99,6 +99,23 @@ in RAM sections, allowing labeled space allocations to overlap.
.Pp
Instead, use
.Ic UNION .
.Ss Section-local charmaps
Deprecated in 0.3.9, removed in 0.4.0.
.Pp
Defining a
.Ic CHARMAP
inside a
.Ic SECTION
when the current global charmap was the
.Sq main
one used to only define that character mapping within that
.Ic SECTION .
.Pp
Instead, use
.Ic PUSHC
and
.Ic POPC
and switch to a different character mapping for that section.
.Ss __FILE__ and __LINE__
Deprecated in 0.6.0, removed in 0.7.0.
.Pp
@@ -266,6 +283,14 @@ Instead, use
.Ql LDH [C], A
and
.Ql LDH A, [C] .
.Pp
Note that
.Ql LD [$FF00+C], A
and
.Ql LD A, [$FF00+C]
were also deprecated in 0.9.0, but were
.Em undeprecated
in 0.9.1.
.Ss LDH [n8], A and LDH A, [n8]
Deprecated in 0.9.0.
.Pp
@@ -288,11 +313,16 @@ Deprecated in 0.3.0, removed in 0.4.0.
.Pp
Instead, use
.Ql LD HL, SP + e8 .
.Ss LDHL, SP, e8
.Ss LDHL SP, e8
Supported in ASMotor, removed in RGBDS.
.Pp
Instead, use
.Ql LD HL, SP + e8 .
.Ss OPT z
Deprecated in 0.4.0, removed in 0.5.0.
.Pp
Instead, use
.Ic OPT p .
.Ss rgbasm -i
Deprecated in 0.6.0, removed in 0.8.0.
.Pp
@@ -348,6 +378,36 @@ because $16384$ turns = $16384 tau$ radians = $32768 pi$ radians, and $sin ( 327
.EQ
delim off
.EN
.Ss % operator behavior with negative dividend or divisor
Changed in 0.5.0.
.Pp
Instead of having the same sign as the dividend (a remainder operation),
.Ql %
has the same sign as the divisor (a modulo operation).
.Pp
For example, previously we had:
.Bl -bullet -offset indent
.It
.Ql 13 % 10 == 3
.It
.Ql -13 % 10 == -3
.It
.Ql 13 % -10 == 3
.It
.Ql -13 % -10 == -3
.El
.Pp
Instead, now we have:
.Bl -bullet -offset indent
.It
.Ql 13 % 10 == 3
.It
.Ql -13 % 10 == 7
.It
.Ql 13 % -10 == -7
.It
.Ql -13 % -10 == -3
.El
.Ss ** operator associativity
Changed in 0.9.0.
.Pp
@@ -360,6 +420,68 @@ Previously we had
.Pp
Instead, now we have
.Ql p ** q ** r == p ** (q ** r) .
.Sh BUGS
These are misfeatures that may have been possible by mistake.
They do not get deprecated, just fixed.
.Ss Space between exported labels' colons
Fixed in 0.7.0.
.Pp
Labels with two colons used to ignore a space between them; for example,
.Ql Label:\ : .
.Pp
Instead, use
.Ql Label:: .
.Ss Space between label and colon
Fixed in 0.9.0.
.Pp
Space between a label and its colon(s) used to be ignored; for example,
.Ql Label\ :
and
.Ql Label\ :: .
Now they are treated as invocations of the
.Ql Label
macro with
.Ql \&:
and
.Ql ::
as arguments.
.Pp
Instead, use
.Ql Label:
and
.Ql Label:: .
.Ss ADD r16 with implicit first HL operand
Fixed in 0.5.0.
.Pp
For example,
.Ql ADD BC
used to be treated as
.Ql ADD HL, BC ,
and likewise for
.Ql DE ,
.Ql HL ,
and
.Ql SP .
.Pp
Instead, use an explicit first
.Ql HL
operand.
.Ss = instead of SET
Fixed in 0.4.0.
.Pp
The
.Ic =
operator used to be an alias for the
.Ic SET
keyword, which included using
.Ic =
for the
.Ic SET
.Em instruction .
.Pp
Instead, just use
.Ic SET
for the instruction.
.Sh SEE ALSO
.Xr rgbasm 1 ,
.Xr gbz80 7 ,

View File

@@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: MIT
.\"
.Dd December 25, 2024
.Dd July 31, 2025
.Dt RGBASM 1
.Os
.Sh NAME
@@ -8,13 +8,14 @@
.Nd Game Boy assembler
.Sh SYNOPSIS
.Nm
.Op Fl EVvw
.Op Fl EhVvw
.Op Fl b Ar chars
.Op Fl D Ar name Ns Op = Ns Ar value
.Op Fl g Ar chars
.Op Fl I Ar path
.Op Fl M Ar depend_file
.Op Fl MG
.Op Fl MC
.Op Fl MP
.Op Fl MT Ar target_file
.Op Fl MQ Ar target_file
@@ -51,8 +52,19 @@ is invalid because it could also be
The arguments are as follows:
.Bl -tag -width Ds
.It Fl b Ar chars , Fl \-binary-digits Ar chars
Change the two characters used for binary constants.
The defaults are 01.
Allow two characters to be used for binary constants in addition to the default
.Sq 0
and
.Sq 1 .
Valid characters are numbers other than
.Sq 0
and
.Sq 1 ,
letters,
.Sq \&. ,
.Sq # ,
or
.Sq @ .
.It Fl D Ar name Ns Oo = Ns Ar value Oc , Fl \-define Ar name Ns Oo = Ns Ar value Oc
Add a string symbol to the compiled source code.
This is equivalent to
@@ -65,18 +77,35 @@ is not specified.
.It Fl E , Fl \-export-all
Export all labels, including unreferenced and local labels.
.It Fl g Ar chars , Fl \-gfx-chars Ar chars
Change the four characters used for gfx constants.
Allow four characters to be used for graphics constants in addition to the default
.Sq 0 ,
.Sq 1 ,
.Sq 2 ,
and
.Sq 3 .
Valid characters are numbers other than
.Sq 0
to
.Sq 3 ,
letters,
.Sq \&. ,
.Sq # ,
or
.Sq @ .
The defaults are 0123.
.It Fl h , Fl \-help
Print help text for the program and exit.
.It Fl I Ar path , Fl \-include Ar path
Add a new
.Dq include path ;
.Ar path
must point to a directory.
When a
When any
.Ic INCLUDE
.Pq including the implicit one from Fl P
.Pq including the implicit one from Fl P ,
.Ic INCBIN ,
or
.Ic INCBIN
.Ic READFILE
is attempted,
.Nm
first looks up the provided path from its working directory; if this fails, it tries again from each of the
@@ -92,17 +121,33 @@ To be used in conjunction with
.Fl M .
This makes
.Nm
assume that missing files are auto-generated: when
assume that missing files are auto-generated: when any
.Ic INCLUDE
.Pq including the implicit one from Fl P
.Pq including the implicit one from Fl P ,
.Ic INCBIN ,
or
.Ic INCBIN
.Ic READFILE
is attempted on a non-existent file, it is added as a dependency, then
.Nm
exits normally instead of erroring out.
This feature is used in automatic updating of makefiles.
exits normally or continues processing (depending on whether
.Fl MC
was enabled) instead of erroring out.
This feature is used in automatic updating of Makefiles.
.It Fl MC
Implies
.Fl MG .
This makes
.Nm
continue processing after a non-existent dependency file, instead of exiting.
Note that this is
.Em not
recommended if any non-existent dependencies would have influenced subsequent processing, e.g. by causing an
.Ic IF
condition to take a different branch.
.It Fl MP
When enabled, this causes a phony target to be added for each dependency other than the main file.
When enabled, this adds a phony target to the rules emitted by
.Fl M
for each dependency other than the main file.
This prevents
.Xr make 1
from erroring out when dependency files are deleted.
@@ -131,6 +176,7 @@ This acts as if a
.Ql Ic INCLUDE Qq Ar include_file
was read before the input
.Ar asmfile .
Multiple files can be pre-included in the order they were provided.
.It Fl p Ar pad_value , Fl \-pad-value Ar pad_value
Use this as the value for
.Ic DS
@@ -242,7 +288,7 @@ Enables literally every warning.
.El
.Pp
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 Wcharmap-redef
enables the warning that
.Fl Wno-charmap-redef
@@ -273,7 +319,7 @@ This warning is enabled by
.Fl Wall .
.It Fl Wbuiltin-args
Warn about incorrect arguments to built-in functions, such as
.Fn STRSUB
.Fn STRSLICE
with indexes outside of the string's bounds.
This warning is enabled by
.Fl Wall .
@@ -309,11 +355,7 @@ Block comments cannot be nested, so the first
.Ql */
will end the whole comment.
.It Fl Wno-obsolete
Warn when obsolete constructs such as the
.Ic _PI
constant or
.Ic PRINTT
directive are encountered.
Warn when obsolete features are encountered, which have been deprecated and may later be removed.
.It Fl Wnumeric-string=
Warn when a multi-character string is treated as a number.
.Fl Wnumeric-string=0

View File

@@ -2,7 +2,7 @@
.\"
.\" SPDX-License-Identifier: MIT
.\"
.Dd December 25, 2024
.Dd July 31, 2025
.Dt RGBASM 5
.Os
.Sh NAME
@@ -280,7 +280,7 @@ There are a number of numeric formats.
.It Binary Ta Li % , 0b , 0B Ta 01
.It Fixed-point Ta none Ta 01234.56789
.It Precise fixed-point Ta none Ta 12.34q8
.It Character constant Ta none Ta \(dqABYZ\(dq
.It Character constant Ta none Ta 'ABYZ'
.It Game Boy graphics Ta Li \` Ta 0123
.El
.Pp
@@ -293,11 +293,14 @@ or
The "character constant" form yields the value the character maps to in the current charmap.
For example, by default
.Pq refer to Xr ascii 7
.Sq \(dqA\(dq
.Sq 'A'
yields 65.
A character constant must represent a single value, so it cannot include multiple characters, or characters which map to multiple values.
See
.Sx Character maps
for information on charmaps.
for information on charmaps, and
.Sx String expressions
for information on escape characters allowed in character constants.
.Pp
The last one, Game Boy graphics, is quite interesting and useful.
After the backtick, 8 digits between 0 and 3 are expected, corresponding to pixel values.
@@ -538,7 +541,8 @@ There are a number of escape sequences you can use within a string:
.Bl -column -offset indent "Sequence"
.It Sy Sequence Ta Sy Meaning
.It Ql \e\e Ta Backslash Pq escapes the escape character itself
.It Ql \e" Ta Double quote Pq does not terminate the string
.It Ql \e" Ta Double quote Pq does not terminate a string
.It Ql \e' Ta Single quote Pq does not terminate a character literal
.It Ql \e{ Ta Open curly brace Pq does not start interpolation
.It Ql \e} Ta Close curly brace Pq does not end interpolation
.It Ql \en Ta Newline Pq ASCII $0A
@@ -548,7 +552,7 @@ There are a number of escape sequences you can use within a string:
.El
.Pp
Multi-line strings are contained in triple quotes
.Pq Ql \&"\&"\&"for instance\&"\&"\&" .
.Pq Ql \&"\&"\&"for instance""" .
Escape sequences work the same way in multi-line strings; however, literal newline characters will be included as-is, without needing to escape them with
.Ql \er
or
@@ -560,26 +564,30 @@ Inside them, backslashes and braces are treated like regular characters, so they
For example, the raw string
.Ql #"\et\e1{s}\e"
is equivalent to the regular string
.Ql "\e\et\e\e1\e{s}\e\e" .
.Ql \&"\e\et\e\e1\e{s}\e\e" .
(Note that this prevents raw strings from including the double quote character.)
Raw strings also may be contained in triple quotes for them to be multi-line, so they can include literal newline or quote characters (although still not three quotes in a row).
.Pp
The following functions operate on string expressions.
Most of them return a string, however some of these functions actually return an integer and can be used as part of an integer expression!
.Bl -column "STRSUB(str, pos, len)"
You can use the
.Sq ++
operator to concatenate two strings.
.Ql \&"str" ++ \&"ing"
is equivalent to
.Ql \&"string" ,
or to
.Ql STRCAT("str", \&"ing") .
.Pp
The following functions operate on string expressions, and return strings themselves.
.Bl -column "STRSLICE(str, start, stop)"
.It Sy Name Ta Sy Operation
.It Fn STRLEN str Ta Returns the number of characters in Ar str .
.It Fn STRCAT strs... Ta Concatenates Ar strs .
.It Fn STRCMP str1 str2 Ta Returns -1 if Ar str1 No is alphabetically lower than Ar str2 No , zero if they match, 1 if Ar str1 No is greater than Ar str2 .
.It Fn STRIN str1 str2 Ta Returns the first position of Ar str2 No in Ar str1 No or zero if it's not present Pq first character is position 1 .
.It Fn STRRIN str1 str2 Ta Returns the last position of Ar str2 No in Ar str1 No or zero if it's not present Pq first character is position 1 .
.It Fn STRSUB str pos len Ta Returns a substring from Ar str No starting at Ar pos No (first character is position 1, last is position -1) and Ar len No characters long. If Ar len No is not specified the substring continues to the end of Ar str .
.It Fn STRUPR str Ta Returns Ar str No with all ASCII letters
.Pq Ql a-z
in uppercase.
.It Fn STRLWR str Ta Returns Ar str No with all ASCII letters
.Pq Ql A-Z
in lowercase.
.It Fn STRSLICE str start stop Ta Returns a substring of Ar str No starting at Ar start No and ending at Ar stop No (exclusive). If Ar stop No is not specified, the substring continues to the end of Ar str .
.It Fn STRRPL str old new Ta Returns Ar str No with each non-overlapping occurrence of the substring Ar old No replaced with Ar new .
.It Fn STRFMT fmt args... Ta Returns the string Ar fmt No with each
.Ql %spec
@@ -589,9 +597,45 @@ pattern replaced by interpolating the format
with its corresponding argument in
.Ar args
.Pq So %% Sc is replaced by the So % Sc character .
.It Fn INCHARMAP str Ta Returns 1 if Ar str No has an entry in the current charmap, and 0 otherwise .
.It Fn CHARLEN str Ta Returns the number of charmap entries in Ar str No with the current charmap .
.It Fn CHARSUB str pos Ta Returns the substring for the charmap entry at Ar pos No in Ar str No (first character is position 1, last is position -1) with the current charmap .
.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 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.
.El
.Pp
The following functions operate on string expressions, but return integers.
.Bl -column "STRRFIND(str, sub)"
.It Sy Name Ta Sy Operation
.It Fn STRLEN str Ta Returns the number of characters in Ar str .
.It Fn STRCMP str1 str2 Ta Compares Ar str1 No and Ar str2 No according to ASCII ordering of their characters. Returns -1 if Ar str1 No is lower than Ar str2 Ns , 1 if Ar str1 No is greater than Ar str2 Ns , or 0 if they match.
.It Fn STRFIND str sub Ta Returns the first index of Ar sub No in Ar str Ns , or -1 if it's not present.
.It Fn STRRFIND str sub Ta Returns the last index of Ar sub No in Ar str Ns , or -1 if it's not present.
.It Fn BYTELEN str Ta Returns the number of bytes in Ar str . Pq Non-ASCII characters can be multiple bytes.
.It Fn STRBYTE str idx Ta Returns the byte value at Ar idx No in Ar str .
.It Fn INCHARMAP str Ta Returns 1 if Ar str No has an entry in the current charmap, or 0 otherwise.
.It Fn CHARLEN str Ta Returns the number of charmap entries in Ar str No with the current charmap.
.It Fn CHARCMP str1 str2 Ta Compares Ar str1 No and Ar str2 No according to their charmap entry values with the current charmap. Returns -1 if Ar str1 No is lower than Ar str2 Ns , 1 if Ar str1 No is greater than Ar str2 Ns , or 0 if they match.
.It Fn CHARSIZE char Ta Returns how many values are in the charmap entry for Ar char No with the current charmap.
.It Fn CHARVAL char idx Ta Returns the value at Ar idx No of the charmap entry for Ar char .
.El
.Pp
Note that indexes count starting from 0 at the beginning, or from -1 at the end.
The characters of a string are counted by
.Ql STRLEN ;
the charmap entries of a string are counted by
.Ql CHARLEN ;
and the values of a charmap entry are counted by
.Ql CHARSIZE .
.Pp
The following legacy functions are similar to other functions that operate on string expressions, but for historical reasons, they count starting from
.Em position 1 ,
not from index 0!
(Position -1 still counts from the end.)
.Bl -column "STRSUB(str, pos, len)"
.It Sy Name Ta Sy Operation
.It Fn STRSUB str pos len Ta Returns a substring of Ar str No starting at Ar pos No and Ar len No characters long. If Ar len No is not specified, the substring continues to the end of Ar str No .
.It Fn STRIN str sub Ta Returns the first position of Ar sub No in Ar str Ns , or 0 if it's not present.
.It Fn STRRIN str sub Ta Returns the last position of Ar sub No in Ar str Ns , or 0 if it's not present.
.It Fn CHARSUB str pos Ta Returns the substring of Ar str No for the charmap entry at Ar pos No with the current charmap . Pq Ar pos No counts charmap entries, not characters.
.El
.Ss Character maps
When writing text strings that are meant to be displayed on the Game Boy, the character encoding in the ROM may need to be different than the source file encoding.
@@ -673,6 +717,9 @@ If
.Ar arg
is a section type keyword, it returns the size of that section type.
The result is not constant, since only RGBLINK can compute its value.
If
.Ar arg
is an 8-bit or 16-bit register, it returns the size of that register.
.It Fn STARTOF arg Ta If
.Ar arg
is a string, this function returns the starting address of the section named
@@ -1052,7 +1099,7 @@ and
.Ic WRAMX
types are still considered different.
.It
Different constraints (alignment, bank, etc.) can be specified for each unionized section declaration, but they must all be compatible.
Different constraints (alignment, bank, etc.) can be specified for each section fragment declaration, but they must all be compatible.
For example, alignment must be compatible with any fixed address, all specified banks must be the same, etc.
.It
A section fragment may not be unionized; after all, that wouldn't make much sense.
@@ -1075,6 +1122,120 @@ first, followed by the one from
and the one from
.Ql bar.o
last.
.Ss Fragment literals
Fragment literals are useful for short blocks of code or data that are only referenced once.
They are section fragments created by surrounding instructions or directives with
.Ql [[
double brackets
.Ql ]] ,
without a separate
.Ic SECTION FRAGMENT
declaration.
.Pp
The content of a fragment literal becomes a
.Ic SECTION FRAGMENT ,
sharing the same name and bank as its parent ROM section, but without any other constraints.
The parent section also becomes a
.Ic FRAGMENT
if it was not one already, so that it can be merged with its fragment literals.
RGBLINK merges the fragments in no particular order.
.Pp
A fragment literal can take the place of any 16-bit integer constant
.Ql n16
from the
.Xr gbz80 7
documentation, as well as a
.Ic DW
item.
The fragment literal then evaluates to its starting address.
For example, you can
.Ic CALL
or
.Ic JP
to a fragment literal.
.Pp
This code using named labels:
.Bd -literal -offset indent
DataTable:
dw First
dw Second
dw Third
First: db 1
Second: db 4
Third: db 9
Routine:
push hl
ld hl, Left
jr z, .got_it
ld hl, Right
\&.got_it
call .print
pop hl
ret
\&.print:
ld de, $1003
ld bc, STARTOF(VRAM)
jp Print
Left: db "left\e0"
Right: db "right\e0"
.Ed
.Pp
is equivalent to this code using fragment literals:
.Bd -literal -offset indent
DataTable:
dw [[ db 1 ]]
dw [[ db 4 ]]
dw [[ db 9 ]]
Routine:
push hl
ld hl, [[ db "left\e0" ]]
jr z, .got_it
ld hl, [[ db "right\e0" ]]
\&.got_it
call [[
ld de, $1003
ld bc, STARTOF(VRAM)
jp Print
]]
pop hl
ret
.Ed
.Pp
The difference is that the example using fragment literals does not declare a particular order for its pieces.
.Pp
Fragment literals can be arbitrarily nested, so extreme use cases are
.Em technically
possible.
This code using named labels:
.Bd -literal -offset indent
dw FortyTwo
FortyTwo:
call Sub1
jr Sub2
Sub1:
ld a, [Twenty]
ret
Twenty: db 20
Sub2:
jp Sub3
Sub3:
call Sub1
inc a
add a
ret
.Ed
.Pp
is equivalent to this code using fragment literals:
.Bd -literal -offset indent
dw [[
call [[
Sub1: ld a, [ [[db 20]] ] :: ret
]]
jr [[
jp [[ call Sub1 :: inc a :: add a :: ret ]]
]]
]]
.Ed
.Sh SYMBOLS
RGBDS supports several types of symbols:
.Bl -hang
@@ -1104,7 +1265,9 @@ Additionally, label names can contain up to a single dot
.Ql \&. ,
which may not be the first character.
.Pp
A symbol cannot have the same name as a reserved keyword, unless it is prefixed by a hash
A symbol cannot have the same name as a reserved keyword, unless its name is a
.Dq raw identifier
prefixed by a hash
.Sq # .
For example,
.Ql #load
@@ -1279,7 +1442,7 @@ it at the same time.
below).
.Ss Numeric constants
.Ic EQU
is used to define immutable numeric symbols.
is used to define numeric constant symbols.
Unlike
.Sq =
above, constants defined this way cannot be redefined.
@@ -1387,6 +1550,8 @@ This expansion is disabled in a few contexts:
and
.Ql MACRO name
will not expand string constants in their names.
Expansion is also disabled if the string constant's name is a raw identifier prefixed by a hash
.Sq # .
.Bd -literal -offset indent
DEF COUNTREG EQUS "[hl+]"
ld a, COUNTREG
@@ -1650,10 +1815,9 @@ Use
.Ic INCBIN
to include a raw binary file as it is.
If the file isn't found in the current directory, the include-path list passed to
.Xr rgbasm 1
(see the
.Xr rgbasm 1 Ap s
.Fl I
option) on the command line will be searched.
option on the command line will be searched.
.Bd -literal -offset indent
INCBIN "titlepic.bin"
INCBIN "sprites/hero.bin"
@@ -1852,9 +2016,11 @@ being the second, and so on. Since there are only nine digits, you can only use
To use the rest, you put the argument number in angle brackets, like
.Ic \e<10> .
.Pp
This bracketed syntax supports decimal numbers and numeric constant symbols.
This bracketed syntax supports decimal numbers and numeric symbols, where negative values count from the last argument.
For example,
.Ql \e<_NARG>
or
.Ql \e<-1>
will get the last argument.
.Pp
Other macro arguments and symbol interpolations will also be expanded inside the angle brackets.
@@ -2204,11 +2370,10 @@ block, all of them but the first one are ignored.
Use
.Ic INCLUDE
to process another assembler file and then return to the current file when done.
If the file isn't found in the current directory, the include path list (see the
If the file isn't found in the current directory, the include-path list passed to
.Xr rgbasm 1 Ap s
.Fl I
option in
.Xr rgbasm 1 )
will be searched.
option on the command line will be searched.
You may nest
.Ic INCLUDE
calls infinitely (or until you run out of memory, whichever comes first).

View File

@@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: MIT
.\"
.Dd December 25, 2024
.Dd July 31, 2025
.Dt RGBDS 5
.Os
.Sh NAME
@@ -388,10 +388,19 @@ The value is then ANDed with $00FF
check.
Checks if the value is a valid
.Ql rst
.Pq see Do RST vec Dc in Xr gbz80 7
vector, that is one of $00, $08, $10, $18, $20, $28, $30, or $38.
vector
.Pq see Do RST vec Dc in Xr gbz80 7 ,
that is, one of $00, $08, $10, $18, $20, $28, $30, or $38.
The value is then ORed with $C7
.Pq Ql \&| $C7 .
.It Li $62 Ta Ql bit/res/set
check; followed by the instruction's
.Cm BYTE
mask.
Checks if the value is a valid bit index
.Pq see e.g. Do BIT u3, r8 Dc in Xr gbz80 7 ,
that is, from 0 to 7.
The value is then ORed with the instruction's mask.
.It Li $80 Ta Integer literal; followed by the
.Cm LONG
integer.

View File

@@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: MIT
.\"
.Dd December 25, 2024
.Dd July 31, 2025
.Dt RGBDS 7
.Os
.Sh NAME
@@ -51,9 +51,13 @@ adapts the code to be more UNIX-like and releases this version as rgbds-linux.
forks Nossum's repository.
The fork becomes the reference implementation of RGBDS.
.It
2010-09-25: S\(/orensen continues development of
.Lk https://github.com/asmotor/asmotor ASMotor
to this day.
.It
2015-01-18:
.An stag019
begins implementing RGBGFX, a PNGtoGame Boy graphics converter, for eventual integration into RGBDS.
begins implementing RGBGFX, a PNG-to-Game Boy graphics converter, for eventual integration into RGBDS.
.It
2016-09-05: RGBGFX is integrated into Bentley's repository.
.It

View File

@@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: MIT
.\"
.Dd December 25, 2024
.Dd July 31, 2025
.Dt RGBFIX 1
.Os
.Sh NAME
@@ -8,7 +8,7 @@
.Nd Game Boy header utility and checksum fixer
.Sh SYNOPSIS
.Nm
.Op Fl jOsVv
.Op Fl hjOsVvw
.Op Fl C | c
.Op Fl f Ar fix_spec
.Op Fl i Ar game_id
@@ -17,9 +17,11 @@
.Op Fl l Ar licensee_id
.Op Fl m Ar mbc_type
.Op Fl n Ar rom_version
.Op Fl o Ar out_file
.Op Fl p Ar pad_value
.Op Fl r Ar ram_size
.Op Fl t Ar title_str
.Op Fl W Ar warning
.Op Ar
.Sh DESCRIPTION
The
@@ -91,11 +93,13 @@ Fix the global checksum
.It Cm G
Trash the global checksum.
.El
.It Fl h , Fl \-help
Print help text for the program and exit.
.It Fl i Ar game_id , Fl \-game-id Ar game_id
Set the game ID string
.Pq Ad 0x13F Ns \(en Ns Ad 0x142
to a given string.
If it's longer than 4 chars, it will be truncated, and a warning emitted.
If it's longer than 4 characters, it will be truncated.
.It Fl j , Fl \-non-japanese
Set the non-Japanese region flag
.Pq Ad 0x14A
@@ -104,7 +108,7 @@ to 0x01.
Set the new licensee string
.Pq Ad 0x144 Ns \(en Ns Ad 0x145
to a given string.
If it's longer than 2 chars, it will be truncated, and a warning emitted.
If it's longer than 2 characters, it will be truncated.
.It Fl L Ar logo_file , Fl \-logo Ar logo_file
Specify a logo file to use instead of the official Nintendo logo.
The file must be 48 bytes of 1bpp tile data; the source image should be 48 pixels wide and 8 pixels tall.
@@ -121,6 +125,8 @@ to a given value from 0 to 0xFF.
This value may also be an MBC name.
The list of accepted names can be obtained by passing
.Ql Cm help
or
.Ql Cm list
as the argument.
Any amount of whitespace (space and tabs) is allowed around plus signs, and the order of "components" is free, as long as the MBC name is first.
There are special considerations to take for the TPP1 mapper; see the
@@ -131,7 +137,11 @@ Set the ROM version
.Pq Ad 0x14C
to a given value from 0 to 0xFF.
.It Fl O , Fl \-overwrite
Allow overwriting different non-zero bytes in the header without a warning being emitted.
Alias for
.Fl Wno-overwrite .
.It Fl o Ar out_file , Fl \-output Ar out_file
Write the modified ROM image to the given file, or '-' to write to standard output.
If not specified, the input files are modified in-place, or written to standard output if read from standard input.
.It Fl p Ar pad_value , Fl \-pad-value Ar pad_value
Pad the ROM image to a valid size with a given pad value from 0 to 255 (0xFF).
.Nm
@@ -148,15 +158,14 @@ to a given value from 0 to 0xFF.
Set the SGB flag
.Pq Ad 0x146
to 0x03.
This flag will be ignored by the SGB unless the old licensee code is 0x33!
If this is given as well as
.Fl l ,
but is not set to 0x33, a warning will be printed.
This flag will be ignored by the SGB unless the old licensee code
.Pq Fl -l
is 0x33!
.It Fl t Ar title , Fl \-title Ar title
Set the title string
.Pq Ad 0x134 Ns \(en Ns Ad 0x143
to a given string.
If the title is longer than the max length, it will be truncated, and a warning emitted.
If the title is longer than the maximum length, it will be truncated.
The max length is 11 characters if the game ID
.Pq Fl i
is specified, 15 characters if the CGB flag
@@ -169,6 +178,77 @@ Print the version of the program and exit.
.It Fl v , Fl \-validate
Equivalent to
.Fl f Cm lhg .
.It Fl W Ar warning , Fl \-warning Ar warning
Set warning flag
.Ar warning .
A warning message will be printed if
.Ar warning
is an unknown warning flag.
See the
.Sx DIAGNOSTICS
section for a list of warnings.
.It Fl w
Disable all warning output, even when turned into errors.
.El
.Sh DIAGNOSTICS
Warnings are diagnostic messages that indicate possibly erroneous behavior that does not necessarily compromise the header-fixing process.
The following options alter the way warnings are processed.
.Bl -tag -width Ds
.It Fl Werror
Make all warnings into errors.
This can be negated as
.Fl Wno-error
to prevent turning all warnings into errors.
.It Fl Werror=
Make the specified warning or meta warning into an error.
A warning's name is appended
.Pq example: Fl Werror=overwrite ,
and this warning is implicitly enabled and turned into an error.
This can be negated as
.Fl Wno-error=
to prevent turning a specified warning into an error, even if
.Fl Werror
is in effect.
.El
.Pp
The following warnings are
.Dq meta
warnings, that enable a collection of other warnings.
If a specific warning is toggled via a meta flag and a specific one, the more specific one takes priority.
The position on the command-line acts as a tie breaker, the last one taking effect.
.Bl -tag -width Ds
.It Fl Wall
This enables warnings that are likely to indicate an error or undesired behavior, and that can easily be fixed.
.It Fl Weverything
Enables literally every warning.
.El
.Pp
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,
.Fl Wtruncation
enables the warning that
.Fl Wno-truncation
disables; and
.Fl Wall
enables every warning that
.Fl Wno-all
disables).
Only the non-default flag is listed here.
Ignoring the
.Dq no-
prefix, entries are listed alphabetically.
.Bl -tag -width Ds
.It Fl Wno-mbc
Warn when there are inconsistencies with or caveats about the specified MBC type.
.It Fl Wno-overwrite
Warn when overwriting different non-zero bytes in the header.
.It Fl Wno-sgb
Warn when the SGB flag
.Pq Fl s
conflicts with the old licensee code
.Pq Fl l .
.It Fl Wno-truncation
Warn when truncating values to fit the available space.
.El
.Sh EXAMPLES
Most values in the ROM header do not matter to the actual console, and most are seldom useful anyway.
@@ -222,7 +302,7 @@ Therefore,
.Nm
will ignore the
.Ql RAM
feature on a TPP1 mapper with a warning.
feature on a TPP1 mapper.
.Ss Special considerations
TPP1 overwrites the byte at
.Ad 0x14A ,

View File

@@ -2,7 +2,7 @@
.\"
.\" SPDX-License-Identifier: MIT
.\"
.Dd December 25, 2024
.Dd July 31, 2025
.Dt RGBGFX 1
.Os
.Sh NAME
@@ -10,7 +10,7 @@
.Nd Game Boy graphics converter
.Sh SYNOPSIS
.Nm
.Op Fl CmOuVXYZ
.Op Fl CmhOuVwXYZ
.Op Fl v Op Fl v No ...
.Op Fl a Ar attrmap | Fl A
.Op Fl b Ar base_ids
@@ -18,6 +18,7 @@
.Op Fl d Ar depth
.Op Fl i Ar input_tiles
.Op Fl L Ar slice
.Op Fl l Ar base_pal
.Op Fl N Ar nb_tiles
.Op Fl n Ar nb_pals
.Op Fl o Ar out_file
@@ -26,6 +27,7 @@
.Op Fl r Ar width
.Op Fl s Ar nb_colors
.Op Fl t Ar tilemap | Fl T
.Op Fl W Ar warning
.Op Fl x Ar quantity
.Ar file
.Sh DESCRIPTION
@@ -103,6 +105,19 @@ and has the same size.
Same as
.Fl a Ar base_path Ns .attrmap
.Pq see Sx Automatic output paths .
.It Fl B Ar color , Fl \-background-color Ar color
Set a background color to be omitted from output.
Colors are accepted in
.Ql #rgb
or
.Ql #rrggbb
format, or as
.Ql transparent .
Input tiles which are entirely the specified background color are ignored and will not be output in tile data file.
The tilemap, atrribute map, or palette map files
.Em will
use placeholder values where background tiles were.
If a background color is specified, it cannot be used within tiles which are not ignored.
.It Fl b Ar base_ids , Fl \-base-tiles Ar base_ids
Set the base IDs for tile map output.
.Ar base_ids
@@ -126,7 +141,7 @@ begins with a hash character
.Ql # ,
it is treated as an inline palette specification.
It should contain a comma-separated list of hexadecimal colors, each beginning with a hash.
Colors are accepted either as
Colors are accepted in
.Ql #rgb
or
.Ql #rrggbb
@@ -146,6 +161,24 @@ is the case-insensitive word
then the first four colors of the input PNG's embedded palette are used.
It is an error if the PNG is not indexed, or if colors other than these 4 are used.
.Pq This is different from the default behavior of indexed PNGs, as then unused entries in the embedded palette are ignored, whereas they are not with Fl c Cm embedded .
.It Sy DMG palette spec
If
.Ar pal_spec
starts with case-insensitive
.Cm dmg= ,
then the following two-digit hexadecimal number specifies four grayscale DMG color indexes.
The number functions like the DMG's $FF47
.Sy BGP
register
(see
.Lk https://gbdev.io/pandocs/Palettes.html Pan Docs
for more information):
the low two bits 0-1 specify which gray shade goes in color index 0,
the next two bits 2-3 specify which gray shade goes in color index 1,
and so on.
Gray shade 0 is the lightest (white), 3 is the darkest (black).
The same gray shade cannot go in two color indexes.
To specify a DMG palette, the input PNG must have all its colors in shades of gray, without any transparent colors.
.It Sy external palette spec
Otherwise,
.Ar pal_spec
@@ -165,6 +198,8 @@ for a list of formats and their descriptions.
.It Fl d Ar depth , Fl \-depth Ar depth
Set the bit depth of the output tile data, in bits per pixel (bpp), either 1 or 2 (the default).
This changes how tile data is output, and the maximum number of colors per palette (2 and 4 respectively).
.It Fl h , Fl \-help
Print help text for the program and exit.
.It Fl i Ar input_tiles , Fl \-input-tileset Ar input_tiles
Use the specified input tiles in addition to having
.Nm
@@ -228,6 +263,11 @@ The first number pair specifies the X and Y coordinates of the top-left pixel th
The second number pair specifies how many tiles to process horizontally and vertically, respectively.
.Pp
.Fl L Sy is ignored in reverse mode , No no padding is inserted .
.It Fl l Ar base_pal , Fl \-base-palette Ar base_pal
Set the base ID for attribute map and palette map output.
.Ar base_pal
should be a number between 0 and 255.
It defaults to 0.
.It Fl m , Fl \-mirror-tiles
Deduplicate tiles that are horizontally and/or vertically symmetrical mirror images of each other.
Only one of each unique tile will be saved in the tile data file, with mirror images counting as duplicates.
@@ -354,6 +394,17 @@ Some internal debug printing is enabled.
The verbosity level does not go past 6.
.Pp
Note that verbose output is only intended to be consumed by humans, and may change without notice between RGBDS releases; relying on those for scripts is not advised.
.It Fl W Ar warning , Fl \-warning Ar warning
Set warning flag
.Ar warning .
A warning message will be printed if
.Ar warning
is an unknown warning flag.
See the
.Sx DIAGNOSTICS
section for a list of warnings.
.It Fl w
Disable all warning output, even when turned into errors.
.It Fl X , Fl \-mirror-x
Deduplicate tiles that are horizontally symmetrical mirror images of each other across the X axis.
Implies
@@ -465,6 +516,9 @@ Useful to force several images to share the same palette.
Plaintext lines of hexadecimal colors in
.Ql rrggbb
format.
.It Cm png
An image of square color swatches, with each row defining the colors for one palette.
Color swatches can be any square size.
.It Cm psp
.Lk https://www.selapa.net/swatches/colors/fileformats.php#psp_pal Paint Shop Pro palette .
.El
@@ -513,6 +567,8 @@ Otherwise, if the PNG only contains shades of gray, they will be categorized int
.Dq bins
as there are colors per palette, and the palette is set to these bins.
The darkest gray will end up in bin #0, and so on; note that this is the opposite of the RGB method below.
This is equivalent to having specified a DMG palette of
.Fl c Cm dmg=E4 .
If two distinct grays end up in the same bin, the RGB method is used instead.
.Pp
Be careful that
@@ -667,6 +723,68 @@ assume that tiles were not deduplicated, and should be laid out in the order the
.Nm
assumes that no tiles were mirrored.
.El
.Sh DIAGNOSTICS
Warnings are diagnostic messages that indicate possibly erroneous behavior that does not necessarily compromise the conversion process.
The following options alter the way warnings are processed.
.Bl -tag -width Ds
.It Fl Werror
Make all warnings into errors.
This can be negated as
.Fl Wno-error
to prevent turning all warnings into errors.
.It Fl Werror=
Make the specified warning or meta warning into an error.
A warning's name is appended
.Pq example: Fl Werror=embedded ,
and this warning is implicitly enabled and turned into an error.
This can be negated as
.Fl Wno-error=
to prevent turning a specified warning into an error, even if
.Fl Werror
is in effect.
.El
.Pp
The following warnings are
.Dq meta
warnings, that enable a collection of other warnings.
If a specific warning is toggled via a meta flag and a specific one, the more specific one takes priority.
The position on the command-line acts as a tie breaker, the last one taking effect.
.Bl -tag -width Ds
.It Fl Wall
This enables warnings that are likely to indicate an error or undesired behavior, and that can easily be fixed.
.It Fl Weverything
Enables literally every warning.
.El
.Pp
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,
.Fl Wtrim-nonempty
enables the warning that
.Fl Wno-trim-nonempty
disables; and
.Fl Wall
enables every warning that
.Fl Wno-all
disables).
Only the non-default flag is listed here.
Ignoring the
.Dq no-
prefix, entries are listed alphabetically.
.Bl -tag -width Ds
.It Fl Wembedded
Warn when a generated palette is sorted according to the input PNG's embedded palette but
.Fl c Cm embedded
was not provided.
This warning is enabled by
.Fl Weverything .
.It Fl Wtrim-nonempty
Warn when
.Fl x
trims a nonempty tile.
An "empty" tile uses entirely color 0 of its palette.
This warning is enabled by
.Fl Wall .
.El
.Sh EXAMPLES
The following will only validate the
.Ql tileset.png

View File

@@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: MIT
.\"
.Dd December 25, 2024
.Dd July 31, 2025
.Dt RGBLINK 1
.Os
.Sh NAME
@@ -8,7 +8,7 @@
.Nd Game Boy linker
.Sh SYNOPSIS
.Nm
.Op Fl dMtVvwx
.Op Fl dhMtVvwx
.Op Fl l Ar linker_script
.Op Fl m Ar map_file
.Op Fl n Ar sym_file
@@ -16,6 +16,7 @@
.Op Fl o Ar out_file
.Op Fl p Ar pad_value
.Op Fl S Ar spec
.Op Fl W Ar warning
.Ar
.Sh DESCRIPTION
The
@@ -67,6 +68,8 @@ Enable DMG mode.
Prohibit the use of sections that doesn't exist on a DMG, such as VRAM bank 1.
This option automatically enables
.Fl w .
.It Fl h , Fl \-help
Print help text for the program and exit.
.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.
The attributes assigned in the linker script must be consistent with any assigned in the code.
@@ -112,6 +115,15 @@ Useful for ROMs that fit in 32 KiB.
Print the version of the program and exit.
.It Fl v , Fl \-verbose
Verbose: enable printing more information to standard error.
.It Fl W Ar warning , Fl \-warning Ar warning
Set warning flag
.Ar warning .
A warning message will be printed if
.Ar warning
is an unknown warning flag.
See the
.Sx DIAGNOSTICS
section for a list of warnings.
.It Fl w , Fl \-wramx
Expand the WRAM0 section size from 4 KiB to the full 8 KiB assigned to WRAM.
WRAMX sections that are fixed to a bank other than 1 become errors, other WRAMX sections are treated as WRAM0.
@@ -119,8 +131,8 @@ WRAMX sections that are fixed to a bank other than 1 become errors, other WRAMX
Disables padding the end of the final file.
This option automatically enables
.Fl t .
You can use this when not not making a ROM.
When making a ROM, be careful that not using this is not a replacement for
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
.Xr rgbfix 1 Ap s Fl p
option!
.El
@@ -174,6 +186,84 @@ as
.Ic WRAMX
sections will be treated as
.Ic WRAM0 .
.Sh DIAGNOSTICS
Warnings are diagnostic messages that indicate possibly erroneous behavior that does not necessarily compromise the linking process.
The following options alter the way warnings are processed.
.Bl -tag -width Ds
.It Fl Werror
Make all warnings into errors.
This can be negated as
.Fl Wno-error
to prevent turning all warnings into errors.
.It Fl Werror=
Make the specified warning or meta warning into an error.
A warning's name is appended
.Pq example: Fl Werror=assert ,
and this warning is implicitly enabled and turned into an error.
This can be negated as
.Fl Wno-error=
to prevent turning a specified warning into an error, even if
.Fl Werror
is in effect.
.El
.Pp
The following warnings are
.Dq meta
warnings, that enable a collection of other warnings.
If a specific warning is toggled via a meta flag and a specific one, the more specific one takes priority.
The position on the command-line acts as a tie breaker, the last one taking effect.
.Bl -tag -width Ds
.It Fl Wall
This enables warnings that are likely to indicate an error or undesired behavior, and that can easily be fixed.
.It Fl Weverything
Enables literally every warning.
.El
.Pp
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,
.Fl Wobsolete
enables the warning that
.Fl Wno-obsolete
disables; and
.Fl Wall
enables every warning that
.Fl Wno-all
disables).
Only the non-default flag is listed here.
Ignoring the
.Dq no-
prefix, entries are listed alphabetically.
.Bl -tag -width Ds
.It Fl Wno-assert
Warn when
.Ic WARN Ns No -type
assertions fail. (See
.Dq Aborting the assembly process
in
.Xr rgbasm 5
for
.Ic ASSERT ) .
.It Fl Wdiv
Warn when dividing the smallest negative integer (-2**31) by -1, which yields itself due to integer overflow.
This warning is enabled by
.Fl Wall .
.It Fl Wno-obsolete
Warn when obsolete features are encountered, which have been deprecated and may later be removed.
.It Fl Wshift
Warn when shifting right a negative value.
Use a division by 2**N instead.
This warning is enabled by
.Fl Wall .
.It Fl Wshift-amount
Warn when a shift's operand is negative or greater than 32.
This warning is enabled by
.Fl Wall .
.It Fl Wno-truncation
Warn when an implicit truncation (for example,
.Ic db
to an 8-bit value) loses some bits.
This occurs when an N-bit value is 2**N or greater, or less than -2**N.
.El
.Sh EXAMPLES
All you need for a basic ROM is an object file, which can be made into a ROM image like so:
.Pp

View File

@@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: MIT
.\"
.Dd December 25, 2024
.Dd July 31, 2025
.Dt RGBLINK 5
.Os
.Sh NAME
@@ -24,18 +24,20 @@ They are simply ignored.
.Pp
Keywords are composed of letters and digits (but they can't start with a digit); they are all case-insensitive.
.Pp
Numbers can be written in decimal format, or in binary using the
.Ql %
prefix, or in hexadecimal using the
.Ql $
prefix (hexadecimal digits are case-insensitive).
Note that unlike
.Xr rgbasm 5 ,
an octal
.Ql &
prefix is not supported, nor are
.Ql _
digit separators.
Numbers can be written in a number of formats.
.Bl -column -offset indent "Hexadecimal" "Possible prefixes"
.It Sy Format type Ta Sy Possible prefixes Ta Sy Accepted characters
.It Decimal Ta none Ta 0123456789
.It Hexadecimal Ta Li $ , 0x , 0X Ta 0123456789ABCDEF
.It Octal Ta Li & , 0o , 0O Ta 01234567
.It Binary Ta Li % , 0b , 0B Ta 01
.El
.Pp
Underscores are also accepted in numbers, except at the beginning of one.
This can be useful for grouping digits, like
.Ql 1_234
or
.Ql $ff_80 .
.Pp
Strings begin with a double quote, and end at the next (non-escaped) double quote.
Strings must not contain literal newline characters.
@@ -46,8 +48,9 @@ are supported, specifically
.Ql \e" ,
.Ql \en ,
.Ql \er ,
.Ql \et ,
and
.Ql \et .
.Ql \e0 .
Other backslash escape sequences in
.Xr rgbasm 5
are only relevant to assembly code and do not apply in linker scripts.
@@ -120,7 +123,7 @@ causes all sections between it and the next
.Ic ORG
or bank specification to be placed at addresses automatically determined by
.Nm .
.Pq It is, however, compatible with Ic ALIGN No below.
.Pq \&It is, however, compatible with Ic ALIGN No below.
.Pp
.Ql Ic ALIGN Ar addr , Ar offset
increases the

View File

@@ -3,16 +3,17 @@
configure_file(version.cpp _version.cpp ESCAPE_QUOTES)
set(common_src
"error.cpp"
"extern/getopt.cpp"
"diagnostics.cpp"
"usage.cpp"
"_version.cpp"
)
find_package(BISON 3.0.0 REQUIRED)
set(BISON_FLAGS "-Wall -Dparse.lac=full -Dlr.type=ielr")
set(BISON_FLAGS "-Wall -Dlr.type=ielr")
# Set some optimization flags on versions that support them
if(BISON_VERSION VERSION_GREATER_EQUAL "3.5")
set(BISON_FLAGS "${BISON_FLAGS} -Dapi.token.raw=true")
set(BISON_FLAGS "${BISON_FLAGS} -Dparse.lac=full -Dapi.token.raw=true")
endif()
if(BISON_VERSION VERSION_GREATER_EQUAL "3.6")
set(BISON_FLAGS "${BISON_FLAGS} -Dparse.error=detailed")
@@ -34,6 +35,7 @@ BISON_TARGET(LINKER_SCRIPT_PARSER "link/script.y"
set(rgbasm_src
"${BISON_ASM_PARSER_OUTPUT_SOURCE}"
"asm/actions.cpp"
"asm/charmap.cpp"
"asm/fixpoint.cpp"
"asm/format.cpp"
@@ -53,26 +55,11 @@ set(rgbasm_src
"util.cpp"
)
set(rgbfix_src
"fix/main.cpp"
)
set(rgbgfx_src
"gfx/main.cpp"
"gfx/pal_packing.cpp"
"gfx/pal_sorting.cpp"
"gfx/pal_spec.cpp"
"gfx/process.cpp"
"gfx/proto_palette.cpp"
"gfx/reverse.cpp"
"gfx/rgba.cpp"
"extern/getopt.cpp"
"error.cpp"
)
set(rgblink_src
"${BISON_LINKER_SCRIPT_PARSER_OUTPUT_SOURCE}"
"link/assign.cpp"
"link/lexer.cpp"
"link/layout.cpp"
"link/main.cpp"
"link/object.cpp"
"link/output.cpp"
@@ -80,12 +67,33 @@ set(rgblink_src
"link/sdas_obj.cpp"
"link/section.cpp"
"link/symbol.cpp"
"link/warning.cpp"
"extern/utf8decoder.cpp"
"linkdefs.cpp"
"opmath.cpp"
"util.cpp"
)
set(rgbfix_src
"fix/main.cpp"
"fix/mbc.cpp"
"fix/warning.cpp"
)
set(rgbgfx_src
"gfx/color_set.cpp"
"gfx/main.cpp"
"gfx/pal_packing.cpp"
"gfx/pal_sorting.cpp"
"gfx/pal_spec.cpp"
"gfx/png.cpp"
"gfx/process.cpp"
"gfx/reverse.cpp"
"gfx/rgba.cpp"
"gfx/warning.cpp"
"util.cpp"
)
foreach(PROG "asm" "fix" "gfx" "link")
add_executable(rgb${PROG}
${rgb${PROG}_src}

469
src/asm/actions.cpp Normal file
View File

@@ -0,0 +1,469 @@
// SPDX-License-Identifier: MIT
#include "asm/actions.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "extern/utf8decoder.hpp"
#include "helpers.hpp"
#include "asm/charmap.hpp"
#include "asm/format.hpp"
#include "asm/fstack.hpp"
#include "asm/symbol.hpp"
#include "asm/warning.hpp"
std::optional<std::string> act_ReadFile(std::string const &name, uint32_t maxLen) {
FILE *file = nullptr;
if (std::optional<std::string> fullPath = fstk_FindFile(name); fullPath) {
file = fopen(fullPath->c_str(), "rb");
}
if (!file) {
if (fstk_FileError(name, "READFILE")) {
// 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`
return std::nullopt;
}
return "";
}
Defer closeFile{[&] { fclose(file); }};
size_t readSize = maxLen;
if (fseek(file, 0, SEEK_END) == 0) {
// If the file is seekable and shorter than the max length,
// just read as many bytes as there are
if (long fileSize = ftell(file); static_cast<size_t>(fileSize) < readSize) {
readSize = fileSize;
}
fseek(file, 0, SEEK_SET);
} else if (errno != ESPIPE) {
error("Error determining size of READFILE file '%s': %s", name.c_str(), strerror(errno));
}
std::string contents;
contents.resize(readSize);
if (fread(&contents[0], 1, readSize, file) < readSize || ferror(file)) {
error("Error reading READFILE file '%s': %s", name.c_str(), strerror(errno));
return "";
}
return contents;
}
uint32_t act_StringToNum(std::vector<int32_t> const &str) {
uint32_t length = str.size();
if (length == 1) {
// The string is a single character with a single value,
// which can be used directly as a number.
return static_cast<uint32_t>(str[0]);
}
warning(WARNING_OBSOLETE, "Treating multi-unit strings as numbers is deprecated");
for (int32_t v : str) {
if (!checkNBit(v, 8, "All character units")) {
break;
}
}
uint32_t r = 0;
for (uint32_t i = length < 4 ? 0 : length - 4; i < length; ++i) {
r <<= 8;
r |= static_cast<uint8_t>(str[i]);
}
return r;
}
static void errorInvalidUTF8Byte(uint8_t byte, char const *functionName) {
error("%s: Invalid UTF-8 byte 0x%02hhX", functionName, byte);
}
size_t act_StringLen(std::string const &str, bool printErrors) {
size_t len = 0;
uint32_t state = UTF8_ACCEPT;
uint32_t codepoint = 0;
for (char c : str) {
uint8_t byte = static_cast<uint8_t>(c);
switch (decode(&state, &codepoint, byte)) {
case UTF8_REJECT:
if (printErrors) {
errorInvalidUTF8Byte(byte, "STRLEN");
}
state = UTF8_ACCEPT;
// fallthrough
case UTF8_ACCEPT:
++len;
break;
}
}
// Check for partial code point.
if (state != UTF8_ACCEPT) {
if (printErrors) {
error("STRLEN: Incomplete UTF-8 character");
}
++len;
}
return len;
}
std::string act_StringSlice(std::string const &str, uint32_t start, uint32_t stop) {
size_t strLen = str.length();
size_t index = 0;
uint32_t state = UTF8_ACCEPT;
uint32_t codepoint = 0;
uint32_t curIdx = 0;
// Advance to starting index in source string.
while (index < strLen && curIdx < start) {
switch (decode(&state, &codepoint, str[index])) {
case UTF8_REJECT:
errorInvalidUTF8Byte(str[index], "STRSLICE");
state = UTF8_ACCEPT;
// fallthrough
case UTF8_ACCEPT:
++curIdx;
break;
}
++index;
}
// An index 1 past the end of the string is allowed, but will trigger the
// "Length too big" warning below if the length is nonzero.
if (index >= strLen && start > curIdx) {
warning(
WARNING_BUILTIN_ARG,
"STRSLICE: Start index %" PRIu32 " is past the end of the string",
start
);
}
size_t startIndex = index;
// Advance to ending index in source string.
while (index < strLen && curIdx < stop) {
switch (decode(&state, &codepoint, str[index])) {
case UTF8_REJECT:
errorInvalidUTF8Byte(str[index], "STRSLICE");
state = UTF8_ACCEPT;
// fallthrough
case UTF8_ACCEPT:
++curIdx;
break;
}
++index;
}
// Check for partial code point.
if (state != UTF8_ACCEPT) {
error("STRSLICE: Incomplete UTF-8 character");
++curIdx;
}
if (curIdx < stop) {
warning(
WARNING_BUILTIN_ARG,
"STRSLICE: Stop index %" PRIu32 " is past the end of the string",
stop
);
}
return str.substr(startIndex, index - startIndex);
}
std::string act_StringSub(std::string const &str, uint32_t pos, uint32_t len) {
size_t strLen = str.length();
size_t index = 0;
uint32_t state = UTF8_ACCEPT;
uint32_t codepoint = 0;
uint32_t curPos = 1;
// Advance to starting position in source string.
while (index < strLen && curPos < pos) {
switch (decode(&state, &codepoint, str[index])) {
case UTF8_REJECT:
errorInvalidUTF8Byte(str[index], "STRSUB");
state = UTF8_ACCEPT;
// fallthrough
case UTF8_ACCEPT:
++curPos;
break;
}
++index;
}
// A position 1 past the end of the string is allowed, but will trigger the
// "Length too big" warning below if the length is nonzero.
if (index >= strLen && pos > curPos) {
warning(
WARNING_BUILTIN_ARG, "STRSUB: Position %" PRIu32 " is past the end of the string", pos
);
}
size_t startIndex = index;
uint32_t curLen = 0;
// Compute the result length in bytes.
while (index < strLen && curLen < len) {
switch (decode(&state, &codepoint, str[index])) {
case UTF8_REJECT:
errorInvalidUTF8Byte(str[index], "STRSUB");
state = UTF8_ACCEPT;
// fallthrough
case UTF8_ACCEPT:
++curLen;
break;
}
++index;
}
// Check for partial code point.
if (state != UTF8_ACCEPT) {
error("STRSUB: Incomplete UTF-8 character");
++curLen;
}
if (curLen < len) {
warning(WARNING_BUILTIN_ARG, "STRSUB: Length too big: %" PRIu32, len);
}
return str.substr(startIndex, index - startIndex);
}
size_t act_CharLen(std::string const &str) {
std::string_view view = str;
size_t len;
for (len = 0; charmap_ConvertNext(view, nullptr); ++len) {}
return len;
}
std::string act_StringChar(std::string const &str, uint32_t idx) {
std::string_view view = str;
size_t charLen = 1;
// Advance to starting index in source string.
for (uint32_t curIdx = 0; charLen && curIdx < idx; ++curIdx) {
charLen = charmap_ConvertNext(view, nullptr);
}
std::string_view start = view;
if (!charmap_ConvertNext(view, nullptr)) {
warning(
WARNING_BUILTIN_ARG, "STRCHAR: Index %" PRIu32 " is past the end of the string", idx
);
}
start = start.substr(0, start.length() - view.length());
return std::string(start);
}
std::string act_CharSub(std::string const &str, uint32_t pos) {
std::string_view view = str;
size_t charLen = 1;
// Advance to starting position in source string.
for (uint32_t curPos = 1; charLen && curPos < pos; ++curPos) {
charLen = charmap_ConvertNext(view, nullptr);
}
std::string_view start = view;
if (!charmap_ConvertNext(view, nullptr)) {
warning(
WARNING_BUILTIN_ARG, "CHARSUB: Position %" PRIu32 " is past the end of the string", pos
);
}
start = start.substr(0, start.length() - view.length());
return std::string(start);
}
int32_t act_CharCmp(std::string_view str1, std::string_view str2) {
std::vector<int32_t> seq1, seq2;
size_t idx1 = 0, idx2 = 0;
for (;;) {
if (idx1 >= seq1.size()) {
idx1 = 0;
seq1.clear();
charmap_ConvertNext(str1, &seq1);
}
if (idx2 >= seq2.size()) {
idx2 = 0;
seq2.clear();
charmap_ConvertNext(str2, &seq2);
}
if (seq1.empty() != seq2.empty()) {
return seq1.empty() ? -1 : 1;
} else if (seq1.empty()) {
return 0;
} else {
int32_t value1 = seq1[idx1++], value2 = seq2[idx2++];
if (value1 != value2) {
return (value1 > value2) - (value1 < value2);
}
}
}
}
uint32_t act_AdjustNegativeIndex(int32_t idx, size_t len, char const *functionName) {
// String functions adjust negative index arguments the same way,
// such that position -1 is the last character of a string.
if (idx < 0) {
idx += len;
}
if (idx < 0) {
warning(WARNING_BUILTIN_ARG, "%s: Index starts at 0", functionName);
idx = 0;
}
return static_cast<uint32_t>(idx);
}
uint32_t act_AdjustNegativePos(int32_t pos, size_t len, char const *functionName) {
// STRSUB and CHARSUB adjust negative position arguments the same way,
// such that position -1 is the last character of a string.
if (pos < 0) {
pos += len + 1;
}
if (pos < 1) {
warning(WARNING_BUILTIN_ARG, "%s: Position starts at 1", functionName);
pos = 1;
}
return static_cast<uint32_t>(pos);
}
std::string
act_StringReplace(std::string_view str, std::string const &old, std::string const &rep) {
if (old.empty()) {
warning(WARNING_EMPTY_STRRPL, "STRRPL: Cannot replace an empty string");
return std::string(str);
}
std::string rpl;
while (!str.empty()) {
auto pos = str.find(old);
if (pos == str.npos) {
rpl.append(str);
break;
}
rpl.append(str, 0, pos);
rpl.append(rep);
str.remove_prefix(pos + old.size());
}
return rpl;
}
std::string act_StringFormat(
std::string const &spec, std::vector<std::variant<uint32_t, std::string>> const &args
) {
std::string str;
size_t argIndex = 0;
for (size_t i = 0; spec[i] != '\0'; ++i) {
int c = spec[i];
if (c != '%') {
str += c;
continue;
}
c = spec[++i];
if (c == '%') {
str += c;
continue;
}
FormatSpec fmt{};
while (c != '\0') {
fmt.useCharacter(c);
if (fmt.isFinished()) {
break;
}
c = spec[++i];
}
if (fmt.isEmpty()) {
error("STRFMT: Illegal '%%' at end of format string");
str += '%';
break;
}
if (!fmt.isValid()) {
error("STRFMT: Invalid format spec for argument %zu", argIndex + 1);
str += '%';
} else if (argIndex >= args.size()) {
// Will warn after formatting is done.
str += '%';
} else if (std::holds_alternative<uint32_t>(args[argIndex])) {
fmt.appendNumber(str, std::get<uint32_t>(args[argIndex]));
} else {
fmt.appendString(str, std::get<std::string>(args[argIndex]));
}
++argIndex;
}
if (argIndex < args.size()) {
error("STRFMT: %zu unformatted argument(s)", args.size() - argIndex);
} else if (argIndex > args.size()) {
error(
"STRFMT: Not enough arguments for format spec, got: %zu, need: %zu",
args.size(),
argIndex
);
}
return str;
}
void act_CompoundAssignment(std::string const &symName, RPNCommand op, int32_t constValue) {
Expression oldExpr, constExpr, newExpr;
int32_t newValue;
oldExpr.makeSymbol(symName);
constExpr.makeNumber(constValue);
newExpr.makeBinaryOp(op, std::move(oldExpr), constExpr);
newValue = newExpr.getConstVal();
sym_AddVar(symName, newValue);
}
void act_FailAssert(AssertionType type) {
switch (type) {
case ASSERT_FATAL:
fatal("Assertion failed");
case ASSERT_ERROR:
error("Assertion failed");
break;
case ASSERT_WARN:
warning(WARNING_ASSERT, "Assertion failed");
break;
}
}
void act_FailAssertMsg(AssertionType type, std::string const &message) {
switch (type) {
case ASSERT_FATAL:
fatal("Assertion failed: %s", message.c_str());
case ASSERT_ERROR:
error("Assertion failed: %s", message.c_str());
break;
case ASSERT_WARN:
warning(WARNING_ASSERT, "Assertion failed: %s", message.c_str());
break;
}
}

View File

@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
#include "asm/charmap.hpp"
@@ -11,6 +11,7 @@
#include <unordered_map>
#include <utility>
#include "extern/utf8decoder.hpp"
#include "helpers.hpp"
#include "util.hpp"
@@ -32,33 +33,48 @@ struct Charmap {
std::vector<CharmapNode> nodes; // first node is reserved for the root node
};
// Traverse the trie depth-first to derive the character mappings in definition order
template<typename F>
bool forEachChar(Charmap const &charmap, F callback) {
// clang-format off: nested initializers
for (std::stack<std::pair<size_t, std::string>> prefixes({{0, ""}}); !prefixes.empty();) {
// clang-format on
auto [nodeIdx, mapping] = std::move(prefixes.top());
prefixes.pop();
CharmapNode const &node = charmap.nodes[nodeIdx];
if (node.isTerminal() && !callback(nodeIdx, mapping)) {
return false;
}
for (unsigned c = 0; c < std::size(node.next); ++c) {
if (size_t nextIdx = node.next[c]; nextIdx) {
prefixes.push({nextIdx, mapping + static_cast<char>(c)});
}
}
}
return true;
}
static std::deque<Charmap> charmapList;
static std::unordered_map<std::string, size_t> charmapMap; // Indexes into `charmapList`
static Charmap *currentCharmap;
std::stack<Charmap *> charmapStack;
static std::stack<Charmap *> charmapStack;
bool charmap_ForEach(
void (*mapFunc)(std::string const &),
void (*charFunc)(std::string const &, std::vector<int32_t>)
) {
for (Charmap const &charmap : charmapList) {
// Traverse the trie depth-first to derive the character mappings in definition order
std::map<size_t, std::string> mappings;
for (std::stack<std::pair<size_t, std::string>> prefixes({{0, ""}}); !prefixes.empty();) {
auto [nodeIdx, mapping] = std::move(prefixes.top());
prefixes.pop();
CharmapNode const &node = charmap.nodes[nodeIdx];
if (node.isTerminal())
mappings[nodeIdx] = mapping;
for (unsigned c = 0; c < 256; c++) {
if (size_t nextIdx = node.next[c]; nextIdx)
prefixes.push({nextIdx, mapping + static_cast<char>(c)});
}
}
forEachChar(charmap, [&mappings](size_t nodeIdx, std::string const &mapping) {
mappings[nodeIdx] = mapping;
return true;
});
mapFunc(charmap.name);
for (auto [nodeIdx, mapping] : mappings)
for (auto [nodeIdx, mapping] : mappings) {
charFunc(mapping, charmap.nodes[nodeIdx].value);
}
}
return !charmapList.empty();
}
@@ -67,14 +83,15 @@ void charmap_New(std::string const &name, std::string const *baseName) {
size_t baseIdx = SIZE_MAX;
if (baseName != nullptr) {
if (auto search = charmapMap.find(*baseName); search == charmapMap.end())
error("Base charmap '%s' doesn't exist\n", baseName->c_str());
else
if (auto search = charmapMap.find(*baseName); search == charmapMap.end()) {
error("Base charmap '%s' doesn't exist", baseName->c_str());
} else {
baseIdx = search->second;
}
}
if (charmapMap.find(name) != charmapMap.end()) {
error("Charmap '%s' already exists\n", name.c_str());
error("Charmap '%s' already exists", name.c_str());
return;
}
@@ -82,10 +99,11 @@ void charmap_New(std::string const &name, std::string const *baseName) {
charmapMap[name] = charmapList.size();
Charmap &charmap = charmapList.emplace_back();
if (baseIdx != SIZE_MAX)
if (baseIdx != SIZE_MAX) {
charmap.nodes = charmapList[baseIdx].nodes; // Copies `charmapList[baseIdx].nodes`
else
} else {
charmap.nodes.emplace_back(); // Zero-init the root node
}
charmap.name = name;
@@ -93,10 +111,11 @@ void charmap_New(std::string const &name, std::string const *baseName) {
}
void charmap_Set(std::string const &name) {
if (auto search = charmapMap.find(name); search == charmapMap.end())
error("Charmap '%s' doesn't exist\n", name.c_str());
else
if (auto search = charmapMap.find(name); search == charmapMap.end()) {
error("Charmap '%s' doesn't exist", name.c_str());
} else {
currentCharmap = &charmapList[search->second];
}
}
void charmap_Push() {
@@ -105,7 +124,7 @@ void charmap_Push() {
void charmap_Pop() {
if (charmapStack.empty()) {
error("No entries in the charmap stack\n");
error("No entries in the charmap stack");
return;
}
@@ -115,13 +134,13 @@ void charmap_Pop() {
void charmap_CheckStack() {
if (!charmapStack.empty()) {
warning(WARNING_UNMATCHED_DIRECTIVE, "`PUSHC` without corresponding `POPC`\n");
warning(WARNING_UNMATCHED_DIRECTIVE, "`PUSHC` without corresponding `POPC`");
}
}
void charmap_Add(std::string const &mapping, std::vector<int32_t> &&value) {
if (mapping.empty()) {
error("Cannot map an empty string\n");
error("Cannot map an empty string");
return;
}
@@ -146,30 +165,59 @@ void charmap_Add(std::string const &mapping, std::vector<int32_t> &&value) {
CharmapNode &node = charmap.nodes[nodeIdx];
if (node.isTerminal())
warning(WARNING_CHARMAP_REDEF, "Overriding charmap mapping\n");
if (node.isTerminal()) {
warning(WARNING_CHARMAP_REDEF, "Overriding charmap mapping");
}
std::swap(node.value, value);
}
bool charmap_HasChar(std::string const &input) {
bool charmap_HasChar(std::string const &mapping) {
Charmap const &charmap = *currentCharmap;
size_t nodeIdx = 0;
for (char c : input) {
for (char c : mapping) {
nodeIdx = charmap.nodes[nodeIdx].next[static_cast<uint8_t>(c)];
if (!nodeIdx)
if (!nodeIdx) {
return false;
}
}
return charmap.nodes[nodeIdx].isTerminal();
}
static CharmapNode const *charmapEntry(std::string const &mapping) {
Charmap const &charmap = *currentCharmap;
size_t nodeIdx = 0;
for (char c : mapping) {
nodeIdx = charmap.nodes[nodeIdx].next[static_cast<uint8_t>(c)];
if (!nodeIdx) {
return nullptr;
}
}
return &charmap.nodes[nodeIdx];
}
size_t charmap_CharSize(std::string const &mapping) {
CharmapNode const *node = charmapEntry(mapping);
return node && node->isTerminal() ? node->value.size() : 0;
}
std::optional<int32_t> charmap_CharValue(std::string const &mapping, size_t idx) {
if (CharmapNode const *node = charmapEntry(mapping);
node && node->isTerminal() && idx < node->value.size()) {
return node->value[idx];
}
return std::nullopt;
}
std::vector<int32_t> charmap_Convert(std::string const &input) {
std::vector<int32_t> output;
for (std::string_view inputView = input; charmap_ConvertNext(inputView, &output);)
;
for (std::string_view inputView = input; charmap_ConvertNext(inputView, &output);) {}
return output;
}
@@ -186,16 +234,17 @@ size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output
for (size_t nodeIdx = 0; inputIdx < input.length();) {
nodeIdx = charmap.nodes[nodeIdx].next[static_cast<uint8_t>(input[inputIdx])];
if (!nodeIdx)
if (!nodeIdx) {
break;
}
inputIdx++; // Consume that char
++inputIdx; // Consume that char
if (charmap.nodes[nodeIdx].isTerminal()) {
matchIdx = nodeIdx; // This node matches, register it
rewindDistance = 0; // If no longer match is found, rewind here
} else {
rewindDistance++;
++rewindDistance;
}
}
@@ -207,27 +256,43 @@ size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output
if (matchIdx) { // A match was found, use it
std::vector<int32_t> const &value = charmap.nodes[matchIdx].value;
if (output)
if (output) {
output->insert(output->end(), RANGE(value));
}
matchLen = value.size();
} else if (inputIdx < input.length()) { // No match found, but there is some input left
int firstChar = input[inputIdx];
size_t codepointLen = 0;
// This will write the codepoint's value to `output`, little-endian
size_t codepointLen = readUTF8Char(output, input.data() + inputIdx);
for (uint32_t state = UTF8_ACCEPT, codepoint = 0;
inputIdx + codepointLen < input.length();) {
if (decode(&state, &codepoint, input[inputIdx + codepointLen]) == UTF8_REJECT) {
error("Input string is not valid UTF-8");
codepointLen = 1;
break;
}
++codepointLen;
if (state == UTF8_ACCEPT) {
break;
}
}
if (codepointLen == 0)
error("Input string is not valid UTF-8\n");
if (output) {
output->insert(
output->end(), input.data() + inputIdx, input.data() + inputIdx + codepointLen
);
}
// Warn if this character is not mapped but any others are
if (charmap.nodes.size() > 1)
warning(WARNING_UNMAPPED_CHAR_1, "Unmapped character %s\n", printChar(firstChar));
else if (charmap.name != DEFAULT_CHARMAP_NAME)
if (int firstChar = input[inputIdx]; charmap.nodes.size() > 1) {
warning(WARNING_UNMAPPED_CHAR_1, "Unmapped character %s", printChar(firstChar));
} else if (charmap.name != DEFAULT_CHARMAP_NAME) {
warning(
WARNING_UNMAPPED_CHAR_2,
"Unmapped character %s not in " DEFAULT_CHARMAP_NAME " charmap\n",
"Unmapped character %s not in " DEFAULT_CHARMAP_NAME " charmap",
printChar(firstChar)
);
}
inputIdx += codepointLen;
matchLen = codepointLen;
@@ -236,3 +301,20 @@ size_t charmap_ConvertNext(std::string_view &input, std::vector<int32_t> *output
input = input.substr(inputIdx);
return matchLen;
}
std::string charmap_Reverse(std::vector<int32_t> const &value, bool &unique) {
Charmap const &charmap = *currentCharmap;
std::string revMapping;
unique = forEachChar(charmap, [&](size_t nodeIdx, std::string const &mapping) {
if (charmap.nodes[nodeIdx].value == value) {
if (revMapping.empty()) {
revMapping = mapping;
} else {
revMapping.clear();
return false;
}
}
return true;
});
return revMapping;
}

View File

@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: MIT */
// SPDX-License-Identifier: MIT
// Fixed-point math routines
@@ -10,25 +10,17 @@
#define M_PI 3.14159265358979323846
#endif
uint8_t fixPrecision;
uint8_t fix_Precision() {
return fixPrecision;
}
double fix_PrecisionFactor() {
return pow(2.0, fixPrecision);
}
static double fix2double(int32_t i, int32_t q) {
return i / pow(2.0, q);
}
static int32_t double2fix(double d, int32_t q) {
if (isnan(d))
if (isnan(d)) {
return 0;
if (isinf(d))
}
if (isinf(d)) {
return d < 0 ? INT32_MIN : INT32_MAX;
}
return static_cast<int32_t>(round(d * pow(2.0, q)));
}
@@ -73,7 +65,12 @@ int32_t fix_Mul(int32_t i, int32_t j, int32_t q) {
}
int32_t fix_Div(int32_t i, int32_t j, int32_t q) {
return double2fix(fix2double(i, q) / fix2double(j, q), q);
double dividend = fix2double(i, q);
double divisor = fix2double(j, q);
if (fpclassify(divisor) == FP_ZERO) {
return dividend < 0 ? INT32_MIN : dividend > 0 ? INT32_MAX : 0;
}
return double2fix(dividend / divisor, q);
}
int32_t fix_Mod(int32_t i, int32_t j, int32_t q) {
@@ -85,7 +82,11 @@ int32_t fix_Pow(int32_t i, int32_t j, int32_t q) {
}
int32_t fix_Log(int32_t i, int32_t j, int32_t q) {
return double2fix(log(fix2double(i, q)) / log(fix2double(j, q)), q);
double divisor = log(fix2double(j, q));
if (fpclassify(divisor) == FP_ZERO) {
return INT32_MAX;
}
return double2fix(log(fix2double(i, q)) / divisor, q);
}
int32_t fix_Round(int32_t i, int32_t q) {

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