Compare commits

...

157 Commits

Author SHA1 Message Date
Eldred Habert
f9daf27511 Merge pull request #585 from ISSOtm/msvc-ci
Add MSVC in CI
2020-10-06 17:48:13 +02:00
ISSOtm
06f7387466 Avoid using VLA in EQUS dumping
MSVC does not support those...
Also add a `develop` warning about VLAs, to avoid future incidents
2020-10-06 08:55:45 +02:00
ISSOtm
21e50eeff1 Have lexer not require <unistd.h> on MSVC
Required for `open`, `close`, `read`, and `STDIN_FILENO`,
which are defined elsewhere on MSVC.
2020-10-06 08:55:45 +02:00
ISSOtm
fd4cec93cd Compile with MSVC as well in CI 2020-10-06 08:55:41 +02:00
ISSOtm
92f2055a6c Fix implicit cast between enums
This caused `make develop` to fail
2020-10-05 01:53:54 +02:00
Eldred Habert
be9b1198e9 Merge pull request #584 from Xeyler/master
Add directory summary to README.rst
2020-10-04 20:20:08 +02:00
Brigham Campbell
56bea083f9 Add directory summary to README.rst 2020-10-04 12:04:59 -06:00
Eldred Habert
fdfc02ab96 Merge pull request #583 from JL2210/cmake-build-type
Modularize CMake build configuration
2020-10-04 19:45:20 +02:00
Eldred Habert
fc7f042ad6 Merge pull request #551 from NieDzejkob/errors-after-unknown-symbol
link: Suppress cascading errors.
2020-10-04 19:43:38 +02:00
James Larrowe
761c775043 Modularize CMake build configuration
Build type no longer defaults to Release (!)
have separate options for extra warning flags and sanitizers

toss DEVELOP macro

Fix sanitizers with CMake while I'm at it :|
2020-10-04 13:28:00 -04:00
Jakub Kądziołka
b421c983d6 link: Suppress cascading errors. 2020-10-04 18:14:22 +02:00
Eldred Habert
3036b58598 Merge pull request #557 from ISSOtm/new-lexer-electric-boogaloo
New lexer 2 — Electric Boogaloo
2020-10-04 16:45:47 +02:00
ISSOtm
2eca43cd2d Fix critical oversight in lexer buffer refilling
Since the lexer buffer wraps, the refilling gets handled in two steps:
First, iff the buffer would wrap, the buffer is refilled until its end.
Then, if more characters are requested, that amount is refilled too.

An important detail is that `read()` may not return as many characters as
requested; for this reason, the first step checks if its `read()` was
"full", and skips the second step otherwise.
This is also where a bug lied.

After a *lot* of trying, I eventually managed to reproduce the bug on an
OpenBSD VM, and after adding a couple of `assert`s in `peekInternal`, this
is what happened, starting at line 724:

0. `lexerState->nbChars` is 0, `lexerState->index` is 19;
1. We end up with `target` = 42, and `writeIndex` = 19;
2. 42 + 19 is greater than `LEXER_BUF_SIZE` (= 42), so the `if` is entered;
3. Within the first `readChars`, **`read` only returns 16 bytes**,
   advancing `writeIndex` to 35 and `target` to 26;
4. Within the second `readChars`, a `read(26)` is issued, overflowing the
   buffer.

The bug should be clear now: **the check at line 750 failed to work!** Why?
Because `readChars` modifies `writeIndex`.
The fix is simply to cache the number of characters expected, and use that.
2020-10-04 16:10:32 +02:00
ISSOtm
c24694233f Fix incomplete duplication of REPT nodes
"Initialization, sizeof, and the assignment operator ignore the flexible array member."
Oops!
2020-10-04 04:46:01 +02:00
ISSOtm
423a7c4899 Handle \\r better
Translate it to \\n regardless of the lexer mode
2020-10-04 04:46:01 +02:00
ISSOtm
ee9e45b3d4 Change assertion condition in __FILE__ buf dumping
Removes a false positive from Clang static analysis
2020-10-04 04:46:01 +02:00
ISSOtm
5a65188ca9 Implement compact file stacks in object files
Gets rid of `open_memstream`, enabling Windows compatibility again
Also fixes #491 as a nice bonus!
2020-10-04 04:46:01 +02:00
ISSOtm
930080f556 Mark not unmapping macro-containing files as okay
There isn't really a better alternative.
Making several mappings instead requires too much bookkeeping.
2020-10-04 04:46:01 +02:00
ISSOtm
8e7afb0ab3 Move some MSVC-specific defines to platform.h 2020-10-04 04:46:01 +02:00
ISSOtm
138523570e Fix possible uninitialized read on Windows 2020-10-04 04:46:01 +02:00
ISSOtm
82469ac0fd Shim around mmap on Windows 2020-10-04 04:46:01 +02:00
ISSOtm
96cb5e10ed Fix range-dependent dead code in recursion depth check 2020-10-04 04:46:01 +02:00
ISSOtm
b224cab3e0 Harmonize printing distance 2020-10-04 04:46:01 +02:00
ISSOtm
7381d7b92f Remove unnecessarily nested symbol data union 2020-10-04 04:46:01 +02:00
ISSOtm
dbef51ba05 Move isWhitespace to a place where it makes more sense 2020-10-04 04:46:01 +02:00
ISSOtm
c952dd8a6e Fix fixed-point constants not working correctly
And added a test to check their behavior
2020-10-04 04:46:01 +02:00
ISSOtm
b65ea64a58 Add newlines to all test output
MacOS treats them differently, for some reason.
2020-10-04 04:46:01 +02:00
ISSOtm
542b5d18f1 Fix possible capture buffer size overflow
Attempt to grow it to the max size first.
Seriously, if this triggers, *how*
2020-10-04 04:46:01 +02:00
ISSOtm
71a0a42cfb Fix C2x use of static_assert 2020-10-04 04:46:01 +02:00
ISSOtm
ac011fe69f Use common function to discard comments in macro args 2020-10-04 04:46:01 +02:00
ISSOtm
9e3d7a50e6 Handle comments in line continuations 2020-10-04 04:46:00 +02:00
ISSOtm
e33c2ad6a2 Fix INCLUDE ignoring -MG 2020-10-04 04:46:00 +02:00
ISSOtm
615f1072d9 Fix readFractionalPart never shifting characters 2020-10-04 04:46:00 +02:00
ISSOtm
f7b7a97407 Prevent expanding macro args in comments
Also use a cleaner way, instead of hardcoding to capture
2020-10-04 04:46:00 +02:00
ISSOtm
ece6853e0f Implement opt b and opt g 2020-10-04 04:46:00 +02:00
ISSOtm
b7b03ee451 Fix "REPT 0" not being a no-op 2020-10-04 04:45:59 +02:00
ISSOtm
f9b48c0cad Fix else working incorrectly from macros
Since the "skip ELSE blocks" variable is global, it used to get carried
over from the macro's `if` to the outer's.
2020-10-04 04:45:59 +02:00
ISSOtm
aa76603da9 Add line+col trace info to lexer 2020-10-04 04:45:59 +02:00
ISSOtm
b83b9825f8 Fix _NARG crashing outside of macros
And add a test for it
2020-10-04 04:45:59 +02:00
ISSOtm
d641972cde Fix macro args not being restored when exiting macros 2020-10-04 04:45:59 +02:00
ISSOtm
4d1333e124 Fix incorrect error reporting of INCLUDEd files 2020-10-04 04:45:59 +02:00
ISSOtm
35396e6410 Fix files being unmapped when still referenced by macros 2020-10-04 04:45:59 +02:00
ISSOtm
8d18b39eee Support missing register tokens
Made possible by #491
2020-10-04 04:45:59 +02:00
ISSOtm
ae77893021 Fix file name reporting
As noted in the function's code, this is very error-prone, but
will do the job; this needs rewriting due to #491 anyways, so, temporary.
2020-10-04 04:45:59 +02:00
ISSOtm
baeb180acd Apply error reporting changes to tests 2020-10-04 04:45:58 +02:00
ISSOtm
fd02ffb7bd Implement __FILE__ symbol
Also clean up built-in symbol creation
This is not great, but currently okay.
Should be fixed later, like the rest...
2020-10-04 04:45:58 +02:00
ISSOtm
62ecdce0b0 Fix line-continuation-macro test 2020-10-04 04:45:58 +02:00
ISSOtm
e4f2fad215 Support line continuations in main scope 2020-10-04 04:45:58 +02:00
ISSOtm
3f5f9bcaf0 Fix numeric constant overflow checks 2020-10-04 04:45:58 +02:00
ISSOtm
08867b3cec Enable catching invalid macro arg 0 2020-10-04 04:45:55 +02:00
ISSOtm
9081feab51 Reinstate macro arg scan distance
Used to be broken, so it was removed, but doing so prevents escaping them.
So it was instead put back in, but with corrected behavior
2020-10-04 04:39:27 +02:00
ISSOtm
cf992164f7 Fix lexer capture sometimes not being reset 2020-10-04 04:39:27 +02:00
ISSOtm
b27b821e7f Fix RAW lexer length underflow
Also added an assertion to check against more such overflows
2020-10-04 04:39:26 +02:00
ISSOtm
d9ecaabac1 Add debug tracing code to lexer
Hidden behind a #define, like YYDEBUG
2020-10-04 04:39:26 +02:00
ISSOtm
cd747d8175 Fix many lexer bugs
More to come...
2020-10-04 04:39:25 +02:00
ISSOtm
df75fd2ec2 Fix expansion reporting being incorrect 2020-10-04 04:38:53 +02:00
ISSOtm
adcaf4cd46 Fix crash when no macro args are being used 2020-10-04 04:38:53 +02:00
ISSOtm
81a77a9b88 Re-implement block copy to avoid expanding macro args
They were expanded during the capture, and there was no easy way to
avoid expanding them (believe me, after three hours and somehow an OOM, I
gave up trying).
2020-10-04 04:38:53 +02:00
ISSOtm
6e805cd318 Implement macro args
This finally allows running 90% of the test suite, debugging time!
2020-10-04 04:38:53 +02:00
ISSOtm
e11f25024e Add test for built-in file symbol
It's currently defined in fstack.c, making it more prone to accidental
dropping. Let's not repeat the 0.3.9 scenario...
2020-10-04 04:38:53 +02:00
ISSOtm
7c895f8a1b Fix diagnostic formatting
Missing colon and space after the file stack
2020-10-04 04:38:53 +02:00
ISSOtm
38bda7e1bb Fix string expansion reporting
More expansions were allowed than the limit specified, and reporting code
did not account for the extra one that caused overflow
2020-10-04 04:38:52 +02:00
ISSOtm
149db9a022 Fix incorrect freeing of expansions
Freeing an expansion should free its children, not its siblings...
Fixes a use-after-free reported by scan-build. Nice catch!
2020-10-04 04:38:52 +02:00
ISSOtm
fed252bc49 Fix nested expansions being incorrectly handled
The biggest problem was simply that the length of children expansions was
not accounted for when skipping over the parent... this took a lot of
arduous debugging, but it finally works!
2020-10-04 04:38:52 +02:00
ISSOtm
61b2fd9816 Add string expansion reporting
And fix line counting with expansion-made newlines.
This has the same bug as the old lexer (equs-newline's output does not
print the second warning as being part of the expansion).
Additionally, we regress equs-recursion, as we are no longer able to
catch this specific EQUS recursion. Simply enough, the new expansion
begins **after** the old one ends! I have found no way to handle that.
2020-10-04 04:38:52 +02:00
ISSOtm
5ad7a93750 Add EQUS expansion 2020-10-04 04:38:52 +02:00
ISSOtm
2ec10012b6 Fix mmap read offset not being initialized 2020-10-04 04:38:52 +02:00
ISSOtm
e56c6cc291 Fix PC's name not being passed to parser 2020-10-04 04:38:52 +02:00
ISSOtm
4c9a929a14 Implement almost all functionality
Add keywords and identifiers
Add comments
Add number literals
Add strings
Add a lot of new tokens
Add (and clean up) IF etc.
Improve reporting of unexpected chars / garbage bytes
Fix bug with and improved error messages when failing to open file
Add verbose-level messages about how files are opened
Enforce that files finish with a newline
Fix chars returned not being cast to unsigned char (may conflict w/ EOF)
Return null path when no file is open, rather than crash
Unify and improve error printing slightly

Known to be missing: macro expansion, REPT blocks, EQUS expansions
2020-10-04 04:38:50 +02:00
ISSOtm
71f8871702 Implement more functionality
Macro arg detection, first emitted tokens, primitive (bad) column counting
2020-10-04 04:37:58 +02:00
ISSOtm
6dc4ce6599 Implement infrastructure around new lexer
The lexer itself is very much incomplete, but this is intended to be a
safe point to revert to should further implementation go south.
2020-10-04 04:37:58 +02:00
Eldred Habert
3a44cc7722 Merge pull request #582 from ISSOtm/rewrite-charmap
Rewrite charmap system
2020-10-04 04:37:06 +02:00
ISSOtm
4cfed3c98f Rewrite charmap system
Avoid allocating a *ton* of data per charmap
Stop relying on uninitialized data in charmap nodes
Only initialize charmap nodes lazily
2020-10-04 04:31:10 +02:00
Eldred Habert
6af57ff026 Merge pull request #581 from JL2210/cmake-docs
Install manpages with CMake
2020-10-03 22:15:17 +02:00
ISSOtm
2e3db9d56a Clean up label generation
Only check for localness when we already know we have a local
2020-10-03 21:33:30 +02:00
James Larrowe
f8d9fa87ed Install manpages with CMake 2020-10-03 12:37:56 -04:00
ISSOtm
f53ad359a6 Remove whoami step from Windows CI
A carry-over from testing
2020-10-03 02:17:18 +02:00
ISSOtm
84de86beb5 Enable make develop on Ubuntu 20.04 CI as well 2020-10-03 01:05:09 +02:00
Eldred Habert
04e7af2675 Merge pull request #579 from ISSOtm/cmake-ci
Enable CMake in CI
2020-10-02 23:30:26 +02:00
Eldred Habert
4f13a336b9 Merge pull request #578 from JL2210/cmake-pkgconfig
Use pkg-config to detect libpng
2020-10-02 22:30:30 +02:00
ISSOtm
03508119e5 Use CMake in CI as well 2020-10-02 22:24:02 +02:00
James Larrowe
03e20138d3 Use pkg-config to detect libpng
Only fall back to findpng
2020-10-01 18:58:36 -04:00
Antonio Niño Díaz
dfcba36448 test: Update commit of uCity used for testing
This one updates the code to fix all warnings introduced in the last few
months because of updates to RGBDS.
2020-09-27 22:47:56 +01:00
ISSOtm
8e4a9a5c21 Remove assertions from release builds
I believe the CMakeLists already did that, but the Makefile did not.
2020-09-27 17:05:52 +02:00
ISSOtm
a1286e6f0e Make newlines explicit in error messages
In preparation for a change a PR is about to make
2020-09-27 10:54:06 +02:00
ISSOtm
c0808246e5 Silence the mingw test
Use "quiet" instead of "count"...
2020-09-27 10:51:52 +02:00
ISSOtm
ba051e10fb Factor printing assert failures into functions
Saves some code duplication
2020-09-27 09:24:24 +02:00
ISSOtm
9fee0603b1 Fix typo in object file format doc
Thanks @ax6!
2020-09-24 16:43:13 +02:00
ISSOtm
be8ebe6db9 Fix master docs update CI script
GitHub documentation about the syntax is unclear, this should be right
according to the examples I saw.
2020-09-24 16:41:23 +02:00
ISSOtm
548e3dc31c Correct previously-introduced test being a no-op
Forgot to invoke the macro due to a copy-paste error
2020-09-24 16:35:45 +02:00
ISSOtm
9440086d77 Add a test for purging a macro while running
This could cause a crash if the macro name is then used for error reporting
2020-09-24 16:14:18 +02:00
ISSOtm
ec5a1bc71f Fix incorrect obj file documentation
Bit 7 of section types was actually documented in the symbol type
Bit 6 of section types was not documented at all
2020-09-24 10:26:02 +02:00
ISSOtm
9742fa731c Run the quote in file name except on Windows
This should make the CI function again
2020-09-22 18:13:26 +02:00
ISSOtm
91a3c538d9 Enable running regression tests on PRs as well 2020-09-22 17:27:29 +02:00
ISSOtm
e98485da3f Make code style errors fail their CI job
Mimics the behavior of the old Travis script
2020-09-22 02:59:38 +02:00
ISSOtm
6528a955fe Fix checkpatch in CI 2020-09-22 02:44:24 +02:00
ISSOtm
431f77127e Also update master docs when updating script 2020-09-22 01:05:49 +02:00
ISSOtm
2cc58723cb Do not try updating docs if no key is set
This will avoid this randomly failing in forks, unless we want to run it
2020-09-22 00:56:14 +02:00
ISSOtm
268219d74e Avoid warning about /* fallthrough */ comments
We do not have `fallthrough;`, so...
2020-09-21 17:46:44 +02:00
ISSOtm
d09ed3e52e Get rid of flex as a dependency in CMakeLists
It will actually not be needed for new lexer
2020-09-20 03:39:53 +02:00
ISSOtm
421d1f5490 Add regression test for #546
Check for quotes in `__FILE__`
2020-09-20 00:44:29 +02:00
ISSOtm
66784b7122 Fix documentation not mentioning SECTION FRAGMENTsyntax 2020-09-20 00:06:51 +02:00
ISSOtm
54f2d99ce7 Apply two minor fixes to rgbasm(5)
Mustn't → must not
Add a comma to INCBIN sentence to mirror INCLUDE's
2020-09-17 20:45:58 +02:00
ISSOtm
557c799ec9 Update README to point to online install instructions 2020-09-17 03:47:55 +02:00
ISSOtm
d22a667095 Update help text to redirect to new online docs 2020-09-17 03:10:02 +02:00
ISSOtm
30085a5342 Update documentation link in README 2020-09-16 15:52:36 +02:00
ISSOtm
304e6c4279 Sync redirect page generation with site 2020-09-16 06:25:46 +02:00
ISSOtm
12458aae6f Fix permalinks of index pages 2020-09-16 06:04:38 +02:00
ISSOtm
7e5d9683b1 Use mandoc 1.14.5 in CI
This is the version that added -Otoc
2020-09-15 19:24:25 +02:00
ISSOtm
210a4a957a Get rid of in-repo HTML documentation
The online documentation is now managed by a CI hook
2020-09-15 18:39:22 +02:00
ISSOtm
131ad9b315 Fix GitHub link in BUGS sections 2020-09-15 18:35:04 +02:00
ISSOtm
06b4cf57ab Fix example arguments to -MT appearing as options 2020-09-15 18:32:13 +02:00
ISSOtm
dbefdc923a Clean up doc post-processor
Description blurb is already inline from new stylesheet
`Xr` links are already handled by `mandoc` now
Handle spaces between both dashes in long options
Remove `<head>` modifications, as fragments are generated instead
2020-09-15 18:30:40 +02:00
ISSOtm
37e45de9c1 Improve rendering of pages
Make links to other man pages work
Add a table of contents to rgbasm(5)
Make the OS at the bottom of all pages 'Linux'
Apply post-processor that used to be used
2020-09-15 18:27:55 +02:00
ISSOtm
5e63527190 Update repo link at bottom of all man pages 2020-09-15 16:00:17 +02:00
ISSOtm
0cc9026978 Fix docs update action
Make script executable (facepalm)
Fix `run**s**-on` typo
Add key using ssh-agent
Force using SSH for pushing back
2020-09-15 15:57:20 +02:00
ISSOtm
bb6a5441ed Update RGBDS history to mention org move 2020-09-15 15:02:26 +02:00
ISSOtm
0ffda1bf29 Add CI Action to update man pages from master 2020-09-15 15:00:07 +02:00
Antonio Vivace
56b5f77dc8 Mention the new website, add notice about the movement to the gbdev org 2020-09-15 14:01:38 +02:00
Antonio Vivace
6eb284f99e Update README.rst 2020-09-15 13:53:22 +02:00
Marco Spataro
34c2288fd0 Fix __FILE__ when filename contains quotes 2020-09-10 12:49:04 +02:00
ISSOtm
304bb9f902 Remove most Hungarian notation in section module
Seriously, it sucks.
2020-09-06 20:43:13 +02:00
ISSOtm
14be01880d Move UNION code inside section.c
Improves organization and locality
2020-09-06 19:18:10 +02:00
ISSOtm
12b7cf3cd4 Move curOffset into section code
Improves organization
2020-09-06 18:50:19 +02:00
ISSOtm
0d7914bff7 Fix asm/charmap.h not including required header 2020-09-06 17:16:49 +02:00
ISSOtm
d2285953d7 Fix test missed by last PR
We need to fix CI
2020-09-03 14:28:28 +02:00
Eldred Habert
d2801505c3 Merge pull request #562 from Rangi42/strsub-0
Resolve #554: STRSUB("<N-char string>", N, 0) will not warn "Position N is past the end of the string"
2020-09-03 12:12:03 +02:00
ISSOtm
9d62b4b9bb Fix bugs with LOAD section size
LOAD blocks did not properly update their parent's size until after closed
Additionally, section size wasn't correctly sanitized inside LOAD blocks
2020-09-03 12:07:12 +02:00
Rangi
b65b723fa6 Resolve #554: STRSUB("<N-char string>", N, 0) will not warn "Position N is past the end of the string" 2020-08-31 15:40:25 -04:00
ISSOtm
e05321356b Fix truncation warning when adding charmap mapping
It used to warn when mapping negative values
2020-08-31 21:33:53 +02:00
ISSOtm
0778959e98 Remove arbitrary limits on charmap
They were made irrelevant when switching to a trie
2020-08-31 21:26:21 +02:00
Eldred Habert
76331d1c4a Merge pull request #552 from mattcurrie/incbin-length-optional
Make INCBIN's length argument optional
2020-08-23 00:11:54 +02:00
Eldred Habert
9e378ec5cf Bump license year
I feel like there's a 20xx joke to be made here...
2020-08-20 23:02:25 +02:00
ISSOtm
f57d33b17a Update subprojects in test suite
Especially necessary for the "new lexer" branch
2020-08-18 16:40:41 +02:00
Matt Currie
c389e8dccb Fix typo
'arugment' => 'argument'
2020-08-17 13:57:58 +12:00
Matt Currie
90c64a94d1 Revert change to rgbasm.5.html
Will be rebuilt upon release
2020-08-17 13:56:29 +12:00
Eldred Habert
df27af6d1c Merge pull request #553 from JL2210/cmake-develop
Add DEVELOP option to CMake
2020-08-16 22:12:47 +02:00
James Larrowe
c85b48f23e Default to debug when building in develop mode 2020-08-16 13:58:02 -04:00
James Larrowe
5a9f2b7750 Add DEVELOP option to CMake
This requires CMake 3.0 so -Werror won't conflict with link tests.
Remove all version checks to improve simplicity.
2020-08-16 13:23:29 -04:00
ISSOtm
fcfecc6e82 Improve description of @ symbol
As requested by someone on GBDev
2020-08-15 15:23:11 +02:00
Matt Currie
f863a927c1 Make INCBIN's length argument optional
INCBIN can now be used with just a start position to include everything
from the start position until the end of the file.
2020-08-15 17:24:11 +12:00
Eldred Habert
cb4fbdfcd5 Merge pull request #550 from rednex/an/array
Refactor warning array for clarity
2020-08-05 10:53:21 +02:00
Eldred Habert
c6eacde55e Merge pull request #544 from ISSOtm/atomic_ind
Split register-indirect tokens
2020-08-05 10:52:18 +02:00
Antonio Niño Díaz
daf780c7e6 Refactor warning array for clarity 2020-08-04 22:28:56 +01:00
Eldred Habert
613305d234 Add checkpatch link in README 2020-07-31 16:07:00 +02:00
ISSOtm
656432301d Run as many CI jobs as possible when one fails
The other matrixes should not have this problem, so only touch unix testing
2020-07-27 18:34:32 +02:00
ISSOtm
38a80f2816 Fix test failing on OS-dependent trailing newline 2020-07-27 18:30:12 +02:00
ISSOtm
6563133426 Avoid using echo -e in tests 2020-07-27 18:26:05 +02:00
ISSOtm
762474d3ac Let RGBASM write JR offsets in floating sections
This requires some special-casing for `jr @` because the `jr` opcode has
already been emitted, but not the operand, so PC points to the middle.
Moved the RGBLINK test to RGBASM's folder, and created a new RGBLINK test.
2020-07-27 18:17:29 +02:00
ISSOtm
b1adbcc77c Output a non-empty RPN buffer from known expressions
The code expected to never get "known" expressions passed in, as RGBASM
otherwise patches the bytes in by itself; however, JR cannot be patched in
by RGBASM unless the section's base address is known as well!
2020-07-27 17:43:31 +02:00
Eldred Habert
dda052da20 Merge pull request #545 from JL2210/consistent-versions
Use versioning consistent with the Makefile in CMake
2020-07-26 19:16:27 +02:00
James Larrowe
2d6bdbc1b1 Use versioning consistent with the Makefile in CMake
This uses git to determine the dirty version and uses a fallback
if that's not available.
2020-07-23 10:47:01 -04:00
ISSOtm
0600856167 Move incbin slice sign check
The grammar's message is pretty generic, so this one is more useful.
2020-07-23 01:06:28 +02:00
ISSOtm
ce47b57b03 Document version string fallback in Makefile
For a packager just looking at the Makefile, it's not obvious that there
is one...
2020-07-22 16:21:58 +02:00
ISSOtm
f3c916ab96 Allow env vars to override default CFLAGS etc.
Should make the work easier for downstream packagers
2020-07-22 16:21:21 +02:00
ISSOtm
ca6fa6d1d7 Split register-indirect tokens
This allows whitespace between the brackets and the register.
This also fixes #531

Note that `$ff00 + c` is still treated as a single token, because trying to
use an expression on the left side causes a shift/reduce conflict.
This isn't great, but most people seem to be either used to it as-is, or
using the new `ldh a, [c]` syntax.
If this causes problems with a lexer rewrite, it'll be deprecated; but for
now, keep it around, as the support is clunky but bearable.
2020-07-22 15:00:39 +02:00
ISSOtm
fcd37b52b6 Update pret projects to latest commits
Behind pokecrystal by 123, behind pokered by 172
2020-07-22 00:29:38 +02:00
185 changed files with 4925 additions and 9482 deletions

View File

@@ -83,3 +83,6 @@
# Parentheses can make the code clearer # Parentheses can make the code clearer
--ignore UNNECESSARY_PARENTHESES --ignore UNNECESSARY_PARENTHESES
# We don't have `fallthrough;` */
--ignore PREFER_FALLTHROUGH

View File

@@ -38,48 +38,11 @@ BEGIN {
} }
} }
/<div class="Nd">/ {
# Make the description blurb inline, as with terminal output
gsub(/div/, "span")
}
BEGIN {
pages["gbz80", 7] = 1
pages["rgbds", 5] = 1
pages["rgbds", 7] = 1
pages["rgbasm", 1] = 1
pages["rgbasm", 5] = 1
pages["rgblink",1] = 1
pages["rgblink",5] = 1
pages["rgbfix", 1] = 1
pages["rgbgfx", 1] = 1
}
/<a class="Xr">/ {
# Link to other pages in the doc
for (i in pages) {
split(i, page, SUBSEP)
name = page[1]
section = page[2]
gsub(sprintf("<a class=\"Xr\">%s\\(%d\\)", name, section),
sprintf("<a class=\"Xr\" href=\"%s.%d.html\">%s(%d)", name, section, name, section))
}
}
{ {
# Make long opts (defined using `Fl Fl`) into a single tag # Make long opts (defined using `Fl Fl`) into a single tag
gsub(/<code class="Fl">-<\/code><code class="Fl">/, "<code class=\"Fl\">-") gsub(/<code class="Fl">-<\/code>\s*<code class="Fl">/, "<code class=\"Fl\">-")
} }
{ {
print print
} }
/<head>/ {
# Add viewport size <meta> tag for mobile users
print " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"
}
/<link/ {
# Inject our own style overrides
print(" <link rel=\"stylesheet\" href=\"rgbds.css\" type=\"text/css\" media=\"all\"/>")
}

102
.github/actions/get-pages.sh vendored Executable file
View File

@@ -0,0 +1,102 @@
#!/bin/bash
usage() {
cat <<EOF
Usage: $0 [-h] [-r] <rgbds-www> <version>
Copy renders from RGBDS repository to rgbds-www documentation
Execute from the root folder of the RGBDS repo, checked out at the desired tag
<rgbds-www> : Path to the '_documentation' folder in the rgbds-www repository
<version> : Version to be copied, such as 'v0.4.1' or 'master'
-h Display this help message
-r Update "latest stable" redirection pages (use for releases, not master)
EOF
}
update_redirects=0
bad_usage=0
while getopts ":hr" opt; do
case $opt in
r)
update_redirects=1
;;
h)
usage
exit 0
;;
\?)
echo "Unknown option '$OPTARG'"
if [ $bad_usage -eq 0 ]; then
usage
bad_usage=1
fi
;;
esac
done
if [ $bad_usage -ne 0 ]; then
exit 1
fi
shift $(($OPTIND - 1))
declare -A PAGES
PAGES=(
[rgbasm.1.html]=src/asm/rgbasm.1
[rgbasm.5.html]=src/asm/rgbasm.5
[rgblink.1.html]=src/link/rgblink.1
[rgblink.5.html]=src/link/rgblink.5
[rgbfix.1.html]=src/fix/rgbfix.1
[rgbgfx.1.html]=src/gfx/rgbgfx.1
[rgbds.5.html]=src/rgbds.5
[rgbds.7.html]=src/rgbds.7
[gbz80.7.html]=src/gbz80.7
)
WWWPATH="/docs"
mkdir -p "$1/$2"
# `mandoc` uses a different format for referring to man pages present in the **current** directory
# We want that format for RGBDS man pages, and the other one for the t=rest;
# we thus need to copy all pages to a temporary directory, and process them there.
# Copy all pages to current dir
cp "${PAGES[@]}" .
for page in "${!PAGES[@]}"; do
stem="${page%.html}"
manpage="${stem%.?}(${stem#*.})"
descr="$(awk -v 'FS=.Nd ' '/.Nd/ { print $2; }' "${PAGES[$page]}")"
cat - >"$1/$2/$page" <<EOF
---
layout: doc
title: $manpage [$2]
description: RGBDS $2 — $descr
---
EOF
options=fragment,man='%N.%S;https://linux.die.net/man/%S/%N'
if [ $stem = rgbasm.5 ]; then
options+=,toc
fi
mandoc -Thtml -I os=Linux -O$options "${PAGES[$page]##*/}" | .github/actions/doc_postproc.awk >> "$1/$2/$page"
if [ $update_redirects -ne 0 ]; then
cat - >"$1/$page" <<EOF
---
redirect_to: $WWWPATH/$2/${page%.html}
permalink: $WWWPATH/${page%.html}/
title: $manpage [latest stable]
description: RGBDS latest stable — $descr
---
EOF
fi
done
cat - >"$1/$2/index.html" <<EOF
---
layout: doc_index
permalink: /docs/$2/
title: RGBDS online manual [$2]
description: RGBDS $2 - Online manual
---
EOF
# Clean up
rm "${PAGES[@]##*/}"

View File

@@ -5,13 +5,21 @@ jobs:
checkpatch: checkpatch:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - name: Set up repo
run: |
git clone -b "${{ github.event.pull_request.head.ref }}" "${{ github.event.pull_request.head.repo.clone_url }}" rgbds
cd rgbds
git remote add upstream "${{ github.event.pull_request.base.repo.clone_url }}"
git fetch upstream
- name: Set up checkpatch - name: Set up checkpatch
working-directory: rgbds
run: | run: |
wget 'https://raw.githubusercontent.com/torvalds/linux/master/scripts/checkpatch.pl' wget 'https://raw.githubusercontent.com/torvalds/linux/master/scripts/checkpatch.pl'
chmod +x checkpatch.pl chmod +x checkpatch.pl
touch const_structs.checkpatch wget 'https://raw.githubusercontent.com/torvalds/linux/master/scripts/const_structs.checkpatch'
touch spelling.txt wget 'https://raw.githubusercontent.com/torvalds/linux/master/scripts/spelling.txt'
- name: Checkpatch - name: Checkpatch
working-directory: rgbds
run: | run: |
make checkpatch CHECKPATCH=./checkpatch.pl BASE_REF=${{ github.base_ref }} Q= make checkpatch CHECKPATCH=./checkpatch.pl "BASE_REF=${{ github.event.pull_request.base.sha }}" Q= | tee log
if grep -q ERROR: log; then exit 1; else exit 0; fi

View File

@@ -1,5 +1,7 @@
name: "Regression testing" name: "Regression testing"
on: push on:
- push
- pull_request
jobs: jobs:
unix-testing: unix-testing:
@@ -7,10 +9,17 @@ jobs:
matrix: matrix:
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04, macos-10.15] os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04, macos-10.15]
cc: [gcc, clang] cc: [gcc, clang]
buildsys: [make, cmake]
include: include:
- os: ubuntu-18.04 - os: ubuntu-18.04
cc: gcc cc: gcc
target: develop target: develop
cmakevars: -DSANITIZERS=ON -DMORE_WARNINGS=ON -DCMAKE_BUILD_TYPE=Debug
- os: ubuntu-20.04
cc: gcc
target: develop
cmakevars: -DSANITIZERS=ON -DMORE_WARNINGS=ON -DCMAKE_BUILD_TYPE=Debug
fail-fast: false
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@@ -18,12 +27,18 @@ jobs:
shell: bash shell: bash
run: | run: |
./.github/actions/install_deps.sh ${{ matrix.os }} ./.github/actions/install_deps.sh ${{ matrix.os }}
- name: Build - name: Build & install using Make
run: | run: |
make ${{ matrix.target }} -j Q= CC=${{ matrix.cc }} make ${{ matrix.target }} -j Q= CC=${{ matrix.cc }}
- name: Install
run: |
sudo make install -j Q= sudo make install -j Q=
if: matrix.buildsys == 'make'
- name: Build & install using CMake
run: |
cmake -S . -B build -DCMAKE_VERBOSE_MAKEFILE=ON ${{ matrix.cmakevars }}
cmake --build build
cp build/src/rgb{asm,link,fix,gfx} .
sudo cmake --install build
if: matrix.buildsys == 'cmake'
- name: Package binaries - name: Package binaries
run: | run: |
mkdir bins mkdir bins
@@ -31,14 +46,85 @@ jobs:
- name: Upload binaries - name: Upload binaries
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
with: with:
name: rgbds-canary-${{ matrix.os }}-${{ matrix.cc }} name: rgbds-canary-${{ matrix.os }}-${{ matrix.cc }}-${{ matrix.buildsys }}
path: bins path: bins
- name: Test - name: Test
shell: bash shell: bash
run: | run: |
test/run-tests.sh test/run-tests.sh
windows-build: windows-testing:
strategy:
matrix:
bits: [32, 64]
include:
- bits: 32
arch: x86
platform: Win32
- bits: 64
arch: x86_x64
platform: x64
fail-fast: false
runs-on: windows-2019
steps:
- uses: actions/checkout@v2
- name: Get zlib, libpng and bison
run: | # TODO: use an array
$wc = New-Object System.Net.WebClient
$wc.DownloadFile('https://www.zlib.net/zlib1211.zip', 'zlib.zip')
$hash = (Get-FileHash "zlib.zip" -Algorithm SHA256).Hash
if ($hash -ne 'd7510a8ee1918b7d0cad197a089c0a2cd4d6df05fee22389f67f115e738b178d') {
Write-Host "zlib SHA256 mismatch! ($hash)"
exit 1
}
$wc.DownloadFile('https://download.sourceforge.net/libpng/lpng1637.zip', 'libpng.zip')
$hash = (Get-FileHash "libpng.zip" -Algorithm SHA256).Hash
if ($hash -ne '3b4b1cbd0bae6822f749d39b1ccadd6297f05e2b85a83dd2ce6ecd7d09eabdf2') {
Write-Host "libpng SHA256 mismatch! ($hash)"
exit 1
}
$wc.DownloadFile('https://github.com/lexxmark/winflexbison/releases/download/v2.5.23/win_flex_bison-2.5.23.zip', 'winflexbison.zip')
$hash = (Get-FileHash "winflexbison.zip" -Algorithm SHA256).Hash
if ($hash -ne '6AA5C8EA662DA1550020A5804C28BE63FFAA53486DA9F6842E24C379EC422DFC') {
Write-Host "bison SHA256 mismatch! ($hash)"
}
Expand-Archive -DestinationPath . "zlib.zip"
Expand-Archive -DestinationPath . "libpng.zip"
Expand-Archive -DestinationPath install_dir "winflexbison.zip"
Move-Item zlib-1.2.11 zlib
Move-Item lpng1637 libpng
- name: Build zlib
run: | # BUILD_SHARED_LIBS causes the output DLL to be correctly called `zlib1.dll`
cmake -S zlib -B zbuild -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DBUILD_SHARED_LIBS=ON
cmake --build zbuild --config Release
cmake --install zbuild
- name: Build libpng
run: |
cmake -S libpng -B pngbuild -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DPNG_SHARED=ON -DPNG_STATIC=ON -DPNG_TESTS=OFF
cmake --build pngbuild --config Release
cmake --install pngbuild
- name: Build Windows binaries
run: |
cmake -S . -B build -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release
cmake --install build
- name: Package binaries
shell: bash
run: |
mkdir bins
cp install_dir/bin/{rgbasm.exe,rgblink.exe,rgbfix.exe,rgbgfx.exe,zlib1.dll,libpng16.dll} bins
- name: Upload Windows binaries
uses: actions/upload-artifact@v1
with:
name: rgbds-canary-win${{ matrix.bits }}
path: bins
- name: Test
shell: bash
run: |
cp bins/* .
test/run-tests.sh
windows-xbuild:
strategy: strategy:
matrix: matrix:
bits: [32, 64] bits: [32, 64]
@@ -50,6 +136,7 @@ jobs:
- bits: 64 - bits: 64
arch: x86-64 arch: x86-64
triplet: x86_64-w64-mingw32 triplet: x86_64-w64-mingw32
fail-fast: false
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
env: env:
DIST_DIR: win${{ matrix.bits }} DIST_DIR: win${{ matrix.bits }}
@@ -65,7 +152,7 @@ jobs:
- name: Install libpng dev headers for MinGW - name: Install libpng dev headers for MinGW
run: | run: |
sudo ./.github/actions/mingw-w64-libpng-dev.sh ${{ matrix.triplet }} sudo ./.github/actions/mingw-w64-libpng-dev.sh ${{ matrix.triplet }}
- name: Build Windows binaries - name: Cross-build Windows binaries
run: | run: |
make mingw${{ matrix.bits }} -j Q= make mingw${{ matrix.bits }} -j Q=
- name: Package binaries - name: Package binaries
@@ -81,21 +168,22 @@ jobs:
- name: Upload Windows binaries - name: Upload Windows binaries
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
with: with:
name: rgbds-canary-win${{ matrix.bits }} name: rgbds-canary-mingw-win${{ matrix.bits }}
path: bins path: bins
windows-testing: windows-xtesting:
needs: windows-build needs: windows-xbuild
strategy: strategy:
matrix: matrix:
bits: [32, 64] bits: [32, 64]
fail-fast: false
runs-on: windows-2019 runs-on: windows-2019
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Retrieve binaries - name: Retrieve binaries
uses: actions/download-artifact@v1 uses: actions/download-artifact@v1
with: with:
name: rgbds-canary-win${{ matrix.bits }} name: rgbds-canary-mingw-win${{ matrix.bits }}
path: bins path: bins
- name: Extract binaries - name: Extract binaries
shell: bash shell: bash

View File

@@ -0,0 +1,66 @@
name: "Update master docs"
on:
push:
branches:
- master
paths:
- .github/actions/get-pages.sh
- src/gbz80.7
- src/rgbds.5
- src/rgbds.7
- src/asm/rgbasm.1
- src/asm/rgbasm.5
- src/link/rgblink.1
- src/link/rgblink.5
- src/fix/rgbfix.1
- src/gfx/rgbgfx.1
jobs:
build:
if: secrets.SSH_KEY_SECRET != ''
runs-on: ubuntu-18.04
steps:
- name: Checkout rgbds@master
uses: actions/checkout@v2
with:
repository: gbdev/rgbds
ref: master
path: rgbds
- name: Checkout rgbds-www@master
uses: actions/checkout@v2
with:
repository: gbdev/rgbds-www
ref: master
path: rgbds-www
- name: Build and install mandoc
run: |
sudo apt-get -qq update
sudo apt-get install -yq zlib1g-dev
wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.5.tar.gz'
tar xf mandoc-1.14.5.tar.gz
cd mandoc-1.14.5
./configure
make
sudo make install
- name: Update pages
working-directory: rgbds
run: |
./.github/actions/get-pages.sh ../rgbds-www/_documentation master
- name: Push new pages
working-directory: rgbds-www
run: |
mkdir -p -m 700 ~/.ssh
echo "${{ secrets.SSH_KEY_SECRET }}" > ~/.ssh/id_ed25519
chmod 0600 ~/.ssh/id_ed25519
eval $(ssh-agent -s)
ssh-add ~/.ssh/id_ed25519
git config --global user.name "GitHub Action"
git config --global user.email "community@gbdev.io"
git add .
git commit -m "Update RGBDS master documentation"
if git remote | grep -q origin; then
git remote set-url origin git@github.com:gbdev/rgbds-www.git
else
git remote add origin git@github.com:gbdev/rgbds-www.git
fi
git push origin master

View File

@@ -6,35 +6,11 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
# #
cmake_minimum_required(VERSION 2.8.8) cmake_minimum_required(VERSION 3.0)
cmake_policy(VERSION 3.0)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) project(rgbds
set(RGBDS_VER 0.4.1)
set(RGBDS_DESC "Rednex Game Boy Development System")
if(CMAKE_VERSION VERSION_LESS 3.0)
project(rgbds C)
set(PROJECT_VERSION "${RGBDS_VER}")
else()
if(CMAKE_VERSION VERSION_LESS 3.9)
project(rgbds VERSION "${RGBDS_VER}"
LANGUAGES C) LANGUAGES C)
else()
project(rgbds VERSION "${RGBDS_VER}"
DESCRIPTION "${RGBDS_DESC}"
LANGUAGES C)
endif()
endif()
if(CMAKE_VERSION VERSION_LESS 3.9)
set(PROJECT_DESCRIPTION "${RGBDS_DESC}")
endif()
set(DEFAULT_BUILD_TYPE "Release")
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}")
endif()
# get real path of source and binary directories # get real path of source and binary directories
get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH) get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH)
@@ -47,23 +23,47 @@ if(srcdir STREQUAL bindir)
message(FATAL_ERROR "Terminating configuration") message(FATAL_ERROR "Terminating configuration")
endif() endif()
find_package(PNG 1.2 REQUIRED)
find_package(BISON REQUIRED)
find_package(FLEX)
include_directories("${PROJECT_SOURCE_DIR}/include") include_directories("${PROJECT_SOURCE_DIR}/include")
option(SANITIZERS "Build with sanitizers enabled" OFF)
option(MORE_WARNINGS "Turn on more warnings" OFF)
if(MSVC) if(MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W1 /MP -D_CRT_SECURE_NO_WARNINGS") add_compile_options(/W1 /MP)
add_definitions(/D_CRT_SECURE_NO_WARNINGS)
else() else()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -pedantic") add_compile_options(-Wall -pedantic)
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)
add_compile_options(${SAN_FLAGS})
link_libraries(${SAN_FLAGS})
endif() endif()
if(CMAKE_VERSION VERSION_LESS 3.12) if(MORE_WARNINGS)
add_definitions(-DBUILD_VERSION_STRING="${PROJECT_VERSION}") add_compile_options(-Werror -Wextra -Wno-type-limits
else() -Wno-sign-compare -Wvla -Wformat -Wformat-security -Wformat-overflow=2
add_compile_definitions(BUILD_VERSION_STRING="${PROJECT_VERSION}") -Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5
-Wstringop-overflow=4 -Walloc-zero -Wduplicated-cond
-Wfloat-equal -Wshadow -Wcast-qual -Wcast-align -Wlogical-op
-Wnested-externs -Wno-aggressive-loop-optimizations -Winline
-Wundef -Wstrict-prototypes -Wold-style-definition)
endif() endif()
endif()
# Use versioning consistent with Makefile
# the git revision is used but uses the fallback in an archive
execute_process(COMMAND git describe --tags --dirty --always
OUTPUT_VARIABLE GIT_REV
ERROR_QUIET)
string(STRIP "${GIT_REV}" GIT_REV)
add_definitions(-DBUILD_VERSION_STRING="${GIT_REV}")
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED True) set(CMAKE_C_STANDARD_REQUIRED True)

View File

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

View File

@@ -27,22 +27,23 @@ PNGCFLAGS := `${PKG_CONFIG} --cflags libpng`
PNGLDFLAGS := `${PKG_CONFIG} --libs-only-L libpng` PNGLDFLAGS := `${PKG_CONFIG} --libs-only-L libpng`
PNGLDLIBS := `${PKG_CONFIG} --libs-only-l libpng` PNGLDLIBS := `${PKG_CONFIG} --libs-only-l libpng`
# Note: if this comes up empty, `version.c` will automatically fall back to last release number
VERSION_STRING := `git describe --tags --dirty --always 2>/dev/null` VERSION_STRING := `git describe --tags --dirty --always 2>/dev/null`
WARNFLAGS := -Wall WARNFLAGS := -Wall
# Overridable CFLAGS # Overridable CFLAGS
CFLAGS := -O3 CFLAGS ?= -O3 -DNDEBUG
# Non-overridable CFLAGS # Non-overridable CFLAGS
REALCFLAGS := ${CFLAGS} ${WARNFLAGS} -std=gnu11 -D_POSIX_C_SOURCE=200809L \ REALCFLAGS := ${CFLAGS} ${WARNFLAGS} -std=gnu11 -D_POSIX_C_SOURCE=200809L \
-Iinclude -Iinclude
# Overridable LDFLAGS # Overridable LDFLAGS
LDFLAGS := LDFLAGS ?=
# Non-overridable LDFLAGS # Non-overridable LDFLAGS
REALLDFLAGS := ${LDFLAGS} ${WARNFLAGS} \ REALLDFLAGS := ${LDFLAGS} ${WARNFLAGS} \
-DBUILD_VERSION_STRING=\"${VERSION_STRING}\" -DBUILD_VERSION_STRING=\"${VERSION_STRING}\"
YFLAGS := YFLAGS ?=
YACC := yacc YACC := yacc
RM := rm -rf RM := rm -rf
@@ -55,7 +56,6 @@ rgbasm_obj := \
src/asm/asmy.o \ src/asm/asmy.o \
src/asm/charmap.o \ src/asm/charmap.o \
src/asm/fstack.o \ src/asm/fstack.o \
src/asm/globlex.o \
src/asm/lexer.o \ src/asm/lexer.o \
src/asm/macro.o \ src/asm/macro.o \
src/asm/main.o \ src/asm/main.o \
@@ -72,7 +72,7 @@ rgbasm_obj := \
src/hashmap.o \ src/hashmap.o \
src/linkdefs.o src/linkdefs.o
src/asm/globlex.o src/asm/lexer.o src/asm/constexpr.o: src/asm/asmy.h src/asm/lexer.o src/asm/main.o: src/asm/asmy.h
rgblink_obj := \ rgblink_obj := \
src/link/assign.o \ src/link/assign.o \
@@ -127,8 +127,7 @@ rgbgfx: ${rgbgfx_obj}
.c.o: .c.o:
$Q${CC} ${REALCFLAGS} ${PNGCFLAGS} -c -o $@ $< $Q${CC} ${REALCFLAGS} ${PNGCFLAGS} -c -o $@ $<
# Target used to remove all files generated by other Makefile targets, except # Target used to remove all files generated by other Makefile targets
# for the html documentation.
clean: clean:
$Q${RM} rgbasm rgbasm.exe $Q${RM} rgbasm rgbasm.exe
@@ -139,15 +138,6 @@ clean:
$Q${RM} rgbshim.sh $Q${RM} rgbshim.sh
$Q${RM} src/asm/asmy.c src/asm/asmy.h $Q${RM} src/asm/asmy.c src/asm/asmy.h
# Target used to remove all html files generated by the wwwman target
cleanwwwman:
$Q${RM} docs/rgbds.7.html docs/gbz80.7.html docs/rgbds.5.html
$Q${RM} docs/rgbasm.1.html docs/rgbasm.5.html
$Q${RM} docs/rgblink.1.html docs/rgblink.5.html
$Q${RM} docs/rgbfix.1.html
$Q${RM} docs/rgbgfx.1.html
# Target used to install the binaries and man pages. # Target used to install the binaries and man pages.
install: all install: all
@@ -191,29 +181,13 @@ checkpatch:
| ${CHECKPATCH} - || true; \ | ${CHECKPATCH} - || true; \
done done
# Target for the project maintainer to easily create web manuals.
# It relies on mandoc: http://mdocml.bsd.lv
MANDOC := -Thtml -Ios=General -Oman=%N.%S.html -Ostyle=mandoc.css
wwwman:
$Qmandoc ${MANDOC} src/rgbds.7 | src/doc_postproc.awk > docs/rgbds.7.html
$Qmandoc ${MANDOC} src/gbz80.7 | src/doc_postproc.awk > docs/gbz80.7.html
$Qmandoc ${MANDOC} src/rgbds.5 | src/doc_postproc.awk > docs/rgbds.5.html
$Qmandoc ${MANDOC} src/asm/rgbasm.1 | src/doc_postproc.awk > docs/rgbasm.1.html
$Qmandoc ${MANDOC} src/asm/rgbasm.5 | src/doc_postproc.awk > docs/rgbasm.5.html
$Qmandoc ${MANDOC} src/fix/rgbfix.1 | src/doc_postproc.awk > docs/rgbfix.1.html
$Qmandoc ${MANDOC} src/link/rgblink.1 | src/doc_postproc.awk > docs/rgblink.1.html
$Qmandoc ${MANDOC} src/link/rgblink.5 | src/doc_postproc.awk > docs/rgblink.5.html
$Qmandoc ${MANDOC} src/gfx/rgbgfx.1 | src/doc_postproc.awk > docs/rgbgfx.1.html
# This target is used during development in order to prevent adding new issues # 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 # to the source code. All warnings are treated as errors in order to block the
# compilation and make the continous integration infrastructure return failure. # compilation and make the continous integration infrastructure return failure.
develop: develop:
$Qenv $(MAKE) -j WARNFLAGS="-Werror -Wall -Wextra -Wpedantic \ $Qenv $(MAKE) -j WARNFLAGS="-Werror -Wall -Wextra -Wpedantic -Wno-type-limits \
-Wno-sign-compare -Wformat -Wformat-security -Wformat-overflow=2 \ -Wno-sign-compare -Wvla -Wformat -Wformat-security -Wformat-overflow=2 \
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused \ -Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused \
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5 \ -Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5 \
-Wstringop-overflow=4 -Walloc-zero -Wduplicated-cond \ -Wstringop-overflow=4 -Walloc-zero -Wduplicated-cond \
@@ -224,7 +198,7 @@ develop:
-fsanitize=unreachable -fsanitize=vla-bound \ -fsanitize=unreachable -fsanitize=vla-bound \
-fsanitize=signed-integer-overflow -fsanitize=bounds \ -fsanitize=signed-integer-overflow -fsanitize=bounds \
-fsanitize=object-size -fsanitize=bool -fsanitize=enum \ -fsanitize=object-size -fsanitize=bool -fsanitize=enum \
-fsanitize=alignment -fsanitize=null -DDEVELOP" CFLAGS="-g -O0" -fsanitize=alignment -fsanitize=null" CFLAGS="-ggdb3 -O0"
# Targets for the project maintainer to easily create Windows exes. # Targets for the project maintainer to easily create Windows exes.
# This is not for Windows users! # This is not for Windows users!

View File

@@ -15,160 +15,83 @@ other UNIX tools.
This toolchain is maintained on `GitHub <https://github.com/rednex/rgbds>`__. This toolchain is maintained on `GitHub <https://github.com/rednex/rgbds>`__.
The documentation of this toolchain can be viewed online The documentation of this toolchain can be viewed online
`here <https://rednex.github.io/rgbds/>`__, it is generated from the man pages `here <https://rgbds.gbdev.io/docs/>`__, it is generated from the man pages
found in this repository. found in this repository.
1. Installing RGBDS 1. Installing RGBDS
------------------- -------------------
1.1 Windows The `installation procedure <https://rgbds.gbdev.io/install>`__ is available
~~~~~~~~~~~ online for various platforms. `Building from source <https://rgbds.gbdev.io/install/source>`__
is possible using ``make`` or ``cmake``; follow the link for more detailed instructions.
Windows builds are available in the releases page on GitHub
`here <https://github.com/rednex/rgbds/releases>`__.
Extract the zip and then add the executable directory to the path. For example:
::
path %PATH%;C:\Programs\rgbds-0.3.8-win64\win64
Alternatively, the RGBDS executables can be simply dropped in the folder of the project they're used in.
If you require the latest version in development, it should be possible to
compile RGBDS with MinGW or Cygwin by following the instructions to build it on
UNIX systems.
1.2 macOS
~~~~~~~~~
You can build RGBDS by following the instructions below. However, if you would
prefer not to build RGBDS yourself, you may also install it using
`Homebrew <http://brew.sh/>`__.
To install the latest release, use:
.. code:: sh
brew install rgbds
To install RGBDS with all of the current changes in development (as seen on the
``master`` branch on GitHub), use:
.. code:: sh
brew install rgbds --HEAD
1.3 Arch Linux
~~~~~~~~~~~~~~
To install RGBDS through the AUR run
.. code:: sh
yaourt -S rgbds
1.4 Other UNIX-like systems
~~~~~~~~~~~~~~~~~~~~~~~~~~~
No official binaries of RGBDS are distributed for these systems, you must follow
the simple instructions below to compile and install it.
2. Building RGBDS from source
-----------------------------
RGBDS can be built in UNIX-like systems by following the instructions below.
2.1 Dependencies
~~~~~~~~~~~~~~~~
RGBDS requires yacc, libpng and pkg-config to be installed.
On macOS, install the latter two with `Homebrew <http://brew.sh/>`__:
.. code:: sh
brew install libpng pkg-config
On other Unixes, use the built-in package manager. For example, on Debian or
Ubuntu:
.. code:: sh
sudo apt-get install byacc flex pkg-config libpng-dev
You can test if libpng and pkg-config are installed by running ``pkg-config
--cflags libpng``: if the output is a path, then you're good, and if it outputs
an error then you need to install them via a package manager.
2.2 Build process
~~~~~~~~~~~~~~~~~
To build the programs, run in your terminal:
.. code:: sh .. code:: sh
make make
sudo make install
Then, to install the compiled programs and manual pages, run (with appropriate
privileges, e.g, with ``sudo``):
.. code:: sh .. code:: sh
make install cmake -S . -B build
cmake --build build
cmake --install build
After installation, you can read the manuals with the ``man`` command. E.g., 2. RGBDS Folder Organization
----------------------------
.. code:: sh The RGBDS source code file structure somewhat resembles the following:
man 7 rgbds ::
There are some variables in the Makefile that can be redefined by the user. The .
variables described below can affect installation behavior when given on the ├── .github/
make command line. For example, to install RGBDS in your home directory instead │   ├── actions/
of systemwide, run the following: │   │   └── ...
│   └── workflows/
│      └── ...
├── contrib/
│   └── ...
├── include/
│   └── ...
├── src/
│   ├── asm/
│   │   └── ...
│   ├── extern/
│   │   └── ...
│   ├── fix/
│   │   └── ...
│   ├── gfx/
│   │   └── ...
│   ├── link/
│   │   └── ...
│   ├── CMakeLists.txt
│   └── ...
├── test/
│   ├── ...
│ └── run-tests.sh
├── CMakeLists.txt
├── Makefile
└── README.rst
.. code:: sh - ``.github/`` - files and scripts related to the integration of the RGBDS codebase with
GitHub.
make install PREFIX=$HOME - ``contrib/`` - scripts and other resources which may be useful to users and developers of
RGBDS.
To do a verbose build, run: - ``include/`` - header files for each respective C files in `src`.
.. code:: sh - ``src/`` - source code and manual pages for RGBDS.
make Q= * Note that the code unique to each RGBDS tool is stored in its respective subdirectory
(rgbasm -> ``src/asm/``, for example). ``src/extern/`` contains code imported from external sources.
This is the complete list of user-defined variables: - ``test/`` - testing framework used to verify that changes to the code don't break or modify the behavior of RGBDS.
- ``PREFIX``: Location where RGBDS will be installed. Defaults to 3. History
``/usr/local``. ----------
- ``bindir``: Location where the binaries will be installed. Defaults to - Around 1997, Carsten Sørensen (AKA SurfSmurf) writes ASMotor as a
``${PREFIX}/bin``.
- ``mandir``: Location where the manpages will be installed. Defaults to
``${PREFIX}/share/man``.
- ``DESTDIR``: This is prepended to all paths during the installation. It is
mainly used for packaging.
- ``Q``: Whether to quiet the build or not. To make the build more verbose,
clear this variable. Defaults to ``@``.
- ``STRIP``: Whether to strip the installed binaries of debug symbols or not.
Defaults to ``-s``.
- ``BINMODE``: Permissions of the installed binaries. Defaults to ``755``.
- ``MANMODE``: Permissions of the installed manpages. Defaults to ``644``.
- ``CHECKPATCH``: Path of the script ``checkpatch.pl`` of the Linux kernel.
Defaults to ``../linux/scripts/checkpatch.pl``.
3 History
---------
- Around 1997, Carsten Sorensen (AKA SurfSmurf) writes ASMotor as a
general-purpose assembler/linker system for DOS/Win32 general-purpose assembler/linker system for DOS/Win32
- Around 1999, Justin Lloyd (AKA Otaku no Zoku) adapts ASMotor to read and - Around 1999, Justin Lloyd (AKA Otaku no Zoku) adapts ASMotor to read and
@@ -184,3 +107,5 @@ This is the complete list of user-defined variables:
- 2017, Bentley's repository is moved to a neutral name. - 2017, Bentley's repository is moved to a neutral name.
- 2018, codebase relicensed under the MIT license. - 2018, codebase relicensed under the MIT license.
- 2020, repository is moved to the `gbdev <https://github.com/gbdev>`__ organisation. The `rgbds.gbdev.io <https://rgbds.gbdev.io>`__ website serving documentation and downloads is created.

File diff suppressed because it is too large Load Diff

View File

@@ -1,36 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>General Information</title>
<link rel="stylesheet" type="text/css" href="mandoc.css">
</head>
<body>
<h1>RGBDS — Rednex Game Boy Development System</h1>
<h2>Table of Contents</h2>
<ol>
<li>General information
<ul>
<li><a href="rgbds.7.html">RGBDS general information</a></li>
<li><a href="rgbds.5.html">RGBDS object file format</a></li>
</ul>
<li>Language description
<ul>
<li><a href="rgbasm.5.html">RGBASM language description</a></li>
<li><a href="rgblink.5.html">RGBLINK linkerscript language description</a></li>
<li><a href="gbz80.7.html">GBZ80 CPU instruction set description</a></li>
</ul>
<li>Command line usage
<ul>
<li><a href="rgbasm.1.html">RGBASM command-line usage</a></li>
<li><a href="rgblink.1.html">RGBLINK command-line usage</a></li>
<li><a href="rgbfix.1.html">RGBFIX command-line usage</a></li>
<li><a href="rgbgfx.1.html">RGBGFX command-line usage</a></li>
</ul>
</ol>
<h2 id="GitHub Repository">GitHub Repository:</h2>
<ul>
<li><a href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a></li>
</ul>
</body>

View File

@@ -1,347 +0,0 @@
/* $Id: mandoc.css,v 1.45 2019/03/01 10:57:18 schwarze Exp $ */
/*
* Standard style sheet for mandoc(1) -Thtml and man.cgi(8).
*
* Written by Ingo Schwarze <schwarze@openbsd.org>.
* I place this file into the public domain.
* Permission to use, copy, modify, and distribute it for any purpose
* with or without fee is hereby granted, without any conditions.
*/
/* Global defaults. */
html { max-width: 65em; }
body { font-family: Helvetica,Arial,sans-serif; }
table { margin-top: 0em;
margin-bottom: 0em;
border-collapse: collapse; }
/* Some browsers set border-color in a browser style for tbody,
* but not for table, resulting in inconsistent border styling. */
tbody { border-color: inherit; }
tr { border-color: inherit; }
td { vertical-align: top;
padding-left: 0.2em;
padding-right: 0.2em;
border-color: inherit; }
ul, ol, dl { margin-top: 0em;
margin-bottom: 0em; }
li, dt { margin-top: 1em; }
.permalink { border-bottom: thin dotted;
color: inherit;
font: inherit;
text-decoration: inherit; }
* { clear: both }
/* Search form and search results. */
fieldset { border: thin solid silver;
border-radius: 1em;
text-align: center; }
input[name=expr] {
width: 25%; }
table.results { margin-top: 1em;
margin-left: 2em;
font-size: smaller; }
/* Header and footer lines. */
table.head { width: 100%;
border-bottom: 1px dotted #808080;
margin-bottom: 1em;
font-size: smaller; }
td.head-vol { text-align: center; }
td.head-rtitle {
text-align: right; }
table.foot { width: 100%;
border-top: 1px dotted #808080;
margin-top: 1em;
font-size: smaller; }
td.foot-os { text-align: right; }
/* Sections and paragraphs. */
.manual-text {
margin-left: 3.8em; }
.Nd { }
section.Sh { }
h1.Sh { margin-top: 1.2em;
margin-bottom: 0.6em;
margin-left: -3.2em;
font-size: 110%; }
section.Ss { }
h2.Ss { margin-top: 1.2em;
margin-bottom: 0.6em;
margin-left: -1.2em;
font-size: 105%; }
.Pp { margin: 0.6em 0em; }
.Sx { }
.Xr { }
/* Displays and lists. */
.Bd { }
.Bd-indent { margin-left: 3.8em; }
.Bl-bullet { list-style-type: disc;
padding-left: 1em; }
.Bl-bullet > li { }
.Bl-dash { list-style-type: none;
padding-left: 0em; }
.Bl-dash > li:before {
content: "\2014 "; }
.Bl-item { list-style-type: none;
padding-left: 0em; }
.Bl-item > li { }
.Bl-compact > li {
margin-top: 0em; }
.Bl-enum { padding-left: 2em; }
.Bl-enum > li { }
.Bl-compact > li {
margin-top: 0em; }
.Bl-diag { }
.Bl-diag > dt {
font-style: normal;
font-weight: bold; }
.Bl-diag > dd {
margin-left: 0em; }
.Bl-hang { }
.Bl-hang > dt { }
.Bl-hang > dd {
margin-left: 5.5em; }
.Bl-inset { }
.Bl-inset > dt { }
.Bl-inset > dd {
margin-left: 0em; }
.Bl-ohang { }
.Bl-ohang > dt { }
.Bl-ohang > dd {
margin-left: 0em; }
.Bl-tag { margin-top: 0.6em;
margin-left: 5.5em; }
.Bl-tag > dt {
float: left;
margin-top: 0em;
margin-left: -5.5em;
padding-right: 0.5em;
vertical-align: top; }
.Bl-tag > dd {
clear: right;
width: 100%;
margin-top: 0em;
margin-left: 0em;
margin-bottom: 0.6em;
vertical-align: top;
overflow: auto; }
.Bl-compact { margin-top: 0em; }
.Bl-compact > dd {
margin-bottom: 0em; }
.Bl-compact > dt {
margin-top: 0em; }
.Bl-column { }
.Bl-column > tbody > tr { }
.Bl-column > tbody > tr > td {
margin-top: 1em; }
.Bl-compact > tbody > tr > td {
margin-top: 0em; }
.Rs { font-style: normal;
font-weight: normal; }
.RsA { }
.RsB { font-style: italic;
font-weight: normal; }
.RsC { }
.RsD { }
.RsI { font-style: italic;
font-weight: normal; }
.RsJ { font-style: italic;
font-weight: normal; }
.RsN { }
.RsO { }
.RsP { }
.RsQ { }
.RsR { }
.RsT { text-decoration: underline; }
.RsU { }
.RsV { }
.eqn { }
.tbl td { vertical-align: middle; }
.HP { margin-left: 3.8em;
text-indent: -3.8em; }
/* Semantic markup for command line utilities. */
table.Nm { }
code.Nm { font-style: normal;
font-weight: bold;
font-family: inherit; }
.Fl { font-style: normal;
font-weight: bold;
font-family: inherit; }
.Cm { font-style: normal;
font-weight: bold;
font-family: inherit; }
.Ar { font-style: italic;
font-weight: normal; }
.Op { display: inline; }
.Ic { font-style: normal;
font-weight: bold;
font-family: inherit; }
.Ev { font-style: normal;
font-weight: normal;
font-family: monospace; }
.Pa { font-style: italic;
font-weight: normal; }
/* Semantic markup for function libraries. */
.Lb { }
code.In { font-style: normal;
font-weight: bold;
font-family: inherit; }
a.In { }
.Fd { font-style: normal;
font-weight: bold;
font-family: inherit; }
.Ft { font-style: italic;
font-weight: normal; }
.Fn { font-style: normal;
font-weight: bold;
font-family: inherit; }
.Fa { font-style: italic;
font-weight: normal; }
.Vt { font-style: italic;
font-weight: normal; }
.Va { font-style: italic;
font-weight: normal; }
.Dv { font-style: normal;
font-weight: normal;
font-family: monospace; }
.Er { font-style: normal;
font-weight: normal;
font-family: monospace; }
/* Various semantic markup. */
.An { }
.Lk { }
.Mt { }
.Cd { font-style: normal;
font-weight: bold;
font-family: inherit; }
.Ad { font-style: italic;
font-weight: normal; }
.Ms { font-style: normal;
font-weight: bold; }
.St { }
.Ux { }
/* Physical markup. */
.Bf { display: inline; }
.No { font-style: normal;
font-weight: normal; }
.Em { font-style: italic;
font-weight: normal; }
.Sy { font-style: normal;
font-weight: bold; }
.Li { font-style: normal;
font-weight: normal;
font-family: monospace; }
/* Tooltip support. */
h1.Sh, h2.Ss { position: relative; }
.An, .Ar, .Cd, .Cm, .Dv, .Em, .Er, .Ev, .Fa, .Fd, .Fl, .Fn, .Ft,
.Ic, code.In, .Lb, .Lk, .Ms, .Mt, .Nd, code.Nm, .Pa, .Rs,
.St, .Sx, .Sy, .Va, .Vt, .Xr {
display: inline-block;
position: relative; }
.An::before { content: "An"; }
.Ar::before { content: "Ar"; }
.Cd::before { content: "Cd"; }
.Cm::before { content: "Cm"; }
.Dv::before { content: "Dv"; }
.Em::before { content: "Em"; }
.Er::before { content: "Er"; }
.Ev::before { content: "Ev"; }
.Fa::before { content: "Fa"; }
.Fd::before { content: "Fd"; }
.Fl::before { content: "Fl"; }
.Fn::before { content: "Fn"; }
.Ft::before { content: "Ft"; }
.Ic::before { content: "Ic"; }
code.In::before { content: "In"; }
.Lb::before { content: "Lb"; }
.Lk::before { content: "Lk"; }
.Ms::before { content: "Ms"; }
.Mt::before { content: "Mt"; }
.Nd::before { content: "Nd"; }
code.Nm::before { content: "Nm"; }
.Pa::before { content: "Pa"; }
.Rs::before { content: "Rs"; }
h1.Sh::before { content: "Sh"; }
h2.Ss::before { content: "Ss"; }
.St::before { content: "St"; }
.Sx::before { content: "Sx"; }
.Sy::before { content: "Sy"; }
.Va::before { content: "Va"; }
.Vt::before { content: "Vt"; }
.Xr::before { content: "Xr"; }
.An::before, .Ar::before, .Cd::before, .Cm::before,
.Dv::before, .Em::before, .Er::before, .Ev::before,
.Fa::before, .Fd::before, .Fl::before, .Fn::before, .Ft::before,
.Ic::before, code.In::before, .Lb::before, .Lk::before,
.Ms::before, .Mt::before, .Nd::before, code.Nm::before,
.Pa::before, .Rs::before,
h1.Sh::before, h2.Ss::before, .St::before, .Sx::before, .Sy::before,
.Va::before, .Vt::before, .Xr::before {
opacity: 0;
transition: .15s ease opacity;
pointer-events: none;
position: absolute;
bottom: 100%;
box-shadow: 0 0 .35em #000;
padding: .15em .25em;
white-space: nowrap;
font-family: Helvetica,Arial,sans-serif;
font-style: normal;
font-weight: bold;
color: black;
background: #fff; }
.An:hover::before, .Ar:hover::before, .Cd:hover::before, .Cm:hover::before,
.Dv:hover::before, .Em:hover::before, .Er:hover::before, .Ev:hover::before,
.Fa:hover::before, .Fd:hover::before, .Fl:hover::before, .Fn:hover::before,
.Ft:hover::before, .Ic:hover::before, code.In:hover::before,
.Lb:hover::before, .Lk:hover::before, .Ms:hover::before, .Mt:hover::before,
.Nd:hover::before, code.Nm:hover::before, .Pa:hover::before,
.Rs:hover::before, h1.Sh:hover::before, h2.Ss:hover::before, .St:hover::before,
.Sx:hover::before, .Sy:hover::before, .Va:hover::before, .Vt:hover::before,
.Xr:hover::before {
opacity: 1;
pointer-events: inherit; }
/* Overrides to avoid excessive margins on small devices. */
@media (max-width: 37.5em) {
.manual-text {
margin-left: 0.5em; }
h1.Sh, h2.Ss { margin-left: 0em; }
.Bd-indent { margin-left: 2em; }
.Bl-hang > dd {
margin-left: 2em; }
.Bl-tag { margin-left: 2em; }
.Bl-tag > dt {
margin-left: -2em; }
.HP { margin-left: 2em;
text-indent: -2em; }
}

View File

@@ -1,295 +0,0 @@
<!DOCTYPE html>
<html>
<!-- This is an automatically generated file. Do not edit.
This file is part of RGBDS.
Copyright (c) 2010-2019, Anthony J. Bentley and RGBDS contributors.
SPDX-License-Identifier: MIT
-->
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8"/>
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
<link rel="stylesheet" href="rgbds.css" type="text/css" media="all"/>
<title>RGBASM(1)</title>
</head>
<body>
<table class="head">
<tr>
<td class="head-ltitle">RGBASM(1)</td>
<td class="head-vol">General Commands Manual</td>
<td class="head-rtitle">RGBASM(1)</td>
</tr>
</table>
<div class="manual-text">
<section class="Sh">
<h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
<code class="Nm">rgbasm</code> &#x2014;
<span class="Nd">Game Boy assembler</span>
</section>
<section class="Sh">
<h1 class="Sh" id="SYNOPSIS"><a class="permalink" href="#SYNOPSIS">SYNOPSIS</a></h1>
<table class="Nm">
<tr>
<td><code class="Nm">rgbasm</code></td>
<td>[<code class="Fl"><a href="#E">-E</a><a href="#h">h</a><a href="#L">L</a><a href="#V">V</a><a href="#v">v</a><a href="#w">w</a></code>] [<code class="Fl"><a href="#b">-b</a></code>
<var class="Ar">chars</var>] [<code class="Fl"><a href="#D">-D</a></code>
<var class="Ar">name</var>[=<var class="Ar">value</var>]]
[<code class="Fl"><a href="#g">-g</a></code> <var class="Ar">chars</var>]
[<code class="Fl"><a href="#i">-i</a></code> <var class="Ar">path</var>]
[<code class="Fl"><a href="#M">-M</a></code> <var class="Ar">depend_file</var>]
[<code class="Fl"><a href="#M">-M</a><a href="#G">G</a></code>] [<code class="Fl"><a href="#M">-M</a><a href="#P">P</a></code>]
[<code class="Fl"><a href="#M">-M</a><a href="#T">T</a></code> <var class="Ar">target_file</var>]
[<code class="Fl"><a href="#M">-M</a><a href="#Q">Q</a></code> <var class="Ar">target_file</var>]
[<code class="Fl"><a href="#o">-o</a></code> <var class="Ar">out_file</var>]
[<code class="Fl"><a href="#p">-p</a></code> <var class="Ar">pad_value</var>]
[<code class="Fl"><a href="#r">-r</a></code> <var class="Ar">recursion_depth</var>]
[<code class="Fl"><a href="#W">-W</a></code> <var class="Ar">warning</var>]
<var class="Ar">file ...</var></td>
</tr>
</table>
</section>
<section class="Sh">
<h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
The <code class="Nm">rgbasm</code> program creates an RGB object file from an
assembly source file. The input <var class="Ar">file</var> can be a file path,
or <code class="Cm">-</code> denoting <code class="Cm">stdin</code>.
<p class="Pp">Note that options can be abbreviated as long as the abbreviation
is unambiguous: <code class="Fl">--verb</code> is
<code class="Fl">--verbose</code>, but
<code class="Fl">--ver</code> is invalid because it
could also be <code class="Fl">--version</code>. The
arguments are as follows:</p>
<dl class="Bl-tag">
<dt><a class="permalink" href="#b"><code class="Fl" id="b">-b</code></a>
<var class="Ar">chars</var>,
<code class="Fl">--binary-digits</code>
<var class="Ar">chars</var></dt>
<dd>Change the two characters used for binary constants. The defaults are
01.</dd>
<dt><a class="permalink" href="#D"><code class="Fl" id="D">-D</code></a>
<var class="Ar">name</var>[=<var class="Ar">value</var>],
<code class="Fl">-</code> <code class="Fl">-define</code>
<var class="Ar">name</var>[=<var class="Ar">value</var>]</dt>
<dd>Add a string symbol to the compiled source code. This is equivalent to
&#x2018;<code class="Li"><var class="Ar">name</var> <code class="Ic">EQUS
&quot;</code><var class="Ar">value</var>&quot;</code>&#x2019; in code, or
&#x2018;<code class="Li"><var class="Ar">name</var> <code class="Ic">EQUS
&quot;1&quot;</code></code>&#x2019; if <var class="Ar">value</var> is not
specified.</dd>
<dt><a class="permalink" href="#E"><code class="Fl" id="E">-E</code></a>,
<code class="Fl">--export-all</code></dt>
<dd>Export all labels, including unreferenced and local labels.</dd>
<dt><a class="permalink" href="#g"><code class="Fl" id="g">-g</code></a>
<var class="Ar">chars</var>,
<code class="Fl">--gfx-chars</code>
<var class="Ar">chars</var></dt>
<dd>Change the four characters used for gfx constants. The defaults are
0123.</dd>
<dt><a class="permalink" href="#h"><code class="Fl" id="h">-h</code></a>,
<code class="Fl">--halt-without-nop</code></dt>
<dd>By default, <code class="Nm">rgbasm</code> inserts a
<code class="Ic">nop</code> instruction immediately after any
<code class="Ic">halt</code> instruction. The <code class="Fl">-h</code>
option disables this behavior.</dd>
<dt><a class="permalink" href="#i"><code class="Fl" id="i">-i</code></a>
<var class="Ar">path</var>,
<code class="Fl">--include</code>
<var class="Ar">path</var></dt>
<dd>Add an include path.</dd>
<dt><a class="permalink" href="#L"><code class="Fl" id="L">-L</code></a>,
<code class="Fl">--preserve-ld</code></dt>
<dd>Disable the optimization that turns loads of the form <code class="Ic">LD
[$FF00+n8],A</code> into the opcode <code class="Ic">LDH
[$FF00+n8],A</code> in order to have full control of the result in the
final ROM.</dd>
<dt><a class="permalink" href="#M"><code class="Fl" id="M">-M</code></a>
<var class="Ar">depend_file</var>,
<code class="Fl">--dependfile</code>
<var class="Ar">depend_file</var></dt>
<dd>Print <a class="Xr">make(1)</a> dependencies to
<var class="Ar">depend_file</var>.</dd>
<dt><a class="permalink" href="#MG"><code class="Fl" id="MG">-MG</code></a></dt>
<dd>To be used in conjunction with <code class="Fl">-M</code>. This makes
<code class="Nm">rgbasm</code> assume that missing files are
auto-generated: when <code class="Ic">INCLUDE</code> or
<code class="Ic">INCBIN</code> is attempted on a non-existent file, it is
added as a dependency, then <code class="Nm">rgbasm</code> exits normally
instead of erroring out. This feature is used in automatic updating of
makefiles.</dd>
<dt><a class="permalink" href="#MP"><code class="Fl" id="MP">-MP</code></a></dt>
<dd>When enabled, this causes a phony target to be added for each dependency
other than the main file. This prevents <a class="Xr">make(1)</a> from
erroring out when dependency files are deleted.</dd>
<dt><a class="permalink" href="#MT"><code class="Fl" id="MT">-MT</code></a>
<var class="Ar">target_file</var></dt>
<dd>Add a target to the rules emitted by <code class="Fl">-M</code>. The exact
string provided will be written, including spaces and special characters.
<div class="Bd Bd-indent"><code class="Li"><code class="Fl">-MT</code>
<code class="Fl">-fileA</code> <code class="Fl">-MT</code>
<code class="Fl">-fileB</code></code></div>
is equivalent to
<div class="Bd Bd-indent"><code class="Li"><code class="Fl">-MT</code>
<code class="Fl">-'fileA</code>
<code class="Fl">-fileB'</code>.</code></div>
If neither this nor <code class="Fl">-MQ</code> is specified, the output
file name is used.</dd>
<dt><a class="permalink" href="#MQ"><code class="Fl" id="MQ">-MQ</code></a>
<var class="Ar">target_file</var></dt>
<dd>Same as <code class="Fl">-MT</code>, but additionally escapes any special
<a class="Xr">make(1)</a> characters, essentially &#x2018;$&#x2019;.</dd>
<dt><a class="permalink" href="#o"><code class="Fl" id="o">-o</code></a>
<var class="Ar">out_file</var>,
<code class="Fl">--output</code>
<var class="Ar">out_file</var></dt>
<dd>Write an object file to the given filename.</dd>
<dt><a class="permalink" href="#p"><code class="Fl" id="p">-p</code></a>
<var class="Ar">pad_value</var>,
<code class="Fl">--pad-value</code>
<var class="Ar">pad_value</var></dt>
<dd>When padding an image, pad with this value. The default is 0x00.</dd>
<dt><a class="permalink" href="#r"><code class="Fl" id="r">-r</code></a>
<var class="Ar">recursion_depth</var>,
<code class="Fl">--recursion-depth</code>
<var class="Ar">recursion_depth</var></dt>
<dd>Specifies the recursion depth at which RGBASM will assume being in an
infinite loop.</dd>
<dt><a class="permalink" href="#V"><code class="Fl" id="V">-V</code></a>,
<code class="Fl">--version</code></dt>
<dd>Print the version of the program and exit.</dd>
<dt><a class="permalink" href="#v"><code class="Fl" id="v">-v</code></a>,
<code class="Fl">--verbose</code></dt>
<dd>Be verbose.</dd>
<dt><a class="permalink" href="#W"><code class="Fl" id="W">-W</code></a>
<var class="Ar">warning</var>,
<code class="Fl">--warning</code>
<var class="Ar">warning</var></dt>
<dd>Set warning flag <var class="Ar">warning</var>. A warning message will be
printed if <var class="Ar">warning</var> is an unknown warning flag. See
the <a class="Sx" href="#DIAGNOSTICS">DIAGNOSTICS</a> section for a list
of warnings.</dd>
<dt><a class="permalink" href="#w"><code class="Fl" id="w">-w</code></a></dt>
<dd>Disable all warning output, even when turned into errors.</dd>
</dl>
</section>
<section class="Sh">
<h1 class="Sh" id="DIAGNOSTICS"><a class="permalink" href="#DIAGNOSTICS">DIAGNOSTICS</a></h1>
Warnings are diagnostic messages that indicate possibly erroneous behavior that
does not necessarily compromise the assembling process. The following options
alter the way warnings are processed.
<dl class="Bl-tag">
<dt><a class="permalink" href="#Werror"><code class="Fl" id="Werror">-Werror</code></a></dt>
<dd>Make all warnings into errors.</dd>
<dt><a class="permalink" href="#Werror="><code class="Fl" id="Werror=">-Werror=</code></a></dt>
<dd>Make the specified warning into an error. A warning's name is appended
(example: <code class="Fl">-Werror=obsolete</code>), and this warning is
implicitly enabled and turned into an error. This is an error if used with
a meta warning, such as <code class="Fl">-Werror=all</code>.</dd>
</dl>
<p class="Pp">The following warnings are &#x201C;meta&#x201D; 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.</p>
<dl class="Bl-tag">
<dt><a class="permalink" href="#Wall"><code class="Fl" id="Wall">-Wall</code></a></dt>
<dd>This enables warnings that are likely to indicate an error or undesired
behavior, and that can easily be fixed.</dd>
<dt><a class="permalink" href="#Wextra"><code class="Fl" id="Wextra">-Wextra</code></a></dt>
<dd>This enables extra warnings that are less likely to pose a problem, but
that may still be wanted.</dd>
<dt><a class="permalink" href="#Weverything"><code class="Fl" id="Weverything">-Weverything</code></a></dt>
<dd>Enables literally every warning.</dd>
</dl>
<p class="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,
<code class="Fl">-Wempty-entry</code> enables the warning that
<code class="Fl">-Wno-empty-entry</code> disables). Only the non-default
flag is listed here. Ignoring the &#x201C;no-&#x201D; prefix, entries are
listed alphabetically.</p>
<dl class="Bl-tag">
<dt><a class="permalink" href="#Wno-assert"><code class="Fl" id="Wno-assert">-Wno-assert</code></a></dt>
<dd>Warns when <code class="Ic">WARN</code><span class="No">-type</span>
assertions fail. (See &#x201C;Aborting the assembly process&#x201D; in
<a class="Xr" href="rgbasm.5.html">rgbasm(5)</a> for <code class="Ic">ASSERT</code>).</dd>
<dt><a class="permalink" href="#Wbuiltin-args"><code class="Fl" id="Wbuiltin-args">-Wbuiltin-args</code></a></dt>
<dd>Warn about incorrect arguments to built-in functions, such as
<code class="Fn">STRSUB</code>() with indexes outside of the string's
bounds. This warning is enabled by <code class="Fl">-Wall</code>.</dd>
<dt><a class="permalink" href="#Wdiv"><code class="Fl" id="Wdiv">-Wdiv</code></a></dt>
<dd>Warn when dividing the smallest negative integer by -1, which yields
itself due to integer overflow.</dd>
<dt><a class="permalink" href="#Wempty-entry"><code class="Fl" id="Wempty-entry">-Wempty-entry</code></a></dt>
<dd>Warn when an empty entry is encountered in a <code class="Ic">db</code>,
<code class="Ic">dw</code>, <code class="Ic">dl</code> list. This warning
is enabled by <code class="Fl">-Wextra</code>.</dd>
<dt><a class="permalink" href="#Wlarge-constant"><code class="Fl" id="Wlarge-constant">-Wlarge-constant</code></a></dt>
<dd>Warn when a constant too large to fit in a signed 32-bit integer is
encountered. This warning is enabled by
<code class="Fl">-Wall</code>.</dd>
<dt><a class="permalink" href="#Wlong-string"><code class="Fl" id="Wlong-string">-Wlong-string</code></a></dt>
<dd>Warn when a string too long to fit in internal buffers is encountered.
This warning is enabled by <code class="Fl">-Wall</code>.</dd>
<dt><a class="permalink" href="#Wno-obsolete"><code class="Fl" id="Wno-obsolete">-Wno-obsolete</code></a></dt>
<dd>Warn when obsolete constructs such as the <code class="Ic">jp [hl]</code>
instruction or <code class="Ic">HOME</code> section type are
encountered.</dd>
<dt><a class="permalink" href="#Wshift"><code class="Fl" id="Wshift">-Wshift</code></a></dt>
<dd>Warn when shifting right a negative value. Use a division by 2^N
instead.</dd>
<dt><a class="permalink" href="#Wshift-amount"><code class="Fl" id="Wshift-amount">-Wshift-amount</code></a></dt>
<dd>Warn when a shift's operand is negative or greater than 32.</dd>
<dt><a class="permalink" href="#Wno-truncation"><code class="Fl" id="Wno-truncation">-Wno-truncation</code></a></dt>
<dd>Warn when an implicit truncation (for example, <code class="Ic">db</code>)
loses some bits.</dd>
<dt><a class="permalink" href="#Wno-user"><code class="Fl" id="Wno-user">-Wno-user</code></a></dt>
<dd>Warn when the <code class="Ic">WARN</code> built-in is executed. (See
&#x201C;Aborting the assembly process&#x201D; in
<a class="Xr" href="rgbasm.5.html">rgbasm(5)</a> for <code class="Ic">WARN</code>).</dd>
</dl>
</section>
<section class="Sh">
<h1 class="Sh" id="EXAMPLES"><a class="permalink" href="#EXAMPLES">EXAMPLES</a></h1>
You can assemble a source file in two ways.
<p class="Pp">Straightforward way:</p>
<div class="Bd Bd-indent"><code class="Li">$ rgbasm -o bar.o
foo.asm</code></div>
<p class="Pp">Pipes way:</p>
<div class="Bd Bd-indent"><code class="Li">$ cat foo.asm | rgbasm -o bar.o
-</code></div>
<div class="Bd Bd-indent"><code class="Li">$ rgbasm -o bar.o - &lt;
foo.asm</code></div>
<p class="Pp">The resulting object file is not yet a usable ROM image&#x2014;it
must first be run through <a class="Xr" href="rgblink.1.html">rgblink(1)</a> and then
<a class="Xr" href="rgbfix.1.html">rgbfix(1)</a>.</p>
</section>
<section class="Sh">
<h1 class="Sh" id="BUGS"><a class="permalink" href="#BUGS">BUGS</a></h1>
Please report bugs on
<a class="Lk" href="https://github.com/rednex/rgbds/issues">GitHub</a>.
</section>
<section class="Sh">
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
ALSO</a></h1>
<a class="Xr" href="rgbasm.5.html">rgbasm(5)</a>, <a class="Xr" href="rgbfix.1.html">rgbfix(1)</a>,
<a class="Xr" href="rgblink.1.html">rgblink(1)</a>, <a class="Xr" href="rgbds.5.html">rgbds(5)</a>,
<a class="Xr" href="rgbds.7.html">rgbds(7)</a>, <a class="Xr" href="gbz80.7.html">gbz80(7)</a>
</section>
<section class="Sh">
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
<code class="Nm">rgbasm</code> was originally written by Carsten S&#x00F8;rensen
as part of the ASMotor package, and was later packaged in RGBDS by Justin
Lloyd. It is now maintained by a number of contributors at
<a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.
</section>
</div>
<table class="foot">
<tr>
<td class="foot-date">July 8, 2019</td>
<td class="foot-os">General</td>
</tr>
</table>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -1,363 +0,0 @@
<!DOCTYPE html>
<html>
<!-- This is an automatically generated file. Do not edit.
This file is part of RGBDS.
Copyright (c) 2017-2018, Antonio Nino Diaz and RGBDS contributors.
SPDX-License-Identifier: MIT
-->
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8"/>
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
<link rel="stylesheet" href="rgbds.css" type="text/css" media="all"/>
<title>RGBDS(5)</title>
</head>
<body>
<table class="head">
<tr>
<td class="head-ltitle">RGBDS(5)</td>
<td class="head-vol">File Formats Manual</td>
<td class="head-rtitle">RGBDS(5)</td>
</tr>
</table>
<div class="manual-text">
<section class="Sh">
<h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
<code class="Nm">rgbds</code> &#x2014;
<span class="Nd">object file format documentation</span>
</section>
<section class="Sh">
<h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
This is the description of the object files used by <a class="Xr" href="rgbasm.1.html">rgbasm(1)</a>
and <a class="Xr" href="rgblink.1.html">rgblink(1)</a>. <i class="Em">Please note that the
specifications may change.</i> This toolchain is in development and new
features may require adding more information to the current format, or
modifying some fields, which would break compatibility with older versions.
</section>
<section class="Sh">
<h1 class="Sh" id="FILE_STRUCTURE"><a class="permalink" href="#FILE_STRUCTURE">FILE
STRUCTURE</a></h1>
The following types are used:
<p class="Pp"><var class="Ar">LONG</var> is a 32&#x2010;bit integer stored in
little&#x2010;endian format. <var class="Ar">BYTE</var> is an 8&#x2010;bit
integer. <var class="Ar">STRING</var> is a 0&#x2010;terminated string of
<var class="Ar">BYTE</var>.</p>
<div class="Bd Pp">
<pre>
; Header
BYTE ID[4] ; &quot;RGB9&quot;
LONG RevisionNumber ; The format's revision number this file uses
LONG NumberOfSymbols ; The number of symbols used in this file
LONG NumberOfSections ; The number of sections used in this file
; Symbols
REPT NumberOfSymbols ; Number of symbols defined in this object file.
STRING Name ; The name of this symbol. Local symbols are stored
; as &quot;Scope.Symbol&quot;.
BYTE Type ; 0 = LOCAL symbol only used in this file.
; 1 = IMPORT this symbol from elsewhere
; 2 = EXPORT this symbol to other objects.
; Bit 7 is independent from the above value, and
; encodes whether the section is unionized
IF (Type &amp; 0x7F) != 1 ; If symbol is defined in this object file.
STRING FileName ; File where the symbol is defined.
LONG LineNum ; Line number in the file where the symbol is defined.
LONG SectionID ; The section number (of this object file) in which
; this symbol is defined. If it doesn't belong to any
; specific section (like a constant), this field has
; the value -1.
LONG Value ; The symbols value. It's the offset into that
; symbol's section.
ENDC
ENDR
; Sections
REPT NumberOfSections
STRING Name ; Name of the section
LONG Size ; Size in bytes of this section
BYTE Type ; 0 = WRAM0
; 1 = VRAM
; 2 = ROMX
; 3 = ROM0
; 4 = HRAM
; 5 = WRAMX
; 6 = SRAM
; 7 = OAM
LONG Org ; Address to fix this section at. -1 if the linker should
; decide (floating address).
LONG Bank ; Bank to load this section into. -1 if the linker should
; decide (floating bank). This field is only valid for ROMX,
; VRAM, WRAMX and SRAM sections.
BYTE Align ; Alignment of this section, as N bits. 0 when not specified.
LONG Ofs ; Offset relative to the alignment specified above.
; Must be below 1 &lt;&lt; Align.
IF (Type == ROMX) || (Type == ROM0) ; Sections that can contain data.
BYTE Data[Size] ; Raw data of the section.
LONG NumberOfPatches ; Number of patches to apply.
REPT NumberOfPatches
STRING SourceFile ; Name of the source file (for printing error
; messages).
LONG Offset ; Offset into the section where patch should
; be applied (in bytes).
LONG PCSectionID ; Index within the file of the section in which
; PC is located.
; This is usually the same section that the
; patch should be applied into, except e.g.
; with LOAD blocks.
LONG PCOffset ; PC's offset into the above section.
; Used because the section may be floating, so
; PC's value is not known to RGBASM.
BYTE Type ; 0 = BYTE patch.
; 1 = little endian WORD patch.
; 2 = little endian LONG patch.
; 3 = JR offset value BYTE patch.
LONG RPNSize ; Size of the buffer with the RPN.
; expression.
BYTE RPN[RPNSize] ; RPN expression. Definition below.
ENDR
ENDC
ENDR
; Assertions
LONG NumberOfAssertions
REPT NumberOfAssertions
STRING SourceFile ; Name of the source file (for printing the failure).
LONG Offset ; Offset into the section where the assertion is located.
LONG SectionID ; Index within the file of the section in which PC is
; located, or -1 if defined outside a section.
LONG PCOffset ; PC's offset into the above section.
; Used because the section may be floating, so PC's value
; is not known to RGBASM.
BYTE Type ; 0 = Prints the message but allows linking to continue
; 1 = Prints the message and evaluates other assertions,
; but linking fails afterwards
; 2 = Prints the message and immediately fails linking
LONG RPNSize ; Size of the RPN expression's buffer.
BYTE RPN[RPNSize] ; RPN expression, same as patches. Assert fails if == 0.
STRING Message ; A message displayed when the assert fails. If set to
; the empty string, a generic message is printed instead.
ENDR
</pre>
</div>
<section class="Ss">
<h2 class="Ss" id="RPN_DATA"><a class="permalink" href="#RPN_DATA">RPN
DATA</a></h2>
Expressions in the object file are stored as RPN. This is an expression of the
form &#x201C;2 5 +&#x201D;. This will first push the value &#x201C;2&#x201D;
to the stack, then &#x201C;5&#x201D;. The &#x201C;+&#x201D; operator pops two
arguments from the stack, adds them, and then pushes the result on the stack,
effectively replacing the two top arguments with their sum. In the RGB format,
RPN expressions are stored as <var class="Ar">BYTE</var>s with some bytes
being special prefixes for integers and symbols.
<table class="Bl-column Bd-indent">
<tr>
<th>Value</th>
<th>Meaning</th>
</tr>
<tr>
<td><a class="permalink" href="#$00"><code class="Li" id="$00">$00</code></a></td>
<td><a class="permalink" href="#+_operator"><code class="Li" id="+_operator">+
operator</code></a></td>
</tr>
<tr>
<td><a class="permalink" href="#$01"><code class="Li" id="$01">$01</code></a></td>
<td><a class="permalink" href="#-_operator"><code class="Li" id="-_operator">-
operator</code></a></td>
</tr>
<tr>
<td><a class="permalink" href="#$02"><code class="Li" id="$02">$02</code></a></td>
<td><a class="permalink" href="#*_operator"><code class="Li" id="*_operator">*
operator</code></a></td>
</tr>
<tr>
<td><a class="permalink" href="#$03"><code class="Li" id="$03">$03</code></a></td>
<td><a class="permalink" href="#/_operator"><code class="Li" id="/_operator">/
operator</code></a></td>
</tr>
<tr>
<td><a class="permalink" href="#$04"><code class="Li" id="$04">$04</code></a></td>
<td><a class="permalink" href="#__operator"><code class="Li" id="__operator">%
operator</code></a></td>
</tr>
<tr>
<td><a class="permalink" href="#$05"><code class="Li" id="$05">$05</code></a></td>
<td><a class="permalink" href="#unary_-"><code class="Li" id="unary_-">unary
-</code></a></td>
</tr>
<tr>
<td><a class="permalink" href="#$10"><code class="Li" id="$10">$10</code></a></td>
<td>|
<a class="permalink" href="#operator"><code class="Li" id="operator">operator</code></a></td>
</tr>
<tr>
<td><a class="permalink" href="#$11"><code class="Li" id="$11">$11</code></a></td>
<td><a class="permalink" href="#&amp;_operator"><code class="Li" id="&amp;_operator">&amp;
operator</code></a></td>
</tr>
<tr>
<td><a class="permalink" href="#$12"><code class="Li" id="$12">$12</code></a></td>
<td><a class="permalink" href="#__operator_2"><code class="Li" id="__operator_2">^
operator</code></a></td>
</tr>
<tr>
<td><a class="permalink" href="#$13"><code class="Li" id="$13">$13</code></a></td>
<td><a class="permalink" href="#unary_~"><code class="Li" id="unary_~">unary
~</code></a></td>
</tr>
<tr>
<td><a class="permalink" href="#$21"><code class="Li" id="$21">$21</code></a></td>
<td><a class="permalink" href="#&amp;&amp;_comparison"><code class="Li" id="&amp;&amp;_comparison">&amp;&amp;
comparison</code></a></td>
</tr>
<tr>
<td><a class="permalink" href="#$22"><code class="Li" id="$22">$22</code></a></td>
<td><a class="permalink" href="#___comparison"><code class="Li" id="___comparison">||
comparison</code></a></td>
</tr>
<tr>
<td><a class="permalink" href="#$23"><code class="Li" id="$23">$23</code></a></td>
<td><a class="permalink" href="#unary__!"><code class="Li" id="unary__!">unary&#x00A0;!</code></a></td>
</tr>
<tr>
<td><a class="permalink" href="#$30"><code class="Li" id="$30">$30</code></a></td>
<td><a class="permalink" href="#==_comparison"><code class="Li" id="==_comparison">==
comparison</code></a></td>
</tr>
<tr>
<td><a class="permalink" href="#$31"><code class="Li" id="$31">$31</code></a></td>
<td><a class="permalink" href="#!=_comparison"><code class="Li" id="!=_comparison">!=
comparison</code></a></td>
</tr>
<tr>
<td><a class="permalink" href="#$32"><code class="Li" id="$32">$32</code></a></td>
<td><a class="permalink" href="#__comparison"><code class="Li" id="__comparison">&gt;
comparison</code></a></td>
</tr>
<tr>
<td><a class="permalink" href="#$33"><code class="Li" id="$33">$33</code></a></td>
<td><a class="permalink" href="#__comparison_2"><code class="Li" id="__comparison_2">&lt;
comparison</code></a></td>
</tr>
<tr>
<td><a class="permalink" href="#$34"><code class="Li" id="$34">$34</code></a></td>
<td><a class="permalink" href="#_=_comparison"><code class="Li" id="_=_comparison">&gt;=
comparison</code></a></td>
</tr>
<tr>
<td><a class="permalink" href="#$35"><code class="Li" id="$35">$35</code></a></td>
<td><a class="permalink" href="#_=_comparison_2"><code class="Li" id="_=_comparison_2">&lt;=
comparison</code></a></td>
</tr>
<tr>
<td><a class="permalink" href="#$40"><code class="Li" id="$40">$40</code></a></td>
<td><a class="permalink" href="#___operator"><code class="Li" id="___operator">&lt;&lt;
operator</code></a></td>
</tr>
<tr>
<td><a class="permalink" href="#$41"><code class="Li" id="$41">$41</code></a></td>
<td><a class="permalink" href="#___operator_2"><code class="Li" id="___operator_2">&gt;&gt;
operator</code></a></td>
</tr>
<tr>
<td><a class="permalink" href="#$50"><code class="Li" id="$50">$50</code></a></td>
<td><a class="permalink" href="#BANK(symbol)"><code class="Li" id="BANK(symbol)">BANK(symbol)</code></a>,
a <var class="Ar">LONG</var> Symbol ID follows.</td>
</tr>
<tr>
<td><a class="permalink" href="#$51"><code class="Li" id="$51">$51</code></a></td>
<td><a class="permalink" href="#BANK(section_name)"><code class="Li" id="BANK(section_name)">BANK(section_name)</code></a>,
a null-terminated string follows.</td>
</tr>
<tr>
<td><a class="permalink" href="#$52"><code class="Li" id="$52">$52</code></a></td>
<td><a class="permalink" href="#Current_BANK()"><code class="Li" id="Current_BANK()">Current
BANK()</code></a></td>
</tr>
<tr>
<td><a class="permalink" href="#$60"><code class="Li" id="$60">$60</code></a></td>
<td><a class="permalink" href="#HRAMCheck"><code class="Li" id="HRAMCheck">HRAMCheck</code></a>.
Checks if the value is in HRAM, ANDs it with 0xFF.</td>
</tr>
<tr>
<td><a class="permalink" href="#$61"><code class="Li" id="$61">$61</code></a></td>
<td><a class="permalink" href="#RSTCheck"><code class="Li" id="RSTCheck">RSTCheck</code></a>.
Checks if the value is a RST vector, ORs it with 0xC7.</td>
</tr>
<tr>
<td><a class="permalink" href="#$80"><code class="Li" id="$80">$80</code></a></td>
<td><var class="Ar">LONG</var> integer follows.</td>
</tr>
<tr>
<td><a class="permalink" href="#$81"><code class="Li" id="$81">$81</code></a></td>
<td><var class="Ar">LONG</var> symbol ID follows.</td>
</tr>
</table>
</section>
</section>
<section class="Sh">
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
ALSO</a></h1>
<a class="Xr" href="rgbasm.1.html">rgbasm(1)</a>, <a class="Xr" href="rgblink.1.html">rgblink(1)</a>,
<a class="Xr" href="rgbds.7.html">rgbds(7)</a>, <a class="Xr" href="gbz80.7.html">gbz80(7)</a>
</section>
<section class="Sh">
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
<code class="Nm">rgbds</code> was originally written by Carsten S&#x00F8;rensen
as part of the ASMotor package, and was later packaged in RGBDS by Justin
Lloyd. It is now maintained by a number of contributors at
<a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.
</section>
</div>
<table class="foot">
<tr>
<td class="foot-date">January 26, 2018</td>
<td class="foot-os">General</td>
</tr>
</table>
</body>
</html>

View File

@@ -1,80 +0,0 @@
<!DOCTYPE html>
<html>
<!-- This is an automatically generated file. Do not edit.
This file is part of RGBDS.
Copyright (c) 2010-2018, Anthony J. Bentley and RGBDS contributors.
SPDX-License-Identifier: MIT
-->
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8"/>
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
<link rel="stylesheet" href="rgbds.css" type="text/css" media="all"/>
<title>RGBDS(7)</title>
</head>
<body>
<table class="head">
<tr>
<td class="head-ltitle">RGBDS(7)</td>
<td class="head-vol">Miscellaneous Information Manual</td>
<td class="head-rtitle">RGBDS(7)</td>
</tr>
</table>
<div class="manual-text">
<section class="Sh">
<h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
<code class="Nm">rgbds</code> &#x2014;
<span class="Nd">Rednex Game Boy Development System</span>
</section>
<section class="Sh">
<h1 class="Sh" id="EXAMPLES"><a class="permalink" href="#EXAMPLES">EXAMPLES</a></h1>
To get a working ROM image from a single assembly source file:
<div class="Bd Pp Bd-indent">
<pre>
$ rgbasm -o bar.o foo.asm
$ rgblink -o baz.gb bar.o
$ rgbfix -v -p 0 baz.gb
</pre>
</div>
</section>
<section class="Sh">
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
ALSO</a></h1>
<a class="Xr" href="rgbasm.1.html">rgbasm(1)</a>, <a class="Xr" href="rgbfix.1.html">rgbfix(1)</a>,
<a class="Xr" href="rgblink.1.html">rgblink(1)</a>, <a class="Xr" href="rgbds.5.html">rgbds(5)</a>,
<a class="Xr" href="gbz80.7.html">gbz80(7)</a>
</section>
<section class="Sh">
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
<dl class="Bl-ohang">
<dt></dt>
<dd>1997, Carsten S&#x00F8;rensen (AKA SurfSmurf) writes ASMotor as a
general-purpose assembler/linker system for DOS/Win32.</dd>
<dt></dt>
<dd>1999, Justin Lloyd (AKA Otaku no Zoku) adapts ASMotor to read and produce
GBZ80 assembly/machine code, and releases this version as RGBDS.</dd>
<dt></dt>
<dd>2009, Vegard Nossum adapts the code to be more UNIX-like and releases this
version as rgbds-linux on GitHub.</dd>
<dt></dt>
<dd>2010, Anthony J. Bentley forks that repository. The fork becomes the
reference implementation of rgbds.</dd>
<dt></dt>
<dd>2017, Bentley's repository is moved to a neutral name. It is now
maintained by a number of contributors at
<a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.</dd>
<dt></dt>
<dd>2018, codebase relicensed under the MIT license.</dd>
</dl>
</section>
</div>
<table class="foot">
<tr>
<td class="foot-date">March 7, 2018</td>
<td class="foot-os">General</td>
</tr>
</table>
</body>
</html>

View File

@@ -1,63 +0,0 @@
/* Overrides to default mandoc styling for HTML renders of RGBDS man pages */
html {
/* Reduce contrast */
background-color: #f8f8f8;
color: #222;
/* Override `mandoc.css`'s sowe can put it on <body> instead */
max-width: none;
}
body {
/* Center body horizontally (requires <html> to span full width) */
margin: 0 auto;
/* `mandoc.css`'s default, but it's applied to <html> there */
max-width: 65em;
/* Improve readability */
font-size: 16px;
line-height: 1.4;
text-align: justify;
/* Prevent text from bumping sides on mobile devices */
padding: 10px 20px 10px 10px;
}
@media print {
body {
/* Max width doesn't make sense for print */
max-width: none;
/* Make font slightly smaller for printing */
font-size: 14px;
}
}
code, pre {
font-size: smaller;
}
pre {
/* Avoid horizontal page scrolling on mobile */
overflow: auto;
}
/* Separate lines in tables */
table.Bl-column {
border-collapse: collapse;
}
table.Bl-column tr:not(:first-child) > td,
table.Bl-column tr:not(:first-child) > th {
border-top: 1px solid #aaa;
}
table.Bl-column th {
/* Apply `.Sy` style to table headers */
font-style: normal;
font-weight: bold;
}
table.Bl-column td,
table.Bl-column th {
/* Add horizontal spacing between columns */
padding: 2px 7px 0;
}

View File

@@ -1,220 +0,0 @@
<!DOCTYPE html>
<html>
<!-- This is an automatically generated file. Do not edit.
This file is part of RGBDS.
Copyright (c) 2010-2017, Anthony J. Bentley and RGBDS contributors.
SPDX-License-Identifier: MIT
-->
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8"/>
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
<link rel="stylesheet" href="rgbds.css" type="text/css" media="all"/>
<title>RGBFIX(1)</title>
</head>
<body>
<table class="head">
<tr>
<td class="head-ltitle">RGBFIX(1)</td>
<td class="head-vol">General Commands Manual</td>
<td class="head-rtitle">RGBFIX(1)</td>
</tr>
</table>
<div class="manual-text">
<section class="Sh">
<h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
<code class="Nm">rgbfix</code> &#x2014;
<span class="Nd">Game Boy header utility and checksum fixer</span>
</section>
<section class="Sh">
<h1 class="Sh" id="SYNOPSIS"><a class="permalink" href="#SYNOPSIS">SYNOPSIS</a></h1>
<table class="Nm">
<tr>
<td><code class="Nm">rgbfix</code></td>
<td>[<code class="Fl"><a href="#j">-j</a><a href="#s">s</a><a href="#V">V</a><a href="#v">v</a></code>] [<code class="Fl"><a href="#C">-C</a></code> |
<code class="Fl"><a href="#c">-c</a></code>] [<code class="Fl"><a href="#f">-f</a></code>
<var class="Ar">fix_spec</var>] [<code class="Fl"><a href="#i">-i</a></code>
<var class="Ar">game_id</var>] [<code class="Fl"><a href="#k">-k</a></code>
<var class="Ar">licensee_str</var>] [<code class="Fl"><a href="#l">-l</a></code>
<var class="Ar">licensee_id</var>] [<code class="Fl"><a href="#m">-m</a></code>
<var class="Ar">mbc_type</var>] [<code class="Fl"><a href="#n">-n</a></code>
<var class="Ar">rom_version</var>] [<code class="Fl"><a href="#p">-p</a></code>
<var class="Ar">pad_value</var>] [<code class="Fl"><a href="#r">-r</a></code>
<var class="Ar">ram_size</var>] [<code class="Fl"><a href="#t">-t</a></code>
<var class="Ar">title_str</var>] <var class="Ar">file</var></td>
</tr>
</table>
</section>
<section class="Sh">
<h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
The <code class="Nm">rgbfix</code> program changes headers of Game Boy ROM
images. It also performs other correctness operations, such as padding.
<p class="Pp">Note that options can be abbreviated as long as the abbreviation
is unambiguous: <code class="Fl">--verb</code> is
<code class="Fl">--verbose</code>, but
<code class="Fl">--ver</code> is invalid because it
could also be <code class="Fl">--version</code>. The
arguments are as follows:</p>
<dl class="Bl-tag">
<dt><a class="permalink" href="#C"><code class="Fl" id="C">-C</code></a>,
<code class="Fl">--color-only</code></dt>
<dd>Set the Game Boy Color&#x2013;only flag: <span class="Ad">0x143</span> =
0xC0. If both this and the <code class="Fl">-c</code> flag are set, this
takes precedence.</dd>
<dt><a class="permalink" href="#c"><code class="Fl" id="c">-c</code></a>,
<code class="Fl">--color-compatible</code></dt>
<dd>Set the Game Boy Color&#x2013;compatible flag:
<span class="Ad">0x143</span> = 0x80. If both this and the
<code class="Fl">-C</code> flag are set, <code class="Fl">-C</code> takes
precedence.</dd>
<dt><a class="permalink" href="#f"><code class="Fl" id="f">-f</code></a>
<var class="Ar">fix_spec</var>,
<code class="Fl">--fix-spec</code>
<var class="Ar">fix_spec</var></dt>
<dd>Fix certain header values that the Game Boy checks for correctness.
Alternatively, intentionally trash these values by writing their binary
inverse instead. <var class="Ar">fix_spec</var> is a string containing any
combination of the following characters:
<p class="Pp"></p>
<dl class="Bl-tag Bl-compact">
<dt><a class="permalink" href="#l"><code class="Cm" id="l">l</code></a></dt>
<dd>Fix the Nintendo logo
(<span class="Ad">0x104</span>&#x2013;<span class="Ad">0x133</span>).</dd>
<dt><a class="permalink" href="#L"><code class="Cm" id="L">L</code></a></dt>
<dd>Trash the Nintendo logo.</dd>
<dt><a class="permalink" href="#h"><code class="Cm" id="h">h</code></a></dt>
<dd>Fix the header checksum (<span class="Ad">0x14D</span>).</dd>
<dt><a class="permalink" href="#H"><code class="Cm" id="H">H</code></a></dt>
<dd>Trash the header checksum.</dd>
<dt><a class="permalink" href="#g"><code class="Cm" id="g">g</code></a></dt>
<dd>Fix the global checksum
(<span class="Ad">0x14E</span>&#x2013;<span class="Ad">0x14F</span>).</dd>
<dt><a class="permalink" href="#G"><code class="Cm" id="G">G</code></a></dt>
<dd>Trash the global checksum.</dd>
</dl>
</dd>
<dt><a class="permalink" href="#i"><code class="Fl" id="i">-i</code></a>
<var class="Ar">game_id</var>,
<code class="Fl">--game-id</code>
<var class="Ar">game_id</var></dt>
<dd>Set the game ID string
(<span class="Ad">0x13F</span>&#x2013;<span class="Ad">0x142</span>) to a
given string of exactly 4 characters. If both this and the title are set,
the game ID will overwrite the overlapping portion of the title.</dd>
<dt><a class="permalink" href="#j"><code class="Fl" id="j">-j</code></a>,
<code class="Fl">--non-japanese</code></dt>
<dd>Set the non-Japanese region flag: <span class="Ad">0x14A</span> = 1.</dd>
<dt><a class="permalink" href="#k"><code class="Fl" id="k">-k</code></a>
<var class="Ar">licensee_str</var>,
<code class="Fl">--new-licensee</code>
<var class="Ar">licensee_str</var></dt>
<dd>Set the new licensee string
(<span class="Ad">0x144</span>&#x2013;<span class="Ad">0x145</span>) to a
given string, truncated to at most two characters.</dd>
<dt><a class="permalink" href="#l_2"><code class="Fl" id="l_2">-l</code></a>
<var class="Ar">licensee_id</var>,
<code class="Fl">--old-licensee</code>
<var class="Ar">licensee_id</var></dt>
<dd>Set the old licensee code, <span class="Ad">0x14B</span>, to a given value
from 0 to 0xFF. This value is deprecated and should be set to 0x33 in all
new software.</dd>
<dt><a class="permalink" href="#m"><code class="Fl" id="m">-m</code></a>
<var class="Ar">mbc_type</var>,
<code class="Fl">--mbc-type</code>
<var class="Ar">mbc_type</var></dt>
<dd>Set the MBC type, <span class="Ad">0x147</span>, to a given value from 0
to 0xFF.</dd>
<dt><a class="permalink" href="#n"><code class="Fl" id="n">-n</code></a>
<var class="Ar">rom_version</var>,
<code class="Fl">--rom-version</code>
<var class="Ar">rom_version</var></dt>
<dd>Set the ROM version, <span class="Ad">0x14C</span>, to a given value from
0 to 0xFF.</dd>
<dt><a class="permalink" href="#p"><code class="Fl" id="p">-p</code></a>
<var class="Ar">pad_value</var>,
<code class="Fl">--pad-value</code>
<var class="Ar">pad_value</var></dt>
<dd>Pad the image to a valid size with a given pad value from 0 to 0xFF.
<code class="Nm">rgbfix</code> will automatically pick a size from 32 KiB,
64 KiB, 128 KiB, ..., 8192 KiB. The cartridge size byte
(<span class="Ad">0x148</span>) will be changed to reflect this new
size.</dd>
<dt><a class="permalink" href="#r"><code class="Fl" id="r">-r</code></a>
<var class="Ar">ram_size</var>,
<code class="Fl">--ram-size</code>
<var class="Ar">ram_size</var></dt>
<dd>Set the RAM size, <span class="Ad">0x149</span>, to a given value from 0
to 0xFF.</dd>
<dt><a class="permalink" href="#s"><code class="Fl" id="s">-s</code></a>,
<code class="Fl">--sgb-compatible</code></dt>
<dd>Set the SGB flag: <span class="Ad">0x146</span> = 3. This flag will be
ignored by the SGB unless the old licensee code is 0x33!</dd>
<dt><a class="permalink" href="#t"><code class="Fl" id="t">-t</code></a>
<var class="Ar">title</var>,
<code class="Fl">--title</code>
<var class="Ar">title</var></dt>
<dd>Set the title string
(<span class="Ad">0x134</span>&#x2013;<span class="Ad">0x143</span>) to a
given string, truncated to at most 16 characters. It is recommended to use
15 characters instead, to avoid clashing with the CGB flag
(<code class="Fl">-c</code> or <code class="Fl">-C</code>). If both this
and the game ID are set, the game ID will overwrite the overlapping
portion of the title.</dd>
<dt><a class="permalink" href="#V"><code class="Fl" id="V">-V</code></a>,
<code class="Fl">--version</code></dt>
<dd>Print the version of the program and exit.</dd>
<dt><a class="permalink" href="#v"><code class="Fl" id="v">-v</code></a>,
<code class="Fl">--validate</code></dt>
<dd>Equivalent to <code class="Fl">-f</code> <code class="Cm">lhg</code>.</dd>
</dl>
</section>
<section class="Sh">
<h1 class="Sh" id="EXAMPLES"><a class="permalink" href="#EXAMPLES">EXAMPLES</a></h1>
Most values in the ROM header are only cosmetic. The bare minimum requirements
for a workable program are the header checksum, the Nintendo logo, and (if
needed) the CGB/SGB flags. It is a good idea to pad the image to a valid size
as well (&#x201C;valid&#x201D; meaning a power of 2, times 32 KiB).
<p class="Pp">The following will make a plain, non-color Game Boy game without
checking for a valid size:</p>
<p class="Pp"></p>
<div class="Bd Bd-indent">$ rgbfix -v foo.gb</div>
<p class="Pp">The following will make a SGB-enabled, color-enabled game with a
title of &#x201C;foobar&#x201D;, and pad it to a valid size. (The Game Boy
itself does not use the title, but some emulators or ROM managers do.)</p>
<p class="Pp"></p>
<div class="Bd Bd-indent">$ rgbfix -vcs -l 0x33 -p 255 -t foobar baz.gb</div>
<p class="Pp">The following will duplicate the header (sans global checksum) of
the game &#x201C;Survival Kids&#x201D;:</p>
<p class="Pp"></p>
<div class="Bd Bd-indent">$ rgbfix -cjsv -k A4 -l 0x33 -m 0x1B -p 0xFF -r 3 -t
SURVIVALKIDAVKE SurvivalKids.gbc</div>
</section>
<section class="Sh">
<h1 class="Sh" id="BUGS"><a class="permalink" href="#BUGS">BUGS</a></h1>
Please report bugs on
<a class="Lk" href="https://github.com/rednex/rgbds/issues">GitHub</a>.
</section>
<section class="Sh">
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
ALSO</a></h1>
<a class="Xr" href="rgbasm.1.html">rgbasm(1)</a>, <a class="Xr" href="rgblink.1.html">rgblink(1)</a>,
<a class="Xr" href="rgbds.7.html">rgbds(7)</a>
</section>
<section class="Sh">
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
<code class="Nm">rgbfix</code> was originally released by Carsten
S&#x00F8;rensen as a standalone program called gbfix, and was later packaged
in RGBDS by Justin Lloyd. It is now maintained by a number of contributors at
<a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.
</section>
</div>
<table class="foot">
<tr>
<td class="foot-date">December 5, 2019</td>
<td class="foot-os">General</td>
</tr>
</table>
</body>
</html>

View File

@@ -1,220 +0,0 @@
<!DOCTYPE html>
<html>
<!-- This is an automatically generated file. Do not edit.
This file is part of RGBDS.
Copyright (c) 2013-2018, stag019 and RGBDS contributors.
SPDX-License-Identifier: MIT
-->
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8"/>
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
<link rel="stylesheet" href="rgbds.css" type="text/css" media="all"/>
<title>RGBGFX(1)</title>
</head>
<body>
<table class="head">
<tr>
<td class="head-ltitle">RGBGFX(1)</td>
<td class="head-vol">General Commands Manual</td>
<td class="head-rtitle">RGBGFX(1)</td>
</tr>
</table>
<div class="manual-text">
<section class="Sh">
<h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
<code class="Nm">rgbgfx</code> &#x2014;
<span class="Nd">Game Boy graphics converter</span>
</section>
<section class="Sh">
<h1 class="Sh" id="SYNOPSIS"><a class="permalink" href="#SYNOPSIS">SYNOPSIS</a></h1>
<table class="Nm">
<tr>
<td><code class="Nm">rgbgfx</code></td>
<td>[<code class="Fl"><a href="#C">-C</a><a href="#D">D</a><a href="#h">h</a><a href="#m">m</a><a href="#u">u</a><a href="#V">V</a><a href="#v">v</a></code>] [<code class="Fl"><a href="#f">-f</a></code> |
<code class="Fl"><a href="#F">-F</a></code>] [<code class="Fl"><a href="#a">-a</a></code>
<var class="Ar">attrmap</var> | <code class="Fl"><a href="#A">-A</a></code>]
[<code class="Fl"><a href="#d">-d</a></code> <var class="Ar">depth</var>]
[<code class="Fl"><a href="#o">-o</a></code> <var class="Ar">out_file</var>]
[<code class="Fl"><a href="#p">-p</a></code> <var class="Ar">pal_file</var> |
<code class="Fl"><a href="#P">-P</a></code>] [<code class="Fl"><a href="#t">-t</a></code>
<var class="Ar">tilemap</var> | <code class="Fl"><a href="#T">-T</a></code>]
[<code class="Fl"><a href="#x">-x</a></code> <var class="Ar">tiles</var>]
<var class="Ar">file</var></td>
</tr>
</table>
</section>
<section class="Sh">
<h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
The <code class="Nm">rgbgfx</code> program converts PNG images into the Nintendo
Game Boy's planar tile format.
<p class="Pp">The resulting colors and their palette indices are determined
differently depending on the input PNG file:</p>
<ul class="Bl-dash">
<li>If the file has an embedded palette, that palette's color and order are
used.</li>
<li>If not, and the image only contains shades of gray, rgbgfx maps them to
the indices appropriate for each shade. Any undetermined indices are set
to respective default shades of gray. For example: if the bit depth is 2
and the image contains light gray and black, they become the second and
fourth colors, and the first and third colors get set to default white and
dark gray. If the image has multiple shades that map to the same index,
the palette is instead determined as if the image had color.</li>
<li>If the image has color (or the grayscale method failed), the colors are
sorted from lightest to darkest.</li>
</ul>
<p class="Pp">The input image may not contain more colors than the selected bit
depth allows. Transparent pixels are set to palette index 0.</p>
</section>
<section class="Sh">
<h1 class="Sh" id="ARGUMENTS"><a class="permalink" href="#ARGUMENTS">ARGUMENTS</a></h1>
Note that options can be abbreviated as long as the abbreviation is unambiguous:
<code class="Fl">--verb</code> is
<code class="Fl">-</code> <code class="Fl">-verbose</code>, but
<code class="Fl">--ver</code> is invalid because it
could also be <code class="Fl">--version</code>. The
arguments are as follows:
<dl class="Bl-tag">
<dt><a class="permalink" href="#a"><code class="Fl" id="a">-a</code></a>
<var class="Ar">attrmap,</var>
<code class="Fl">--attr-map</code>
<var class="Ar">attrmap</var></dt>
<dd>Generate a file of tile mirroring attributes for OAM or (CGB-only)
background tiles. For each tile in the input file, a byte is written
representing the dimensions that the associated tile in the output file
should be mirrored. Useful in combination with <code class="Fl">-m</code>
to keep track the mirror direction of mirrored duplicate tiles.</dd>
<dt><a class="permalink" href="#A"><code class="Fl" id="A">-A</code></a>,
<code class="Fl">--output-attr-map</code></dt>
<dd>Same as <code class="Fl">-a</code>, but the attrmap file output name is
made by taking the input filename, removing the file extension, and
appending <span class="Pa">.attrmap</span>.</dd>
<dt><a class="permalink" href="#C"><code class="Fl" id="C">-C</code></a>,
<code class="Fl">--color-curve</code></dt>
<dd>Use the color curve of the Game Boy Color when generating palettes.</dd>
<dt><a class="permalink" href="#D"><code class="Fl" id="D">-D</code></a>,
<code class="Fl">--debug</code></dt>
<dd>Debug features are enabled.</dd>
<dt><a class="permalink" href="#d"><code class="Fl" id="d">-d</code></a>
<var class="Ar">depth</var>,
<code class="Fl">--depth</code>
<var class="Ar">depth</var></dt>
<dd>The bit depth of the output image (either 1 or 2). By default, the bit
depth is 2 (two bits per pixel).</dd>
<dt><a class="permalink" href="#f"><code class="Fl" id="f">-f</code></a>,
<code class="Fl">--fix</code></dt>
<dd>Fix the input PNG file to be a correctly indexed image.</dd>
<dt><a class="permalink" href="#F"><code class="Fl" id="F">-F</code></a>,
<code class="Fl">--fix-and-save</code></dt>
<dd>Same as <code class="Fl">-f</code>, but additionally, the supplied command
line parameters are saved within the PNG and will be loaded and
automatically used next time.</dd>
<dt><a class="permalink" href="#h"><code class="Fl" id="h">-h</code></a>,
<code class="Fl">--horizontal</code></dt>
<dd>Lay out tiles horizontally rather than vertically.</dd>
<dt><a class="permalink" href="#m"><code class="Fl" id="m">-m</code></a>,
<code class="Fl">--mirror-tiles</code></dt>
<dd>Truncate tiles by checking for tiles that are mirrored versions of others
and omitting these from the output file. Useful with tilemaps and attrmaps
together to keep track of the duplicated tiles and the dimension mirrored.
Tiles are checked for horizontal, vertical, and horizontal-vertical
mirroring. Implies <code class="Fl">-u</code>.</dd>
<dt><a class="permalink" href="#o"><code class="Fl" id="o">-o</code></a>
<var class="Ar">out_file</var>,
<code class="Fl">--output</code>
<var class="Ar">out_file</var></dt>
<dd>The name of the output file.</dd>
<dt><a class="permalink" href="#p"><code class="Fl" id="p">-p</code></a>
<var class="Ar">pal_file</var>,
<code class="Fl">--palette</code>
<var class="Ar">pal_file</var></dt>
<dd>Output the image's palette in standard GBC palette format: bytes (8 bytes
for two bits per pixel, 4 bytes for one bit per pixel) containing the
RGB15 values in little-endian byte order. If the palette contains too few
colors, the remaining entries are set to black.</dd>
<dt><a class="permalink" href="#P"><code class="Fl" id="P">-P</code></a>,
<code class="Fl">--output-palette</code></dt>
<dd>Same as <code class="Fl">-p</code>, but the palette file output name is
made by taking the input PNG file's filename, removing the file extension,
and appending <span class="Pa">.pal</span>.</dd>
<dt><a class="permalink" href="#t"><code class="Fl" id="t">-t</code></a>
<var class="Ar">tilemap</var>,
<code class="Fl">--tilemap</code>
<var class="Ar">tilemap</var></dt>
<dd>Generate a file of tile indices. For each tile in the input file, a byte
is written representing the index of the associated tile in the output
file. Useful in combination with <code class="Fl">-u</code> or
<code class="Fl">-m</code> to keep track of duplicate tiles.</dd>
<dt><a class="permalink" href="#T"><code class="Fl" id="T">-T</code></a>,
<code class="Fl">--output-tilemap</code></dt>
<dd>Same as <code class="Fl">-t</code>, but the tilemap file output name is
made by taking the input filename, removing the file extension, and
appending <span class="Pa">.tilemap</span>.</dd>
<dt><a class="permalink" href="#u"><code class="Fl" id="u">-u</code></a>,
<code class="Fl">--unique-tiles</code></dt>
<dd>Truncate tiles by checking for tiles that are exact duplicates of others
and omitting these from the output file. Useful with tilemaps to keep
track of the duplicated tiles.</dd>
<dt><a class="permalink" href="#V"><code class="Fl" id="V">-V</code></a>,
<code class="Fl">--version</code></dt>
<dd>Print the version of the program and exit.</dd>
<dt><a class="permalink" href="#v"><code class="Fl" id="v">-v</code></a>,
<code class="Fl">--verbose</code></dt>
<dd>Verbose. Print errors when the command line parameters and the parameters
in the PNG file don't match.</dd>
<dt><a class="permalink" href="#x"><code class="Fl" id="x">-x</code></a>
<var class="Ar">tiles</var>,
<code class="Fl">--trim-end</code>
<var class="Ar">tiles</var></dt>
<dd>Trim the end of the output file by this many tiles.</dd>
</dl>
</section>
<section class="Sh">
<h1 class="Sh" id="EXAMPLES"><a class="permalink" href="#EXAMPLES">EXAMPLES</a></h1>
The following will take a PNG file with a bit depth of 1, 2, or 8, and output
planar 2bpp data:
<p class="Pp"></p>
<div class="Bd Bd-indent">$ rgbgfx -o out.2bpp in.png</div>
<p class="Pp">The following creates a planar 2bpp file with only unique tiles,
and its tilemap <span class="Pa">out.tilemap</span>:</p>
<p class="Pp"></p>
<div class="Bd Bd-indent">$ rgbgfx -T -u -o out.2bpp in.png</div>
<p class="Pp">The following creates a planar 2bpp file with only unique tiles
<span class="Pa">accounting for tile mirroring</span> and its associated
tilemap <span class="Pa">out.tilemap</span> and attrmap
<span class="Pa">out.attrmap</span>:</p>
<p class="Pp"></p>
<div class="Bd Bd-indent">$ rgbgfx -A -T -m -o out.2bpp in.png</div>
<p class="Pp">The following will do nothing:</p>
<p class="Pp"></p>
<div class="Bd Bd-indent">$ rgbgfx in.png</div>
</section>
<section class="Sh">
<h1 class="Sh" id="BUGS"><a class="permalink" href="#BUGS">BUGS</a></h1>
Please report bugs on
<a class="Lk" href="https://github.com/rednex/rgbds/issues">GitHub</a>.
</section>
<section class="Sh">
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
ALSO</a></h1>
<a class="Xr" href="rgbds.7.html">rgbds(7)</a>, <a class="Xr" href="rgbasm.1.html">rgbasm(1)</a>,
<a class="Xr" href="rgblink.1.html">rgblink(1)</a>, <a class="Xr" href="rgbfix.1.html">rgbfix(1)</a>,
<a class="Xr" href="gbz80.7.html">gbz80(7)</a>
</section>
<section class="Sh">
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
<code class="Nm">rgbgfx</code> was created by <span class="An">stag019</span> to
be included in RGBDS. It is now maintained by a number of contributors at
<a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.
</section>
</div>
<table class="foot">
<tr>
<td class="foot-date">December 5, 2019</td>
<td class="foot-os">General</td>
</tr>
</table>
</body>
</html>

View File

@@ -1,192 +0,0 @@
<!DOCTYPE html>
<html>
<!-- This is an automatically generated file. Do not edit.
This file is part of RGBDS.
Copyright (c) 2010-2019, Anthony J. Bentley and RGBDS contributors.
SPDX-License-Identifier: MIT
-->
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8"/>
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
<link rel="stylesheet" href="rgbds.css" type="text/css" media="all"/>
<title>RGBLINK(1)</title>
</head>
<body>
<table class="head">
<tr>
<td class="head-ltitle">RGBLINK(1)</td>
<td class="head-vol">General Commands Manual</td>
<td class="head-rtitle">RGBLINK(1)</td>
</tr>
</table>
<div class="manual-text">
<section class="Sh">
<h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
<code class="Nm">rgblink</code> &#x2014;
<span class="Nd">Game Boy linker</span>
</section>
<section class="Sh">
<h1 class="Sh" id="SYNOPSIS"><a class="permalink" href="#SYNOPSIS">SYNOPSIS</a></h1>
<table class="Nm">
<tr>
<td><code class="Nm">rgblink</code></td>
<td>[<code class="Fl"><a href="#d">-d</a><a href="#t">t</a><a href="#V">V</a><a href="#v">v</a><a href="#w">w</a><a href="#x">x</a></code>] [<code class="Fl"><a href="#l">-l</a></code>
<var class="Ar">linker_script</var>] [<code class="Fl"><a href="#m">-m</a></code>
<var class="Ar">map_file</var>] [<code class="Fl"><a href="#n">-n</a></code>
<var class="Ar">sym_file</var>] [<code class="Fl"><a href="#O">-O</a></code>
<var class="Ar">overlay_file</var>] [<code class="Fl"><a href="#o">-o</a></code>
<var class="Ar">out_file</var>] [<code class="Fl"><a href="#p">-p</a></code>
<var class="Ar">pad_value</var>] [<code class="Fl"><a href="#s">-s</a></code>
<var class="Ar">symbol</var>] <var class="Ar">file ...</var></td>
</tr>
</table>
</section>
<section class="Sh">
<h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
The <code class="Nm">rgblink</code> program links RGB object files, typically
created by <a class="Xr" href="rgbasm.1.html">rgbasm(1)</a>, into a single Game Boy ROM file. The
format is documented in <a class="Xr" href="rgbds.5.html">rgbds(5)</a>.
<p class="Pp">ROM0 sections are placed in the first 16 KiB of the output ROM,
and ROMX sections are placed in any 16 KiB &#x201C;bank&#x201D; except the
first. If your ROM will only be 32 KiB, you can use the
<code class="Fl">-t</code> option to change this.</p>
<p class="Pp">Similarly, WRAM0 sections are placed in the first 4 KiB of WRAM
(&#x201C;bank 0&#x201D;), and WRAMX sections are placed in any bank of the
last 4 KiB. If your ROM doesn't use banked WRAM, you can use the
<code class="Fl">-w</code> option to change this.</p>
<p class="Pp">Also, if your ROM is designed for a monochrome Game Boy, you can
make sure that you don't use any incompatible section by using the
<code class="Fl">-d</code> option, which implies <code class="Fl">-w</code>
but also prohibits the use of banked VRAM.</p>
<p class="Pp">Note that options can be abbreviated as long as the abbreviation
is unambiguous: <code class="Fl">--verb</code> is
<code class="Fl">--verbose</code>, but
<code class="Fl">--ver</code> is invalid because it
could also be <code class="Fl">--version</code>. The
arguments are as follows:</p>
<dl class="Bl-tag">
<dt><a class="permalink" href="#d"><code class="Fl" id="d">-d</code></a>,
<code class="Fl">--dmg</code></dt>
<dd>Enable DMG mode. Prohibit the use of sections that doesn't exist on a DMG,
such as VRAM bank 1. This option automatically enables
<code class="Fl">-w</code>.</dd>
<dt><a class="permalink" href="#l"><code class="Fl" id="l">-l</code></a>
<var class="Ar">linker_script,</var>
<code class="Fl">--linkerscript</code>
<var class="Ar">linker_script</var></dt>
<dd>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. See <a class="Xr" href="rgblink.5.html">rgblink(5)</a>
for more information about the linker script format.</dd>
<dt><a class="permalink" href="#m"><code class="Fl" id="m">-m</code></a>
<var class="Ar">map_file</var>,
<code class="Fl">--map</code>
<var class="Ar">map_file</var></dt>
<dd>Write a map file to the given filename, listing how sections and symbols
were assigned.</dd>
<dt><a class="permalink" href="#n"><code class="Fl" id="n">-n</code></a>
<var class="Ar">sym_file</var>,
<code class="Fl">--sym</code>
<var class="Ar">sym_file</var></dt>
<dd>Write a symbol file to the given filename, listing the address of all
exported symbols. Several external programs can use this information, for
example to help debugging ROMs.</dd>
<dt><a class="permalink" href="#O"><code class="Fl" id="O">-O</code></a>
<var class="Ar">overlay_file</var>,
<code class="Fl">--overlay</code>
<var class="Ar">overlay_file</var></dt>
<dd>If specified, sections will be overlaid &quot;on top&quot; of the provided
ROM image. In that case, all sections must be fixed. This may be used to
patch an existing binary.</dd>
<dt><a class="permalink" href="#o"><code class="Fl" id="o">-o</code></a>
<var class="Ar">out_file</var>,
<code class="Fl">--output</code>
<var class="Ar">out_file</var></dt>
<dd>Write the ROM image to the given file.</dd>
<dt><a class="permalink" href="#p"><code class="Fl" id="p">-p</code></a>
<var class="Ar">pad_value</var>,
<code class="Fl">--pad</code>
<var class="Ar">pad_value</var></dt>
<dd>When inserting padding between sections, pad with this value. Has no
effect if <code class="Fl">-O</code> is specified. The default is 0.</dd>
<dt><a class="permalink" href="#s"><code class="Fl" id="s">-s</code></a>
<var class="Ar">symbol</var>,
<code class="Fl">--smart</code>
<var class="Ar">symbol</var></dt>
<dd>This option is ignored. It was supposed to perform smart linking but fell
into disrepair, and so has been removed. It will be reimplemented at some
point.</dd>
<dt><a class="permalink" href="#t"><code class="Fl" id="t">-t</code></a>,
<code class="Fl">--tiny</code></dt>
<dd>Expand the ROM0 section size from 16 KiB to the full 32 KiB assigned to
ROM. ROMX sections that are fixed to a bank other than 1 become errors,
other ROMX sections are treated as ROM0. Useful for ROMs that fit in 32
KiB.</dd>
<dt><a class="permalink" href="#V"><code class="Fl" id="V">-V</code></a>,
<code class="Fl">--version</code></dt>
<dd>Print the version of the program and exit.</dd>
<dt><a class="permalink" href="#v"><code class="Fl" id="v">-v</code></a>,
<code class="Fl">--verbose</code></dt>
<dd>Verbose: enable printing more information to standard error.</dd>
<dt><a class="permalink" href="#w"><code class="Fl" id="w">-w</code></a>,
<code class="Fl">--wramx</code></dt>
<dd>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.</dd>
<dt><a class="permalink" href="#x"><code class="Fl" id="x">-x</code></a>,
<code class="Fl">--nopad</code></dt>
<dd>Disables padding the end of the final file. This option automatically
enables <code class="Fl">-t</code>. 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 <a class="Xr" href="rgbfix.1.html">rgbfix(1)</a>'s <code class="Fl">-p</code>
option!</dd>
</dl>
</section>
<section class="Sh">
<h1 class="Sh" id="EXAMPLES"><a class="permalink" href="#EXAMPLES">EXAMPLES</a></h1>
All you need for a basic ROM is an object file, which can be made into a ROM
image like so:
<p class="Pp"></p>
<div class="Bd Bd-indent">$ rgblink -o bar.gb foo.o</div>
<p class="Pp">The resulting <var class="Ar">bar.gb</var> will not have correct
checksums (unless you put them in the assembly source). You should use
<a class="Xr" href="rgbfix.1.html">rgbfix(1)</a> to fix these so that the program will actually
run in a Game Boy:</p>
<p class="Pp"></p>
<div class="Bd Bd-indent"><code class="Li">$ rgbfix -v bar.gb</code></div>
<p class="Pp">Here is a more complete example:</p>
<p class="Pp"></p>
<div class="Bd Bd-indent"><code class="Li">$ rgblink -o bin/game.gb -n
bin/game.sym -p 0xFF obj/title.o obj/engine.o</code></div>
</section>
<section class="Sh">
<h1 class="Sh" id="BUGS"><a class="permalink" href="#BUGS">BUGS</a></h1>
Please report bugs on
<a class="Lk" href="https://github.com/rednex/rgbds/issues">GitHub</a>.
</section>
<section class="Sh">
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
ALSO</a></h1>
<a class="Xr" href="rgbasm.1.html">rgbasm(1)</a>, <a class="Xr" href="rgblink.5.html">rgblink(5)</a>,
<a class="Xr" href="rgbfix.1.html">rgbfix(1)</a>, <a class="Xr" href="rgbds.5.html">rgbds(5)</a>,
<a class="Xr" href="rgbds.7.html">rgbds(7)</a>
</section>
<section class="Sh">
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
<code class="Nm">rgblink</code> was originally written by Carsten
S&#x00F8;rensen as part of the ASMotor package, and was later packaged in
RGBDS by Justin Lloyd. It is now maintained by a number of contributors at
<a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.
</section>
</div>
<table class="foot">
<tr>
<td class="foot-date">November 26, 2019</td>
<td class="foot-os">General</td>
</tr>
</table>
</body>
</html>

View File

@@ -1,111 +0,0 @@
<!DOCTYPE html>
<html>
<!-- This is an automatically generated file. Do not edit.
This file is part of RGBDS.
Copyright (c) 2017-2018, Antonio Nino Diaz and RGBDS contributors.
SPDX-License-Identifier: MIT
-->
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="utf-8"/>
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
<link rel="stylesheet" href="rgbds.css" type="text/css" media="all"/>
<title>RGBLINK(5)</title>
</head>
<body>
<table class="head">
<tr>
<td class="head-ltitle">RGBLINK(5)</td>
<td class="head-vol">File Formats Manual</td>
<td class="head-rtitle">RGBLINK(5)</td>
</tr>
</table>
<div class="manual-text">
<section class="Sh">
<h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
<code class="Nm">rgblink</code> &#x2014;
<span class="Nd">linker script file format</span>
</section>
<section class="Sh">
<h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
The linker script is an external file that allows the user to specify the order
of sections at link time and in a centralized manner.
<p class="Pp">A linker script consists on a series of banks followed by a list
of sections and, optionally, commands. They can be lowercase or uppercase,
it is ignored. Any line can contain a comment starting with
&#x2018;<code class="Li">;</code>&#x2019; that ends at the end of the
line:</p>
<div class="Bd Pp Bd-indent">
<pre>
ROMX $F ; This is a comment
&quot;Functions to read array&quot;
ALIGN 8
&quot;Array aligned to 256 bytes&quot;
WRAMX 2
&quot;Some variables&quot;
</pre>
</div>
<p class="Pp">Numbers can be in decimal or hexadecimal format (the prefix is
&#x2018;<code class="Li">$</code>&#x2019;). It is an error if any section
name or command is found before setting a bank.</p>
<p class="Pp">Files can be included by using the <code class="Ic">INCLUDE</code>
keyword, followed by a string with the path of the file that has to be
included.</p>
<p class="Pp">The possible bank types are: <code class="Cm">ROM0</code>,
<code class="Cm">ROMX</code>, <code class="Cm">VRAM</code>,
<code class="Cm">SRAM</code>, <code class="Cm">WRAM0</code>,
<code class="Cm">WRAMX</code>, <code class="Cm">OAM</code> and
<code class="Cm">HRAM</code>. Unless there is a single bank, which can occur
with types <code class="Cm">ROMX</code>, <code class="Cm">VRAM</code>,
<code class="Cm">SRAM</code> and <code class="Cm">WRAMX</code>, it is needed
to specify a bank number after the type.</p>
<p class="Pp">When a new bank statement is found, sections found after it will
be placed right from the beginning of that bank. If the linker script
switches to a different bank and then comes back to a previous one, it will
continue from the last address that was used.</p>
<p class="Pp">The only two commands are <code class="Ic">ORG</code> and
<code class="Ic">ALIGN</code>:</p>
<ul class="Bl-bullet">
<li><a class="permalink" href="#ORG"><code class="Ic" id="ORG">ORG</code></a>
sets the address in which new sections will be placed. It can not be lower
than the current address.</li>
<li><a class="permalink" href="#ALIGN"><code class="Ic" id="ALIGN">ALIGN</code></a>
will increase the address until it is aligned to the specified boundary
(it tries to set to 0 the number of bits specified after the command:
&#x2018;<code class="Li">ALIGN 8</code>&#x2019; will align to $100).</li>
</ul>
<p class="Pp"><b class="Sy">Note:</b> The bank, alignment, address and type of
sections can be specified both in the source code and in the linker script.
For a section to be able to be placed with the linker script, the bank,
address and alignment must be left unassigned in the source code or be
compatible with what is specified in the linker script. For example,
&#x2018;<code class="Li">ALIGN[8]</code>&#x2019; in the source code is
compatible with &#x2018;<code class="Li">ORG $F00</code>&#x2019; in the
linker script.</p>
</section>
<section class="Sh">
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
ALSO</a></h1>
<a class="Xr" href="rgbasm.1.html">rgbasm(1)</a>, <a class="Xr" href="rgblink.1.html">rgblink(1)</a>,
<a class="Xr" href="rgbfix.1.html">rgbfix(1)</a>, <a class="Xr" href="rgbds.5.html">rgbds(5)</a>,
<a class="Xr" href="rgbds.7.html">rgbds(7)</a>
</section>
<section class="Sh">
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
<code class="Nm">rgblink</code> was originally written by Carsten
S&#x00F8;rensen as part of the ASMotor package, and was later packaged in
RGBDS by Justin Lloyd. It is now maintained by a number of contributors at
<a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.
</section>
</div>
<table class="foot">
<tr>
<td class="foot-date">November 26, 2019</td>
<td class="foot-os">General</td>
</tr>
</table>
</body>
</html>

View File

@@ -21,22 +21,11 @@
#include "asm/localasm.h" #include "asm/localasm.h"
#include "asm/symbol.h" #include "asm/symbol.h"
#define MAXUNIONS 128
#define MAXMACROARGS 99999 #define MAXMACROARGS 99999
#define MAXINCPATHS 128 #define MAXINCPATHS 128
extern int32_t nLineNo;
extern uint32_t nTotalLines; extern uint32_t nTotalLines;
extern uint32_t nIFDepth; extern uint32_t nIFDepth;
extern bool skipElif;
extern uint32_t nUnionDepth;
extern uint32_t unionStart[MAXUNIONS];
extern uint32_t unionSize[MAXUNIONS];
extern char tzCurrentFileName[_MAX_PATH + 1];
extern struct Section *pCurrentSection; extern struct Section *pCurrentSection;
extern bool oDontExpandStrings;
size_t symvaluetostring(char *dest, size_t maxLength, char *sym,
const char *mode);
#endif /* RGBDS_ASM_ASM_H */ #endif /* RGBDS_ASM_ASM_H */

View File

@@ -11,36 +11,12 @@
#include <stdint.h> #include <stdint.h>
#define MAXCHARMAPS 512
#define CHARMAPLENGTH 16
#define MAXCHARNODES (MAXCHARMAPS * CHARMAPLENGTH + 1)
/*
* A node for trie structure.
*/
struct Charnode {
uint8_t code; /* the value in a key-value pair. */
uint8_t isCode; /* has 1 if it's a code node, not just a bridge node. */
struct Charnode *next[256]; /* each index representing the next possible
* character from its current state.
*/
};
struct Charmap {
char name[MAXSYMLEN + 1];
int32_t charCount; /* user-side count. */
int32_t nodeCount; /* node-side count. */
struct Charnode nodes[MAXCHARNODES]; /* first node is reserved for the
* root node in charmap.
*/
};
void charmap_InitMain(void);
struct Charmap *charmap_New(const char *name, const char *baseName); struct Charmap *charmap_New(const char *name, const char *baseName);
void charmap_Delete(struct Charmap *charmap);
void charmap_Set(const char *name); void charmap_Set(const char *name);
void charmap_Push(void); void charmap_Push(void);
void charmap_Pop(void); void charmap_Pop(void);
int32_t charmap_Add(char *input, uint8_t output); void charmap_Add(char *mapping, uint8_t value);
int32_t charmap_Convert(char **input); size_t charmap_Convert(char const *input, uint8_t *output);
#endif /* RGBDS_ASM_CHARMAP_H */ #endif /* RGBDS_ASM_CHARMAP_H */

View File

@@ -21,36 +21,59 @@
#include "types.h" #include "types.h"
struct MacroArgs; struct FileStackNode {
struct FileStackNode *parent; /* Pointer to parent node, for error reporting */
/* Line at which the parent context was exited; meaningless for the root level */
uint32_t lineNo;
struct sContext { struct FileStackNode *next; /* Next node in the output linked list */
YY_BUFFER_STATE FlexHandle; bool referenced; /* If referenced, don't free! */
struct Symbol const *pMacro; uint32_t ID; /* Set only if referenced: ID within the object file, -1 if not output yet */
struct sContext *pNext;
char tzFileName[_MAX_PATH + 1]; enum {
struct MacroArgs *macroArgs; NODE_REPT,
uint32_t uniqueID; NODE_FILE,
int32_t nLine; NODE_MACRO,
uint32_t nStatus; } type;
FILE *pFile;
char *pREPTBlock;
uint32_t nREPTBlockCount;
uint32_t nREPTBlockSize;
int32_t nREPTBodyFirstLine;
int32_t nREPTBodyLastLine;
}; };
extern unsigned int nMaxRecursionDepth; struct FileStackReptNode { /* NODE_REPT */
struct FileStackNode node;
uint32_t reptDepth;
/* WARNING: if changing this type, change overflow check in `fstk_Init` */
uint32_t iters[]; /* REPT iteration counts since last named node, in reverse depth order */
};
void fstk_RunInclude(char *tzFileName); struct FileStackNamedNode { /* NODE_FILE, NODE_MACRO */
void fstk_Init(char *s); struct FileStackNode node;
void fstk_Dump(void); char name[]; /* File name for files, file::macro name for macros */
void fstk_DumpToStr(char *buf, size_t len); };
void fstk_DumpStringExpansions(void);
void fstk_AddIncludePath(char *s); extern size_t nMaxRecursionDepth;
void fstk_RunMacro(char *s, struct MacroArgs *args);
void fstk_RunRept(uint32_t count, int32_t nReptLineNo); struct MacroArgs;
FILE *fstk_FindFile(char const *fname, char **incPathUsed);
int32_t fstk_GetLine(void); void fstk_Dump(struct FileStackNode const *node, uint32_t lineNo);
void fstk_DumpCurrent(void);
struct FileStackNode *fstk_GetFileStack(void);
/* The lifetime of the returned chars is until reaching the end of that file */
char const *fstk_GetFileName(void);
void fstk_AddIncludePath(char const *s);
/**
* @param path The user-provided file name
* @param fullPath The address of a pointer, which will be made to point at the full path
* The pointer's value must be a valid argument to `realloc`, including NULL
* @param size Current size of the buffer, or 0 if the pointer is NULL
* @return True if the file was found, false if no path worked
*/
bool fstk_FindFile(char const *path, char **fullPath, size_t *size);
bool yywrap(void);
void fstk_RunInclude(char const *path);
void fstk_RunMacro(char const *macroName, struct MacroArgs *args);
void fstk_RunRept(uint32_t count, int32_t nReptLineNo, char *body, size_t size);
void fstk_Init(char const *mainPath, size_t maxRecursionDepth);
#endif /* RGBDS_ASM_FSTACK_H */ #endif /* RGBDS_ASM_FSTACK_H */

View File

@@ -9,78 +9,65 @@
#ifndef RGBDS_ASM_LEXER_H #ifndef RGBDS_ASM_LEXER_H
#define RGBDS_ASM_LEXER_H #define RGBDS_ASM_LEXER_H
#include <stdint.h>
#include <stdio.h>
#define LEXHASHSIZE (1 << 11)
#define MAXSTRLEN 255 #define MAXSTRLEN 255
struct sLexInitString { struct LexerState;
char *tzName; extern struct LexerState *lexerState;
uint32_t nToken; extern struct LexerState *lexerStateEOL;
static inline struct LexerState *lexer_GetState(void)
{
return lexerState;
}
static inline void lexer_SetState(struct LexerState *state)
{
lexerState = state;
}
static inline void lexer_SetStateAtEOL(struct LexerState *state)
{
lexerStateEOL = state;
}
extern char const *binDigits;
extern char const *gfxDigits;
static inline void lexer_SetBinDigits(char const *digits)
{
binDigits = digits;
}
static inline void lexer_SetGfxDigits(char const *digits)
{
gfxDigits = digits;
}
/*
* `path` is referenced, but not held onto..!
*/
struct LexerState *lexer_OpenFile(char const *path);
struct LexerState *lexer_OpenFileView(char *buf, size_t size, uint32_t lineNo);
void lexer_RestartRept(uint32_t lineNo);
void lexer_DeleteState(struct LexerState *state);
void lexer_Init(void);
enum LexerMode {
LEXER_NORMAL,
LEXER_RAW,
LEXER_SKIP_TO_ELIF,
LEXER_SKIP_TO_ENDC
}; };
struct sLexFloat { void lexer_SetMode(enum LexerMode mode);
uint32_t (*Callback)(char *s, uint32_t size); void lexer_ToggleStringExpansion(bool enable);
uint32_t nToken;
};
struct yy_buffer_state { char const *lexer_GetFileName(void);
/* Actual starting address */ uint32_t lexer_GetLineNo(void);
char *pBufferRealStart; uint32_t lexer_GetColNo(void);
/* Address where the data is initially written after a safety margin */ void lexer_DumpStringExpansions(void);
char *pBufferStart;
char *pBuffer;
size_t nBufferSize;
uint32_t oAtLineStart;
};
enum eLexerState {
LEX_STATE_NORMAL,
LEX_STATE_MACROARGS
};
struct sStringExpansionPos {
char *tzName;
char *pBuffer;
char *pBufferPos;
struct sStringExpansionPos *pParent;
};
#define INITIAL 0
#define macroarg 3
typedef struct yy_buffer_state *YY_BUFFER_STATE;
void setup_lexer(void);
void yy_set_state(enum eLexerState i);
YY_BUFFER_STATE yy_create_buffer(FILE *f);
YY_BUFFER_STATE yy_scan_bytes(char const *mem, uint32_t size);
void yy_delete_buffer(YY_BUFFER_STATE buf);
void yy_switch_to_buffer(YY_BUFFER_STATE buf);
uint32_t lex_FloatAlloc(const struct sLexFloat *tok);
void lex_FloatAddRange(uint32_t id, uint16_t start, uint16_t end);
void lex_FloatDeleteRange(uint32_t id, uint16_t start, uint16_t end);
void lex_FloatAddFirstRange(uint32_t id, uint16_t start, uint16_t end);
void lex_FloatDeleteFirstRange(uint32_t id, uint16_t start, uint16_t end);
void lex_FloatAddSecondRange(uint32_t id, uint16_t start, uint16_t end);
void lex_FloatDeleteSecondRange(uint32_t id, uint16_t start, uint16_t end);
void lex_Init(void);
void lex_AddStrings(const struct sLexInitString *lex);
void lex_SetBuffer(char *buffer, uint32_t len);
void lex_BeginStringExpansion(const char *tzName);
int yywrap(void);
int yylex(void); int yylex(void);
void yyunput(char c); void lexer_CaptureRept(char **capture, size_t *size);
void yyunputstr(const char *s); void lexer_CaptureMacroBody(char **capture, size_t *size);
void yyskipbytes(uint32_t count);
void yyunputbytes(uint32_t count);
extern YY_BUFFER_STATE pCurrentBuffer;
extern struct sStringExpansionPos *pCurrentStringExpansion;
void upperstring(char *s);
void lowerstring(char *s);
#endif /* RGBDS_ASM_LEXER_H */ #endif /* RGBDS_ASM_LEXER_H */

View File

@@ -28,6 +28,7 @@ char const *macro_GetArg(uint32_t i);
uint32_t macro_GetUniqueID(void); uint32_t macro_GetUniqueID(void);
char const *macro_GetUniqueIDStr(void); char const *macro_GetUniqueIDStr(void);
void macro_SetUniqueID(uint32_t id); void macro_SetUniqueID(uint32_t id);
uint32_t macro_UseNewUniqueID(void);
void macro_ShiftCurrentArgs(void); void macro_ShiftCurrentArgs(void);
uint32_t macro_NbArgs(void); uint32_t macro_NbArgs(void);

View File

@@ -26,8 +26,6 @@ extern uint32_t ulNewMacroSize;
extern int32_t nGBGfxID; extern int32_t nGBGfxID;
extern int32_t nBinaryID; extern int32_t nBinaryID;
extern uint32_t curOffset; /* Offset into the current section */
extern struct sOptions DefaultOptions; extern struct sOptions DefaultOptions;
extern struct sOptions CurrentOptions; extern struct sOptions CurrentOptions;
extern bool haltnop; extern bool haltnop;
@@ -45,6 +43,10 @@ void opt_Push(void);
void opt_Pop(void); void opt_Pop(void);
void opt_Parse(char *s); void opt_Parse(char *s);
void upperstring(char *s);
void lowerstring(char *s);
/* TODO: are these really needed? */
#define YY_FATAL_ERROR fatalerror #define YY_FATAL_ERROR fatalerror
#ifdef YYLMAX #ifdef YYLMAX

View File

@@ -18,6 +18,8 @@ struct Expression;
extern char *tzObjectname; extern char *tzObjectname;
extern struct Section *pSectionList, *pCurrentSection; extern struct Section *pSectionList, *pCurrentSection;
void out_RegisterNode(struct FileStackNode *node);
void out_ReplaceNode(struct FileStackNode *node);
void out_SetFileName(char *s); void out_SetFileName(char *s);
void out_CreatePatch(uint32_t type, struct Expression const *expr, void out_CreatePatch(uint32_t type, struct Expression const *expr,
uint32_t ofs); uint32_t ofs);

View File

@@ -49,6 +49,8 @@ static inline bool rpn_isSymbol(const struct Expression *expr)
void rpn_Symbol(struct Expression *expr, char *tzSym); void rpn_Symbol(struct Expression *expr, char *tzSym);
void rpn_Number(struct Expression *expr, uint32_t i); void rpn_Number(struct Expression *expr, uint32_t i);
void rpn_LOGNOT(struct Expression *expr, const struct Expression *src); void rpn_LOGNOT(struct Expression *expr, const struct Expression *src);
struct Symbol const *rpn_SymbolOf(struct Expression const *expr);
bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *sym);
void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr, void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
const struct Expression *src1, const struct Expression *src1,
const struct Expression *src2); const struct Expression *src2);

View File

@@ -17,17 +17,17 @@
struct Expression; struct Expression;
struct Section { struct Section {
char *pzName; char *name;
enum SectionType nType; enum SectionType type;
enum SectionModifier modifier; enum SectionModifier modifier;
uint32_t size; uint32_t size;
uint32_t nOrg; uint32_t org;
uint32_t nBank; uint32_t bank;
uint8_t nAlign; uint8_t align;
uint16_t alignOfs; uint16_t alignOfs;
struct Section *pNext; struct Section *next;
struct Patch *pPatches; struct Patch *patches;
uint8_t *tData; uint8_t *data;
}; };
struct SectionSpec { struct SectionSpec {
@@ -36,8 +36,8 @@ struct SectionSpec {
uint16_t alignOfs; uint16_t alignOfs;
}; };
struct Section *out_FindSectionByName(const char *pzName); struct Section *out_FindSectionByName(const char *name);
void out_NewSection(char const *pzName, uint32_t secttype, uint32_t org, void out_NewSection(char const *name, uint32_t secttype, uint32_t org,
struct SectionSpec const *attributes, struct SectionSpec const *attributes,
enum SectionModifier mod); enum SectionModifier mod);
void out_SetLoadSection(char const *name, uint32_t secttype, uint32_t org, void out_SetLoadSection(char const *name, uint32_t secttype, uint32_t org,
@@ -45,9 +45,15 @@ void out_SetLoadSection(char const *name, uint32_t secttype, uint32_t org,
void out_EndLoadSection(void); void out_EndLoadSection(void);
struct Section *sect_GetSymbolSection(void); struct Section *sect_GetSymbolSection(void);
uint32_t sect_GetSymbolOffset(void);
uint32_t sect_GetOutputOffset(void); uint32_t sect_GetOutputOffset(void);
void sect_AlignPC(uint8_t alignment, uint16_t offset); void sect_AlignPC(uint8_t alignment, uint16_t offset);
void sect_StartUnion(void);
void sect_NextUnionMember(void);
void sect_EndUnion(void);
void sect_CheckUnionClosed(void);
void out_AbsByte(uint8_t b); void out_AbsByte(uint8_t b);
void out_AbsByteGroup(uint8_t const *s, int32_t length); void out_AbsByteGroup(uint8_t const *s, int32_t length);
void out_Skip(int32_t skip, bool ds); void out_Skip(int32_t skip, bool ds);
@@ -57,7 +63,7 @@ void out_RelBytes(struct Expression *expr, uint32_t n);
void out_RelWord(struct Expression *expr); void out_RelWord(struct Expression *expr);
void out_RelLong(struct Expression *expr); void out_RelLong(struct Expression *expr);
void out_PCRelByte(struct Expression *expr); void out_PCRelByte(struct Expression *expr);
void out_BinaryFile(char const *s); void out_BinaryFile(char const *s, int32_t startPos);
void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length); void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length);
void out_PushSection(void); void out_PushSection(void);

View File

@@ -34,20 +34,22 @@ struct Symbol {
enum SymbolType type; enum SymbolType type;
bool isExported; /* Whether the symbol is to be exported */ bool isExported; /* Whether the symbol is to be exported */
bool isBuiltin; /* Whether the symbol is a built-in */ bool isBuiltin; /* Whether the symbol is a built-in */
struct Symbol const *scope;
struct Section *section; struct Section *section;
char fileName[_MAX_PATH + 1]; /* File where the symbol was defined. */ struct FileStackNode *src; /* Where the symbol was defined */
uint32_t fileLine; /* Line where the symbol was defined. */ uint32_t fileLine; /* Line where the symbol was defined */
bool hasCallback;
union { union {
struct { /* If sym_IsNumeric */ /* If sym_IsNumeric */
int32_t value; int32_t value;
int32_t (*callback)(void); int32_t (*numCallback)(void);
}; /* For SYM_MACRO */
struct { /* For SYM_MACRO */ struct {
uint32_t macroSize; size_t macroSize;
char *macro; char *macro;
}; };
/* For SYM_EQUS, TODO: separate "base" fields from SYM_MACRO */
char const *(*strCallback)(void); /* For SYM_EQUS */
}; };
uint32_t ID; /* ID of the symbol in the object file (-1 if none) */ uint32_t ID; /* ID of the symbol in the object file (-1 if none) */
@@ -71,7 +73,7 @@ static inline bool sym_IsConstant(struct Symbol const *sym)
if (sym->type == SYM_LABEL) { if (sym->type == SYM_LABEL) {
struct Section const *sect = sym_GetSection(sym); struct Section const *sect = sym_GetSection(sym);
return sect && sect->nOrg != -1; return sect && sect->org != -1;
} }
return sym->type == SYM_EQU || sym->type == SYM_SET; return sym->type == SYM_EQU || sym->type == SYM_SET;
} }
@@ -102,6 +104,8 @@ static inline bool sym_IsExported(struct Symbol const *sym)
*/ */
static inline char const *sym_GetStringValue(struct Symbol const *sym) static inline char const *sym_GetStringValue(struct Symbol const *sym)
{ {
if (sym->hasCallback)
return sym->strCallback();
return sym->macro; return sym->macro;
} }
@@ -109,15 +113,16 @@ void sym_ForEach(void (*func)(struct Symbol *, void *), void *arg);
int32_t sym_GetValue(struct Symbol const *sym); int32_t sym_GetValue(struct Symbol const *sym);
void sym_SetExportAll(bool set); void sym_SetExportAll(bool set);
struct Symbol *sym_AddLocalReloc(char const *symName); struct Symbol *sym_AddLocalLabel(char const *symName);
struct Symbol *sym_AddReloc(char const *symName); struct Symbol *sym_AddLabel(char const *symName);
void sym_Export(char const *symName); void sym_Export(char const *symName);
struct Symbol *sym_AddEqu(char const *symName, int32_t value); struct Symbol *sym_AddEqu(char const *symName, int32_t value);
struct Symbol *sym_AddSet(char const *symName, int32_t value); struct Symbol *sym_AddSet(char const *symName, int32_t value);
uint32_t sym_GetPCValue(void); uint32_t sym_GetPCValue(void);
uint32_t sym_GetConstantSymValue(struct Symbol const *sym);
uint32_t sym_GetConstantValue(char const *s); uint32_t sym_GetConstantValue(char const *s);
struct Symbol *sym_FindSymbol(char const *symName); struct Symbol *sym_FindSymbol(char const *symName);
struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo); struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size);
struct Symbol *sym_Ref(char const *symName); struct Symbol *sym_Ref(char const *symName);
struct Symbol *sym_AddString(char const *symName, char const *value); struct Symbol *sym_AddString(char const *symName, char const *value);
uint32_t sym_GetDefinedValue(char const *s); uint32_t sym_GetDefinedValue(char const *s);
@@ -125,7 +130,7 @@ void sym_Purge(char const *symName);
void sym_Init(void); void sym_Init(void);
/* Functions to save and restore the current symbol scope. */ /* Functions to save and restore the current symbol scope. */
struct Symbol *sym_GetCurrentSymbolScope(void); char const *sym_GetCurrentSymbolScope(void);
void sym_SetCurrentSymbolScope(struct Symbol *newScope); void sym_SetCurrentSymbolScope(char const *newScope);
#endif /* RGBDS_SYMBOL_H */ #endif /* RGBDS_SYMBOL_H */

View File

@@ -12,6 +12,7 @@
#include <stdint.h> #include <stdint.h>
uint32_t calchash(const char *s); uint32_t calchash(const char *s);
int32_t readUTF8Char(char *dest, char *src); char const *print(int c);
size_t readUTF8Char(uint8_t *dest, char const *src);
#endif /* RGBDS_UTIL_H */ #endif /* RGBDS_UTIL_H */

View File

@@ -14,18 +14,19 @@
extern unsigned int nbErrors; extern unsigned int nbErrors;
enum WarningID { enum WarningID {
WARNING_ASSERT, WARNING_ASSERT, /* Assertions */
WARNING_BUILTIN_ARG, WARNING_BUILTIN_ARG, /* Invalid args to builtins */
WARNING_DIV, WARNING_CHARMAP_REDEF, /* Charmap entry re-definition */
WARNING_EMPTY_DATA_DIRECTIVE, WARNING_DIV, /* Division undefined behavior */
WARNING_EMPTY_ENTRY, WARNING_EMPTY_DATA_DIRECTIVE, /* `db`, `dw` or `dl` with no directive in ROM */
WARNING_LARGE_CONSTANT, WARNING_EMPTY_ENTRY, /* Empty entry in `db`, `dw` or `dl` */
WARNING_LONG_STR, WARNING_LARGE_CONSTANT, /* Constants too large */
WARNING_OBSOLETE, WARNING_LONG_STR, /* String too long for internal buffers */
WARNING_SHIFT, WARNING_OBSOLETE, /* Obsolete things */
WARNING_SHIFT_AMOUNT, WARNING_SHIFT, /* Shifting undefined behavior */
WARNING_TRUNCATION, WARNING_SHIFT_AMOUNT, /* Strange shift amount */
WARNING_USER, WARNING_TRUNCATION, /* Implicit truncation loses some bits */
WARNING_USER, /* User warnings */
NB_WARNINGS, NB_WARNINGS,
@@ -61,6 +62,6 @@ noreturn_ void fatalerror(const char *fmt, ...);
* get a list of all errors at the end, making it easier to fix all of them at * get a list of all errors at the end, making it easier to fix all of them at
* once. * once.
*/ */
void yyerror(const char *fmt, ...); void error(const char *fmt, ...);
#endif #endif

View File

@@ -1,7 +1,7 @@
/* /*
* This file is part of RGBDS. * This file is part of RGBDS.
* *
* Copyright (c) 2014-2018, RGBDS contributors. * Copyright (c) 2014-2020, RGBDS contributors.
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@@ -23,10 +23,6 @@
#define trap_ #define trap_
#endif #endif
#ifndef DEVELOP
#define DEVELOP 0
#endif
/* Macros for stringification */ /* Macros for stringification */
#define STR(x) #x #define STR(x) #x
#define EXPAND_AND_STR(x) STR(x) #define EXPAND_AND_STR(x) STR(x)

View File

@@ -29,15 +29,45 @@ extern bool beVerbose;
extern bool isWRA0Mode; extern bool isWRA0Mode;
extern bool disablePadding; extern bool disablePadding;
struct FileStackNode {
struct FileStackNode *parent;
/* Line at which the parent context was exited; meaningless for the root level */
uint32_t lineNo;
enum {
NODE_REPT,
NODE_FILE,
NODE_MACRO,
} type;
union {
char *name; /* NODE_FILE, NODE_MACRO */
struct { /* NODE_REPT */
uint32_t reptDepth;
uint32_t *iters;
};
};
};
/* Helper macro for printing verbose-mode messages */ /* Helper macro for printing verbose-mode messages */
#define verbosePrint(...) do { \ #define verbosePrint(...) do { \
if (beVerbose) \ if (beVerbose) \
fprintf(stderr, __VA_ARGS__); \ fprintf(stderr, __VA_ARGS__); \
} while (0) } while (0)
void error(char const *fmt, ...); /**
* Dump a file stack to stderr
* @param node The leaf node to dump the context of
*/
char const *dumpFileStack(struct FileStackNode const *node);
noreturn_ void fatal(char const *fmt, ...); void warning(struct FileStackNode const *where, uint32_t lineNo,
char const *fmt, ...) format_(printf, 3, 4);
void error(struct FileStackNode const *where, uint32_t lineNo,
char const *fmt, ...) format_(printf, 3, 4);
noreturn_ void fatal(struct FileStackNode const *where, uint32_t lineNo,
char const *fmt, ...) format_(printf, 3, 4);
/** /**
* Opens a file if specified, and aborts on error. * Opens a file if specified, and aborts on error.

View File

@@ -14,8 +14,9 @@
/** /**
* Read an object (.o) file, and add its info to the data structures. * Read an object (.o) file, and add its info to the data structures.
* @param fileName A path to the object file to be read * @param fileName A path to the object file to be read
* @param i The ID of the file
*/ */
void obj_ReadFile(char const *fileName); void obj_ReadFile(char const *fileName, unsigned int i);
/** /**
* Perform validation on the object files' contents * Perform validation on the object files' contents
@@ -27,6 +28,12 @@ void obj_DoSanityChecks(void);
*/ */
void obj_CheckAssertions(void); void obj_CheckAssertions(void);
/**
* Sets up object file reading
* @param nbFiles The number of object files that will be read
*/
void obj_Setup(unsigned int nbFiles);
/** /**
* `free`s all object memory that was allocated. * `free`s all object memory that was allocated.
*/ */

View File

@@ -19,6 +19,7 @@
#include "linkdefs.h" #include "linkdefs.h"
struct FileStackNode;
struct Section; struct Section;
struct AttachedSymbol { struct AttachedSymbol {
@@ -27,7 +28,8 @@ struct AttachedSymbol {
}; };
struct Patch { struct Patch {
char *fileName; struct FileStackNode const *src;
uint32_t lineNo;
int32_t offset; int32_t offset;
uint32_t pcSectionID; uint32_t pcSectionID;
uint32_t pcOffset; uint32_t pcOffset;

View File

@@ -16,12 +16,14 @@
#include "linkdefs.h" #include "linkdefs.h"
struct FileStackNode;
struct Symbol { struct Symbol {
/* Info contained in the object files */ /* Info contained in the object files */
char *name; char *name;
enum ExportLevel type; enum ExportLevel type;
char const *objFileName; char const *objFileName;
char *fileName; struct FileStackNode const *src;
int32_t lineNo; int32_t lineNo;
int32_t sectionID; int32_t sectionID;
union { union {

View File

@@ -14,7 +14,7 @@
#define RGBDS_OBJECT_VERSION_STRING "RGB%1u" #define RGBDS_OBJECT_VERSION_STRING "RGB%1u"
#define RGBDS_OBJECT_VERSION_NUMBER 9U #define RGBDS_OBJECT_VERSION_NUMBER 9U
#define RGBDS_OBJECT_REV 5U #define RGBDS_OBJECT_REV 6U
enum AssertionType { enum AssertionType {
ASSERT_WARN, ASSERT_WARN,

View File

@@ -32,4 +32,11 @@
# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
#endif #endif
/* MSVC doesn't use POSIX types or defines for `read` */
#ifdef _MSC_VER
# define STDIN_FILENO 0
# define ssize_t int
# define SSIZE_MAX INT_MAX
#endif
#endif /* RGBDS_PLATFORM_H */ #endif /* RGBDS_PLATFORM_H */

View File

@@ -12,28 +12,26 @@ set(common_src
"version.c" "version.c"
) )
find_package(BISON REQUIRED)
find_package(PkgConfig)
if(NOT PKG_CONFIG_FOUND)
# fallback to find_package
find_package(PNG REQUIRED)
else()
pkg_check_modules(LIBPNG REQUIRED libpng)
endif()
BISON_TARGET(ASMy "asm/asmy.y" BISON_TARGET(ASMy "asm/asmy.y"
"${PROJECT_SOURCE_DIR}/src/asm/asmy.c" "${PROJECT_SOURCE_DIR}/src/asm/asmy.c"
DEFINES_FILE "${PROJECT_SOURCE_DIR}/src/asm/asmy.h" DEFINES_FILE "${PROJECT_SOURCE_DIR}/src/asm/asmy.h"
) )
# Lexer is not present yet
if(False) # FLEX_FOUND
FLEX_TARGET(Lexer "asm/lexer.l"
"${PROJECT_SOURCE_DIR}/src/asm/lexer.c"
)
ADD_FLEX_BISON_DEPENDENCY(Lexer ASMy)
set(Lexer_SOURCE "${FLEX_Lexer_OUTPUTS}")
else()
set(Lexer_SOURCE "asm/lexer.c")
endif()
set(rgbasm_src set(rgbasm_src
"${BISON_ASMy_OUTPUT_SOURCE}" "${BISON_ASMy_OUTPUT_SOURCE}"
"${Lexer_SOURCE}"
"asm/charmap.c" "asm/charmap.c"
"asm/fstack.c" "asm/fstack.c"
"asm/globlex.c" "asm/lexer.c"
"asm/macro.c" "asm/macro.c"
"asm/main.c" "asm/main.c"
"asm/math.c" "asm/math.c"
@@ -79,10 +77,26 @@ foreach(PROG "asm" "fix" "gfx" "link")
install(TARGETS rgb${PROG} RUNTIME DESTINATION bin) install(TARGETS rgb${PROG} RUNTIME DESTINATION bin)
endforeach() endforeach()
if(CMAKE_VERSION VERSION_LESS 2.8.12) set(MANDIR "share/man")
add_definitions(${PNG_DEFINITIONS}) set(man1 "asm/rgbasm.1"
include_directories(${PNG_INCLUDE_DIRS}) "fix/rgbfix.1"
target_link_libraries(rgbgfx ${PNG_LIBRARIES}) "gfx/rgbgfx.1"
"link/rgblink.1")
set(man5 "asm/rgbasm.5"
"link/rgblink.5"
"rgbds.5")
set(man7 "gbz80.7"
"rgbds.7")
foreach(SECTION "man1" "man5" "man7")
set(DEST "${MANDIR}/${SECTION}")
install(FILES ${${SECTION}} DESTINATION ${DEST})
endforeach()
if(LIBPNG_FOUND) # pkg-config
target_include_directories(rgbgfx PRIVATE ${LIBPNG_INCLUDE_DIRS})
target_link_directories(rgbgfx PRIVATE ${LIBPNG_LIBRARY_DIRS})
target_link_libraries(rgbgfx PRIVATE ${LIBPNG_LIBRARIES})
else() else()
target_compile_definitions(rgbgfx PRIVATE ${PNG_DEFINITIONS}) target_compile_definitions(rgbgfx PRIVATE ${PNG_DEFINITIONS})
target_include_directories(rgbgfx PRIVATE ${PNG_INCLUDE_DIRS}) target_include_directories(rgbgfx PRIVATE ${PNG_INCLUDE_DIRS})
@@ -92,9 +106,5 @@ endif()
include(CheckLibraryExists) include(CheckLibraryExists)
check_library_exists("m" "sin" "" HAS_LIBM) check_library_exists("m" "sin" "" HAS_LIBM)
if(HAS_LIBM) if(HAS_LIBM)
if(CMAKE_VERSION VERSION_LESS 2.8.12)
target_link_libraries(rgbasm LINK_PRIVATE "m")
else()
target_link_libraries(rgbasm PRIVATE "m") target_link_libraries(rgbasm PRIVATE "m")
endif() endif()
endif()

File diff suppressed because it is too large Load Diff

View File

@@ -22,18 +22,36 @@
#include "hashmap.h" #include "hashmap.h"
#define CHARMAP_HASH_SIZE (1 << 9) /*
* Charmaps are stored using a structure known as "trie".
* Essentially a tree, where each nodes stores a single character's worth of info:
* whether there exists a mapping that ends at the current character,
*/
struct Charnode {
bool isTerminal; /* Whether there exists a mapping that ends here */
uint8_t value; /* If the above is true, its corresponding value */
/* This MUST be indexes and not pointers, because pointers get invalidated by `realloc`!! */
size_t next[255]; /* Indexes of where to go next, 0 = nowhere */
};
#define INITIAL_CAPACITY 32
struct Charmap {
char *name;
size_t usedNodes; /* How many nodes are being used */
size_t capacity; /* How many nodes have been allocated */
struct Charnode nodes[]; /* first node is reserved for the root node */
};
static HashMap charmaps;
static struct Charmap *currentCharmap;
struct CharmapStackEntry { struct CharmapStackEntry {
struct Charmap *charmap; struct Charmap *charmap;
struct CharmapStackEntry *next; struct CharmapStackEntry *next;
}; };
static HashMap charmaps;
static struct Charmap *mainCharmap;
static struct Charmap *currentCharmap;
struct CharmapStackEntry *charmapStack; struct CharmapStackEntry *charmapStack;
static inline struct Charmap *charmap_Get(const char *name) static inline struct Charmap *charmap_Get(const char *name)
@@ -41,16 +59,21 @@ static inline struct Charmap *charmap_Get(const char *name)
return hash_GetElement(charmaps, name); return hash_GetElement(charmaps, name);
} }
static void CopyNode(struct Charmap *dest, static inline struct Charmap *resizeCharmap(struct Charmap *map, size_t capacity)
const struct Charmap *src,
int nodeIdx)
{ {
dest->nodes[nodeIdx].code = src->nodes[nodeIdx].code; struct Charmap *new = realloc(map, sizeof(*map) + sizeof(*map->nodes) * capacity);
dest->nodes[nodeIdx].isCode = src->nodes[nodeIdx].isCode;
for (int i = 0; i < 256; i++) if (!new)
if (src->nodes[nodeIdx].next[i]) fatalerror("Failed to %s charmap: %s\n",
dest->nodes[nodeIdx].next[i] = dest->nodes + map ? "create" : "resize", strerror(errno));
(src->nodes[nodeIdx].next[i] - src->nodes); new->capacity = capacity;
return new;
}
static inline void initNode(struct Charnode *node)
{
node->isTerminal = false;
memset(node->next, 0, sizeof(node->next));
} }
struct Charmap *charmap_New(const char *name, const char *baseName) struct Charmap *charmap_New(const char *name, const char *baseName)
@@ -61,33 +84,28 @@ struct Charmap *charmap_New(const char *name, const char *baseName)
base = charmap_Get(baseName); base = charmap_Get(baseName);
if (base == NULL) if (base == NULL)
yyerror("Base charmap '%s' doesn't exist", baseName); error("Base charmap '%s' doesn't exist\n", baseName);
} }
struct Charmap *charmap = charmap_Get(name); struct Charmap *charmap = charmap_Get(name);
if (charmap != NULL) { if (charmap) {
yyerror("Charmap '%s' already exists", name); error("Charmap '%s' already exists\n", name);
return NULL; return charmap;
} }
charmap = malloc(sizeof(*charmap));
if (charmap == NULL)
fatalerror("Failed to create charmap: %s", strerror(errno));
/* Init the new charmap's fields */ /* Init the new charmap's fields */
snprintf(charmap->name, sizeof(charmap->name), "%s", name); if (base) {
if (base != NULL) { charmap = resizeCharmap(NULL, base->capacity);
charmap->charCount = base->charCount; charmap->usedNodes = base->usedNodes;
charmap->nodeCount = base->nodeCount;
for (int i = 0; i < MAXCHARNODES; i++) memcpy(charmap->nodes, base->nodes, sizeof(base->nodes[0]) * charmap->usedNodes);
CopyNode(charmap, base, i);
} else { } else {
charmap->charCount = 0; charmap = resizeCharmap(NULL, INITIAL_CAPACITY);
charmap->nodeCount = 0; charmap->usedNodes = 1;
memset(charmap->nodes, 0, sizeof(charmap->nodes)); initNode(&charmap->nodes[0]); /* Init the root node */
} }
charmap->name = strdup(name);
hash_AddElement(charmaps, charmap->name, charmap); hash_AddElement(charmaps, charmap->name, charmap);
currentCharmap = charmap; currentCharmap = charmap;
@@ -95,12 +113,18 @@ struct Charmap *charmap_New(const char *name, const char *baseName)
return charmap; return charmap;
} }
void charmap_Delete(struct Charmap *charmap)
{
free(charmap->name);
free(charmap);
}
void charmap_Set(const char *name) void charmap_Set(const char *name)
{ {
struct Charmap *charmap = charmap_Get(name); struct Charmap *charmap = charmap_Get(name);
if (charmap == NULL) if (charmap == NULL)
yyerror("Charmap '%s' doesn't exist", name); error("Charmap '%s' doesn't exist\n", name);
else else
currentCharmap = charmap; currentCharmap = charmap;
} }
@@ -109,9 +133,9 @@ void charmap_Push(void)
{ {
struct CharmapStackEntry *stackEntry; struct CharmapStackEntry *stackEntry;
stackEntry = malloc(sizeof(struct CharmapStackEntry)); stackEntry = malloc(sizeof(*stackEntry));
if (stackEntry == NULL) if (stackEntry == NULL)
fatalerror("No memory for charmap stack"); fatalerror("Failed to alloc charmap stack entry: %s\n", strerror(errno));
stackEntry->charmap = currentCharmap; stackEntry->charmap = currentCharmap;
stackEntry->next = charmapStack; stackEntry->next = charmapStack;
@@ -121,8 +145,10 @@ void charmap_Push(void)
void charmap_Pop(void) void charmap_Pop(void)
{ {
if (charmapStack == NULL) if (charmapStack == NULL) {
fatalerror("No entries in the charmap stack"); error("No entries in the charmap stack\n");
return;
}
struct CharmapStackEntry *top = charmapStack; struct CharmapStackEntry *top = charmapStack;
@@ -131,111 +157,86 @@ void charmap_Pop(void)
free(top); free(top);
} }
void charmap_InitMain(void) void charmap_Add(char *mapping, uint8_t value)
{ {
mainCharmap = charmap_New("main", NULL); struct Charnode *node = &currentCharmap->nodes[0];
}
int32_t charmap_Add(char *input, uint8_t output) for (uint8_t c; *mapping; mapping++) {
{ c = *mapping - 1;
int32_t i;
uint8_t v;
struct Charmap *charmap = currentCharmap; if (node->next[c]) {
struct Charnode *curr_node, *temp_node; node = &currentCharmap->nodes[node->next[c]];
if (charmap->charCount >= MAXCHARMAPS || strlen(input) > CHARMAPLENGTH)
return -1;
curr_node = &charmap->nodes[0];
for (i = 0; (v = (uint8_t)input[i]); i++) {
if (curr_node->next[v]) {
curr_node = curr_node->next[v];
} else { } else {
temp_node = &charmap->nodes[charmap->nodeCount + 1]; /* Register next available node */
node->next[c] = currentCharmap->usedNodes;
/* If no more nodes are available, get new ones */
if (currentCharmap->usedNodes == currentCharmap->capacity) {
currentCharmap->capacity *= 2;
currentCharmap = resizeCharmap(currentCharmap, currentCharmap->capacity);
}
curr_node->next[v] = temp_node; /* Switch to and init new node */
curr_node = temp_node; node = &currentCharmap->nodes[currentCharmap->usedNodes++];
initNode(node);
++charmap->nodeCount;
} }
} }
/* prevent duplicated keys by accepting only first key-value pair. */ if (node->isTerminal)
if (curr_node->isCode) warning(WARNING_CHARMAP_REDEF, "Overriding charmap mapping");
return charmap->charCount;
curr_node->code = output; node->isTerminal = true;
curr_node->isCode = 1; node->value = value;
return ++charmap->charCount;
} }
int32_t charmap_Convert(char **input) size_t charmap_Convert(char const *input, uint8_t *output)
{ {
struct Charmap *charmap = currentCharmap;
struct Charnode *charnode;
char *output;
char outchar[8];
int32_t i, match, length;
uint8_t v, foundCode;
output = malloc(strlen(*input));
if (output == NULL)
fatalerror("Not enough memory for buffer");
length = 0;
while (**input) {
charnode = &charmap->nodes[0];
/* /*
* Find the longest valid match which has been registered in * The goal is to match the longest mapping possible.
* charmap, possibly yielding multiple or no matches. * For that, advance through the trie with each character read.
* The longest match is taken, meaning partial matches shorter * If that would lead to a dead end, rewind characters until the last match, and output.
* than the longest one are ignored. * If no match, read a UTF-8 codepoint and output that.
*/ */
for (i = match = 0; (v = (*input)[i]);) { size_t outputLen = 0;
if (!charnode->next[v]) struct Charnode const *node = &currentCharmap->nodes[0];
struct Charnode const *match = NULL;
size_t rewindDistance = 0;
for (;;) {
/* We still want NULs to reach the `else` path, to give a chance to rewind */
uint8_t c = *input - 1;
if (*input && node->next[c]) {
input++; /* Consume that char */
rewindDistance++;
node = &currentCharmap->nodes[node->next[c]];
if (node->isTerminal) {
match = node;
rewindDistance = 0; /* Rewind from after the match */
}
} else {
input -= rewindDistance; /* Rewind */
rewindDistance = 0;
node = &currentCharmap->nodes[0];
if (match) { /* Arrived at a dead end with a match found */
*output++ = match->value;
outputLen++;
match = NULL; /* Reset match for next round */
} else if (*input) { /* No match found */
size_t codepointLen = readUTF8Char(output, input);
input += codepointLen; /* OK because UTF-8 has no NUL in multi-byte chars */
output += codepointLen;
outputLen += codepointLen;
}
if (!*input)
break; break;
charnode = charnode->next[v];
i++;
if (charnode->isCode) {
match = i;
foundCode = charnode->code;
} }
} }
if (match) { return outputLen;
output[length] = foundCode;
length++;
} else {
/*
* put a utf-8 character
* if failed to find a match.
*/
match = readUTF8Char(outchar, *input);
if (match) {
memcpy(output + length, *input, match);
} else {
output[length] = 0;
match = 1;
}
length += match;
}
*input += match;
}
*input = output;
return length;
} }

View File

@@ -6,540 +6,470 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
/* #include <sys/stat.h>
* FileStack routines #include <assert.h>
*/
#include <errno.h> #include <errno.h>
#include <inttypes.h> #include <inttypes.h>
#include <limits.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "asm/fstack.h" #include "asm/fstack.h"
#include "asm/lexer.h"
#include "asm/macro.h" #include "asm/macro.h"
#include "asm/main.h" #include "asm/main.h"
#include "asm/output.h" #include "asm/symbol.h"
#include "asm/warning.h" #include "asm/warning.h"
#include "platform.h" /* S_ISDIR (stat macro) */
#include "extern/err.h" #ifdef LEXER_DEBUG
#define dbgPrint(...) fprintf(stderr, "[lexer] " __VA_ARGS__)
#else
#define dbgPrint(...)
#endif
#include "platform.h" // S_ISDIR (stat macro) struct Context {
#include "types.h" struct Context *parent;
struct FileStackNode *fileInfo;
struct LexerState *lexerState;
uint32_t uniqueID;
struct MacroArgs *macroArgs; /* Macro args are *saved* here */
uint32_t nbReptIters;
};
static struct sContext *pFileStack; static struct Context *contextStack;
static unsigned int nFileStackDepth; static size_t contextDepth = 0;
unsigned int nMaxRecursionDepth; #define DEFAULT_MAX_DEPTH 64
static struct Symbol const *pCurrentMacro; size_t nMaxRecursionDepth;
static YY_BUFFER_STATE CurrentFlexHandle;
static FILE *pCurrentFile;
static uint32_t nCurrentStatus;
char tzCurrentFileName[_MAX_PATH + 1];
static char IncludePaths[MAXINCPATHS][_MAX_PATH + 1];
static int32_t NextIncPath;
static uint32_t nMacroCount;
static char *pCurrentREPTBlock; static unsigned int nbIncPaths = 0;
static uint32_t nCurrentREPTBlockSize; static char const *includePaths[MAXINCPATHS];
static uint32_t nCurrentREPTBlockCount;
static int32_t nCurrentREPTBodyFirstLine;
static int32_t nCurrentREPTBodyLastLine;
uint32_t ulMacroReturnValue; char const *dumpNodeAndParents(struct FileStackNode const *node)
/*
* defines for nCurrentStatus
*/
#define STAT_isInclude 0 /* 'Normal' state as well */
#define STAT_isMacro 1
#define STAT_isMacroArg 2
#define STAT_isREPTBlock 3
/* Max context stack size */
/*
* Context push and pop
*/
static void pushcontext(void)
{ {
struct sContext **ppFileStack; char const *name;
if (++nFileStackDepth > nMaxRecursionDepth) if (node->type == NODE_REPT) {
fatalerror("Recursion limit (%u) exceeded", nMaxRecursionDepth); assert(node->parent); /* REPT nodes should always have a parent */
struct FileStackReptNode const *reptInfo = (struct FileStackReptNode const *)node;
ppFileStack = &pFileStack; name = dumpNodeAndParents(node->parent);
while (*ppFileStack) fprintf(stderr, "(%" PRIu32 ") -> %s", node->lineNo, name);
ppFileStack = &((*ppFileStack)->pNext); for (uint32_t i = reptInfo->reptDepth; i--; )
fprintf(stderr, "::REPT~%" PRIu32, reptInfo->iters[i]);
*ppFileStack = malloc(sizeof(struct sContext)); } else {
name = ((struct FileStackNamedNode const *)node)->name;
if (*ppFileStack == NULL) if (node->parent) {
fatalerror("No memory for context"); dumpNodeAndParents(node->parent);
fprintf(stderr, "(%" PRIu32 ") -> %s", node->lineNo, name);
(*ppFileStack)->FlexHandle = CurrentFlexHandle; } else {
(*ppFileStack)->pNext = NULL; fputs(name, stderr);
strcpy((char *)(*ppFileStack)->tzFileName, (char *)tzCurrentFileName);
(*ppFileStack)->nLine = nLineNo;
switch ((*ppFileStack)->nStatus = nCurrentStatus) {
case STAT_isMacroArg:
case STAT_isMacro:
(*ppFileStack)->macroArgs = macro_GetCurrentArgs();
(*ppFileStack)->pMacro = pCurrentMacro;
break;
case STAT_isInclude:
(*ppFileStack)->pFile = pCurrentFile;
break;
case STAT_isREPTBlock:
(*ppFileStack)->macroArgs = macro_GetCurrentArgs();
(*ppFileStack)->pREPTBlock = pCurrentREPTBlock;
(*ppFileStack)->nREPTBlockSize = nCurrentREPTBlockSize;
(*ppFileStack)->nREPTBlockCount = nCurrentREPTBlockCount;
(*ppFileStack)->nREPTBodyFirstLine = nCurrentREPTBodyFirstLine;
(*ppFileStack)->nREPTBodyLastLine = nCurrentREPTBodyLastLine;
break;
default:
fatalerror("%s: Internal error.", __func__);
} }
(*ppFileStack)->uniqueID = macro_GetUniqueID(); }
return name;
nLineNo = 0;
} }
static int32_t popcontext(void) void fstk_Dump(struct FileStackNode const *node, uint32_t lineNo)
{ {
struct sContext *pLastFile, **ppLastFile; dumpNodeAndParents(node);
fprintf(stderr, "(%" PRIu32 ")", lineNo);
if (nCurrentStatus == STAT_isREPTBlock) {
if (--nCurrentREPTBlockCount) {
char *pREPTIterationWritePtr;
unsigned long nREPTIterationNo;
int nNbCharsWritten;
int nNbCharsLeft;
yy_delete_buffer(CurrentFlexHandle);
CurrentFlexHandle =
yy_scan_bytes(pCurrentREPTBlock,
nCurrentREPTBlockSize);
yy_switch_to_buffer(CurrentFlexHandle);
macro_SetUniqueID(nMacroCount++);
/* Increment REPT count in file path */
pREPTIterationWritePtr =
strrchr(tzCurrentFileName, '~') + 1;
nREPTIterationNo =
strtoul(pREPTIterationWritePtr, NULL, 10);
nNbCharsLeft = sizeof(tzCurrentFileName)
- (pREPTIterationWritePtr - tzCurrentFileName);
nNbCharsWritten = snprintf(pREPTIterationWritePtr,
nNbCharsLeft, "%lu",
nREPTIterationNo + 1);
if (nNbCharsWritten >= nNbCharsLeft) {
/*
* The string is probably corrupted somehow,
* revert the change to avoid a bad error
* output.
*/
sprintf(pREPTIterationWritePtr, "%lu",
nREPTIterationNo);
fatalerror("Cannot write REPT count to file path");
} }
nLineNo = nCurrentREPTBodyFirstLine; void fstk_DumpCurrent(void)
return 0;
}
}
pLastFile = pFileStack;
if (pLastFile == NULL)
return 1;
ppLastFile = &pFileStack;
while (pLastFile->pNext) {
ppLastFile = &(pLastFile->pNext);
pLastFile = *ppLastFile;
}
yy_delete_buffer(CurrentFlexHandle);
nLineNo = nCurrentStatus == STAT_isREPTBlock ? nCurrentREPTBodyLastLine
: pLastFile->nLine;
if (nCurrentStatus == STAT_isInclude)
fclose(pCurrentFile);
if (nCurrentStatus == STAT_isMacro
|| nCurrentStatus == STAT_isREPTBlock)
nLineNo++;
CurrentFlexHandle = pLastFile->FlexHandle;
strcpy((char *)tzCurrentFileName, (char *)pLastFile->tzFileName);
switch (pLastFile->nStatus) {
struct MacroArgs *args;
case STAT_isMacroArg:
case STAT_isMacro:
args = macro_GetCurrentArgs();
if (nCurrentStatus == STAT_isMacro) {
macro_FreeArgs(args);
free(args);
}
macro_UseNewArgs(pLastFile->macroArgs);
pCurrentMacro = pLastFile->pMacro;
break;
case STAT_isInclude:
pCurrentFile = pLastFile->pFile;
break;
case STAT_isREPTBlock:
args = macro_GetCurrentArgs();
if (nCurrentStatus == STAT_isMacro) {
macro_FreeArgs(args);
free(args);
}
macro_UseNewArgs(pLastFile->macroArgs);
pCurrentREPTBlock = pLastFile->pREPTBlock;
nCurrentREPTBlockSize = pLastFile->nREPTBlockSize;
nCurrentREPTBlockCount = pLastFile->nREPTBlockCount;
nCurrentREPTBodyFirstLine = pLastFile->nREPTBodyFirstLine;
break;
default:
fatalerror("%s: Internal error.", __func__);
}
macro_SetUniqueID(pLastFile->uniqueID);
nCurrentStatus = pLastFile->nStatus;
nFileStackDepth--;
free(*ppLastFile);
*ppLastFile = NULL;
yy_switch_to_buffer(CurrentFlexHandle);
return 0;
}
int32_t fstk_GetLine(void)
{ {
struct sContext *pLastFile, **ppLastFile; if (!contextStack) {
fputs("at top level", stderr);
switch (nCurrentStatus) { return;
case STAT_isInclude: }
/* This is the normal mode, also used when including a file. */ fstk_Dump(contextStack->fileInfo, lexer_GetLineNo());
return nLineNo;
case STAT_isMacro:
break; /* Peek top file of the stack */
case STAT_isMacroArg:
return nLineNo; /* ??? */
case STAT_isREPTBlock:
break; /* Peek top file of the stack */
default:
fatalerror("%s: Internal error.", __func__);
} }
pLastFile = pFileStack; struct FileStackNode *fstk_GetFileStack(void)
if (pLastFile != NULL) {
while (pLastFile->pNext) {
ppLastFile = &(pLastFile->pNext);
pLastFile = *ppLastFile;
}
return pLastFile->nLine;
}
/*
* This is only reached if the lexer is in REPT or MACRO mode but there
* are no saved contexts with the origin of said REPT or MACRO.
*/
fatalerror("%s: Internal error.", __func__);
}
int yywrap(void)
{ {
return popcontext(); struct FileStackNode *node = contextStack->fileInfo;
/* Mark node and all of its parents as referenced if not already so they don't get freed */
while (node && !node->referenced) {
node->ID = -1;
node->referenced = true;
node = node->parent;
}
return contextStack->fileInfo;
} }
/* char const *fstk_GetFileName(void)
* Dump the context stack to stderr
*/
void fstk_Dump(void)
{ {
const struct sContext *pLastFile; /* Iterating via the nodes themselves skips nested REPTs */
struct FileStackNode const *node = contextStack->fileInfo;
pLastFile = pFileStack; while (node->type != NODE_FILE)
node = node->parent;
while (pLastFile) { return ((struct FileStackNamedNode const *)node)->name;
fprintf(stderr, "%s(%" PRId32 ") -> ", pLastFile->tzFileName,
pLastFile->nLine);
pLastFile = pLastFile->pNext;
} }
fprintf(stderr, "%s(%" PRId32 ")", tzCurrentFileName, nLineNo); void fstk_AddIncludePath(char const *path)
}
void fstk_DumpToStr(char *buf, size_t buflen)
{ {
const struct sContext *pLastFile = pFileStack; if (path[0] == '\0')
int retcode; return;
size_t len = buflen; if (nbIncPaths >= MAXINCPATHS) {
error("Too many include directories passed from command line\n");
return;
}
size_t len = strlen(path);
size_t allocSize = len + (path[len - 1] != '/') + 1;
char *str = malloc(allocSize);
while (pLastFile) { if (!str) {
retcode = snprintf(&buf[buflen - len], len, "%s(%" PRId32 ") -> ", /* Attempt to continue without that path */
pLastFile->tzFileName, pLastFile->nLine); error("Failed to allocate new include path: %s\n", strerror(errno));
if (retcode < 0) return;
fatalerror("Failed to dump file stack to string: %s", }
strerror(errno)); memcpy(str, path, len);
else if (retcode >= len) char *end = str + len - 1;
len = 0;
else if (*end++ != '/')
len -= retcode; *end++ = '/';
pLastFile = pLastFile->pNext; *end = '\0';
includePaths[nbIncPaths++] = str;
} }
retcode = snprintf(&buf[buflen - len], len, "%s(%" PRId32 ")", static void printDep(char const *path)
tzCurrentFileName, nLineNo);
if (retcode < 0)
fatalerror("Failed to dump file stack to string: %s",
strerror(errno));
else if (retcode >= len)
len = 0;
else
len -= retcode;
if (!len)
warning(WARNING_LONG_STR, "File stack dump too long, got truncated");
}
/*
* Dump the string expansion stack to stderr
*/
void fstk_DumpStringExpansions(void)
{
const struct sStringExpansionPos *pExpansion = pCurrentStringExpansion;
while (pExpansion) {
fprintf(stderr, "while expanding symbol \"%s\"\n",
pExpansion->tzName);
pExpansion = pExpansion->pParent;
}
}
/*
* Extra includepath stuff
*/
void fstk_AddIncludePath(char *s)
{
if (NextIncPath == MAXINCPATHS)
fatalerror("Too many include directories passed from command line");
// Find last occurrence of slash; is it at the end of the string?
char const *lastSlash = strrchr(s, '/');
char const *pattern = lastSlash && *(lastSlash + 1) == 0 ? "%s" : "%s/";
if (snprintf(IncludePaths[NextIncPath++], _MAX_PATH, pattern,
s) >= _MAX_PATH)
fatalerror("Include path too long '%s'", s);
}
static void printdep(const char *fileName)
{ {
if (dependfile) { if (dependfile) {
fprintf(dependfile, "%s: %s\n", tzTargetFileName, fileName); fprintf(dependfile, "%s: %s\n", tzTargetFileName, path);
if (oGeneratePhonyDeps) if (oGeneratePhonyDeps)
fprintf(dependfile, "%s:\n", fileName); fprintf(dependfile, "%s:\n", path);
} }
} }
static FILE *getFile(char const *pathname) static bool isPathValid(char const *path)
{ {
struct stat statbuf; struct stat statbuf;
if (stat(pathname, &statbuf) != 0) if (stat(path, &statbuf) != 0)
return NULL; return false;
/* Reject directories */ /* Reject directories */
if (S_ISDIR(statbuf.st_mode)) return !S_ISDIR(statbuf.st_mode);
return NULL;
return fopen(pathname, "rb");
} }
FILE *fstk_FindFile(char const *fname, char **incPathUsed) bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
{ {
if (fname == NULL) if (!*size) {
return NULL; *size = 64; /* This is arbitrary, really */
*fullPath = realloc(*fullPath, *size);
char path[_MAX_PATH]; if (!*fullPath)
FILE *f = getFile(fname); error("realloc error during include path search: %s\n",
strerror(errno));
if (f) {
printdep(fname);
return f;
} }
for (size_t i = 0; i < NextIncPath; ++i) { if (*fullPath) {
/* for (size_t i = 0; i <= nbIncPaths; ++i) {
* The function snprintf() does not write more than `size` bytes char const *incPath = i ? includePaths[i - 1] : "";
* (including the terminating null byte ('\0')). If the output int len = snprintf(*fullPath, *size, "%s%s", incPath, path);
* was truncated due to this limit, the return value is the
* number of characters (excluding the terminating null byte)
* which would have been written to the final string if enough
* space had been available. Thus, a return value of `size` or
* more means that the output was truncated.
*/
int fullpathlen = snprintf(path, sizeof(path), "%s%s",
IncludePaths[i], fname);
if (fullpathlen >= (int)sizeof(path)) /* Oh how I wish `asnprintf` was standard... */
continue; if (len >= *size) { /* `len` doesn't include the terminator, `size` does */
*size = len + 1;
*fullPath = realloc(*fullPath, *size);
if (!*fullPath) {
error("realloc error during include path search: %s\n",
strerror(errno));
break;
}
len = sprintf(*fullPath, "%s%s", incPath, path);
}
f = getFile(path); if (len < 0) {
if (f) { error("snprintf error during include path search: %s\n",
printdep(path); strerror(errno));
} else if (isPathValid(*fullPath)) {
if (incPathUsed) printDep(*fullPath);
*incPathUsed = IncludePaths[i]; return true;
return f; }
} }
} }
errno = ENOENT; errno = ENOENT;
if (oGeneratedMissingIncludes) if (oGeneratedMissingIncludes)
printdep(fname); printDep(path);
return NULL; return false;
}
bool yywrap(void)
{
if (contextStack->fileInfo->type == NODE_REPT) { /* The context is a REPT block, which may loop */
struct FileStackReptNode *fileInfo = (struct FileStackReptNode *)contextStack->fileInfo;
/* If the node is referenced, we can't edit it; duplicate it */
if (contextStack->fileInfo->referenced) {
size_t size = sizeof(*fileInfo) + sizeof(fileInfo->iters[0]) * fileInfo->reptDepth;
struct FileStackReptNode *copy = malloc(size);
if (!copy)
fatalerror("Failed to duplicate REPT file node: %s\n", strerror(errno));
/* Copy all info but the referencing */
memcpy(copy, fileInfo, size);
copy->node.next = NULL;
copy->node.referenced = false;
fileInfo = copy;
contextStack->fileInfo = (struct FileStackNode *)fileInfo;
}
fileInfo->iters[0]++;
/* If this wasn't the last iteration, wrap instead of popping */
if (fileInfo->iters[0] <= contextStack->nbReptIters) {
lexer_RestartRept(contextStack->fileInfo->lineNo);
contextStack->uniqueID = macro_UseNewUniqueID();
return false;
}
} else if (!contextStack->parent) {
return true;
}
dbgPrint("Popping context\n");
struct Context *context = contextStack;
contextStack = contextStack->parent;
contextDepth--;
lexer_DeleteState(context->lexerState);
/* Restore args if a macro (not REPT) saved them */
if (context->fileInfo->type == NODE_MACRO) {
dbgPrint("Restoring macro args %p\n", contextStack->macroArgs);
macro_UseNewArgs(contextStack->macroArgs);
}
/* Free the file stack node */
if (!context->fileInfo->referenced)
free(context->fileInfo);
/* Free the entry and make its parent the current entry */
free(context);
lexer_SetState(contextStack->lexerState);
macro_SetUniqueID(contextStack->uniqueID);
return false;
} }
/* /*
* Set up an include file for parsing * Make sure not to switch the lexer state before calling this, so the saved line no is correct
* BE CAREFUL!! This modifies the file stack directly, you should have set up the file info first
*/ */
void fstk_RunInclude(char *tzFileName) static void newContext(struct FileStackNode *fileInfo)
{ {
char *incPathUsed = ""; if (++contextDepth >= nMaxRecursionDepth)
FILE *f = fstk_FindFile(tzFileName, &incPathUsed); fatalerror("Recursion limit (%zu) exceeded\n", nMaxRecursionDepth);
struct Context *context = malloc(sizeof(*context));
if (f == NULL) { if (!context)
if (oGeneratedMissingIncludes) { fatalerror("Failed to allocate memory for new context: %s\n", strerror(errno));
fileInfo->parent = contextStack->fileInfo;
fileInfo->lineNo = 0; /* Init to a default value, see struct definition for info */
fileInfo->referenced = false;
fileInfo->lineNo = lexer_GetLineNo();
context->fileInfo = fileInfo;
/*
* Link new entry to its parent so it's reachable later
* ERRORS SHOULD NOT OCCUR AFTER THIS!!
*/
context->parent = contextStack;
contextStack = context;
}
void fstk_RunInclude(char const *path)
{
dbgPrint("Including path \"%s\"\n", path);
char *fullPath = NULL;
size_t size = 0;
if (!fstk_FindFile(path, &fullPath, &size)) {
free(fullPath);
if (oGeneratedMissingIncludes)
oFailedOnMissingInclude = true; oFailedOnMissingInclude = true;
else
error("Unable to open included file '%s': %s\n", path, strerror(errno));
return; return;
} }
yyerror("Unable to open included file '%s': %s", tzFileName, dbgPrint("Full path: \"%s\"\n", fullPath);
strerror(errno));
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + size);
if (!fileInfo) {
error("Failed to alloc file info for INCLUDE: %s\n", strerror(errno));
return; return;
} }
fileInfo->node.type = NODE_FILE;
strcpy(fileInfo->name, fullPath);
free(fullPath);
pushcontext(); newContext((struct FileStackNode *)fileInfo);
nLineNo = 1; contextStack->lexerState = lexer_OpenFile(fileInfo->name);
nCurrentStatus = STAT_isInclude; if (!contextStack->lexerState)
snprintf(tzCurrentFileName, sizeof(tzCurrentFileName), "%s%s", fatalerror("Failed to set up lexer for file include\n");
incPathUsed, tzFileName); lexer_SetStateAtEOL(contextStack->lexerState);
if (verbose) /* We're back at top-level, so most things are reset */
printf("Assembling %s\n", tzCurrentFileName); contextStack->uniqueID = 0;
pCurrentFile = f; macro_SetUniqueID(0);
CurrentFlexHandle = yy_create_buffer(pCurrentFile);
yy_switch_to_buffer(CurrentFlexHandle);
/* Dirty hack to give the INCLUDE directive a linefeed */
yyunput('\n');
nLineNo--;
} }
/* void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
* Set up a macro for parsing
*/
void fstk_RunMacro(char *s, struct MacroArgs *args)
{ {
struct Symbol const *sym = sym_FindSymbol(s); dbgPrint("Running macro \"%s\"\n", macroName);
int nPrintedChars;
if (sym == NULL) { struct Symbol *macro = sym_FindSymbol(macroName);
yyerror("Macro \"%s\" not defined", s);
if (!macro) {
error("Macro \"%s\" not defined\n", macroName);
return; return;
} }
if (sym->type != SYM_MACRO) { if (macro->type != SYM_MACRO) {
yyerror("\"%s\" is not a macro", s); error("\"%s\" is not a macro\n", macroName);
return; return;
} }
contextStack->macroArgs = macro_GetCurrentArgs();
pushcontext(); /* Compute total length of this node's name: <base name>::<macro> */
macro_SetUniqueID(nMacroCount++); size_t reptNameLen = 0;
/* Minus 1 because there is a newline at the beginning of the buffer */ struct FileStackNode const *node = macro->src;
nLineNo = sym->fileLine - 1;
if (node->type == NODE_REPT) {
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
/* 4294967295 = 2^32 - 1, aka UINT32_MAX */
reptNameLen += reptNode->reptDepth * strlen("::REPT~4294967295");
/* Look for next named node */
do {
node = node->parent;
} while (node->type == NODE_REPT);
}
struct FileStackNamedNode const *baseNode = (struct FileStackNamedNode const *)node;
size_t baseLen = strlen(baseNode->name);
size_t macroNameLen = strlen(macro->name);
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + baseLen
+ reptNameLen + 2 + macroNameLen + 1);
if (!fileInfo) {
error("Failed to alloc file info for \"%s\": %s\n", macro->name, strerror(errno));
return;
}
fileInfo->node.type = NODE_MACRO;
/* Print the name... */
char *dest = fileInfo->name;
memcpy(dest, baseNode->name, baseLen);
dest += baseLen;
if (node->type == NODE_REPT) {
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
for (uint32_t i = reptNode->reptDepth; i--; ) {
int nbChars = sprintf(dest, "::REPT~%" PRIu32, reptNode->iters[i]);
if (nbChars < 0)
fatalerror("Failed to write macro invocation info: %s\n",
strerror(errno));
dest += nbChars;
}
}
*dest++ = ':';
*dest++ = ':';
memcpy(dest, macro->name, macroNameLen + 1);
newContext((struct FileStackNode *)fileInfo);
/* Line minus 1 because buffer begins with a newline */
contextStack->lexerState = lexer_OpenFileView(macro->macro, macro->macroSize,
macro->fileLine - 1);
if (!contextStack->lexerState)
fatalerror("Failed to set up lexer for macro invocation\n");
lexer_SetStateAtEOL(contextStack->lexerState);
contextStack->uniqueID = macro_UseNewUniqueID();
macro_UseNewArgs(args); macro_UseNewArgs(args);
nCurrentStatus = STAT_isMacro;
nPrintedChars = snprintf(tzCurrentFileName, _MAX_PATH + 1,
"%s::%s", sym->fileName, s);
if (nPrintedChars > _MAX_PATH) {
popcontext();
fatalerror("File name + macro name is too large to fit into buffer");
} }
pCurrentMacro = sym; void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
/* TODO: why is `strlen` being used when there's a macro size field? */ {
CurrentFlexHandle = yy_scan_bytes(pCurrentMacro->macro, dbgPrint("Running REPT(%" PRIu32 ")\n", count);
strlen(pCurrentMacro->macro)); if (count == 0)
yy_switch_to_buffer(CurrentFlexHandle); return;
uint32_t reptDepth = contextStack->fileInfo->type == NODE_REPT
? ((struct FileStackReptNode *)contextStack->fileInfo)->reptDepth
: 0;
struct FileStackReptNode *fileInfo = malloc(sizeof(*fileInfo)
+ (reptDepth + 1) * sizeof(fileInfo->iters[0]));
if (!fileInfo) {
error("Failed to alloc file info for REPT: %s\n", strerror(errno));
return;
} }
fileInfo->node.type = NODE_REPT;
fileInfo->reptDepth = reptDepth + 1;
fileInfo->iters[0] = 1;
if (reptDepth)
/* Copy all parent iter counts */
memcpy(&fileInfo->iters[1],
((struct FileStackReptNode *)contextStack->fileInfo)->iters,
reptDepth * sizeof(fileInfo->iters[0]));
newContext((struct FileStackNode *)fileInfo);
/* Correct our line number, which currently points to the `ENDR` line */
contextStack->fileInfo->lineNo = reptLineNo;
contextStack->lexerState = lexer_OpenFileView(body, size, reptLineNo);
if (!contextStack->lexerState)
fatalerror("Failed to set up lexer for rept block\n");
lexer_SetStateAtEOL(contextStack->lexerState);
contextStack->uniqueID = macro_UseNewUniqueID();
contextStack->nbReptIters = count;
}
void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
{
struct LexerState *state = lexer_OpenFile(mainPath);
if (!state)
fatalerror("Failed to open main file!\n");
lexer_SetState(state);
char const *fileName = lexer_GetFileName();
size_t len = strlen(fileName);
struct Context *context = malloc(sizeof(*contextStack));
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + len + 1);
if (!context)
fatalerror("Failed to allocate memory for main context: %s\n", strerror(errno));
if (!fileInfo)
fatalerror("Failed to allocate memory for main file info: %s\n", strerror(errno));
context->fileInfo = (struct FileStackNode *)fileInfo;
/* lineNo and reptIter are unused on the top-level context */
context->fileInfo->parent = NULL;
context->fileInfo->referenced = false;
context->fileInfo->type = NODE_FILE;
memcpy(fileInfo->name, fileName, len + 1);
context->parent = NULL;
context->lexerState = state;
context->uniqueID = 0;
macro_SetUniqueID(0);
context->nbReptIters = 0;
/* Now that it's set up properly, register the context */
contextStack = context;
/* /*
* Set up a repeat block for parsing * Check that max recursion depth won't allow overflowing node `malloc`s
* This assumes that the rept node is larger
*/ */
void fstk_RunRept(uint32_t count, int32_t nReptLineNo) #define DEPTH_LIMIT ((SIZE_MAX - sizeof(struct FileStackReptNode)) / sizeof(uint32_t))
{ if (maxRecursionDepth > DEPTH_LIMIT) {
if (count) { error("Recursion depth may not be higher than %zu, defaulting to "
static const char *tzReptStr = "::REPT~1"; EXPAND_AND_STR(DEFAULT_MAX_DEPTH) "\n", DEPTH_LIMIT);
nMaxRecursionDepth = DEFAULT_MAX_DEPTH;
/* For error printing to make sense, fake nLineNo */
nCurrentREPTBodyLastLine = nLineNo;
nLineNo = nReptLineNo;
pushcontext();
macro_SetUniqueID(nMacroCount++);
nCurrentREPTBlockCount = count;
nCurrentStatus = STAT_isREPTBlock;
nCurrentREPTBlockSize = ulNewMacroSize;
pCurrentREPTBlock = tzNewMacro;
nCurrentREPTBodyFirstLine = nReptLineNo + 1;
nLineNo = nReptLineNo;
if (strlen(tzCurrentFileName) + strlen(tzReptStr) > _MAX_PATH)
fatalerror("Cannot append \"%s\" to file path",
tzReptStr);
strcat(tzCurrentFileName, tzReptStr);
CurrentFlexHandle =
yy_scan_bytes(pCurrentREPTBlock, nCurrentREPTBlockSize);
yy_switch_to_buffer(CurrentFlexHandle);
}
}
/*
* Initialize the filestack routines
*/
void fstk_Init(char *pFileName)
{
char tzSymFileName[_MAX_PATH + 1 + 2];
snprintf(tzSymFileName, sizeof(tzSymFileName), "\"%s\"", pFileName);
sym_AddString("__FILE__", tzSymFileName);
pFileStack = NULL;
if (strcmp(pFileName, "-") == 0) {
pCurrentFile = stdin;
} else { } else {
pCurrentFile = fopen(pFileName, "rb"); nMaxRecursionDepth = maxRecursionDepth;
if (pCurrentFile == NULL)
fatalerror("Unable to open file '%s': %s", pFileName,
strerror(errno));
} }
nFileStackDepth = 0; /* Make sure that the default of 64 is OK, though */
assert(DEPTH_LIMIT >= DEFAULT_MAX_DEPTH);
nMacroCount = 0; #undef DEPTH_LIMIT
nCurrentStatus = STAT_isInclude;
snprintf(tzCurrentFileName, _MAX_PATH + 1, "%s", pFileName);
CurrentFlexHandle = yy_create_buffer(pCurrentFile);
yy_switch_to_buffer(CurrentFlexHandle);
nLineNo = 1;
} }

View File

@@ -1,704 +0,0 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "asm/asm.h"
#include "asm/lexer.h"
#include "asm/macro.h"
#include "asm/main.h"
#include "asm/rpn.h"
#include "asm/section.h"
#include "asm/warning.h"
#include "helpers.h"
#include "asmy.h"
bool oDontExpandStrings;
int32_t nGBGfxID = -1;
int32_t nBinaryID = -1;
static int32_t gbgfx2bin(char ch)
{
int32_t i;
for (i = 0; i <= 3; i++) {
if (CurrentOptions.gbgfx[i] == ch)
return i;
}
return 0;
}
static int32_t binary2bin(char ch)
{
int32_t i;
for (i = 0; i <= 1; i++) {
if (CurrentOptions.binary[i] == ch)
return i;
}
return 0;
}
static int32_t char2bin(char ch)
{
if (ch >= 'a' && ch <= 'f')
return (ch - 'a' + 10);
if (ch >= 'A' && ch <= 'F')
return (ch - 'A' + 10);
if (ch >= '0' && ch <= '9')
return (ch - '0');
return 0;
}
typedef int32_t(*x2bin) (char ch);
static int32_t ascii2bin(char *s)
{
char *start = s;
uint32_t radix = 10;
uint32_t result = 0;
x2bin convertfunc = char2bin;
switch (*s) {
case '$':
radix = 16;
s++;
convertfunc = char2bin;
break;
case '&':
radix = 8;
s++;
convertfunc = char2bin;
break;
case '`':
radix = 4;
s++;
convertfunc = gbgfx2bin;
break;
case '%':
radix = 2;
s++;
convertfunc = binary2bin;
break;
default:
/* Handle below */
break;
}
const uint32_t max_q = UINT32_MAX / radix;
const uint32_t max_r = UINT32_MAX % radix;
if (*s == '\0') {
/*
* There are no digits after the radix prefix
* (or the string is empty, which shouldn't happen).
*/
yyerror("Invalid integer constant");
} else if (radix == 4) {
int32_t size = 0;
int32_t c;
while (*s != '\0') {
c = convertfunc(*s++);
result = result * 2 + ((c & 2) << 7) + (c & 1);
size++;
}
/*
* Extending a graphics constant longer than 8 pixels,
* the Game Boy tile width, produces a nonsensical result.
*/
if (size > 8) {
warning(WARNING_LARGE_CONSTANT, "Graphics constant '%s' is too long",
start);
}
} else {
bool overflow = false;
while (*s != '\0') {
int32_t digit = convertfunc(*s++);
if (result > max_q
|| (result == max_q && digit > max_r)) {
overflow = true;
}
result = result * radix + digit;
}
if (overflow)
warning(WARNING_LARGE_CONSTANT, "Integer constant '%s' is too large",
start);
}
return result;
}
uint32_t ParseFixedPoint(char *s, uint32_t size)
{
uint32_t i;
uint32_t dot = 0;
for (i = 0; i < size; i++) {
if (s[i] == '.') {
dot++;
if (dot == 2)
break;
}
}
yyskipbytes(i);
yylval.nConstValue = (int32_t)(atof(s) * 65536);
return 1;
}
uint32_t ParseNumber(char *s, uint32_t size)
{
char dest[256];
if (size > 255)
fatalerror("Number token too long");
strncpy(dest, s, size);
dest[size] = 0;
yylval.nConstValue = ascii2bin(dest);
yyskipbytes(size);
return 1;
}
/*
* If the symbol name ends before the end of the macro arg,
* return a pointer to the rest of the macro arg.
* Otherwise, return NULL.
*/
char const *AppendMacroArg(char whichArg, char *dest, size_t *destIndex)
{
char const *marg;
if (whichArg == '@')
marg = macro_GetUniqueIDStr();
else if (whichArg >= '1' && whichArg <= '9')
marg = macro_GetArg(whichArg - '0');
else
fatalerror("Invalid macro argument '\\%c' in symbol", whichArg);
if (!marg)
fatalerror("Macro argument '\\%c' not defined", whichArg);
char ch;
while ((ch = *marg) != 0) {
if ((ch >= 'a' && ch <= 'z')
|| (ch >= 'A' && ch <= 'Z')
|| (ch >= '0' && ch <= '9')
|| ch == '_'
|| ch == '@'
|| ch == '#'
|| ch == '.') {
if (*destIndex >= MAXSYMLEN)
fatalerror("Symbol too long");
dest[*destIndex] = ch;
(*destIndex)++;
} else {
return marg;
}
marg++;
}
return NULL;
}
uint32_t ParseSymbol(char *src, uint32_t size)
{
char dest[MAXSYMLEN + 1];
size_t srcIndex = 0;
size_t destIndex = 0;
char const *rest = NULL;
while (srcIndex < size) {
char ch = src[srcIndex++];
if (ch == '\\') {
/*
* We don't check if srcIndex is still less than size,
* but that can only fail to be true when the
* following char is neither '@' nor a digit.
* In that case, AppendMacroArg() will catch the error.
*/
ch = src[srcIndex++];
rest = AppendMacroArg(ch, dest, &destIndex);
/* If the symbol's end was in the middle of the token */
if (rest)
break;
} else {
if (destIndex >= MAXSYMLEN)
fatalerror("Symbol too long");
dest[destIndex++] = ch;
}
}
dest[destIndex] = 0;
/* Tell the lexer we read all bytes that we did */
yyskipbytes(srcIndex);
/*
* If an escape's expansion left some chars after the symbol's end,
* such as the `::` in a `Backup\1` expanded to `BackupCamX::`,
* put those into the buffer.
* Note that this NEEDS to be done after the `yyskipbytes` above.
*/
if (rest)
yyunputstr(rest);
/* If the symbol is an EQUS, expand it */
if (!oDontExpandStrings) {
struct Symbol const *sym = sym_FindSymbol(dest);
if (sym && sym->type == SYM_EQUS) {
char const *s;
lex_BeginStringExpansion(dest);
/* Feed the symbol's contents into the buffer */
yyunputstr(s = sym_GetStringValue(sym));
/* Lines inserted this way shall not increase nLineNo */
while (*s) {
if (*s++ == '\n')
nLineNo--;
}
return 0;
}
}
strcpy(yylval.tzSym, dest);
return 1;
}
uint32_t PutMacroArg(char *src, uint32_t size)
{
char const *s;
yyskipbytes(size);
if ((size == 2 && src[1] >= '1' && src[1] <= '9')) {
s = macro_GetArg(src[1] - '0');
if (s != NULL)
yyunputstr(s);
else
yyerror("Macro argument '\\%c' not defined", src[1]);
} else {
yyerror("Invalid macro argument '\\%c'", src[1]);
}
return 0;
}
uint32_t PutUniqueID(char *src, uint32_t size)
{
(void)src;
char const *s;
yyskipbytes(size);
s = macro_GetUniqueIDStr();
if (s != NULL)
yyunputstr(s);
else
yyerror("Macro unique label string not defined");
return 0;
}
enum {
T_LEX_MACROARG = 3000,
T_LEX_MACROUNIQUE
};
const struct sLexInitString lexer_strings[] = {
{"adc", T_Z80_ADC},
{"add", T_Z80_ADD},
{"and", T_Z80_AND},
{"bit", T_Z80_BIT},
{"call", T_Z80_CALL},
{"ccf", T_Z80_CCF},
{"cpl", T_Z80_CPL},
{"cp", T_Z80_CP},
{"daa", T_Z80_DAA},
{"dec", T_Z80_DEC},
{"di", T_Z80_DI},
{"ei", T_Z80_EI},
{"halt", T_Z80_HALT},
{"inc", T_Z80_INC},
{"jp", T_Z80_JP},
{"jr", T_Z80_JR},
{"ld", T_Z80_LD},
{"ldi", T_Z80_LDI},
{"ldd", T_Z80_LDD},
{"ldio", T_Z80_LDIO},
{"ldh", T_Z80_LDIO},
{"nop", T_Z80_NOP},
{"or", T_Z80_OR},
{"pop", T_Z80_POP},
{"push", T_Z80_PUSH},
{"res", T_Z80_RES},
{"reti", T_Z80_RETI},
{"ret", T_Z80_RET},
{"rlca", T_Z80_RLCA},
{"rlc", T_Z80_RLC},
{"rla", T_Z80_RLA},
{"rl", T_Z80_RL},
{"rrc", T_Z80_RRC},
{"rrca", T_Z80_RRCA},
{"rra", T_Z80_RRA},
{"rr", T_Z80_RR},
{"rst", T_Z80_RST},
{"sbc", T_Z80_SBC},
{"scf", T_Z80_SCF},
{"set", T_POP_SET},
{"sla", T_Z80_SLA},
{"sra", T_Z80_SRA},
{"srl", T_Z80_SRL},
{"stop", T_Z80_STOP},
{"sub", T_Z80_SUB},
{"swap", T_Z80_SWAP},
{"xor", T_Z80_XOR},
{"nz", T_CC_NZ},
{"z", T_CC_Z},
{"nc", T_CC_NC},
/* Handled in list of registers */
/* { "c", T_TOKEN_C }, */
{"[bc]", T_MODE_BC_IND},
{"[de]", T_MODE_DE_IND},
{"[hl]", T_MODE_HL_IND},
{"[hl+]", T_MODE_HL_INDINC},
{"[hl-]", T_MODE_HL_INDDEC},
{"[hli]", T_MODE_HL_INDINC},
{"[hld]", T_MODE_HL_INDDEC},
{"af", T_MODE_AF},
{"bc", T_MODE_BC},
{"de", T_MODE_DE},
{"hl", T_MODE_HL},
{"sp", T_MODE_SP},
{"[c]", T_MODE_C_IND},
{"[$ff00+c]", T_MODE_C_IND},
{"[$ff00 + c]", T_MODE_C_IND},
{"a", T_TOKEN_A},
{"b", T_TOKEN_B},
{"c", T_TOKEN_C},
{"d", T_TOKEN_D},
{"e", T_TOKEN_E},
{"h", T_TOKEN_H},
{"l", T_TOKEN_L},
{"||", T_OP_LOGICOR},
{"&&", T_OP_LOGICAND},
{"==", T_OP_LOGICEQU},
{">", T_OP_LOGICGT},
{"<", T_OP_LOGICLT},
{">=", T_OP_LOGICGE},
{"<=", T_OP_LOGICLE},
{"!=", T_OP_LOGICNE},
{"!", T_OP_LOGICNOT},
{"|", T_OP_OR},
{"^", T_OP_XOR},
{"&", T_OP_AND},
{"<<", T_OP_SHL},
{">>", T_OP_SHR},
{"+", T_OP_ADD},
{"-", T_OP_SUB},
{"*", T_OP_MUL},
{"/", T_OP_DIV},
{"%", T_OP_MOD},
{"~", T_OP_NOT},
{"def", T_OP_DEF},
{"fragment", T_POP_FRAGMENT},
{"bank", T_OP_BANK},
{"align", T_OP_ALIGN},
{"round", T_OP_ROUND},
{"ceil", T_OP_CEIL},
{"floor", T_OP_FLOOR},
{"div", T_OP_FDIV},
{"mul", T_OP_FMUL},
{"sin", T_OP_SIN},
{"cos", T_OP_COS},
{"tan", T_OP_TAN},
{"asin", T_OP_ASIN},
{"acos", T_OP_ACOS},
{"atan", T_OP_ATAN},
{"atan2", T_OP_ATAN2},
{"high", T_OP_HIGH},
{"low", T_OP_LOW},
{"isconst", T_OP_ISCONST},
{"strcmp", T_OP_STRCMP},
{"strin", T_OP_STRIN},
{"strsub", T_OP_STRSUB},
{"strlen", T_OP_STRLEN},
{"strcat", T_OP_STRCAT},
{"strupr", T_OP_STRUPR},
{"strlwr", T_OP_STRLWR},
{"include", T_POP_INCLUDE},
{"printt", T_POP_PRINTT},
{"printi", T_POP_PRINTI},
{"printv", T_POP_PRINTV},
{"printf", T_POP_PRINTF},
{"export", T_POP_EXPORT},
{"xdef", T_POP_XDEF},
{"global", T_POP_GLOBAL},
{"ds", T_POP_DS},
{"db", T_POP_DB},
{"dw", T_POP_DW},
{"dl", T_POP_DL},
{"section", T_POP_SECTION},
{"purge", T_POP_PURGE},
{"rsreset", T_POP_RSRESET},
{"rsset", T_POP_RSSET},
{"incbin", T_POP_INCBIN},
{"charmap", T_POP_CHARMAP},
{"newcharmap", T_POP_NEWCHARMAP},
{"setcharmap", T_POP_SETCHARMAP},
{"pushc", T_POP_PUSHC},
{"popc", T_POP_POPC},
{"fail", T_POP_FAIL},
{"warn", T_POP_WARN},
{"fatal", T_POP_FATAL},
{"assert", T_POP_ASSERT},
{"static_assert", T_POP_STATIC_ASSERT},
{"macro", T_POP_MACRO},
/* Not needed but we have it here just to protect the name */
{"endm", T_POP_ENDM},
{"shift", T_POP_SHIFT},
{"rept", T_POP_REPT},
/* Not needed but we have it here just to protect the name */
{"endr", T_POP_ENDR},
{"load", T_POP_LOAD},
{"endl", T_POP_ENDL},
{"if", T_POP_IF},
{"else", T_POP_ELSE},
{"elif", T_POP_ELIF},
{"endc", T_POP_ENDC},
{"union", T_POP_UNION},
{"nextu", T_POP_NEXTU},
{"endu", T_POP_ENDU},
{"wram0", T_SECT_WRAM0},
{"vram", T_SECT_VRAM},
{"romx", T_SECT_ROMX},
{"rom0", T_SECT_ROM0},
{"hram", T_SECT_HRAM},
{"wramx", T_SECT_WRAMX},
{"sram", T_SECT_SRAM},
{"oam", T_SECT_OAM},
{"rb", T_POP_RB},
{"rw", T_POP_RW},
{"equ", T_POP_EQU},
{"equs", T_POP_EQUS},
/* Handled before in list of CPU instructions */
/* {"set", T_POP_SET}, */
{"=", T_POP_EQUAL},
{"pushs", T_POP_PUSHS},
{"pops", T_POP_POPS},
{"pusho", T_POP_PUSHO},
{"popo", T_POP_POPO},
{"opt", T_POP_OPT},
{NULL, 0}
};
const struct sLexFloat tNumberToken = {
ParseNumber,
T_NUMBER
};
const struct sLexFloat tFixedPointToken = {
ParseFixedPoint,
T_NUMBER
};
const struct sLexFloat tIDToken = {
ParseSymbol,
T_ID
};
const struct sLexFloat tMacroArgToken = {
PutMacroArg,
T_LEX_MACROARG
};
const struct sLexFloat tMacroUniqueToken = {
PutUniqueID,
T_LEX_MACROUNIQUE
};
void setup_lexer(void)
{
uint32_t id;
lex_Init();
lex_AddStrings(lexer_strings);
//Macro arguments
id = lex_FloatAlloc(&tMacroArgToken);
lex_FloatAddFirstRange(id, '\\', '\\');
lex_FloatAddSecondRange(id, '1', '9');
id = lex_FloatAlloc(&tMacroUniqueToken);
lex_FloatAddFirstRange(id, '\\', '\\');
lex_FloatAddSecondRange(id, '@', '@');
//Decimal constants
id = lex_FloatAlloc(&tNumberToken);
lex_FloatAddFirstRange(id, '0', '9');
lex_FloatAddSecondRange(id, '0', '9');
lex_FloatAddRange(id, '0', '9');
//Binary constants
id = lex_FloatAlloc(&tNumberToken);
nBinaryID = id;
lex_FloatAddFirstRange(id, '%', '%');
lex_FloatAddSecondRange(id, CurrentOptions.binary[0],
CurrentOptions.binary[0]);
lex_FloatAddSecondRange(id, CurrentOptions.binary[1],
CurrentOptions.binary[1]);
lex_FloatAddRange(id, CurrentOptions.binary[0],
CurrentOptions.binary[0]);
lex_FloatAddRange(id, CurrentOptions.binary[1],
CurrentOptions.binary[1]);
//Octal constants
id = lex_FloatAlloc(&tNumberToken);
lex_FloatAddFirstRange(id, '&', '&');
lex_FloatAddSecondRange(id, '0', '7');
lex_FloatAddRange(id, '0', '7');
//Gameboy gfx constants
id = lex_FloatAlloc(&tNumberToken);
nGBGfxID = id;
lex_FloatAddFirstRange(id, '`', '`');
lex_FloatAddSecondRange(id, CurrentOptions.gbgfx[0],
CurrentOptions.gbgfx[0]);
lex_FloatAddSecondRange(id, CurrentOptions.gbgfx[1],
CurrentOptions.gbgfx[1]);
lex_FloatAddSecondRange(id, CurrentOptions.gbgfx[2],
CurrentOptions.gbgfx[2]);
lex_FloatAddSecondRange(id, CurrentOptions.gbgfx[3],
CurrentOptions.gbgfx[3]);
lex_FloatAddRange(id, CurrentOptions.gbgfx[0], CurrentOptions.gbgfx[0]);
lex_FloatAddRange(id, CurrentOptions.gbgfx[1], CurrentOptions.gbgfx[1]);
lex_FloatAddRange(id, CurrentOptions.gbgfx[2], CurrentOptions.gbgfx[2]);
lex_FloatAddRange(id, CurrentOptions.gbgfx[3], CurrentOptions.gbgfx[3]);
//Hex constants
id = lex_FloatAlloc(&tNumberToken);
lex_FloatAddFirstRange(id, '$', '$');
lex_FloatAddSecondRange(id, '0', '9');
lex_FloatAddSecondRange(id, 'A', 'F');
lex_FloatAddSecondRange(id, 'a', 'f');
lex_FloatAddRange(id, '0', '9');
lex_FloatAddRange(id, 'A', 'F');
lex_FloatAddRange(id, 'a', 'f');
//ID 's
id = lex_FloatAlloc(&tIDToken);
lex_FloatAddFirstRange(id, 'a', 'z');
lex_FloatAddFirstRange(id, 'A', 'Z');
lex_FloatAddFirstRange(id, '_', '_');
lex_FloatAddSecondRange(id, '.', '.');
lex_FloatAddSecondRange(id, 'a', 'z');
lex_FloatAddSecondRange(id, 'A', 'Z');
lex_FloatAddSecondRange(id, '0', '9');
lex_FloatAddSecondRange(id, '_', '_');
lex_FloatAddSecondRange(id, '\\', '\\');
lex_FloatAddSecondRange(id, '@', '@');
lex_FloatAddSecondRange(id, '#', '#');
lex_FloatAddRange(id, '.', '.');
lex_FloatAddRange(id, 'a', 'z');
lex_FloatAddRange(id, 'A', 'Z');
lex_FloatAddRange(id, '0', '9');
lex_FloatAddRange(id, '_', '_');
lex_FloatAddRange(id, '\\', '\\');
lex_FloatAddRange(id, '@', '@');
lex_FloatAddRange(id, '#', '#');
//Local ID
id = lex_FloatAlloc(&tIDToken);
lex_FloatAddFirstRange(id, '.', '.');
lex_FloatAddSecondRange(id, 'a', 'z');
lex_FloatAddSecondRange(id, 'A', 'Z');
lex_FloatAddSecondRange(id, '_', '_');
lex_FloatAddRange(id, 'a', 'z');
lex_FloatAddRange(id, 'A', 'Z');
lex_FloatAddRange(id, '0', '9');
lex_FloatAddRange(id, '_', '_');
lex_FloatAddRange(id, '\\', '\\');
lex_FloatAddRange(id, '@', '@');
lex_FloatAddRange(id, '#', '#');
// "@"
id = lex_FloatAlloc(&tIDToken);
lex_FloatAddFirstRange(id, '@', '@');
//Fixed point constants
id = lex_FloatAlloc(&tFixedPointToken);
lex_FloatAddFirstRange(id, '.', '.');
lex_FloatAddFirstRange(id, '0', '9');
lex_FloatAddSecondRange(id, '.', '.');
lex_FloatAddSecondRange(id, '0', '9');
lex_FloatAddRange(id, '.', '.');
lex_FloatAddRange(id, '0', '9');
}

File diff suppressed because it is too large Load Diff

View File

@@ -29,7 +29,8 @@ struct MacroArgs {
sizeof(((struct MacroArgs){0}).args[0]) * (nbArgs)) sizeof(((struct MacroArgs){0}).args[0]) * (nbArgs))
static struct MacroArgs *macroArgs = NULL; static struct MacroArgs *macroArgs = NULL;
static uint32_t uniqueID = -1; static uint32_t uniqueID = 0;
static uint32_t maxUniqueID = 0;
/* /*
* The initialization is somewhat harmful, since it is never used, but it * The initialization is somewhat harmful, since it is never used, but it
* guarantees the size of the buffer will be correct. I was unable to find a * guarantees the size of the buffer will be correct. I was unable to find a
@@ -48,7 +49,7 @@ struct MacroArgs *macro_NewArgs(void)
struct MacroArgs *args = malloc(SIZEOF_ARGS(INITIAL_ARG_SIZE)); struct MacroArgs *args = malloc(SIZEOF_ARGS(INITIAL_ARG_SIZE));
if (!args) if (!args)
fatalerror("Unable to register macro arguments: %s", strerror(errno)); fatalerror("Unable to register macro arguments: %s\n", strerror(errno));
args->nbArgs = 0; args->nbArgs = 0;
args->shift = 0; args->shift = 0;
@@ -60,16 +61,16 @@ void macro_AppendArg(struct MacroArgs **argPtr, char *s)
{ {
#define macArgs (*argPtr) #define macArgs (*argPtr)
if (macArgs->nbArgs == MAXMACROARGS) if (macArgs->nbArgs == MAXMACROARGS)
yyerror("A maximum of " EXPAND_AND_STR(MAXMACROARGS) error("A maximum of " EXPAND_AND_STR(MAXMACROARGS)
" arguments is allowed"); " arguments is allowed\n");
if (macArgs->nbArgs >= macArgs->capacity) { if (macArgs->nbArgs >= macArgs->capacity) {
macArgs->capacity *= 2; macArgs->capacity *= 2;
/* Check that overflow didn't roll us back */ /* Check that overflow didn't roll us back */
if (macArgs->capacity <= macArgs->nbArgs) if (macArgs->capacity <= macArgs->nbArgs)
fatalerror("Failed to add new macro argument: possible capacity overflow"); fatalerror("Failed to add new macro argument: possible capacity overflow\n");
macArgs = realloc(macArgs, SIZEOF_ARGS(macArgs->capacity)); macArgs = realloc(macArgs, SIZEOF_ARGS(macArgs->capacity));
if (!macArgs) if (!macArgs)
fatalerror("Error adding new macro argument: %s", strerror(errno)); fatalerror("Error adding new macro argument: %s\n", strerror(errno));
} }
macArgs->args[macArgs->nbArgs++] = s; macArgs->args[macArgs->nbArgs++] = s;
#undef macArgs #undef macArgs
@@ -88,6 +89,9 @@ void macro_FreeArgs(struct MacroArgs *args)
char const *macro_GetArg(uint32_t i) char const *macro_GetArg(uint32_t i)
{ {
if (!macroArgs)
return NULL;
uint32_t realIndex = i + macroArgs->shift - 1; uint32_t realIndex = i + macroArgs->shift - 1;
return realIndex >= macroArgs->nbArgs ? NULL return realIndex >= macroArgs->nbArgs ? NULL
@@ -107,15 +111,23 @@ char const *macro_GetUniqueIDStr(void)
void macro_SetUniqueID(uint32_t id) void macro_SetUniqueID(uint32_t id)
{ {
uniqueID = id; uniqueID = id;
if (id == -1) { if (id == 0) {
uniqueIDPtr = NULL; uniqueIDPtr = NULL;
} else { } else {
if (uniqueID > maxUniqueID)
maxUniqueID = uniqueID;
/* The buffer is guaranteed to be the correct size */ /* The buffer is guaranteed to be the correct size */
sprintf(uniqueIDBuf, "_%" PRIu32, id); sprintf(uniqueIDBuf, "_%" PRIu32, id);
uniqueIDPtr = uniqueIDBuf; uniqueIDPtr = uniqueIDBuf;
} }
} }
uint32_t macro_UseNewUniqueID(void)
{
macro_SetUniqueID(++maxUniqueID);
return maxUniqueID;
}
void macro_ShiftCurrentArgs(void) void macro_ShiftCurrentArgs(void)
{ {
if (macroArgs->shift != macroArgs->nbArgs) if (macroArgs->shift != macroArgs->nbArgs)

View File

@@ -6,6 +6,7 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <ctype.h>
#include <errno.h> #include <errno.h>
#include <float.h> #include <float.h>
#include <inttypes.h> #include <inttypes.h>
@@ -17,13 +18,15 @@
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include "asm/symbol.h" #include "asm/charmap.h"
#include "asm/fstack.h" #include "asm/fstack.h"
#include "asm/lexer.h" #include "asm/lexer.h"
#include "asm/output.h"
#include "asm/main.h" #include "asm/main.h"
#include "asm/charmap.h" #include "asm/output.h"
#include "asm/rpn.h"
#include "asm/symbol.h"
#include "asm/warning.h" #include "asm/warning.h"
#include "asmy.h"
#include "extern/err.h" #include "extern/err.h"
#include "extern/getopt.h" #include "extern/getopt.h"
@@ -31,8 +34,6 @@
#include "helpers.h" #include "helpers.h"
#include "version.h" #include "version.h"
extern int yyparse(void);
size_t cldefines_index; size_t cldefines_index;
size_t cldefines_numindices; size_t cldefines_numindices;
size_t cldefines_bufsize; size_t cldefines_bufsize;
@@ -40,12 +41,7 @@ const size_t cldefine_entrysize = 2 * sizeof(void *);
char **cldefines; char **cldefines;
clock_t nStartClock, nEndClock; clock_t nStartClock, nEndClock;
uint32_t nTotalLines, nIFDepth, nUnionDepth; uint32_t nTotalLines, nIFDepth;
bool skipElif;
uint32_t unionStart[128], unionSize[128];
int32_t nLineNo;
uint32_t curOffset;
#if defined(YYDEBUG) && YYDEBUG #if defined(YYDEBUG) && YYDEBUG
extern int yydebug; extern int yydebug;
@@ -70,71 +66,16 @@ bool warnings; /* True to enable warnings, false to disable them. */
struct sOptionStackEntry { struct sOptionStackEntry {
struct sOptions Options; struct sOptions Options;
struct sOptionStackEntry *pNext; struct sOptionStackEntry *next;
}; };
struct sOptionStackEntry *pOptionStack; struct sOptionStackEntry *pOptionStack;
void opt_SetCurrentOptions(struct sOptions *pOpt) void opt_SetCurrentOptions(struct sOptions *opt)
{ {
if (nGBGfxID != -1) { CurrentOptions = *opt;
lex_FloatDeleteRange(nGBGfxID, CurrentOptions.gbgfx[0], lexer_SetGfxDigits(CurrentOptions.gbgfx);
CurrentOptions.gbgfx[0]); lexer_SetBinDigits(CurrentOptions.binary);
lex_FloatDeleteRange(nGBGfxID, CurrentOptions.gbgfx[1],
CurrentOptions.gbgfx[1]);
lex_FloatDeleteRange(nGBGfxID, CurrentOptions.gbgfx[2],
CurrentOptions.gbgfx[2]);
lex_FloatDeleteRange(nGBGfxID, CurrentOptions.gbgfx[3],
CurrentOptions.gbgfx[3]);
lex_FloatDeleteSecondRange(nGBGfxID, CurrentOptions.gbgfx[0],
CurrentOptions.gbgfx[0]);
lex_FloatDeleteSecondRange(nGBGfxID, CurrentOptions.gbgfx[1],
CurrentOptions.gbgfx[1]);
lex_FloatDeleteSecondRange(nGBGfxID, CurrentOptions.gbgfx[2],
CurrentOptions.gbgfx[2]);
lex_FloatDeleteSecondRange(nGBGfxID, CurrentOptions.gbgfx[3],
CurrentOptions.gbgfx[3]);
}
if (nBinaryID != -1) {
lex_FloatDeleteRange(nBinaryID, CurrentOptions.binary[0],
CurrentOptions.binary[0]);
lex_FloatDeleteRange(nBinaryID, CurrentOptions.binary[1],
CurrentOptions.binary[1]);
lex_FloatDeleteSecondRange(nBinaryID, CurrentOptions.binary[0],
CurrentOptions.binary[0]);
lex_FloatDeleteSecondRange(nBinaryID, CurrentOptions.binary[1],
CurrentOptions.binary[1]);
}
CurrentOptions = *pOpt;
if (nGBGfxID != -1) {
lex_FloatAddRange(nGBGfxID, CurrentOptions.gbgfx[0],
CurrentOptions.gbgfx[0]);
lex_FloatAddRange(nGBGfxID, CurrentOptions.gbgfx[1],
CurrentOptions.gbgfx[1]);
lex_FloatAddRange(nGBGfxID, CurrentOptions.gbgfx[2],
CurrentOptions.gbgfx[2]);
lex_FloatAddRange(nGBGfxID, CurrentOptions.gbgfx[3],
CurrentOptions.gbgfx[3]);
lex_FloatAddSecondRange(nGBGfxID, CurrentOptions.gbgfx[0],
CurrentOptions.gbgfx[0]);
lex_FloatAddSecondRange(nGBGfxID, CurrentOptions.gbgfx[1],
CurrentOptions.gbgfx[1]);
lex_FloatAddSecondRange(nGBGfxID, CurrentOptions.gbgfx[2],
CurrentOptions.gbgfx[2]);
lex_FloatAddSecondRange(nGBGfxID, CurrentOptions.gbgfx[3],
CurrentOptions.gbgfx[3]);
}
if (nBinaryID != -1) {
lex_FloatAddRange(nBinaryID, CurrentOptions.binary[0],
CurrentOptions.binary[0]);
lex_FloatAddRange(nBinaryID, CurrentOptions.binary[1],
CurrentOptions.binary[1]);
lex_FloatAddSecondRange(nBinaryID, CurrentOptions.binary[0],
CurrentOptions.binary[0]);
lex_FloatAddSecondRange(nBinaryID, CurrentOptions.binary[1],
CurrentOptions.binary[1]);
}
} }
void opt_Parse(char *s) void opt_Parse(char *s)
@@ -151,7 +92,7 @@ void opt_Parse(char *s)
newopt.gbgfx[2] = s[3]; newopt.gbgfx[2] = s[3];
newopt.gbgfx[3] = s[4]; newopt.gbgfx[3] = s[4];
} else { } else {
yyerror("Must specify exactly 4 characters for option 'g'"); error("Must specify exactly 4 characters for option 'g'\n");
} }
break; break;
case 'b': case 'b':
@@ -159,11 +100,11 @@ void opt_Parse(char *s)
newopt.binary[0] = s[1]; newopt.binary[0] = s[1];
newopt.binary[1] = s[2]; newopt.binary[1] = s[2];
} else { } else {
yyerror("Must specify exactly 2 characters for option 'b'"); error("Must specify exactly 2 characters for option 'b'\n");
} }
break; break;
case 'z': case 'z':
warning(WARNING_OBSOLETE, "Option 'z' is a deprecated alias for 'p'"); warning(WARNING_OBSOLETE, "Option 'z' is a deprecated alias for 'p'\n");
/* fallthrough */ /* fallthrough */
case 'p': case 'p':
if (strlen(&s[1]) <= 2) { if (strlen(&s[1]) <= 2) {
@@ -172,15 +113,15 @@ void opt_Parse(char *s)
result = sscanf(&s[1], "%x", &fillchar); result = sscanf(&s[1], "%x", &fillchar);
if (result != EOF && result != 1) if (result != EOF && result != 1)
yyerror("Invalid argument for option 'z'"); error("Invalid argument for option 'z'\n");
else else
newopt.fillchar = fillchar; newopt.fillchar = fillchar;
} else { } else {
yyerror("Invalid argument for option 'z'"); error("Invalid argument for option 'z'\n");
} }
break; break;
default: default:
yyerror("Unknown option"); error("Unknown option\n");
break; break;
} }
@@ -194,23 +135,23 @@ void opt_Push(void)
pOpt = malloc(sizeof(struct sOptionStackEntry)); pOpt = malloc(sizeof(struct sOptionStackEntry));
if (pOpt == NULL) if (pOpt == NULL)
fatalerror("No memory for option stack"); fatalerror("No memory for option stack\n");
pOpt->Options = CurrentOptions; pOpt->Options = CurrentOptions;
pOpt->pNext = pOptionStack; pOpt->next = pOptionStack;
pOptionStack = pOpt; pOptionStack = pOpt;
} }
void opt_Pop(void) void opt_Pop(void)
{ {
if (pOptionStack == NULL) if (pOptionStack == NULL)
fatalerror("No entries in the option stack"); fatalerror("No entries in the option stack\n");
struct sOptionStackEntry *pOpt; struct sOptionStackEntry *pOpt;
pOpt = pOptionStack; pOpt = pOptionStack;
opt_SetCurrentOptions(&(pOpt->Options)); opt_SetCurrentOptions(&(pOpt->Options));
pOptionStack = pOpt->pNext; pOptionStack = pOpt->next;
free(pOpt); free(pOpt);
} }
@@ -221,17 +162,17 @@ void opt_AddDefine(char *s)
if (cldefines_index >= cldefines_numindices) { if (cldefines_index >= cldefines_numindices) {
/* Check for overflows */ /* Check for overflows */
if ((cldefines_numindices * 2) < cldefines_numindices) if ((cldefines_numindices * 2) < cldefines_numindices)
fatalerror("No memory for command line defines"); fatalerror("No memory for command line defines\n");
if ((cldefines_bufsize * 2) < cldefines_bufsize) if ((cldefines_bufsize * 2) < cldefines_bufsize)
fatalerror("No memory for command line defines"); fatalerror("No memory for command line defines\n");
cldefines_numindices *= 2; cldefines_numindices *= 2;
cldefines_bufsize *= 2; cldefines_bufsize *= 2;
cldefines = realloc(cldefines, cldefines_bufsize); cldefines = realloc(cldefines, cldefines_bufsize);
if (!cldefines) if (!cldefines)
fatalerror("No memory for command line defines"); fatalerror("No memory for command line defines\n");
} }
equals = strchr(s, '='); equals = strchr(s, '=');
if (equals) { if (equals) {
@@ -252,6 +193,22 @@ static void opt_ParseDefines(void)
sym_AddString(cldefines[i], cldefines[i + 1]); sym_AddString(cldefines[i], cldefines[i + 1]);
} }
void upperstring(char *s)
{
while (*s) {
*s = toupper(*s);
s++;
}
}
void lowerstring(char *s)
{
while (*s) {
*s = tolower(*s);
s++;
}
}
/* Escapes Make-special chars from a string */ /* Escapes Make-special chars from a string */
static char *make_escape(const char *str) static char *make_escape(const char *str)
{ {
@@ -324,7 +281,7 @@ static void print_usage(void)
" -V, --version print RGBASM version and exit\n" " -V, --version print RGBASM version and exit\n"
" -W, --warning <warning> enable or disable warnings\n" " -W, --warning <warning> enable or disable warnings\n"
"\n" "\n"
"For help, use `man rgbasm' or go to https://rednex.github.io/rgbds/\n", "For help, use `man rgbasm' or go to https://rgbds.gbdev.io/docs/\n",
stderr); stderr);
exit(1); exit(1);
} }
@@ -345,17 +302,17 @@ int main(int argc, char *argv[])
cldefines_bufsize = cldefines_numindices * cldefine_entrysize; cldefines_bufsize = cldefines_numindices * cldefine_entrysize;
cldefines = malloc(cldefines_bufsize); cldefines = malloc(cldefines_bufsize);
if (!cldefines) if (!cldefines)
fatalerror("No memory for command line defines"); fatalerror("No memory for command line defines\n");
#if defined(YYDEBUG) && YYDEBUG #if defined(YYDEBUG) && YYDEBUG
yydebug = 1; yydebug = 1;
#endif #endif
nMaxRecursionDepth = 64;
oGeneratePhonyDeps = false; oGeneratePhonyDeps = false;
oGeneratedMissingIncludes = false; oGeneratedMissingIncludes = false;
oFailedOnMissingInclude = false; oFailedOnMissingInclude = false;
tzTargetFileName = NULL; tzTargetFileName = NULL;
uint32_t maxRecursionDepth = 64;
size_t nTargetFileNameLen = 0; size_t nTargetFileNameLen = 0;
DefaultOptions.gbgfx[0] = '0'; DefaultOptions.gbgfx[0] = '0';
@@ -434,7 +391,7 @@ int main(int argc, char *argv[])
break; break;
case 'r': case 'r':
nMaxRecursionDepth = strtoul(optarg, &ep, 0); maxRecursionDepth = strtoul(optarg, &ep, 0);
if (optarg[0] == '\0' || *ep != '\0') if (optarg[0] == '\0' || *ep != '\0')
errx(1, "Invalid argument for option 'r'"); errx(1, "Invalid argument for option 'r'");
@@ -517,8 +474,6 @@ int main(int argc, char *argv[])
tzMainfile = argv[argc - 1]; tzMainfile = argv[argc - 1];
setup_lexer();
if (verbose) if (verbose)
printf("Assembling %s\n", tzMainfile); printf("Assembling %s\n", tzMainfile);
@@ -529,20 +484,20 @@ int main(int argc, char *argv[])
fprintf(dependfile, "%s: %s\n", tzTargetFileName, tzMainfile); fprintf(dependfile, "%s: %s\n", tzTargetFileName, tzMainfile);
} }
/* Init file stack; important to do first, since it provides the file name, line, etc */
lexer_Init();
fstk_Init(tzMainfile, maxRecursionDepth);
nStartClock = clock(); nStartClock = clock();
nLineNo = 1;
nTotalLines = 0; nTotalLines = 0;
nIFDepth = 0; nIFDepth = 0;
skipElif = true;
nUnionDepth = 0;
sym_Init(); sym_Init();
sym_SetExportAll(exportall); sym_SetExportAll(exportall);
fstk_Init(tzMainfile);
opt_ParseDefines();
charmap_InitMain();
yy_set_state(LEX_STATE_NORMAL); opt_ParseDefines();
charmap_New("main", NULL);
opt_SetCurrentOptions(&DefaultOptions); opt_SetCurrentOptions(&DefaultOptions);
if (yyparse() != 0 || nbErrors != 0) if (yyparse() != 0 || nbErrors != 0)
@@ -554,10 +509,7 @@ int main(int argc, char *argv[])
errx(1, "Unterminated IF construct (%" PRIu32 " levels)!", errx(1, "Unterminated IF construct (%" PRIu32 " levels)!",
nIFDepth); nIFDepth);
if (nUnionDepth != 0) { sect_CheckUnionClosed();
errx(1, "Unterminated UNION construct (%" PRIu32 " levels)!",
nUnionDepth);
}
double timespent; double timespent;

View File

@@ -12,6 +12,7 @@
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
@@ -33,14 +34,15 @@
#include "platform.h" // strdup #include "platform.h" // strdup
struct Patch { struct Patch {
char tzFilename[_MAX_PATH + 1]; struct FileStackNode const *src;
uint32_t lineNo;
uint32_t nOffset; uint32_t nOffset;
struct Section *pcSection; struct Section *pcSection;
uint32_t pcOffset; uint32_t pcOffset;
uint8_t nType; uint8_t type;
uint32_t nRPNSize; uint32_t nRPNSize;
uint8_t *pRPN; uint8_t *pRPN;
struct Patch *pNext; struct Patch *next;
}; };
struct Assertion { struct Assertion {
@@ -62,19 +64,17 @@ static uint32_t nbSymbols = 0; /* Length of the above list */
static struct Assertion *assertions = NULL; static struct Assertion *assertions = NULL;
static struct FileStackNode *fileStackNodes = NULL;
/* /*
* Count the number of sections used in this object * Count the number of sections used in this object
*/ */
static uint32_t countsections(void) static uint32_t countsections(void)
{ {
struct Section *pSect;
uint32_t count = 0; uint32_t count = 0;
pSect = pSectionList; for (struct Section const *sect = pSectionList; sect; sect = sect->next)
while (pSect) {
count++; count++;
pSect = pSect->pNext;
}
return count; return count;
} }
@@ -82,12 +82,12 @@ static uint32_t countsections(void)
/* /*
* Count the number of patches used in this object * Count the number of patches used in this object
*/ */
static uint32_t countpatches(struct Section const *pSect) static uint32_t countpatches(struct Section const *sect)
{ {
uint32_t r = 0; uint32_t r = 0;
for (struct Patch const *patch = pSect->pPatches; patch != NULL; for (struct Patch const *patch = sect->patches; patch != NULL;
patch = patch->pNext) patch = patch->next)
r++; r++;
return r; return r;
@@ -129,24 +129,68 @@ static void fputstring(char const *s, FILE *f)
fputc(0, f); fputc(0, f);
} }
static uint32_t getNbFileStackNodes(void)
{
return fileStackNodes ? fileStackNodes->ID + 1 : 0;
}
void out_RegisterNode(struct FileStackNode *node)
{
/* If node is not already registered, register it (and parents), and give it a unique ID */
while (node->ID == -1) {
node->ID = getNbFileStackNodes();
if (node->ID == -1)
fatalerror("Reached too many file stack nodes; try splitting the file up\n");
node->next = fileStackNodes;
fileStackNodes = node;
/* Also register the node's parents */
node = node->parent;
if (!node)
break;
}
}
void out_ReplaceNode(struct FileStackNode *node)
{
(void)node;
#if 0
This is code intended to replace a node, which is pretty useless until ref counting is added...
struct FileStackNode **ptr = &fileStackNodes;
/*
* The linked list is supposed to have decrementing IDs, so iterate with less memory reads,
* to hopefully hit the cache less. A debug check is added after, in case a change is made
* that breaks this assumption.
*/
for (uint32_t i = fileStackNodes->ID; i != node->ID; i--)
ptr = &(*ptr)->next;
assert((*ptr)->ID == node->ID);
node->next = (*ptr)->next;
assert(!node->next || node->next->ID == node->ID - 1); /* Catch inconsistencies early */
/* TODO: unreference the node */
*ptr = node;
#endif
}
/* /*
* Return a section's ID * Return a section's ID
*/ */
static uint32_t getsectid(struct Section const *pSect) static uint32_t getsectid(struct Section const *sect)
{ {
struct Section const *sec; struct Section const *sec = pSectionList;
uint32_t ID = 0; uint32_t ID = 0;
sec = pSectionList;
while (sec) { while (sec) {
if (sec == pSect) if (sec == sect)
return ID; return ID;
ID++; ID++;
sec = sec->pNext; sec = sec->next;
} }
fatalerror("Unknown section '%s'", pSect->pzName); fatalerror("Unknown section '%s'\n", sect->name);
} }
static uint32_t getSectIDIfAny(struct Section const *sect) static uint32_t getSectIDIfAny(struct Section const *sect)
@@ -157,42 +201,45 @@ static uint32_t getSectIDIfAny(struct Section const *sect)
/* /*
* Write a patch to a file * Write a patch to a file
*/ */
static void writepatch(struct Patch const *pPatch, FILE *f) static void writepatch(struct Patch const *patch, FILE *f)
{ {
fputstring(pPatch->tzFilename, f); assert(patch->src->ID != -1);
fputlong(pPatch->nOffset, f);
fputlong(getSectIDIfAny(pPatch->pcSection), f); fputlong(patch->src->ID, f);
fputlong(pPatch->pcOffset, f); fputlong(patch->lineNo, f);
fputc(pPatch->nType, f); fputlong(patch->nOffset, f);
fputlong(pPatch->nRPNSize, f); fputlong(getSectIDIfAny(patch->pcSection), f);
fwrite(pPatch->pRPN, 1, pPatch->nRPNSize, f); fputlong(patch->pcOffset, f);
fputc(patch->type, f);
fputlong(patch->nRPNSize, f);
fwrite(patch->pRPN, 1, patch->nRPNSize, f);
} }
/* /*
* Write a section to a file * Write a section to a file
*/ */
static void writesection(struct Section const *pSect, FILE *f) static void writesection(struct Section const *sect, FILE *f)
{ {
fputstring(pSect->pzName, f); fputstring(sect->name, f);
fputlong(pSect->size, f); fputlong(sect->size, f);
bool isUnion = pSect->modifier == SECTION_UNION; bool isUnion = sect->modifier == SECTION_UNION;
bool isFragment = pSect->modifier == SECTION_FRAGMENT; bool isFragment = sect->modifier == SECTION_FRAGMENT;
fputc(pSect->nType | isUnion << 7 | isFragment << 6, f); fputc(sect->type | isUnion << 7 | isFragment << 6, f);
fputlong(pSect->nOrg, f); fputlong(sect->org, f);
fputlong(pSect->nBank, f); fputlong(sect->bank, f);
fputc(pSect->nAlign, f); fputc(sect->align, f);
fputlong(pSect->alignOfs, f); fputlong(sect->alignOfs, f);
if (sect_HasData(pSect->nType)) { if (sect_HasData(sect->type)) {
fwrite(pSect->tData, 1, pSect->size, f); fwrite(sect->data, 1, sect->size, f);
fputlong(countpatches(pSect), f); fputlong(countpatches(sect), f);
for (struct Patch const *patch = pSect->pPatches; patch != NULL; for (struct Patch const *patch = sect->patches; patch != NULL;
patch = patch->pNext) patch = patch->next)
writepatch(patch, f); writepatch(patch, f);
} }
} }
@@ -206,26 +253,35 @@ static void writesymbol(struct Symbol const *sym, FILE *f)
if (!sym_IsDefined(sym)) { if (!sym_IsDefined(sym)) {
fputc(SYMTYPE_IMPORT, f); fputc(SYMTYPE_IMPORT, f);
} else { } else {
assert(sym->src->ID != -1);
fputc(sym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, f); fputc(sym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, f);
fputstring(sym->fileName, f); fputlong(sym->src->ID, f);
fputlong(sym->fileLine, f); fputlong(sym->fileLine, f);
fputlong(getSectIDIfAny(sym_GetSection(sym)), f); fputlong(getSectIDIfAny(sym_GetSection(sym)), f);
fputlong(sym->value, f); fputlong(sym->value, f);
} }
} }
static void registerSymbol(struct Symbol *sym)
{
*objectSymbolsTail = sym;
objectSymbolsTail = &sym->next;
out_RegisterNode(sym->src);
if (nbSymbols == -1)
fatalerror("Registered too many symbols (%" PRIu32
"); try splitting up your files\n", (uint32_t)-1);
sym->ID = nbSymbols++;
}
/* /*
* Returns a symbol's ID within the object file * Returns a symbol's ID within the object file
* If the symbol does not have one, one is assigned by registering the symbol * If the symbol does not have one, one is assigned by registering the symbol
*/ */
static uint32_t getSymbolID(struct Symbol *sym) static uint32_t getSymbolID(struct Symbol *sym)
{ {
if (sym->ID == -1) { if (sym->ID == -1 && !sym_IsPC(sym))
sym->ID = nbSymbols++; registerSymbol(sym);
*objectSymbolsTail = sym;
objectSymbolsTail = &sym->next;
}
return sym->ID; return sym->ID;
} }
@@ -303,31 +359,45 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
/* /*
* Allocate a new patch structure and link it into the list * Allocate a new patch structure and link it into the list
* WARNING: all patches are assumed to eventually be written, so the file stack node is registered
*/ */
static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, uint32_t ofs)
uint32_t ofs)
{ {
struct Patch *pPatch = malloc(sizeof(struct Patch)); struct Patch *patch = malloc(sizeof(struct Patch));
uint32_t rpnSize = expr->isKnown ? 5 : expr->nRPNPatchSize;
struct FileStackNode *node = fstk_GetFileStack();
if (!pPatch) if (!patch)
fatalerror("No memory for patch: %s", strerror(errno)); fatalerror("No memory for patch: %s\n", strerror(errno));
pPatch->pRPN = malloc(sizeof(*pPatch->pRPN) * expr->nRPNPatchSize);
if (!pPatch->pRPN) patch->pRPN = malloc(sizeof(*patch->pRPN) * rpnSize);
fatalerror("No memory for patch's RPN expression: %s", if (!patch->pRPN)
strerror(errno)); fatalerror("No memory for patch's RPN expression: %s\n", strerror(errno));
pPatch->nRPNSize = 0; patch->type = type;
pPatch->nType = type; patch->src = node;
fstk_DumpToStr(pPatch->tzFilename, sizeof(pPatch->tzFilename)); out_RegisterNode(node);
pPatch->nOffset = ofs; patch->lineNo = lexer_GetLineNo();
pPatch->pcSection = sect_GetSymbolSection(); patch->nOffset = ofs;
pPatch->pcOffset = curOffset; patch->pcSection = sect_GetSymbolSection();
patch->pcOffset = sect_GetSymbolOffset();
writerpn(pPatch->pRPN, &pPatch->nRPNSize, expr->tRPN, expr->nRPNLength); /* If the expression's value is known, output a constant RPN expression directly */
assert(pPatch->nRPNSize == expr->nRPNPatchSize); if (expr->isKnown) {
patch->nRPNSize = rpnSize;
/* Make sure to update `rpnSize` above if modifying this! */
patch->pRPN[0] = RPN_CONST;
patch->pRPN[1] = (uint32_t)(expr->nVal) & 0xFF;
patch->pRPN[2] = (uint32_t)(expr->nVal) >> 8;
patch->pRPN[3] = (uint32_t)(expr->nVal) >> 16;
patch->pRPN[4] = (uint32_t)(expr->nVal) >> 24;
} else {
patch->nRPNSize = 0;
writerpn(patch->pRPN, &patch->nRPNSize, expr->tRPN, expr->nRPNLength);
}
assert(patch->nRPNSize == rpnSize);
return pPatch; return patch;
} }
/* /*
@@ -335,10 +405,10 @@ static struct Patch *allocpatch(uint32_t type, struct Expression const *expr,
*/ */
void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs) void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs)
{ {
struct Patch *pPatch = allocpatch(type, expr, ofs); struct Patch *patch = allocpatch(type, expr, ofs);
pPatch->pNext = pCurrentSection->pPatches; patch->next = pCurrentSection->patches;
pCurrentSection->pPatches = pPatch; pCurrentSection->patches = patch;
} }
/** /**
@@ -371,13 +441,28 @@ static void writeassert(struct Assertion *assert, FILE *f)
fputstring(assert->message, f); fputstring(assert->message, f);
} }
static void writeFileStackNode(struct FileStackNode const *node, FILE *f)
{
fputlong(node->parent ? node->parent->ID : -1, f);
fputlong(node->lineNo, f);
fputc(node->type, f);
if (node->type != NODE_REPT) {
fputstring(((struct FileStackNamedNode const *)node)->name, f);
} else {
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
fputlong(reptNode->reptDepth, f);
/* Iters are stored by decreasing depth, so reverse the order for output */
for (uint32_t i = reptNode->reptDepth; i--; )
fputlong(reptNode->iters[i], f);
}
}
static void registerExportedSymbol(struct Symbol *symbol, void *arg) static void registerExportedSymbol(struct Symbol *symbol, void *arg)
{ {
(void)arg; (void)arg;
if (sym_IsExported(symbol) && symbol->ID == -1) { if (sym_IsExported(symbol) && symbol->ID == -1) {
*objectSymbolsTail = symbol; registerSymbol(symbol);
objectSymbolsTail = &symbol->next;
nbSymbols++;
} }
} }
@@ -400,10 +485,19 @@ void out_WriteObject(void)
fputlong(nbSymbols, f); fputlong(nbSymbols, f);
fputlong(countsections(), f); fputlong(countsections(), f);
fputlong(getNbFileStackNodes(), f);
for (struct FileStackNode const *node = fileStackNodes; node; node = node->next) {
writeFileStackNode(node, f);
if (node->next && node->next->ID != node->ID - 1)
fatalerror("Internal error: fstack node #%" PRIu32 " follows #%" PRIu32
". Please report this to the developers!\n",
node->next->ID, node->ID);
}
for (struct Symbol const *sym = objectSymbols; sym; sym = sym->next) for (struct Symbol const *sym = objectSymbols; sym; sym = sym->next)
writesymbol(sym, f); writesymbol(sym, f);
for (struct Section *sect = pSectionList; sect; sect = sect->pNext) for (struct Section *sect = pSectionList; sect; sect = sect->next)
writesection(sect, f); writesection(sect, f);
fputlong(countasserts(), f); fputlong(countasserts(), f);

View File

@@ -112,9 +112,9 @@ from erroring out when dependency files are deleted.
Add a target to the rules emitted by Add a target to the rules emitted by
.Fl M . .Fl M .
The exact string provided will be written, including spaces and special characters. The exact string provided will be written, including spaces and special characters.
.Dl Fl MT fileA Fl MT fileB .Dl Fl MT No fileA Fl MT No fileB
is equivalent to is equivalent to
.Dl Fl MT 'fileA fileB' . .Dl Fl MT No 'fileA fileB' .
If neither this nor If neither this nor
.Fl MQ .Fl MQ
is specified, the output file name is used. is specified, the output file name is used.
@@ -203,6 +203,10 @@ Warn about incorrect arguments to built-in functions, such as
with indexes outside of the string's bounds. with indexes outside of the string's bounds.
This warning is enabled by This warning is enabled by
.Fl Wall . .Fl Wall .
.It Fl Wcharmap-redef
Warn when re-defining a charmap mapping.
This warning is enabled by
.Fl Wall .
.It Fl Wdiv .It Fl Wdiv
Warn when dividing the smallest negative integer by -1, which yields itself due to integer overflow. Warn when dividing the smallest negative integer by -1, which yields itself due to integer overflow.
.It Fl Wempty-entry .It Fl Wempty-entry
@@ -260,7 +264,7 @@ and then
.Xr rgbfix 1 . .Xr rgbfix 1 .
.Sh BUGS .Sh BUGS
Please report bugs on Please report bugs on
.Lk https://github.com/rednex/rgbds/issues GitHub . .Lk https://github.com/gbdev/rgbds/issues GitHub .
.Sh SEE ALSO .Sh SEE ALSO
.Xr rgbasm 5 , .Xr rgbasm 5 ,
.Xr rgbfix 1 , .Xr rgbfix 1 ,
@@ -272,4 +276,4 @@ Please report bugs on
.Nm .Nm
was originally written by Carsten S\(/orensen as part of the ASMotor package, and was later packaged in RGBDS by Justin Lloyd. was originally written by Carsten S\(/orensen as part of the ASMotor package, and was later packaged in RGBDS by Justin Lloyd.
It is now maintained by a number of contributors at It is now maintained by a number of contributors at
.Lk https://github.com/rednex/rgbds . .Lk https://github.com/gbdev/rgbds .

View File

@@ -283,7 +283,7 @@ Most of them return a string, however some of these functions actually return an
.It Sy Name Ta Sy Operation .It Sy Name Ta Sy Operation
.It Fn STRLEN string Ta Returns the number of characters in Ar string . .It Fn STRLEN string Ta Returns the number of characters in Ar string .
.It Fn STRCAT str1 str2 Ta Appends Ar str2 No to Ar str1 . .It Fn STRCAT str1 str2 Ta Appends Ar str2 No to Ar str1 .
.It Fn STRCMP str1 str2 Ta Returns negative if Ar str1 No is alphabetically lower than Ar str2 No , zero if they match, positive if Ar str1 No is greater than Ar str2 . .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 position of Ar str2 No in Ar str1 No or zero if it's not present Pq first character is position 1 . .It Fn STRIN str1 str2 Ta Returns the 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 Po first character is position 1 Pc and Ar len No characters long. .It Fn STRSUB str pos len Ta Returns a substring from Ar str No starting at Ar pos Po first character is position 1 Pc and Ar len No characters long.
.It Fn STRUPR str Ta Converts all characters in Ar str No to capitals and returns the new string. .It Fn STRUPR str Ta Converts all characters in Ar str No to capitals and returns the new string.
@@ -681,6 +681,11 @@ Similarly, the size of an unionized section is the largest of all its declaratio
.Ss Section Fragments .Ss Section Fragments
Section fragments are sections with a small twist: when several of the same name are encountered, they are concatenated instead of producing an error. Section fragments are sections with a small twist: when several of the same name are encountered, they are concatenated instead of producing an error.
This works within the same file (paralleling the behavior "plain" sections has in previous versions), but also across object files. This works within the same file (paralleling the behavior "plain" sections has in previous versions), but also across object files.
To declare an section fragment, add a
.Ic FRAGMENT
keyword after the
.Ic SECTION
one; the declaration is otherwise not different.
However, similarly to However, similarly to
.Sx Unionized Sections , .Sx Unionized Sections ,
some rules must be followed: some rules must be followed:
@@ -743,7 +748,7 @@ Symbol names can contain letters, numbers, underscores, hashes and
However, they must begin with either a letter, a number, or an underscore. However, they must begin with either a letter, a number, or an underscore.
Periods are allowed exclusively for labels, as described below. Periods are allowed exclusively for labels, as described below.
A symbol cannot have the same name as a reserved keyword. A symbol cannot have the same name as a reserved keyword.
.Em \&In the line where a symbol is defined there mustn't be any whitespace before it , .Em \&In the line where a symbol is defined there must not be any whitespace before it ,
otherwise otherwise
.Nm .Nm
will treat it as a macro invocation. will treat it as a macro invocation.
@@ -964,7 +969,7 @@ The following symbols are defined by the assembler:
.Pp .Pp
.Bl -column -offset indent "EQUS" "__ISO_8601_LOCAL__" .Bl -column -offset indent "EQUS" "__ISO_8601_LOCAL__"
.It Sy Type Ta Sy Name Ta Sy Contents .It Sy Type Ta Sy Name Ta Sy Contents
.It Ic EQU Ta Dv @ Ta PC value .It Ic EQU Ta Dv @ Ta PC value (essentially, the current memory address)
.It Ic EQU Ta Dv _PI Ta Fixed point \[*p] .It Ic EQU Ta Dv _PI Ta Fixed point \[*p]
.It Ic SET Ta Dv _RS Ta _RS Counter .It Ic SET Ta Dv _RS Ta _RS Counter
.It Ic EQU Ta Dv _NARG Ta Number of arguments passed to macro .It Ic EQU Ta Dv _NARG Ta Number of arguments passed to macro
@@ -1080,6 +1085,8 @@ The example below includes 256 bytes from data.bin, starting from byte 78.
.Bd -literal -offset indent .Bd -literal -offset indent
INCBIN "data.bin",78,256 INCBIN "data.bin",78,256
.Ed .Ed
.Pp
The length argument is optional. If only the start position is specified, the bytes from the start position until the end of the file will be included.
.Ss Unions .Ss Unions
.Pp .Pp
Unions allow multiple memory allocations to overlap, like unions in C. Unions allow multiple memory allocations to overlap, like unions in C.
@@ -1423,7 +1430,7 @@ immediately aborts.
Use Use
.Ic INCLUDE .Ic INCLUDE
to process another assembler file and then return to the current file when done. 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 (see the
.Fl i .Fl i
option in option in
.Xr rgbasm 1 ) .Xr rgbasm 1 )
@@ -1548,4 +1555,4 @@ is a shorthand for
was originally written by Carsten S\(/orensen as part of the ASMotor package, was originally written by Carsten S\(/orensen as part of the ASMotor package,
and was later packaged in RGBDS by Justin Lloyd. and was later packaged in RGBDS by Justin Lloyd.
It is now maintained by a number of contributors at It is now maintained by a number of contributors at
.Lk https://github.com/rednex/rgbds . .Lk https://github.com/gbdev/rgbds .

View File

@@ -31,7 +31,7 @@
/* If we had `asprintf` this would be great, but alas. */ \ /* If we had `asprintf` this would be great, but alas. */ \
_expr->reason = malloc(128); /* Use an initial reasonable size */ \ _expr->reason = malloc(128); /* Use an initial reasonable size */ \
if (!_expr->reason) \ if (!_expr->reason) \
fatalerror("Can't allocate err string: %s", strerror(errno)); \ fatalerror("Can't allocate err string: %s\n", strerror(errno)); \
int size = snprintf(_expr->reason, 128, __VA_ARGS__); \ int size = snprintf(_expr->reason, 128, __VA_ARGS__); \
if (size >= 128) { /* If this wasn't enough, try again */ \ if (size >= 128) { /* If this wasn't enough, try again */ \
_expr->reason = realloc(_expr->reason, size + 1); \ _expr->reason = realloc(_expr->reason, size + 1); \
@@ -51,8 +51,8 @@ static uint8_t *reserveSpace(struct Expression *expr, uint32_t size)
* To avoid generating humongous object files, cap the * To avoid generating humongous object files, cap the
* size of RPN expressions * size of RPN expressions
*/ */
fatalerror("RPN expression cannot grow larger than %lu bytes", fatalerror("RPN expression cannot grow larger than "
(unsigned long)MAXRPNLEN); EXPAND_AND_STR(MAXRPNLEN) " bytes\n");
else if (expr->nRPNCapacity > MAXRPNLEN / 2) else if (expr->nRPNCapacity > MAXRPNLEN / 2)
expr->nRPNCapacity = MAXRPNLEN; expr->nRPNCapacity = MAXRPNLEN;
else else
@@ -60,8 +60,7 @@ static uint8_t *reserveSpace(struct Expression *expr, uint32_t size)
expr->tRPN = realloc(expr->tRPN, expr->nRPNCapacity); expr->tRPN = realloc(expr->tRPN, expr->nRPNCapacity);
if (!expr->tRPN) if (!expr->tRPN)
fatalerror("Failed to grow RPN expression: %s", fatalerror("Failed to grow RPN expression: %s\n", strerror(errno));
strerror(errno));
} }
uint8_t *ptr = expr->tRPN + expr->nRPNLength; uint8_t *ptr = expr->tRPN + expr->nRPNLength;
@@ -108,17 +107,15 @@ void rpn_Symbol(struct Expression *expr, char *tzSym)
struct Symbol *sym = sym_FindSymbol(tzSym); struct Symbol *sym = sym_FindSymbol(tzSym);
if (sym_IsPC(sym) && !sect_GetSymbolSection()) { if (sym_IsPC(sym) && !sect_GetSymbolSection()) {
yyerror("PC has no value outside a section"); error("PC has no value outside a section\n");
rpn_Number(expr, 0); rpn_Number(expr, 0);
} else if (!sym || !sym_IsConstant(sym)) { } else if (!sym || !sym_IsConstant(sym)) {
rpn_Init(expr); rpn_Init(expr);
expr->isSymbol = true; expr->isSymbol = true;
sym_Ref(tzSym); sym_Ref(tzSym);
makeUnknown(expr, sym_IsPC(sym) makeUnknown(expr, sym_IsPC(sym) ? "PC is not constant at assembly time"
? "PC is not constant at assembly time" : "'%s' is not constant at assembly time", tzSym);
: "'%s' is not constant at assembly time",
tzSym);
expr->nRPNPatchSize += 5; /* 1-byte opcode + 4-byte symbol ID */ expr->nRPNPatchSize += 5; /* 1-byte opcode + 4-byte symbol ID */
size_t nameLen = strlen(tzSym) + 1; /* Don't forget NUL! */ size_t nameLen = strlen(tzSym) + 1; /* Don't forget NUL! */
@@ -145,14 +142,14 @@ void rpn_BankSelf(struct Expression *expr)
rpn_Init(expr); rpn_Init(expr);
if (!pCurrentSection) { if (!pCurrentSection) {
yyerror("PC has no bank outside a section"); error("PC has no bank outside a section\n");
expr->nVal = 1; expr->nVal = 1;
} else if (pCurrentSection->nBank == -1) { } else if (pCurrentSection->bank == -1) {
makeUnknown(expr, "Current section's bank is not known"); makeUnknown(expr, "Current section's bank is not known");
expr->nRPNPatchSize++; expr->nRPNPatchSize++;
*reserveSpace(expr, 1) = RPN_BANK_SELF; *reserveSpace(expr, 1) = RPN_BANK_SELF;
} else { } else {
expr->nVal = pCurrentSection->nBank; expr->nVal = pCurrentSection->bank;
} }
} }
@@ -168,16 +165,16 @@ void rpn_BankSymbol(struct Expression *expr, char const *tzSym)
rpn_Init(expr); rpn_Init(expr);
if (sym && !sym_IsLabel(sym)) { if (sym && !sym_IsLabel(sym)) {
yyerror("BANK argument must be a label"); error("BANK argument must be a label\n");
} else { } else {
sym_Ref(tzSym); sym_Ref(tzSym);
if (!sym) if (!sym)
/* If the symbol didn't exist, `sym_Ref` created it */ /* If the symbol didn't exist, `sym_Ref` created it */
sym = sym_FindSymbol(tzSym); sym = sym_FindSymbol(tzSym);
if (sym_GetSection(sym) && sym_GetSection(sym)->nBank != -1) { if (sym_GetSection(sym) && sym_GetSection(sym)->bank != -1) {
/* Symbol's section is known and bank is fixed */ /* Symbol's section is known and bank is fixed */
expr->nVal = sym_GetSection(sym)->nBank; expr->nVal = sym_GetSection(sym)->bank;
} else { } else {
makeUnknown(expr, "\"%s\"'s bank is not known", tzSym); makeUnknown(expr, "\"%s\"'s bank is not known", tzSym);
expr->nRPNPatchSize += 5; /* opcode + 4-byte sect ID */ expr->nRPNPatchSize += 5; /* opcode + 4-byte sect ID */
@@ -196,8 +193,8 @@ void rpn_BankSection(struct Expression *expr, char const *tzSectionName)
struct Section *pSection = out_FindSectionByName(tzSectionName); struct Section *pSection = out_FindSectionByName(tzSectionName);
if (pSection && pSection->nBank != -1) { if (pSection && pSection->bank != -1) {
expr->nVal = pSection->nBank; expr->nVal = pSection->bank;
} else { } else {
makeUnknown(expr, "Section \"%s\"'s bank is not known", makeUnknown(expr, "Section \"%s\"'s bank is not known",
tzSectionName); tzSectionName);
@@ -223,8 +220,7 @@ void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src)
/* That range is valid, but only keep the lower byte */ /* That range is valid, but only keep the lower byte */
expr->nVal &= 0xFF; expr->nVal &= 0xFF;
} else if (expr->nVal < 0 || expr->nVal > 0xFF) { } else if (expr->nVal < 0 || expr->nVal > 0xFF) {
yyerror("Source address $%" PRIx32 " not between $FF00 to $FFFF", error("Source address $%" PRIx32 " not between $FF00 to $FFFF\n", expr->nVal);
expr->nVal);
} }
} }
@@ -235,8 +231,7 @@ void rpn_CheckRST(struct Expression *expr, const struct Expression *src)
if (rpn_isKnown(expr)) { if (rpn_isKnown(expr)) {
/* A valid RST address must be masked with 0x38 */ /* A valid RST address must be masked with 0x38 */
if (expr->nVal & ~0x38) if (expr->nVal & ~0x38)
yyerror("Invalid address $%" PRIx32 " for RST", error("Invalid address $%" PRIx32 " for RST\n", expr->nVal);
expr->nVal);
/* The target is in the "0x38" bits, all other bits are set */ /* The target is in the "0x38" bits, all other bits are set */
expr->nVal |= 0xC7; expr->nVal |= 0xC7;
} else { } else {
@@ -263,8 +258,8 @@ static int32_t shift(int32_t shiftee, int32_t amount)
if (amount >= 0) { if (amount >= 0) {
// Left shift // Left shift
if (amount >= 32) { if (amount >= 32) {
warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %" PRId32, warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %"
amount); PRId32 "\n", amount);
return 0; return 0;
} else { } else {
@@ -279,8 +274,8 @@ static int32_t shift(int32_t shiftee, int32_t amount)
// Right shift // Right shift
amount = -amount; amount = -amount;
if (amount >= 32) { if (amount >= 32) {
warning(WARNING_SHIFT_AMOUNT, "Shifting right by large amount %" PRId32, warning(WARNING_SHIFT_AMOUNT,
amount); "Shifting right by large amount %" PRId32 "\n", amount);
return shiftee < 0 ? -1 : 0; return shiftee < 0 ? -1 : 0;
} else if (shiftee >= 0) { } else if (shiftee >= 0) {
@@ -297,25 +292,28 @@ static int32_t shift(int32_t shiftee, int32_t amount)
} }
} }
static struct Symbol const *symbolOf(struct Expression const *expr) struct Symbol const *rpn_SymbolOf(struct Expression const *expr)
{ {
if (!rpn_isSymbol(expr)) if (!rpn_isSymbol(expr))
return NULL; return NULL;
return sym_FindSymbol((char *)expr->tRPN + 1); return sym_FindSymbol((char *)expr->tRPN + 1);
} }
bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *sym)
{
/* Check if both expressions only refer to a single symbol */
struct Symbol const *sym1 = rpn_SymbolOf(src);
if (!sym1 || !sym || sym1->type != SYM_LABEL || sym->type != SYM_LABEL)
return false;
return sym_GetSection(sym1) == sym_GetSection(sym);
}
static bool isDiffConstant(struct Expression const *src1, static bool isDiffConstant(struct Expression const *src1,
struct Expression const *src2) struct Expression const *src2)
{ {
/* Check if both expressions only refer to a single symbol */ return rpn_IsDiffConstant(src1, rpn_SymbolOf(src2));
struct Symbol const *symbol1 = symbolOf(src1);
struct Symbol const *symbol2 = symbolOf(src2);
if (!symbol1 || !symbol2
|| symbol1->type != SYM_LABEL || symbol2->type != SYM_LABEL)
return false;
return sym_GetSection(symbol1) == sym_GetSection(symbol2);
} }
void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr, void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
@@ -373,18 +371,20 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
break; break;
case RPN_SHL: case RPN_SHL:
if (src2->nVal < 0) if (src2->nVal < 0)
warning(WARNING_SHIFT_AMOUNT, "Shifting left by negative amount %" PRId32, warning(WARNING_SHIFT_AMOUNT,
"Shifting left by negative amount %" PRId32 "\n",
src2->nVal); src2->nVal);
expr->nVal = shift(src1->nVal, src2->nVal); expr->nVal = shift(src1->nVal, src2->nVal);
break; break;
case RPN_SHR: case RPN_SHR:
if (src1->nVal < 0) if (src1->nVal < 0)
warning(WARNING_SHIFT, "Shifting negative value %" PRId32, warning(WARNING_SHIFT, "Shifting negative value %" PRId32 "\n",
src1->nVal); src1->nVal);
if (src2->nVal < 0) if (src2->nVal < 0)
warning(WARNING_SHIFT_AMOUNT, "Shifting right by negative amount %" PRId32, warning(WARNING_SHIFT_AMOUNT,
"Shifting right by negative amount %" PRId32 "\n",
src2->nVal); src2->nVal);
expr->nVal = shift(src1->nVal, -src2->nVal); expr->nVal = shift(src1->nVal, -src2->nVal);
@@ -394,11 +394,11 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
break; break;
case RPN_DIV: case RPN_DIV:
if (src2->nVal == 0) if (src2->nVal == 0)
fatalerror("Division by zero"); fatalerror("Division by zero\n");
if (src1->nVal == INT32_MIN if (src1->nVal == INT32_MIN && src2->nVal == -1) {
&& src2->nVal == -1) { warning(WARNING_DIV, "Division of %" PRId32 " by -1 yields %"
warning(WARNING_DIV, "Division of min value by -1"); PRId32 "\n", INT32_MIN, INT32_MIN);
expr->nVal = INT32_MIN; expr->nVal = INT32_MIN;
} else { } else {
expr->nVal = src1->nVal / src2->nVal; expr->nVal = src1->nVal / src2->nVal;
@@ -406,7 +406,7 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
break; break;
case RPN_MOD: case RPN_MOD:
if (src2->nVal == 0) if (src2->nVal == 0)
fatalerror("Division by zero"); fatalerror("Division by zero\n");
if (src1->nVal == INT32_MIN && src2->nVal == -1) if (src1->nVal == INT32_MIN && src2->nVal == -1)
expr->nVal = 0; expr->nVal = 0;
@@ -424,12 +424,12 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
case RPN_RST: case RPN_RST:
case RPN_CONST: case RPN_CONST:
case RPN_SYM: case RPN_SYM:
fatalerror("%d is not a binary operator", op); fatalerror("%d is not a binary operator\n", op);
} }
} else if (op == RPN_SUB && isDiffConstant(src1, src2)) { } else if (op == RPN_SUB && isDiffConstant(src1, src2)) {
struct Symbol const *symbol1 = symbolOf(src1); struct Symbol const *symbol1 = rpn_SymbolOf(src1);
struct Symbol const *symbol2 = symbolOf(src2); struct Symbol const *symbol2 = rpn_SymbolOf(src2);
expr->nVal = sym_GetValue(symbol1) - sym_GetValue(symbol2); expr->nVal = sym_GetValue(symbol1) - sym_GetValue(symbol2);
expr->isKnown = true; expr->isKnown = true;

View File

@@ -17,15 +17,22 @@
#include "platform.h" // strdup #include "platform.h" // strdup
struct SectionStackEntry { struct SectionStackEntry {
struct Section *pSection; struct Section *section;
struct Symbol *pScope; /* Section's symbol scope */ char const *scope; /* Section's symbol scope */
uint32_t offset; uint32_t offset;
struct SectionStackEntry *pNext; struct SectionStackEntry *next;
}; };
struct SectionStackEntry *pSectionStack; struct SectionStackEntry *sectionStack;
uint32_t curOffset; /* Offset into the current section (see sect_GetSymbolOffset) */
static struct Section *currentLoadSection = NULL; static struct Section *currentLoadSection = NULL;
uint32_t loadOffset = 0; /* The offset of the LOAD section within its parent */ uint32_t loadOffset; /* The offset of the LOAD section within its parent */
struct UnionStackEntry {
uint32_t start;
uint32_t size;
struct UnionStackEntry *next;
} *unionStack = NULL;
/* /*
* A quick check to see if we have an initialized section * A quick check to see if we have an initialized section
@@ -33,7 +40,7 @@ uint32_t loadOffset = 0; /* The offset of the LOAD section within its parent */
static inline void checksection(void) static inline void checksection(void)
{ {
if (pCurrentSection == NULL) if (pCurrentSection == NULL)
fatalerror("Code generation before SECTION directive"); fatalerror("Code generation before SECTION directive\n");
} }
/* /*
@@ -44,41 +51,45 @@ static inline void checkcodesection(void)
{ {
checksection(); checksection();
if (!sect_HasData(pCurrentSection->nType)) if (!sect_HasData(pCurrentSection->type))
fatalerror("Section '%s' cannot contain code or data (not ROM0 or ROMX)", fatalerror("Section '%s' cannot contain code or data (not ROM0 or ROMX)\n",
pCurrentSection->pzName); pCurrentSection->name);
else if (nUnionDepth > 0) }
fatalerror("UNIONs cannot contain code or data");
static inline void checkSectionSize(struct Section const *sect, uint32_t size)
{
uint32_t maxSize = maxsize[sect->type];
if (size > maxSize)
fatalerror("Section '%s' grew too big (max size = 0x%" PRIX32
" bytes, reached 0x%" PRIX32 ").\n", sect->name, maxSize, size);
} }
/* /*
* Check if the section has grown too much. * Check if the section has grown too much.
*/ */
static void reserveSpace(uint32_t delta_size) static inline void reserveSpace(uint32_t delta_size)
{ {
uint32_t maxSize = maxsize[pCurrentSection->nType];
uint32_t newSize = curOffset + delta_size;
/* /*
* This check is here to trap broken code that generates sections that * This check is here to trap broken code that generates sections that
* are too big and to prevent the assembler from generating huge object * are too big and to prevent the assembler from generating huge object
* files or trying to allocate too much memory. * files or trying to allocate too much memory.
* A check at the linking stage is still necessary. * A check at the linking stage is still necessary.
*/ */
if (newSize > maxSize) checkSectionSize(pCurrentSection, curOffset + loadOffset + delta_size);
fatalerror("Section '%s' is too big (max size = 0x%" PRIX32 " bytes, reached 0x%" PRIX32 ").", if (currentLoadSection)
pCurrentSection->pzName, maxSize, newSize); checkSectionSize(currentLoadSection, curOffset + delta_size);
} }
struct Section *out_FindSectionByName(const char *pzName) struct Section *out_FindSectionByName(const char *name)
{ {
struct Section *pSect = pSectionList; struct Section *sect = pSectionList;
while (pSect) { while (sect) {
if (strcmp(pzName, pSect->pzName) == 0) if (strcmp(name, sect->name) == 0)
return pSect; return sect;
pSect = pSect->pNext; sect = sect->next;
} }
return NULL; return NULL;
@@ -87,7 +98,7 @@ struct Section *out_FindSectionByName(const char *pzName)
/* /*
* Find a section by name and type. If it doesn't exist, create it * Find a section by name and type. If it doesn't exist, create it
*/ */
static struct Section *getSection(char const *pzName, enum SectionType type, static struct Section *getSection(char const *name, enum SectionType type,
uint32_t org, struct SectionSpec const *attrs, uint32_t org, struct SectionSpec const *attrs,
enum SectionModifier mod) enum SectionModifier mod)
{ {
@@ -99,16 +110,16 @@ static struct Section *getSection(char const *pzName, enum SectionType type,
if (bank != -1) { if (bank != -1) {
if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM
&& type != SECTTYPE_SRAM && type != SECTTYPE_WRAMX) && type != SECTTYPE_SRAM && type != SECTTYPE_WRAMX)
yyerror("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections"); error("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections\n");
else if (bank < bankranges[type][0] else if (bank < bankranges[type][0]
|| bank > bankranges[type][1]) || bank > bankranges[type][1])
yyerror("%s bank value $%" PRIx32 " out of range ($%" PRIx32 " to $%" PRIx32 ")", error("%s bank value $%" PRIx32 " out of range ($%" PRIx32 " to $%"
typeNames[type], bank, PRIx32 ")\n", typeNames[type], bank,
bankranges[type][0], bankranges[type][1]); bankranges[type][0], bankranges[type][1]);
} }
if (alignOffset >= 1 << alignment) { if (alignOffset >= 1 << alignment) {
yyerror("Alignment offset must not be greater than alignment (%" PRIu16 " < %u)", error("Alignment offset must not be greater than alignment (%" PRIu16 " < %u)\n",
alignOffset, 1U << alignment); alignOffset, 1U << alignment);
alignOffset = 0; alignOffset = 0;
} }
@@ -119,41 +130,42 @@ static struct Section *getSection(char const *pzName, enum SectionType type,
if (org != -1) { if (org != -1) {
if ((org - alignOffset) & mask) if ((org - alignOffset) & mask)
yyerror("Section \"%s\"'s fixed address doesn't match its alignment", error("Section \"%s\"'s fixed address doesn't match its alignment\n",
pzName); name);
alignment = 0; /* Ignore it if it's satisfied */ alignment = 0; /* Ignore it if it's satisfied */
} else if (startaddr[type] & mask) { } else if (startaddr[type] & mask) {
yyerror("Section \"%s\"'s alignment cannot be attained in %s", error("Section \"%s\"'s alignment cannot be attained in %s\n",
pzName, typeNames[type]); name, typeNames[type]);
} }
} }
if (org != -1) { if (org != -1) {
if (org < startaddr[type] || org > endaddr(type)) if (org < startaddr[type] || org > endaddr(type))
yyerror("Section \"%s\"'s fixed address %#" PRIx32 " is outside of range [%#" PRIx16 "; %#" PRIx16 "]", error("Section \"%s\"'s fixed address %#" PRIx32
pzName, org, startaddr[type], endaddr(type)); " is outside of range [%#" PRIx16 "; %#" PRIx16 "]\n",
name, org, startaddr[type], endaddr(type));
} }
if (nbbanks(type) == 1) if (nbbanks(type) == 1)
bank = bankranges[type][0]; bank = bankranges[type][0];
struct Section *pSect = out_FindSectionByName(pzName); struct Section *sect = out_FindSectionByName(name);
if (pSect) { if (sect) {
unsigned int nbSectErrors = 0; unsigned int nbSectErrors = 0;
#define fail(...) \ #define fail(...) \
do { \ do { \
yyerror(__VA_ARGS__); \ error(__VA_ARGS__); \
nbSectErrors++; \ nbSectErrors++; \
} while (0) } while (0)
if (type != pSect->nType) if (type != sect->type)
fail("Section \"%s\" already exists but with type %s", fail("Section \"%s\" already exists but with type %s\n",
pSect->pzName, typeNames[pSect->nType]); sect->name, typeNames[sect->type]);
if (pSect->modifier != mod) if (sect->modifier != mod)
fail("Section \"%s\" already declared as %s section", fail("Section \"%s\" already declared as %s section\n",
pSect->pzName, sectionModNames[pSect->modifier]); sect->name, sectionModNames[sect->modifier]);
/* /*
* Normal sections need to have exactly identical constraints; * Normal sections need to have exactly identical constraints;
* but unionized sections only need "compatible" constraints, * but unionized sections only need "compatible" constraints,
@@ -165,128 +177,127 @@ static struct Section *getSection(char const *pzName, enum SectionType type,
* `EndLoadSection` if modifying the following check! * `EndLoadSection` if modifying the following check!
*/ */
if (sect_HasData(type)) if (sect_HasData(type))
fail("Cannot declare ROM sections as UNION"); fail("Cannot declare ROM sections as UNION\n");
if (org != -1) { if (org != -1) {
/* If both are fixed, they must be the same */ /* If both are fixed, they must be the same */
if (pSect->nOrg != -1 && pSect->nOrg != org) if (sect->org != -1 && sect->org != org)
fail("Section \"%s\" already declared as fixed at different address $%" PRIx32, fail("Section \"%s\" already declared as fixed at different address $%"
pSect->pzName, pSect->nOrg); PRIx32 "\n",
else if (pSect->nAlign != 0 sect->name, sect->org);
&& (mask(pSect->nAlign) else if (sect->align != 0
& (org - pSect->alignOfs))) && (mask(sect->align)
fail("Section \"%s\" already declared as aligned to %u bytes (offset %" PRIu16 ")", & (org - sect->alignOfs)))
pSect->pzName, 1U << pSect->nAlign, fail("Section \"%s\" already declared as aligned to %u bytes (offset %"
pSect->alignOfs); PRIu16 ")\n", sect->name, 1U << sect->align, sect->alignOfs);
else else
/* Otherwise, just override */ /* Otherwise, just override */
pSect->nOrg = org; sect->org = org;
} else if (alignment != 0) { } else if (alignment != 0) {
/* Make sure any fixed address is compatible */ /* Make sure any fixed address is compatible */
if (pSect->nOrg != -1) { if (sect->org != -1) {
if ((pSect->nOrg - alignOffset) if ((sect->org - alignOffset)
& mask(alignment)) & mask(alignment))
fail("Section \"%s\" already declared as fixed at incompatible address $%" PRIx32, fail("Section \"%s\" already declared as fixed at incompatible address $%"
pSect->pzName, PRIx32 "\n", sect->name, sect->org);
pSect->nOrg);
/* Check if alignment offsets are compatible */ /* Check if alignment offsets are compatible */
} else if ((alignOffset & mask(pSect->nAlign)) } else if ((alignOffset & mask(sect->align))
!= (pSect->alignOfs != (sect->alignOfs
& mask(alignment))) { & mask(alignment))) {
fail("Section \"%s\" already declared with incompatible %" PRIu8 "-byte alignment (offset %" PRIu16 ")", fail("Section \"%s\" already declared with incompatible %"
pSect->pzName, pSect->nAlign, PRIu8 "-byte alignment (offset %" PRIu16 ")\n",
pSect->alignOfs); sect->name, sect->align, sect->alignOfs);
} else if (alignment > pSect->nAlign) { } else if (alignment > sect->align) {
/* /*
* If the section is not fixed, * If the section is not fixed,
* its alignment is the largest of both * its alignment is the largest of both
*/ */
pSect->nAlign = alignment; sect->align = alignment;
pSect->alignOfs = alignOffset; sect->alignOfs = alignOffset;
} }
} }
/* If the section's bank is unspecified, override it */ /* If the section's bank is unspecified, override it */
if (pSect->nBank == -1) if (sect->bank == -1)
pSect->nBank = bank; sect->bank = bank;
/* If both specify a bank, it must be the same one */ /* If both specify a bank, it must be the same one */
else if (bank != -1 && pSect->nBank != bank) else if (bank != -1 && sect->bank != bank)
fail("Section \"%s\" already declared with different bank %" PRIu32, fail("Section \"%s\" already declared with different bank %"
pSect->pzName, pSect->nBank); PRIu32 "\n", sect->name, sect->bank);
} else { /* Section fragments are handled identically in RGBASM */ } else { /* Section fragments are handled identically in RGBASM */
/* However, concaternating non-fragments will be made an error */ /* However, concaternating non-fragments will be made an error */
if (pSect->modifier != SECTION_FRAGMENT || mod != SECTION_FRAGMENT) if (sect->modifier != SECTION_FRAGMENT || mod != SECTION_FRAGMENT)
warning(WARNING_OBSOLETE, "Concatenation of non-fragment sections is deprecated"); warning(WARNING_OBSOLETE,
"Concatenation of non-fragment sections is deprecated\n");
if (org != pSect->nOrg) { if (org != sect->org) {
if (pSect->nOrg == -1) if (sect->org == -1)
fail("Section \"%s\" already declared as floating", fail("Section \"%s\" already declared as floating\n",
pSect->pzName); sect->name);
else else
fail("Section \"%s\" already declared as fixed at $%" PRIx32, fail("Section \"%s\" already declared as fixed at $%"
pSect->pzName, pSect->nOrg); PRIx32 "\n", sect->name, sect->org);
} }
if (bank != pSect->nBank) { if (bank != sect->bank) {
if (pSect->nBank == -1) if (sect->bank == -1)
fail("Section \"%s\" already declared as floating bank", fail("Section \"%s\" already declared as floating bank\n",
pSect->pzName); sect->name);
else else
fail("Section \"%s\" already declared as fixed at bank %" PRIu32, fail("Section \"%s\" already declared as fixed at bank %"
pSect->pzName, pSect->nBank); PRIu32 "\n", sect->name, sect->bank);
} }
if (alignment != pSect->nAlign) { if (alignment != sect->align) {
if (pSect->nAlign == 0) if (sect->align == 0)
fail("Section \"%s\" already declared as unaligned", fail("Section \"%s\" already declared as unaligned\n",
pSect->pzName); sect->name);
else else
fail("Section \"%s\" already declared as aligned to %u bytes", fail("Section \"%s\" already declared as aligned to %u bytes\n",
pSect->pzName, sect->name, 1U << sect->align);
1U << pSect->nAlign);
} }
} }
if (nbSectErrors) if (nbSectErrors)
fatalerror("Cannot create section \"%s\" (%u errors)", fatalerror("Cannot create section \"%s\" (%u errors)\n",
pSect->pzName, nbSectErrors); sect->name, nbSectErrors);
#undef fail #undef fail
return pSect; return sect;
} }
pSect = malloc(sizeof(*pSect)); sect = malloc(sizeof(*sect));
if (pSect == NULL) if (sect == NULL)
fatalerror("Not enough memory for section"); fatalerror("Not enough memory for section: %s\n", strerror(errno));
pSect->pzName = strdup(pzName); sect->name = strdup(name);
if (pSect->pzName == NULL) if (sect->name == NULL)
fatalerror("Not enough memory for sectionname"); fatalerror("Not enough memory for section name: %s\n", strerror(errno));
pSect->nType = type; sect->type = type;
pSect->modifier = mod; sect->modifier = mod;
pSect->size = 0; sect->size = 0;
pSect->nOrg = org; sect->org = org;
pSect->nBank = bank; sect->bank = bank;
pSect->nAlign = alignment; sect->align = alignment;
pSect->alignOfs = alignOffset; sect->alignOfs = alignOffset;
pSect->pNext = pSectionList; sect->next = pSectionList;
pSect->pPatches = NULL; sect->patches = NULL;
/* It is only needed to allocate memory for ROM sections. */ /* It is only needed to allocate memory for ROM sections. */
if (sect_HasData(type)) { if (sect_HasData(type)) {
uint32_t sectsize; uint32_t sectsize;
sectsize = maxsize[type]; sectsize = maxsize[type];
pSect->tData = malloc(sectsize); sect->data = malloc(sectsize);
if (pSect->tData == NULL) if (sect->data == NULL)
fatalerror("Not enough memory for section"); fatalerror("Not enough memory for section: %s\n", strerror(errno));
} else { } else {
pSect->tData = NULL; sect->data = NULL;
} }
/* /*
* Add the new section to the list * Add the new section to the list
* at the beginning because order doesn't matter * at the beginning because order doesn't matter
*/ */
pSectionList = pSect; pSectionList = sect;
return pSect; return sect;
#undef mask #undef mask
} }
@@ -295,8 +306,8 @@ static struct Section *getSection(char const *pzName, enum SectionType type,
*/ */
static void changeSection(void) static void changeSection(void)
{ {
if (nUnionDepth > 0) if (unionStack)
fatalerror("Cannot change the section within a UNION"); fatalerror("Cannot change the section within a UNION\n");
sym_SetCurrentSymbolScope(NULL); sym_SetCurrentSymbolScope(NULL);
} }
@@ -304,17 +315,17 @@ static void changeSection(void)
/* /*
* Set the current section by name and type * Set the current section by name and type
*/ */
void out_NewSection(char const *pzName, uint32_t type, uint32_t org, void out_NewSection(char const *name, uint32_t type, uint32_t org,
struct SectionSpec const *attribs, enum SectionModifier mod) struct SectionSpec const *attribs, enum SectionModifier mod)
{ {
if (currentLoadSection) if (currentLoadSection)
fatalerror("Cannot change the section within a `LOAD` block"); fatalerror("Cannot change the section within a `LOAD` block\n");
struct Section *pSect = getSection(pzName, type, org, attribs, mod); struct Section *sect = getSection(name, type, org, attribs, mod);
changeSection(); changeSection();
curOffset = mod == SECTION_UNION ? 0 : pSect->size; curOffset = mod == SECTION_UNION ? 0 : sect->size;
pCurrentSection = pSect; pCurrentSection = sect;
} }
/* /*
@@ -326,20 +337,20 @@ void out_SetLoadSection(char const *name, uint32_t type, uint32_t org,
checkcodesection(); checkcodesection();
if (currentLoadSection) if (currentLoadSection)
fatalerror("`LOAD` blocks cannot be nested"); fatalerror("`LOAD` blocks cannot be nested\n");
struct Section *pSect = getSection(name, type, org, attribs, false); struct Section *sect = getSection(name, type, org, attribs, false);
loadOffset = curOffset; loadOffset = curOffset;
curOffset = 0; /* curOffset -= loadOffset; */ curOffset = 0; /* curOffset -= loadOffset; */
changeSection(); changeSection();
currentLoadSection = pSect; currentLoadSection = sect;
} }
void out_EndLoadSection(void) void out_EndLoadSection(void)
{ {
if (!currentLoadSection) if (!currentLoadSection)
yyerror("Found `ENDL` outside of a `LOAD` block"); error("Found `ENDL` outside of a `LOAD` block\n");
currentLoadSection = NULL; currentLoadSection = NULL;
changeSection(); changeSection();
@@ -352,6 +363,14 @@ struct Section *sect_GetSymbolSection(void)
return currentLoadSection ? currentLoadSection : pCurrentSection; return currentLoadSection ? currentLoadSection : pCurrentSection;
} }
/*
* The offset into the section above
*/
uint32_t sect_GetSymbolOffset(void)
{
return curOffset;
}
uint32_t sect_GetOutputOffset(void) uint32_t sect_GetOutputOffset(void)
{ {
return curOffset + loadOffset; return curOffset + loadOffset;
@@ -361,22 +380,22 @@ void sect_AlignPC(uint8_t alignment, uint16_t offset)
{ {
struct Section *sect = sect_GetSymbolSection(); struct Section *sect = sect_GetSymbolSection();
if (sect->nOrg != -1) { if (sect->org != -1) {
if ((sym_GetPCValue() - offset) % (1 << alignment)) if ((sym_GetPCValue() - offset) % (1 << alignment))
yyerror("Section's fixed address fails required alignment (PC = $%04" PRIx32 ")", error("Section's fixed address fails required alignment (PC = $%04"
sym_GetPCValue()); PRIx32 ")\n", sym_GetPCValue());
} else if (sect->nAlign != 0) { } else if (sect->align != 0) {
if ((((sect->alignOfs + curOffset) % (1 << sect->nAlign)) if ((((sect->alignOfs + curOffset) % (1 << sect->align))
- offset) % (1 << alignment)) { - offset) % (1 << alignment)) {
yyerror("Section's alignment fails required alignment (offset from section start = $%04" PRIx32 ")", error("Section's alignment fails required alignment (offset from section start = $%04"
curOffset); PRIx32 ")\n", curOffset);
} else if (alignment > sect->nAlign) { } else if (alignment > sect->align) {
sect->nAlign = alignment; sect->align = alignment;
sect->alignOfs = sect->alignOfs =
(offset - curOffset) % (1 << alignment); (offset - curOffset) % (1 << alignment);
} }
} else { } else {
sect->nAlign = alignment; sect->align = alignment;
sect->alignOfs = offset; sect->alignOfs = offset;
} }
} }
@@ -384,15 +403,15 @@ void sect_AlignPC(uint8_t alignment, uint16_t offset)
static inline void growSection(uint32_t growth) static inline void growSection(uint32_t growth)
{ {
curOffset += growth; curOffset += growth;
if (curOffset > pCurrentSection->size) if (curOffset + loadOffset > pCurrentSection->size)
pCurrentSection->size = curOffset; pCurrentSection->size = curOffset + loadOffset;
if (currentLoadSection && curOffset > currentLoadSection->size) if (currentLoadSection && curOffset > currentLoadSection->size)
currentLoadSection->size = curOffset; currentLoadSection->size = curOffset;
} }
static inline void writebyte(uint8_t byte) static inline void writebyte(uint8_t byte)
{ {
pCurrentSection->tData[sect_GetOutputOffset()] = byte; pCurrentSection->data[sect_GetOutputOffset()] = byte;
growSection(1); growSection(1);
} }
@@ -416,6 +435,56 @@ static inline void createPatch(enum PatchType type,
out_CreatePatch(type, expr, sect_GetOutputOffset()); out_CreatePatch(type, expr, sect_GetOutputOffset());
} }
void sect_StartUnion(void)
{
if (!pCurrentSection)
fatalerror("UNIONs must be inside a SECTION\n");
if (sect_HasData(pCurrentSection->type))
fatalerror("Cannot use UNION inside of ROM0 or ROMX sections\n");
struct UnionStackEntry *entry = malloc(sizeof(*entry));
if (!entry)
fatalerror("Failed to allocate new union stack entry: %s\n", strerror(errno));
entry->start = curOffset;
entry->size = 0;
entry->next = unionStack;
unionStack = entry;
}
static void endUnionMember(void)
{
uint32_t memberSize = curOffset - unionStack->start;
if (memberSize > unionStack->size)
unionStack->size = memberSize;
curOffset = unionStack->start;
}
void sect_NextUnionMember(void)
{
if (!unionStack)
fatalerror("Found NEXTU outside of a UNION construct\n");
endUnionMember();
}
void sect_EndUnion(void)
{
if (!unionStack)
fatalerror("Found ENDU outside of a UNION construct\n");
endUnionMember();
curOffset += unionStack->size;
struct UnionStackEntry *next = unionStack->next;
free(unionStack);
unionStack = next;
}
void sect_CheckUnionClosed(void)
{
if (unionStack)
fatalerror("Unterminated UNION construct!\n");
}
/* /*
* Output an absolute byte * Output an absolute byte
*/ */
@@ -444,14 +513,11 @@ void out_Skip(int32_t skip, bool ds)
checksection(); checksection();
reserveSpace(skip); reserveSpace(skip);
if (!ds && sect_HasData(pCurrentSection->nType)) if (!ds && sect_HasData(pCurrentSection->type))
warning(WARNING_EMPTY_DATA_DIRECTIVE, "db/dw/dl directive without data in ROM"); warning(WARNING_EMPTY_DATA_DIRECTIVE, "db/dw/dl directive without data in ROM\n");
if (!sect_HasData(pCurrentSection->nType)) { if (!sect_HasData(pCurrentSection->type)) {
growSection(skip); growSection(skip);
} else if (nUnionDepth > 0) {
while (skip--)
writebyte(CurrentOptions.fillchar);
} else { } else {
checkcodesection(); checkcodesection();
while (skip--) while (skip--)
@@ -553,18 +619,24 @@ void out_PCRelByte(struct Expression *expr)
{ {
checkcodesection(); checkcodesection();
reserveSpace(1); reserveSpace(1);
struct Symbol const *pc = sym_FindSymbol("@");
if (!rpn_isKnown(expr) || pCurrentSection->nOrg == -1) { if (!rpn_IsDiffConstant(expr, pc)) {
createPatch(PATCHTYPE_JR, expr); createPatch(PATCHTYPE_JR, expr);
writebyte(0); writebyte(0);
} else { } else {
/* Target is relative to the byte *after* the operand */ struct Symbol const *sym = rpn_SymbolOf(expr);
uint16_t address = sym_GetPCValue() + 1; /* The offset wraps (jump from ROM to HRAM, for example) */
/* The offset wraps (jump from ROM to HRAM, for loopexample) */ int16_t offset;
int16_t offset = expr->nVal - address;
/* Offset is relative to the byte *after* the operand */
if (sym == pc)
offset = -2; /* PC as operand to `jr` is lower than reference PC by 2 */
else
offset = sym_GetValue(sym) - (sym_GetValue(pc) + 1);
if (offset < -128 || offset > 127) { if (offset < -128 || offset > 127) {
yyerror("jr target out of reach (expected -129 < %" PRId16 " < 128)", error("jr target out of reach (expected -129 < %" PRId16 " < 128)\n",
offset); offset);
writebyte(0); writebyte(0);
} else { } else {
@@ -577,17 +649,27 @@ void out_PCRelByte(struct Expression *expr)
/* /*
* Output a binary file * Output a binary file
*/ */
void out_BinaryFile(char const *s) void out_BinaryFile(char const *s, int32_t startPos)
{ {
FILE *f = fstk_FindFile(s, NULL); if (startPos < 0) {
error("Start position cannot be negative (%" PRId32 ")\n", startPos);
startPos = 0;
}
char *fullPath = NULL;
size_t size = 0;
FILE *f = NULL;
if (fstk_FindFile(s, &fullPath, &size))
f = fopen(fullPath, "rb");
if (!f) { if (!f) {
free(fullPath);
if (oGeneratedMissingIncludes) { if (oGeneratedMissingIncludes) {
oFailedOnMissingInclude = true; oFailedOnMissingInclude = true;
return; return;
} }
fatalerror("Error opening INCBIN file '%s': %s", s, fatalerror("Error opening INCBIN file '%s': %s\n", s, strerror(errno));
strerror(errno));
} }
int32_t fsize = -1; int32_t fsize = -1;
@@ -596,12 +678,21 @@ void out_BinaryFile(char const *s)
checkcodesection(); checkcodesection();
if (fseek(f, 0, SEEK_END) != -1) { if (fseek(f, 0, SEEK_END) != -1) {
fsize = ftell(f); fsize = ftell(f);
rewind(f);
reserveSpace(fsize); if (startPos >= fsize) {
} else if (errno != ESPIPE) { error("Specified start position is greater than length of file\n");
yyerror("Error determining size of INCBIN file '%s': %s", s, return;
strerror(errno)); }
fseek(f, startPos, SEEK_SET);
reserveSpace(fsize - startPos);
} else {
if (errno != ESPIPE)
error("Error determining size of INCBIN file '%s': %s\n",
s, strerror(errno));
/* The file isn't seekable, so we'll just skip bytes */
while (startPos--)
(void)fgetc(f);
} }
while ((byte = fgetc(f)) != EOF) { while ((byte = fgetc(f)) != EOF) {
@@ -611,37 +702,40 @@ void out_BinaryFile(char const *s)
} }
if (ferror(f)) if (ferror(f))
yyerror("Error reading INCBIN file '%s': %s", s, error("Error reading INCBIN file '%s': %s\n", s, strerror(errno));
strerror(errno));
fclose(f); fclose(f);
free(fullPath);
} }
void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length) void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
{ {
if (start_pos < 0) { if (start_pos < 0) {
yyerror("Start position cannot be negative (%" PRId32 ")", error("Start position cannot be negative (%" PRId32 ")\n", start_pos);
start_pos);
start_pos = 0; start_pos = 0;
} }
if (length < 0) { if (length < 0) {
yyerror("Number of bytes to read cannot be negative (%" PRId32 ")", error("Number of bytes to read cannot be negative (%" PRId32 ")\n", length);
length);
length = 0; length = 0;
} }
if (length == 0) /* Don't even bother with 0-byte slices */ if (length == 0) /* Don't even bother with 0-byte slices */
return; return;
FILE *f = fstk_FindFile(s, NULL); char *fullPath = NULL;
size_t size = 0;
FILE *f = NULL;
if (fstk_FindFile(s, &fullPath, &size))
f = fopen(fullPath, "rb");
if (!f) { if (!f) {
free(fullPath);
if (oGeneratedMissingIncludes) { if (oGeneratedMissingIncludes) {
oFailedOnMissingInclude = true; oFailedOnMissingInclude = true;
return; return;
} }
fatalerror("Error opening INCBIN file '%s': %s", s, fatalerror("Error opening INCBIN file '%s': %s\n", s, strerror(errno));
strerror(errno));
} }
checkcodesection(); checkcodesection();
@@ -653,17 +747,17 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
fsize = ftell(f); fsize = ftell(f);
if (start_pos >= fsize) { if (start_pos >= fsize) {
yyerror("Specified start position is greater than length of file"); error("Specified start position is greater than length of file\n");
return; return;
} }
if ((start_pos + length) > fsize) if ((start_pos + length) > fsize)
fatalerror("Specified range in INCBIN is out of bounds"); fatalerror("Specified range in INCBIN is out of bounds\n");
fseek(f, start_pos, SEEK_SET); fseek(f, start_pos, SEEK_SET);
} else { } else {
if (errno != ESPIPE) if (errno != ESPIPE)
yyerror("Error determining size of INCBIN file '%s': %s", error("Error determining size of INCBIN file '%s': %s\n",
s, strerror(errno)); s, strerror(errno));
/* The file isn't seekable, so we'll just skip bytes */ /* The file isn't seekable, so we'll just skip bytes */
while (start_pos--) while (start_pos--)
@@ -678,15 +772,15 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
if (byte != EOF) { if (byte != EOF) {
writebyte(byte); writebyte(byte);
} else if (ferror(f)) { } else if (ferror(f)) {
yyerror("Error reading INCBIN file '%s': %s", s, error("Error reading INCBIN file '%s': %s\n", s, strerror(errno));
strerror(errno));
} else { } else {
yyerror("Premature end of file (%" PRId32 " bytes left to read)", error("Premature end of file (%" PRId32 " bytes left to read)\n",
todo + 1); todo + 1);
} }
} }
fclose(f); fclose(f);
free(fullPath);
} }
/* /*
@@ -694,36 +788,34 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
*/ */
void out_PushSection(void) void out_PushSection(void)
{ {
struct SectionStackEntry *pSect; struct SectionStackEntry *sect = malloc(sizeof(*sect));
pSect = malloc(sizeof(struct SectionStackEntry)); if (sect == NULL)
if (pSect == NULL) fatalerror("No memory for section stack: %s\n", strerror(errno));
fatalerror("No memory for section stack"); sect->section = pCurrentSection;
sect->scope = sym_GetCurrentSymbolScope();
pSect->pSection = pCurrentSection; sect->offset = curOffset;
pSect->pScope = sym_GetCurrentSymbolScope(); sect->next = sectionStack;
pSect->offset = curOffset; sectionStack = sect;
pSect->pNext = pSectionStack;
pSectionStack = pSect;
/* TODO: maybe set current section to NULL? */ /* TODO: maybe set current section to NULL? */
} }
void out_PopSection(void) void out_PopSection(void)
{ {
if (pSectionStack == NULL) if (!sectionStack)
fatalerror("No entries in the section stack"); fatalerror("No entries in the section stack\n");
if (currentLoadSection) if (currentLoadSection)
fatalerror("Cannot change the section within a `LOAD` block!"); fatalerror("Cannot change the section within a `LOAD` block!\n");
struct SectionStackEntry *pSect; struct SectionStackEntry *sect;
pSect = pSectionStack; sect = sectionStack;
changeSection(); changeSection();
pCurrentSection = pSect->pSection; pCurrentSection = sect->section;
sym_SetCurrentSymbolScope(pSect->pScope); sym_SetCurrentSymbolScope(sect->scope);
curOffset = pSect->offset; curOffset = sect->offset;
pSectionStack = pSect->pNext; sectionStack = sect->next;
free(pSect); free(sect);
} }

View File

@@ -23,6 +23,7 @@
#include "asm/macro.h" #include "asm/macro.h"
#include "asm/main.h" #include "asm/main.h"
#include "asm/mymath.h" #include "asm/mymath.h"
#include "asm/output.h"
#include "asm/section.h" #include "asm/section.h"
#include "asm/symbol.h" #include "asm/symbol.h"
#include "asm/util.h" #include "asm/util.h"
@@ -36,7 +37,7 @@
HashMap symbols; HashMap symbols;
static struct Symbol *symbolScope; /* Current section symbol scope */ static char const *labelScope; /* Current section's label scope */
static struct Symbol *PCSymbol; static struct Symbol *PCSymbol;
static char savedTIME[256]; static char savedTIME[256];
static char savedDATE[256]; static char savedDATE[256];
@@ -77,19 +78,62 @@ void sym_ForEach(void (*func)(struct Symbol *, void *), void *arg)
static int32_t Callback_NARG(void) static int32_t Callback_NARG(void)
{ {
if (!macro_GetCurrentArgs()) {
error("_NARG does not make sense outside of a macro\n");
return 0;
}
return macro_NbArgs(); return macro_NbArgs();
} }
static int32_t Callback__LINE__(void) static int32_t Callback__LINE__(void)
{ {
return nLineNo; return lexer_GetLineNo();
}
static char const *Callback__FILE__(void)
{
/*
* FIXME: this is dangerous, and here's why this is CURRENTLY okay. It's still bad, fix it.
* There are only two call sites for this; one copies the contents directly, the other is
* EQUS expansions, which cannot straddle file boundaries. So this should be fine.
*/
static char *buf = NULL;
static size_t bufsize = 0;
char const *fileName = fstk_GetFileName();
size_t j = 1;
/* TODO: is there a way for a file name to be empty? */
assert(fileName[0]);
/* The assertion above ensures the loop runs at least once */
for (size_t i = 0; fileName[i]; i++, j++) {
/* Account for the extra backslash inserted below */
if (fileName[i] == '"')
j++;
/* Ensure there will be enough room; DO NOT PRINT ANYTHING ABOVE THIS!! */
if (j + 2 >= bufsize) { /* Always keep room for 2 tail chars */
bufsize = bufsize ? bufsize * 2 : 64;
buf = realloc(buf, bufsize);
if (!buf)
fatalerror("Failed to grow buffer for file name: %s\n",
strerror(errno));
}
/* Escape quotes, since we're returning a string */
if (fileName[i] == '"')
buf[j - 1] = '\\';
buf[j] = fileName[i];
}
/* Write everything after the loop, to ensure the buffer has been allocated */
buf[0] = '"';
buf[j++] = '"';
buf[j] = '\0';
return buf;
} }
static int32_t CallbackPC(void) static int32_t CallbackPC(void)
{ {
struct Section const *section = sect_GetSymbolSection(); struct Section const *section = sect_GetSymbolSection();
return section ? section->nOrg + curOffset : 0; return section ? section->org + sect_GetSymbolOffset() : 0;
} }
/* /*
@@ -97,26 +141,45 @@ static int32_t CallbackPC(void)
*/ */
int32_t sym_GetValue(struct Symbol const *sym) int32_t sym_GetValue(struct Symbol const *sym)
{ {
if (sym_IsNumeric(sym) && sym->callback) if (sym_IsNumeric(sym) && sym->hasCallback)
return sym->callback(); return sym->numCallback();
if (sym->type == SYM_LABEL) if (sym->type == SYM_LABEL)
/* TODO: do not use section's org directly */ /* TODO: do not use section's org directly */
return sym->value + sym_GetSection(sym)->nOrg; return sym->value + sym_GetSection(sym)->org;
return sym->value; return sym->value;
} }
static void dumpFilename(struct Symbol const *sym)
{
if (!sym->src)
fputs("<builtin>", stderr);
else
fstk_Dump(sym->src, sym->fileLine);
}
/*
* Set a symbol's definition filename and line
*/
static void setSymbolFilename(struct Symbol *sym)
{
sym->src = fstk_GetFileStack();
sym->fileLine = lexer_GetLineNo();
}
/* /*
* Update a symbol's definition filename and line * Update a symbol's definition filename and line
*/ */
static void updateSymbolFilename(struct Symbol *sym) static void updateSymbolFilename(struct Symbol *sym)
{ {
if (snprintf(sym->fileName, _MAX_PATH + 1, "%s", struct FileStackNode *oldSrc = sym->src;
tzCurrentFileName) > _MAX_PATH)
fatalerror("%s: File name is too long: '%s'", __func__, setSymbolFilename(sym);
tzCurrentFileName); /* If the old node was referenced, ensure the new one is */
sym->fileLine = fstk_GetLine(); if (oldSrc->referenced && oldSrc->ID != -1)
out_RegisterNode(sym->src);
/* TODO: unref the old node, and use `out_ReplaceNode` instead if deleting it */
} }
/* /*
@@ -127,16 +190,16 @@ static struct Symbol *createsymbol(char const *s)
struct Symbol *symbol = malloc(sizeof(*symbol)); struct Symbol *symbol = malloc(sizeof(*symbol));
if (!symbol) if (!symbol)
fatalerror("Failed to create symbol: %s", strerror(errno)); fatalerror("Failed to create symbol '%s': %s\n", s, strerror(errno));
if (snprintf(symbol->name, MAXSYMLEN + 1, "%s", s) > MAXSYMLEN) if (snprintf(symbol->name, MAXSYMLEN + 1, "%s", s) > MAXSYMLEN)
warning(WARNING_LONG_STR, "Symbol name is too long: '%s'", s); warning(WARNING_LONG_STR, "Symbol name is too long: '%s'\n", s);
symbol->isExported = false; symbol->isExported = false;
symbol->isBuiltin = false; symbol->isBuiltin = false;
symbol->scope = NULL; symbol->hasCallback = false;
symbol->section = NULL; symbol->section = NULL;
updateSymbolFilename(symbol); setSymbolFilename(symbol);
symbol->ID = -1; symbol->ID = -1;
symbol->next = NULL; symbol->next = NULL;
@@ -149,20 +212,20 @@ static struct Symbol *createsymbol(char const *s)
* the name with the parent symbol's name. * the name with the parent symbol's name.
*/ */
static void fullSymbolName(char *output, size_t outputSize, static void fullSymbolName(char *output, size_t outputSize,
char const *localName, const struct Symbol *scope) char const *localName, char const *scopeName)
{ {
const struct Symbol *parent = scope->scope ? scope->scope : scope; int ret = snprintf(output, outputSize, "%s%s", scopeName, localName);
int n = snprintf(output, outputSize, "%s%s", parent->name, localName);
if (n >= (int)outputSize) if (ret < 0)
fatalerror("Symbol name is too long: '%s%s'", parent->name, fatalerror("snprintf error when expanding symbol name: %s", strerror(errno));
localName); else if ((size_t)ret >= outputSize)
fatalerror("Symbol name is too long: '%s%s'\n", scopeName, localName);
} }
/* /*
* Find a symbol by name and scope * Find a symbol by name and scope
*/ */
static struct Symbol *findsymbol(char const *s, struct Symbol const *scope) static struct Symbol *findsymbol(char const *s, char const *scope)
{ {
char fullname[MAXSYMLEN + 1]; char fullname[MAXSYMLEN + 1];
@@ -174,8 +237,7 @@ static struct Symbol *findsymbol(char const *s, struct Symbol const *scope)
char const *separator = strchr(s, '.'); char const *separator = strchr(s, '.');
if (separator && strchr(separator + 1, '.')) if (separator && strchr(separator + 1, '.'))
fatalerror("'%s' is a nonsensical reference to a nested local symbol", fatalerror("'%s' is a nonsensical reference to a nested local symbol\n", s);
s);
return hash_GetElement(symbols, s); return hash_GetElement(symbols, s);
} }
@@ -185,7 +247,7 @@ static struct Symbol *findsymbol(char const *s, struct Symbol const *scope)
*/ */
struct Symbol *sym_FindSymbol(char const *symName) struct Symbol *sym_FindSymbol(char const *symName)
{ {
return findsymbol(symName, symName[0] == '.' ? symbolScope : NULL); return findsymbol(symName, symName[0] == '.' ? labelScope : NULL);
} }
static inline bool isReferenced(struct Symbol const *sym) static inline bool isReferenced(struct Symbol const *sym)
@@ -198,20 +260,21 @@ static inline bool isReferenced(struct Symbol const *sym)
*/ */
void sym_Purge(char const *symName) void sym_Purge(char const *symName)
{ {
struct Symbol *scope = symName[0] == '.' ? symbolScope : NULL; struct Symbol *symbol = sym_FindSymbol(symName);
struct Symbol *symbol = findsymbol(symName, scope);
if (!symbol) { if (!symbol) {
yyerror("'%s' not defined", symName); error("'%s' not defined\n", symName);
} else if (symbol->isBuiltin) { } else if (symbol->isBuiltin) {
yyerror("Built-in symbol '%s' cannot be purged", symName); error("Built-in symbol '%s' cannot be purged\n", symName);
} else if (isReferenced(symbol)) { } else if (isReferenced(symbol)) {
yyerror("Symbol \"%s\" is referenced and thus cannot be purged", error("Symbol \"%s\" is referenced and thus cannot be purged\n", symName);
symName);
} else { } else {
/* Do not keep a reference to the label's name after purging it */
if (symbol->name == labelScope)
labelScope = NULL;
hash_RemoveElement(symbols, symbol->name); hash_RemoveElement(symbols, symbol->name);
if (symbol->type == SYM_MACRO) /* TODO: ideally, also unref the file stack nodes */
free(symbol->macro);
free(symbol); free(symbol);
} }
} }
@@ -221,29 +284,40 @@ uint32_t sym_GetPCValue(void)
struct Section const *sect = sect_GetSymbolSection(); struct Section const *sect = sect_GetSymbolSection();
if (!sect) if (!sect)
yyerror("PC has no value outside a section"); error("PC has no value outside a section\n");
else if (sect->nOrg == -1) else if (sect->org == -1)
yyerror("Expected constant PC but section is not fixed"); error("Expected constant PC but section is not fixed\n");
else else
return CallbackPC(); return CallbackPC();
return 0; return 0;
} }
/* /*
* Return a constant symbols value * Return a constant symbol's value, assuming it's defined
*/
uint32_t sym_GetConstantSymValue(struct Symbol const *sym)
{
if (sym == PCSymbol)
return sym_GetPCValue();
else if (!sym_IsConstant(sym))
error("\"%s\" does not have a constant value\n", sym->name);
else
return sym_GetValue(sym);
return 0;
}
/*
* Return a constant symbol's value
*/ */
uint32_t sym_GetConstantValue(char const *s) uint32_t sym_GetConstantValue(char const *s)
{ {
struct Symbol const *sym = sym_FindSymbol(s); struct Symbol const *sym = sym_FindSymbol(s);
if (sym == NULL) if (sym == NULL)
yyerror("'%s' not defined", s); error("'%s' not defined\n", s);
else if (sym == PCSymbol)
return sym_GetPCValue();
else if (!sym_IsConstant(sym))
yyerror("\"%s\" does not have a constant value", s);
else else
return sym_GetValue(sym); return sym_GetConstantSymValue(sym);
return 0; return 0;
} }
@@ -256,23 +330,23 @@ uint32_t sym_GetDefinedValue(char const *s)
struct Symbol const *sym = sym_FindSymbol(s); struct Symbol const *sym = sym_FindSymbol(s);
if (sym == NULL || !sym_IsDefined(sym)) if (sym == NULL || !sym_IsDefined(sym))
yyerror("'%s' not defined", s); error("'%s' not defined\n", s);
else if (!sym_IsNumeric(sym)) else if (!sym_IsNumeric(sym))
yyerror("'%s' is a macro or string symbol", s); error("'%s' is a macro or string symbol\n", s);
else else
return sym_GetValue(sym); return sym_GetValue(sym);
return 0; return 0;
} }
struct Symbol *sym_GetCurrentSymbolScope(void) char const *sym_GetCurrentSymbolScope(void)
{ {
return symbolScope; return labelScope;
} }
void sym_SetCurrentSymbolScope(struct Symbol *newScope) void sym_SetCurrentSymbolScope(char const *newScope)
{ {
symbolScope = newScope; labelScope = newScope;
} }
/* /*
@@ -286,9 +360,11 @@ static struct Symbol *createNonrelocSymbol(char const *symbolName)
if (!symbol) if (!symbol)
symbol = createsymbol(symbolName); symbol = createsymbol(symbolName);
else if (sym_IsDefined(symbol)) else if (sym_IsDefined(symbol)) {
yyerror("'%s' already defined at %s(%" PRIu32 ")", symbolName, error("'%s' already defined at ", symbolName);
symbol->fileName, symbol->fileLine); dumpFilename(symbol);
putc('\n', stderr);
}
return symbol; return symbol;
} }
@@ -301,7 +377,6 @@ struct Symbol *sym_AddEqu(char const *symName, int32_t value)
struct Symbol *sym = createNonrelocSymbol(symName); struct Symbol *sym = createNonrelocSymbol(symName);
sym->type = SYM_EQU; sym->type = SYM_EQU;
sym->callback = NULL;
sym->value = value; sym->value = value;
return sym; return sym;
@@ -326,7 +401,7 @@ struct Symbol *sym_AddString(char const *symName, char const *value)
char *string = malloc(len + 1); char *string = malloc(len + 1);
if (string == NULL) if (string == NULL)
fatalerror("No memory for string equate"); fatalerror("No memory for string equate: %s\n", strerror(errno));
strcpy(string, value); strcpy(string, value);
sym->type = SYM_EQUS; sym->type = SYM_EQUS;
@@ -344,94 +419,107 @@ struct Symbol *sym_AddSet(char const *symName, int32_t value)
{ {
struct Symbol *sym = findsymbol(symName, NULL); struct Symbol *sym = findsymbol(symName, NULL);
if (sym == NULL) if (sym == NULL) {
sym = createsymbol(symName); sym = createsymbol(symName);
else if (sym_IsDefined(sym) && sym->type != SYM_SET) } else if (sym_IsDefined(sym) && sym->type != SYM_SET) {
yyerror("'%s' already defined as %s at %s(%" PRIu32 ")", error("'%s' already defined as %s at ",
symName, sym->type == SYM_LABEL ? "label" : "constant", symName, sym->type == SYM_LABEL ? "label" : "constant");
sym->fileName, sym->fileLine); dumpFilename(sym);
else putc('\n', stderr);
/* TODO: can the scope be incorrect when talking over refs? */ } else {
/* TODO: can the scope be incorrect when taking over refs? */
updateSymbolFilename(sym); updateSymbolFilename(sym);
}
sym->type = SYM_SET; sym->type = SYM_SET;
sym->callback = NULL;
sym->value = value; sym->value = value;
return sym; return sym;
} }
/* /*
* Add a local (.name) relocatable symbol * Add a label (aka "relocatable symbol")
* @param name The label's full name (so `.name` is invalid)
* @return The created symbol
*/ */
struct Symbol *sym_AddLocalReloc(char const *symName) static struct Symbol *addLabel(char const *name)
{ {
if (!symbolScope) { assert(name[0] != '.'); /* The symbol name must have been expanded prior */
yyerror("Local label '%s' in main scope", symName); struct Symbol *sym = findsymbol(name, NULL); /* Due to this, don't look for expansions */
if (!sym) {
sym = createsymbol(name);
} else if (sym_IsDefined(sym)) {
error("'%s' already defined at ", name);
dumpFilename(sym);
putc('\n', stderr);
return NULL;
} else {
updateSymbolFilename(sym);
}
/* If the symbol already exists as a ref, just "take over" it */
sym->type = SYM_LABEL;
sym->value = sect_GetSymbolOffset();
if (exportall)
sym->isExported = true;
sym->section = sect_GetSymbolSection();
if (sym && !sym->section)
error("Label \"%s\" created outside of a SECTION\n", name);
return sym;
}
/*
* Add a local (.name or Parent.name) relocatable symbol
*/
struct Symbol *sym_AddLocalLabel(char const *name)
{
if (!labelScope) {
error("Local label '%s' in main scope\n", name);
return NULL; return NULL;
} }
char fullname[MAXSYMLEN + 1]; char fullname[MAXSYMLEN + 1];
fullSymbolName(fullname, sizeof(fullname), symName, symbolScope); if (name[0] == '.') {
return sym_AddReloc(fullname); /* If symbol is of the form `.name`, expand to the full `Parent.name` name */
fullSymbolName(fullname, sizeof(fullname), name, labelScope);
name = fullname; /* Use the expanded name instead */
} else {
size_t i = 0;
/* Otherwise, check that `Parent` is in fact the current scope */
while (labelScope[i] && name[i] == labelScope[i])
i++;
/* Assuming no dots in `labelScope` */
assert(strchr(&name[i], '.')); /* There should be at least one dot, though */
size_t parentLen = i + (strchr(&name[i], '.') - name);
/*
* Check that `labelScope[i]` ended the check, guaranteeing that `name` is at least
* as long, and then that this was the entirety of the `Parent` part of `name`.
*/
if (labelScope[i] != '\0' || name[i] != '.')
error("Not currently in the scope of '%.*s'\n", parentLen, name);
if (strchr(&name[parentLen + 1], '.')) /* There will at least be a terminator */
fatalerror("'%s' is a nonsensical reference to a nested local label\n",
name);
}
return addLabel(name);
} }
/* /*
* Add a relocatable symbol * Add a relocatable symbol
*/ */
struct Symbol *sym_AddReloc(char const *symName) struct Symbol *sym_AddLabel(char const *name)
{ {
struct Symbol const *scope = NULL; struct Symbol *sym = addLabel(name);
char *localPtr = strchr(symName, '.');
if (localPtr != NULL) {
if (!symbolScope) {
yyerror("Local label in main scope");
return NULL;
}
scope = symbolScope->scope ? symbolScope->scope : symbolScope;
uint32_t parentLen = localPtr - symName;
if (strchr(localPtr + 1, '.') != NULL)
fatalerror("'%s' is a nonsensical reference to a nested local symbol",
symName);
else if (strlen(scope->name) != parentLen
|| strncmp(symName, scope->name, parentLen) != 0)
yyerror("Not currently in the scope of '%.*s'",
parentLen, symName);
}
struct Symbol *sym = findsymbol(symName, scope);
if (!sym)
sym = createsymbol(symName);
else if (sym_IsDefined(sym))
yyerror("'%s' already defined in %s(%" PRIu32 ")", symName,
sym->fileName, sym->fileLine);
/* If the symbol already exists as a ref, just "take over" it */
sym->type = SYM_LABEL;
sym->callback = NULL;
sym->value = curOffset;
if (exportall)
sym->isExported = true;
sym->scope = scope;
sym->section = sect_GetSymbolSection();
/* Labels need to be assigned a section, except PC */
if (!sym->section && strcmp(symName, "@"))
yyerror("Label \"%s\" created outside of a SECTION",
symName);
updateSymbolFilename(sym);
/* Set the symbol as the new scope */ /* Set the symbol as the new scope */
/* TODO: don't do this for local labels */ if (sym)
symbolScope = findsymbol(symName, scope); labelScope = sym->name;
return symbolScope; return sym;
} }
/* /*
@@ -450,14 +538,14 @@ void sym_Export(char const *symName)
/* /*
* Add a macro definition * Add a macro definition
*/ */
struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo) struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size)
{ {
struct Symbol *sym = createNonrelocSymbol(symName); struct Symbol *sym = createNonrelocSymbol(symName);
sym->type = SYM_MACRO; sym->type = SYM_MACRO;
sym->macroSize = ulNewMacroSize; sym->macroSize = size;
sym->macro = tzNewMacro; sym->macro = body;
updateSymbolFilename(sym); setSymbolFilename(sym); /* TODO: is this really necessary? */
/* /*
* The symbol is created at the line after the `endm`, * The symbol is created at the line after the `endm`,
* override this with the actual definition line * override this with the actual definition line
@@ -477,22 +565,16 @@ struct Symbol *sym_Ref(char const *symName)
if (nsym == NULL) { if (nsym == NULL) {
char fullname[MAXSYMLEN + 1]; char fullname[MAXSYMLEN + 1];
struct Symbol const *scope = NULL;
if (symName[0] == '.') { if (symName[0] == '.') {
if (!symbolScope) if (!labelScope)
fatalerror("Local label reference '%s' in main scope", fatalerror("Local label reference '%s' in main scope\n", symName);
symName); fullSymbolName(fullname, sizeof(fullname), symName, labelScope);
scope = symbolScope->scope ? symbolScope->scope
: symbolScope;
fullSymbolName(fullname, sizeof(fullname), symName,
symbolScope);
symName = fullname; symName = fullname;
} }
nsym = createsymbol(symName); nsym = createsymbol(symName);
nsym->type = SYM_REF; nsym->type = SYM_REF;
nsym->scope = scope;
} }
return nsym; return nsym;
@@ -517,21 +599,36 @@ static inline char const *removeLeadingZeros(char const *ptr)
return ptr; return ptr;
} }
static inline struct Symbol *createBuiltinSymbol(char const *name)
{
struct Symbol *sym = createsymbol(name);
sym->isBuiltin = true;
sym->hasCallback = true;
sym->src = NULL;
sym->fileLine = 0;
return sym;
}
/* /*
* Initialize the symboltable * Initialize the symboltable
*/ */
void sym_Init(void) void sym_Init(void)
{ {
struct Symbol *_NARGSymbol = sym_AddEqu("_NARG", 0); PCSymbol = createBuiltinSymbol("@");
struct Symbol *__LINE__Symbol = sym_AddEqu("__LINE__", 0); struct Symbol *_NARGSymbol = createBuiltinSymbol("_NARG");
struct Symbol *__LINE__Symbol = createBuiltinSymbol("__LINE__");
struct Symbol *__FILE__Symbol = createBuiltinSymbol("__FILE__");
PCSymbol = sym_AddReloc("@"), PCSymbol->type = SYM_LABEL;
PCSymbol->isBuiltin = true; PCSymbol->section = NULL;
PCSymbol->callback = CallbackPC; PCSymbol->numCallback = CallbackPC;
_NARGSymbol->isBuiltin = true; _NARGSymbol->type = SYM_EQU;
_NARGSymbol->callback = Callback_NARG; _NARGSymbol->numCallback = Callback_NARG;
__LINE__Symbol->isBuiltin = true; __LINE__Symbol->type = SYM_EQU;
__LINE__Symbol->callback = Callback__LINE__; __LINE__Symbol->numCallback = Callback__LINE__;
__FILE__Symbol->type = SYM_EQUS;
__FILE__Symbol->strCallback = Callback__FILE__;
sym_AddSet("_RS", 0)->isBuiltin = true; sym_AddSet("_RS", 0)->isBuiltin = true;
sym_AddEqu("__RGBDS_MAJOR__", PACKAGE_VERSION_MAJOR)->isBuiltin = true; sym_AddEqu("__RGBDS_MAJOR__", PACKAGE_VERSION_MAJOR)->isBuiltin = true;
@@ -580,7 +677,7 @@ void sym_Init(void)
addString("__UTC_SECOND__", removeLeadingZeros(savedSECOND)); addString("__UTC_SECOND__", removeLeadingZeros(savedSECOND));
#undef addString #undef addString
symbolScope = NULL; labelScope = NULL;
math_DefinePI(); math_DefinePI();
} }

View File

@@ -6,6 +6,7 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <ctype.h>
#include <stdint.h> #include <stdint.h>
#include "asm/main.h" #include "asm/main.h"
@@ -27,21 +28,54 @@ uint32_t calchash(const char *s)
return hash; return hash;
} }
int32_t readUTF8Char(char *dest, char *src) char const *print(int c)
{ {
uint32_t state; static char buf[5]; /* '\xNN' + '\0' */
uint32_t codep;
int32_t i;
for (i = 0, state = 0;; i++) { if (c == EOF)
return "EOF";
if (isprint(c)) {
buf[0] = c;
buf[1] = '\0';
return buf;
}
buf[0] = '\\';
switch (c) {
case '\n':
buf[1] = 'n';
break;
case '\r':
buf[1] = 'r';
break;
case '\t':
buf[1] = 't';
break;
default: /* Print as hex */
buf[1] = 'x';
sprintf(&buf[2], "%02hhx", c);
return buf;
}
buf[2] = '\0';
return buf;
}
size_t readUTF8Char(uint8_t *dest, char const *src)
{
uint32_t state = 0;
uint32_t codep;
size_t i = 0;
for (;;) {
if (decode(&state, &codep, (uint8_t)src[i]) == 1) if (decode(&state, &codep, (uint8_t)src[i]) == 1)
fatalerror("invalid UTF-8 character"); fatalerror("invalid UTF-8 character\n");
dest[i] = src[i]; dest[i] = src[i];
i++;
if (state == 0) { if (state == 0)
dest[++i] = '\0';
return i; return i;
} }
} }
}

View File

@@ -28,18 +28,19 @@ enum WarningState {
}; };
static enum WarningState const defaultWarnings[NB_WARNINGS] = { static enum WarningState const defaultWarnings[NB_WARNINGS] = {
WARNING_ENABLED, /* Assertions */ [WARNING_ASSERT] = WARNING_ENABLED,
WARNING_DISABLED, /* Invalid args to builtins */ [WARNING_BUILTIN_ARG] = WARNING_DISABLED,
WARNING_DISABLED, /* Division undefined behavior */ [WARNING_CHARMAP_REDEF] = WARNING_DISABLED,
WARNING_DISABLED, /* `db`, `dw`, or `dl` with no directive in ROM */ [WARNING_DIV] = WARNING_DISABLED,
WARNING_DISABLED, /* Empty entry in `db`, `dw` or `dl` */ [WARNING_EMPTY_DATA_DIRECTIVE] = WARNING_DISABLED,
WARNING_DISABLED, /* Constants too large */ [WARNING_EMPTY_ENTRY] = WARNING_DISABLED,
WARNING_DISABLED, /* String too long for internal buffers */ [WARNING_LARGE_CONSTANT] = WARNING_DISABLED,
WARNING_ENABLED, /* Obsolete things */ [WARNING_LONG_STR] = WARNING_DISABLED,
WARNING_DISABLED, /* Shifting undefined behavior */ [WARNING_OBSOLETE] = WARNING_ENABLED,
WARNING_DISABLED, /* Strange shift amount */ [WARNING_SHIFT] = WARNING_DISABLED,
WARNING_ENABLED, /* Implicit truncation loses some bits */ [WARNING_SHIFT_AMOUNT] = WARNING_DISABLED,
WARNING_ENABLED, /* User warnings */ [WARNING_TRUNCATION] = WARNING_ENABLED,
[WARNING_USER] = WARNING_ENABLED,
}; };
static enum WarningState warningStates[NB_WARNINGS]; static enum WarningState warningStates[NB_WARNINGS];
@@ -68,6 +69,7 @@ static enum WarningState warningState(enum WarningID id)
static char const *warningFlags[NB_WARNINGS_ALL] = { static char const *warningFlags[NB_WARNINGS_ALL] = {
"assert", "assert",
"builtin-args", "builtin-args",
"charmap-redef",
"div", "div",
"empty-data-directive", "empty-data-directive",
"empty-entry", "empty-entry",
@@ -92,6 +94,7 @@ enum MetaWarningCommand {
/* Warnings that probably indicate an error */ /* Warnings that probably indicate an error */
static uint8_t const _wallCommands[] = { static uint8_t const _wallCommands[] = {
WARNING_BUILTIN_ARG, WARNING_BUILTIN_ARG,
WARNING_CHARMAP_REDEF,
WARNING_EMPTY_DATA_DIRECTIVE, WARNING_EMPTY_DATA_DIRECTIVE,
WARNING_LARGE_CONSTANT, WARNING_LARGE_CONSTANT,
WARNING_LONG_STR, WARNING_LONG_STR,
@@ -195,24 +198,24 @@ void processWarningFlag(char const *flag)
warnx("Unknown warning `%s`", flag); warnx("Unknown warning `%s`", flag);
} }
void verror(const char *fmt, va_list args, char const *flag) void printDiag(const char *fmt, va_list args, char const *type,
char const *flagfmt, char const *flag)
{ {
fputs("ERROR: ", stderr); fputs(type, stderr);
fstk_Dump(); fstk_DumpCurrent();
fprintf(stderr, flag ? ": [-Werror=%s]\n " : ":\n ", flag); fprintf(stderr, flagfmt, flag);
vfprintf(stderr, fmt, args); vfprintf(stderr, fmt, args);
fputc('\n', stderr); lexer_DumpStringExpansions();
fstk_DumpStringExpansions();
nbErrors++;
} }
void yyerror(const char *fmt, ...) void error(const char *fmt, ...)
{ {
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
verror(fmt, args, NULL); printDiag(fmt, args, "ERROR: ", ":\n ", NULL);
va_end(args); va_end(args);
nbErrors++;
} }
noreturn_ void fatalerror(const char *fmt, ...) noreturn_ void fatalerror(const char *fmt, ...)
@@ -220,7 +223,7 @@ noreturn_ void fatalerror(const char *fmt, ...)
va_list args; va_list args;
va_start(args, fmt); va_start(args, fmt);
verror(fmt, args, NULL); printDiag(fmt, args, "FATAL: ", ":\n ", NULL);
va_end(args); va_end(args);
exit(1); exit(1);
@@ -238,7 +241,7 @@ void warning(enum WarningID id, char const *fmt, ...)
return; return;
case WARNING_ERROR: case WARNING_ERROR:
verror(fmt, args, flag); printDiag(fmt, args, "ERROR: ", ": [-Werror=%s]\n ", flag);
va_end(args); va_end(args);
return; return;
@@ -250,12 +253,7 @@ void warning(enum WarningID id, char const *fmt, ...)
break; break;
} }
fputs("warning: ", stderr); printDiag(fmt, args, "warning: ", ": [-W%s]\n ", flag);
fstk_Dump();
fprintf(stderr, ": [-W%s]\n ", flag);
vfprintf(stderr, fmt, args);
fputc('\n', stderr);
fstk_DumpStringExpansions();
va_end(args); va_end(args);
} }

View File

@@ -63,7 +63,7 @@ static void print_usage(void)
" -V, --version print RGBFIX version and exit\n" " -V, --version print RGBFIX version and exit\n"
" -v, --validate fix the header logo and both checksums (-f lhg)\n" " -v, --validate fix the header logo and both checksums (-f lhg)\n"
"\n" "\n"
"For help, use `man rgbfix' or go to https://rednex.github.io/rgbds/\n", "For help, use `man rgbfix' or go to https://rgbds.gbdev.io/docs/\n",
stderr); stderr);
exit(1); exit(1);
} }

View File

@@ -164,7 +164,7 @@ The following will duplicate the header (sans global checksum) of the game
SurvivalKids.gbc SurvivalKids.gbc
.Sh BUGS .Sh BUGS
Please report bugs on Please report bugs on
.Lk https://github.com/rednex/rgbds/issues GitHub . .Lk https://github.com/gbdev/rgbds/issues GitHub .
.Sh SEE ALSO .Sh SEE ALSO
.Xr rgbasm 1 , .Xr rgbasm 1 ,
.Xr rgblink 1 , .Xr rgblink 1 ,
@@ -173,4 +173,4 @@ Please report bugs on
.Nm .Nm
was originally released by Carsten S\(/orensen as a standalone program called gbfix, and was later packaged in RGBDS by Justin Lloyd. was originally released by Carsten S\(/orensen as a standalone program called gbfix, and was later packaged in RGBDS by Justin Lloyd.
It is now maintained by a number of contributors at It is now maintained by a number of contributors at
.Lk https://github.com/rednex/rgbds . .Lk https://github.com/gbdev/rgbds .

View File

@@ -1859,4 +1859,4 @@ Flags: See
was originally written by Carsten S\(/orensen as part of the ASMotor package, was originally written by Carsten S\(/orensen as part of the ASMotor package,
and was later packaged in RGBDS by Justin Lloyd. and was later packaged in RGBDS by Justin Lloyd.
It is now maintained by a number of contributors at It is now maintained by a number of contributors at
.Lk https://github.com/rednex/rgbds . .Lk https://github.com/gbdev/rgbds .

View File

@@ -66,7 +66,7 @@ static void print_usage(void)
" -u, --unique-tiles optimize out identical tiles\n" " -u, --unique-tiles optimize out identical tiles\n"
" -V, --version print RGBGFX version and exit\n" " -V, --version print RGBGFX version and exit\n"
"\n" "\n"
"For help, use `man rgbgfx' or go to https://rednex.github.io/rgbds/\n", "For help, use `man rgbgfx' or go to https://rgbds.gbdev.io/docs/\n",
stderr); stderr);
exit(1); exit(1);
} }

View File

@@ -143,7 +143,7 @@ The following will do nothing:
.D1 $ rgbgfx in.png .D1 $ rgbgfx in.png
.Sh BUGS .Sh BUGS
Please report bugs on Please report bugs on
.Lk https://github.com/rednex/rgbds/issues GitHub . .Lk https://github.com/gbdev/rgbds/issues GitHub .
.Sh SEE ALSO .Sh SEE ALSO
.Xr rgbds 7 , .Xr rgbds 7 ,
.Xr rgbasm 1 , .Xr rgbasm 1 ,
@@ -156,4 +156,4 @@ was created by
.An stag019 .An stag019
to be included in RGBDS. to be included in RGBDS.
It is now maintained by a number of contributors at It is now maintained by a number of contributors at
.Lk https://github.com/rednex/rgbds . .Lk https://github.com/gbdev/rgbds .

View File

@@ -81,14 +81,14 @@ static void processLinkerScript(void)
/* Check if this doesn't conflict with what the code says */ /* Check if this doesn't conflict with what the code says */
if (section->isBankFixed && placement->bank != section->bank) if (section->isBankFixed && placement->bank != section->bank)
error("Linker script contradicts \"%s\"'s bank placement", error(NULL, 0, "Linker script contradicts \"%s\"'s bank placement",
section->name); section->name);
if (section->isAddressFixed && placement->org != section->org) if (section->isAddressFixed && placement->org != section->org)
error("Linker script contradicts \"%s\"'s address placement", error(NULL, 0, "Linker script contradicts \"%s\"'s address placement",
section->name); section->name);
if (section->isAlignFixed if (section->isAlignFixed
&& (placement->org & section->alignMask) != 0) && (placement->org & section->alignMask) != 0)
error("Linker script contradicts \"%s\"'s alignment", error(NULL, 0, "Linker script contradicts \"%s\"'s alignment",
section->name); section->name);
section->isAddressFixed = true; section->isAddressFixed = true;

View File

@@ -6,8 +6,10 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <assert.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
@@ -39,11 +41,55 @@ bool disablePadding; /* -x */
static uint32_t nbErrors = 0; static uint32_t nbErrors = 0;
void error(char const *fmt, ...) /***** Helper function to dump a file stack to stderr *****/
char const *dumpFileStack(struct FileStackNode const *node)
{
char const *lastName;
if (node->parent) {
lastName = dumpFileStack(node->parent);
/* REPT nodes use their parent's name */
if (node->type != NODE_REPT)
lastName = node->name;
fprintf(stderr, "(%" PRIu32 ") -> %s", node->lineNo, lastName);
if (node->type == NODE_REPT) {
for (uint32_t i = 0; i < node->reptDepth; i++)
fprintf(stderr, "::REPT~%" PRIu32, node->iters[i]);
}
} else {
assert(node->type != NODE_REPT);
lastName = node->name;
fputs(lastName, stderr);
}
return lastName;
}
void warning(struct FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
{ {
va_list ap; va_list ap;
fprintf(stderr, "error: "); fputs("warning: ", stderr);
if (where) {
dumpFileStack(where);
fprintf(stderr, "(%" PRIu32 "): ", lineNo);
}
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
putc('\n', stderr);
}
void error(struct FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
{
va_list ap;
fputs("error: ", stderr);
if (where) {
dumpFileStack(where);
fprintf(stderr, "(%" PRIu32 "): ", lineNo);
}
va_start(ap, fmt); va_start(ap, fmt);
vfprintf(stderr, fmt, ap); vfprintf(stderr, fmt, ap);
va_end(ap); va_end(ap);
@@ -53,11 +99,15 @@ void error(char const *fmt, ...)
nbErrors++; nbErrors++;
} }
noreturn_ void fatal(char const *fmt, ...) noreturn_ void fatal(struct FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
{ {
va_list ap; va_list ap;
fprintf(stderr, "fatal: "); fputs("fatal: ", stderr);
if (where) {
dumpFileStack(where);
fprintf(stderr, "(%" PRIu32 "): ", lineNo);
}
va_start(ap, fmt); va_start(ap, fmt);
vfprintf(stderr, fmt, ap); vfprintf(stderr, fmt, ap);
va_end(ap); va_end(ap);
@@ -132,7 +182,7 @@ static void printUsage(void)
" -x, --nopad disable padding of output binary\n" " -x, --nopad disable padding of output binary\n"
" -V, --version print RGBLINK version and exits\n" " -V, --version print RGBLINK version and exits\n"
"\n" "\n"
"For help, use `man rgblink' or go to https://rednex.github.io/rgbds/\n", "For help, use `man rgblink' or go to https://rgbds.gbdev.io/docs/\n",
stderr); stderr);
} }
@@ -177,11 +227,11 @@ int main(int argc, char *argv[])
case 'p': case 'p':
value = strtoul(optarg, &endptr, 0); value = strtoul(optarg, &endptr, 0);
if (optarg[0] == '\0' || *endptr != '\0') { if (optarg[0] == '\0' || *endptr != '\0') {
error("Invalid argument for option 'p'"); error(NULL, 0, "Invalid argument for option 'p'");
value = 0xFF; value = 0xFF;
} }
if (value > 0xFF) { if (value > 0xFF) {
error("Argument for 'p' must be a byte (between 0 and 0xFF)"); error(NULL, 0, "Argument for 'p' must be a byte (between 0 and 0xFF)");
value = 0xFF; value = 0xFF;
} }
padValue = value; padValue = value;
@@ -189,7 +239,7 @@ int main(int argc, char *argv[])
case 's': case 's':
/* FIXME: nobody knows what this does, figure it out */ /* FIXME: nobody knows what this does, figure it out */
(void)optarg; (void)optarg;
warnx("Nobody has any idea what `-s` does"); warning(NULL, 0, "Nobody has any idea what `-s` does");
break; break;
case 't': case 't':
is32kMode = true; is32kMode = true;
@@ -234,8 +284,8 @@ int main(int argc, char *argv[])
bankranges[SECTTYPE_VRAM][1] = BANK_MIN_VRAM; bankranges[SECTTYPE_VRAM][1] = BANK_MIN_VRAM;
/* Read all object files first, */ /* Read all object files first, */
while (curArgIndex < argc) for (obj_Setup(argc - curArgIndex); curArgIndex < argc; curArgIndex++)
obj_ReadFile(argv[curArgIndex++]); obj_ReadFile(argv[curArgIndex], argc - curArgIndex - 1);
/* then process them, */ /* then process them, */
obj_DoSanityChecks(); obj_DoSanityChecks();

View File

@@ -31,6 +31,11 @@ static struct SymbolList {
struct SymbolList *next; struct SymbolList *next;
} *symbolLists; } *symbolLists;
unsigned int nbObjFiles;
static struct {
struct FileStackNode *nodes;
uint32_t nbNodes;
} *nodes;
static struct Assertion *assertions; static struct Assertion *assertions;
/***** Helper functions for reading object files *****/ /***** Helper functions for reading object files *****/
@@ -170,12 +175,56 @@ static char *readstr(FILE *file)
/***** Functions to parse object files *****/ /***** Functions to parse object files *****/
/** /**
* Reads a RGB6 symbol from a file. * Reads a file stack node form a file.
* @param file The file to read from
* @param nodes The file's array of nodes
* @param i The ID of the node in the array
* @param fileName The filename to report in errors
*/
static void readFileStackNode(FILE *file, struct FileStackNode fileNodes[], uint32_t i,
char const *fileName)
{
uint32_t parentID;
tryReadlong(parentID, file,
"%s: Cannot read node #%" PRIu32 "'s parent ID: %s", fileName, i);
fileNodes[i].parent = parentID == -1 ? NULL : &fileNodes[parentID];
tryReadlong(fileNodes[i].lineNo, file,
"%s: Cannot read node #%" PRIu32 "'s line number: %s", fileName, i);
tryGetc(fileNodes[i].type, file, "%s: Cannot read node #%" PRIu32 "'s type: %s",
fileName, i);
switch (fileNodes[i].type) {
case NODE_FILE:
case NODE_MACRO:
tryReadstr(fileNodes[i].name, file,
"%s: Cannot read node #%" PRIu32 "'s file name: %s", fileName, i);
break;
case NODE_REPT:
tryReadlong(fileNodes[i].reptDepth, file,
"%s: Cannot read node #%" PRIu32 "'s rept depth: %s", fileName, i);
fileNodes[i].iters = malloc(sizeof(*fileNodes[i].iters) * fileNodes[i].reptDepth);
if (!fileNodes[i].iters)
fatal(NULL, 0, "%s: Failed to alloc node #%" PRIu32 "'s iters: %s",
fileName, i, strerror(errno));
for (uint32_t k = 0; k < fileNodes[i].reptDepth; k++)
tryReadlong(fileNodes[i].iters[k], file,
"%s: Cannot read node #%" PRIu32 "'s iter #%" PRIu32 ": %s",
fileName, i, k);
if (!fileNodes[i].parent)
fatal(NULL, 0, "%s is not a valid object file: root node (#%"
PRIu32 ") may not be REPT", fileName, i);
}
}
/**
* Reads a symbol from a file.
* @param file The file to read from * @param file The file to read from
* @param symbol The struct to fill * @param symbol The struct to fill
* @param fileName The filename to report in errors * @param fileName The filename to report in errors
*/ */
static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName) static void readSymbol(FILE *file, struct Symbol *symbol,
char const *fileName, struct FileStackNode fileNodes[])
{ {
tryReadstr(symbol->name, file, "%s: Cannot read symbol name: %s", tryReadstr(symbol->name, file, "%s: Cannot read symbol name: %s",
fileName); fileName);
@@ -184,9 +233,12 @@ static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName)
/* If the symbol is defined in this file, read its definition */ /* If the symbol is defined in this file, read its definition */
if (symbol->type != SYMTYPE_IMPORT) { if (symbol->type != SYMTYPE_IMPORT) {
symbol->objFileName = fileName; symbol->objFileName = fileName;
tryReadstr(symbol->fileName, file, uint32_t nodeID;
"%s: Cannot read \"%s\"'s file name: %s",
tryReadlong(nodeID, file,
"%s: Cannot read \"%s\"'s node ID: %s",
fileName, symbol->name); fileName, symbol->name);
symbol->src = &fileNodes[nodeID];
tryReadlong(symbol->lineNo, file, tryReadlong(symbol->lineNo, file,
"%s: Cannot read \"%s\"'s line number: %s", "%s: Cannot read \"%s\"'s line number: %s",
fileName, symbol->name); fileName, symbol->name);
@@ -202,7 +254,7 @@ static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName)
} }
/** /**
* Reads a RGB6 patch from a file. * Reads a patch from a file.
* @param file The file to read from * @param file The file to read from
* @param patch The struct to fill * @param patch The struct to fill
* @param fileName The filename to report in errors * @param fileName The filename to report in errors
@@ -210,10 +262,16 @@ static void readSymbol(FILE *file, struct Symbol *symbol, char const *fileName)
*/ */
static void readPatch(FILE *file, struct Patch *patch, char const *fileName, static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
char const *sectName, uint32_t i, char const *sectName, uint32_t i,
struct Section *fileSections[]) struct Section *fileSections[], struct FileStackNode fileNodes[])
{ {
tryReadstr(patch->fileName, file, uint32_t nodeID;
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s name: %s",
tryReadlong(nodeID, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s node ID: %s",
fileName, sectName, i);
patch->src = &fileNodes[nodeID];
tryReadlong(patch->lineNo, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s line number: %s",
fileName, sectName, i); fileName, sectName, i);
tryReadlong(patch->offset, file, tryReadlong(patch->offset, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s offset: %s", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s offset: %s",
@@ -221,8 +279,7 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
tryReadlong(patch->pcSectionID, file, tryReadlong(patch->pcSectionID, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
fileName, sectName, i); fileName, sectName, i);
patch->pcSection = patch->pcSectionID == -1 patch->pcSection = patch->pcSectionID == -1 ? NULL
? NULL
: fileSections[patch->pcSectionID]; : fileSections[patch->pcSectionID];
tryReadlong(patch->pcOffset, file, tryReadlong(patch->pcOffset, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
@@ -234,16 +291,17 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s RPN size: %s", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s RPN size: %s",
fileName, sectName, i); fileName, sectName, i);
uint8_t *rpnExpression = patch->rpnExpression = malloc(sizeof(*patch->rpnExpression) * patch->rpnSize);
malloc(sizeof(*rpnExpression) * patch->rpnSize); if (!patch->rpnExpression)
size_t nbElementsRead = fread(rpnExpression, sizeof(*rpnExpression), err(1, "%s: Failed to alloc \"%s\"'s patch #%" PRIu32 "'s RPN expression",
fileName, sectName, i);
size_t nbElementsRead = fread(patch->rpnExpression, sizeof(*patch->rpnExpression),
patch->rpnSize, file); patch->rpnSize, file);
if (nbElementsRead != patch->rpnSize) if (nbElementsRead != patch->rpnSize)
errx(1, "%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s", errx(1, "%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s",
fileName, sectName, i, fileName, sectName, i,
feof(file) ? "Unexpected end of file" : strerror(errno)); feof(file) ? "Unexpected end of file" : strerror(errno));
patch->rpnExpression = rpnExpression;
} }
/** /**
@@ -252,8 +310,8 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
* @param section The struct to fill * @param section The struct to fill
* @param fileName The filename to report in errors * @param fileName The filename to report in errors
*/ */
static void readSection(FILE *file, struct Section *section, static void readSection(FILE *file, struct Section *section, char const *fileName,
char const *fileName, struct Section *fileSections[]) struct Section *fileSections[], struct FileStackNode fileNodes[])
{ {
int32_t tmp; int32_t tmp;
uint8_t byte; uint8_t byte;
@@ -280,7 +338,7 @@ static void readSection(FILE *file, struct Section *section,
fileName, section->name); fileName, section->name);
section->isAddressFixed = tmp >= 0; section->isAddressFixed = tmp >= 0;
if (tmp > UINT16_MAX) { if (tmp > UINT16_MAX) {
error("\"%s\"'s org is too large (%" PRId32 ")", error(NULL, 0, "\"%s\"'s org is too large (%" PRId32 ")",
section->name, tmp); section->name, tmp);
tmp = UINT16_MAX; tmp = UINT16_MAX;
} }
@@ -296,7 +354,7 @@ static void readSection(FILE *file, struct Section *section,
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s alignment offset: %s", tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s alignment offset: %s",
fileName, section->name); fileName, section->name);
if (tmp > UINT16_MAX) { if (tmp > UINT16_MAX) {
error("\"%s\"'s alignment offset is too large (%" PRId32 ")", error(NULL, 0, "\"%s\"'s alignment offset is too large (%" PRId32 ")",
section->name, tmp); section->name, tmp);
tmp = UINT16_MAX; tmp = UINT16_MAX;
} }
@@ -332,7 +390,7 @@ static void readSection(FILE *file, struct Section *section,
section->name); section->name);
for (uint32_t i = 0; i < section->nbPatches; i++) { for (uint32_t i = 0; i < section->nbPatches; i++) {
readPatch(file, &patches[i], fileName, section->name, readPatch(file, &patches[i], fileName, section->name,
i, fileSections); i, fileSections, fileNodes);
} }
section->patches = patches; section->patches = patches;
} }
@@ -375,13 +433,13 @@ static void linkSymToSect(struct Symbol const *symbol, struct Section *section)
*/ */
static void readAssertion(FILE *file, struct Assertion *assert, static void readAssertion(FILE *file, struct Assertion *assert,
char const *fileName, uint32_t i, char const *fileName, uint32_t i,
struct Section *fileSections[]) struct Section *fileSections[], struct FileStackNode fileNodes[])
{ {
char assertName[sizeof("Assertion #" EXPAND_AND_STR(UINT32_MAX))]; char assertName[sizeof("Assertion #" EXPAND_AND_STR(UINT32_MAX))];
snprintf(assertName, sizeof(assertName), "Assertion #%" PRIu32, i); snprintf(assertName, sizeof(assertName), "Assertion #%" PRIu32, i);
readPatch(file, &assert->patch, fileName, assertName, 0, fileSections); readPatch(file, &assert->patch, fileName, assertName, 0, fileSections, fileNodes);
tryReadstr(assert->message, file, "%s: Cannot read assertion's message: %s", tryReadstr(assert->message, file, "%s: Cannot read assertion's message: %s",
fileName); fileName);
} }
@@ -394,11 +452,7 @@ static inline struct Section *getMainSection(struct Section *section)
return section; return section;
} }
/** void obj_ReadFile(char const *fileName, unsigned int fileID)
* Reads an object file of any supported format
* @param fileName The filename to report for errors
*/
void obj_ReadFile(char const *fileName)
{ {
FILE *file = strcmp("-", fileName) ? fopen(fileName, "rb") : stdin; FILE *file = strcmp("-", fileName) ? fopen(fileName, "rb") : stdin;
@@ -438,6 +492,14 @@ void obj_ReadFile(char const *fileName)
nbSectionsToAssign += nbSections; nbSectionsToAssign += nbSections;
tryReadlong(nodes[fileID].nbNodes, file, "%s: Cannot read number of nodes: %s", fileName);
nodes[fileID].nodes = calloc(nodes[fileID].nbNodes, sizeof(nodes[fileID].nodes[0]));
if (!nodes[fileID].nodes)
err(1, "Failed to get memory for %s's nodes", fileName);
verbosePrint("Reading %u nodes...\n", nodes[fileID].nbNodes);
for (uint32_t i = 0; i < nodes[fileID].nbNodes; i++)
readFileStackNode(file, nodes[fileID].nodes, i, fileName);
/* This file's symbols, kept to link sections to them */ /* This file's symbols, kept to link sections to them */
struct Symbol **fileSymbols = struct Symbol **fileSymbols =
malloc(sizeof(*fileSymbols) * nbSymbols + 1); malloc(sizeof(*fileSymbols) * nbSymbols + 1);
@@ -464,7 +526,7 @@ void obj_ReadFile(char const *fileName)
if (!symbol) if (!symbol)
err(1, "%s: Couldn't create new symbol", fileName); err(1, "%s: Couldn't create new symbol", fileName);
readSymbol(file, symbol, fileName); readSymbol(file, symbol, fileName, nodes[fileID].nodes);
fileSymbols[i] = symbol; fileSymbols[i] = symbol;
if (symbol->type == SYMTYPE_EXPORT) if (symbol->type == SYMTYPE_EXPORT)
@@ -485,7 +547,7 @@ void obj_ReadFile(char const *fileName)
err(1, "%s: Couldn't create new section", fileName); err(1, "%s: Couldn't create new section", fileName);
fileSections[i]->nextu = NULL; fileSections[i]->nextu = NULL;
readSection(file, fileSections[i], fileName, fileSections); readSection(file, fileSections[i], fileName, fileSections, nodes[fileID].nodes);
fileSections[i]->fileSymbols = fileSymbols; fileSections[i]->fileSymbols = fileSymbols;
if (nbSymPerSect[i]) { if (nbSymPerSect[i]) {
fileSections[i]->symbols = malloc(nbSymPerSect[i] fileSections[i]->symbols = malloc(nbSymPerSect[i]
@@ -535,7 +597,7 @@ void obj_ReadFile(char const *fileName)
if (!assertion) if (!assertion)
err(1, "%s: Couldn't create new assertion", fileName); err(1, "%s: Couldn't create new assertion", fileName);
readAssertion(file, assertion, fileName, i, fileSections); readAssertion(file, assertion, fileName, i, fileSections, nodes[fileID].nodes);
assertion->fileSymbols = fileSymbols; assertion->fileSymbols = fileSymbols;
assertion->next = assertions; assertion->next = assertions;
assertions = assertion; assertions = assertion;
@@ -555,6 +617,15 @@ void obj_CheckAssertions(void)
patch_CheckAssertions(assertions); patch_CheckAssertions(assertions);
} }
void obj_Setup(unsigned int nbFiles)
{
nbObjFiles = nbFiles;
if (nbFiles > SIZE_MAX / sizeof(*nodes))
fatal(NULL, 0, "Impossible to link more than %zu files!", SIZE_MAX / sizeof(*nodes));
nodes = malloc(sizeof(*nodes) * nbFiles);
}
static void freeSection(struct Section *section, void *arg) static void freeSection(struct Section *section, void *arg)
{ {
(void)arg; (void)arg;
@@ -562,12 +633,8 @@ static void freeSection(struct Section *section, void *arg)
free(section->name); free(section->name);
if (sect_HasData(section->type)) { if (sect_HasData(section->type)) {
free(section->data); free(section->data);
for (int32_t i = 0; i < section->nbPatches; i++) { for (int32_t i = 0; i < section->nbPatches; i++)
struct Patch *patch = &section->patches[i]; free(section->patches[i].rpnExpression);
free(patch->fileName);
free(patch->rpnExpression);
}
free(section->patches); free(section->patches);
} }
free(section->symbols); free(section->symbols);
@@ -577,13 +644,20 @@ static void freeSection(struct Section *section, void *arg)
static void freeSymbol(struct Symbol *symbol) static void freeSymbol(struct Symbol *symbol)
{ {
free(symbol->name); free(symbol->name);
if (symbol->type != SYMTYPE_IMPORT)
free(symbol->fileName);
free(symbol); free(symbol);
} }
void obj_Cleanup(void) void obj_Cleanup(void)
{ {
for (unsigned int i = 0; i < nbObjFiles; i++) {
for (uint32_t j = 0; j < nodes[i].nbNodes; j++) {
if (nodes[i].nodes[j].type == NODE_REPT)
free(nodes[i].nodes[j].iters);
}
free(nodes[i].nodes);
}
free(nodes);
sym_CleanupSymbols(); sym_CleanupSymbols();
sect_ForEach(freeSection, NULL); sect_ForEach(freeSection, NULL);

View File

@@ -6,11 +6,13 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <assert.h>
#include <inttypes.h> #include <inttypes.h>
#include <limits.h> #include <limits.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "link/object.h"
#include "link/patch.h" #include "link/patch.h"
#include "link/section.h" #include "link/section.h"
#include "link/symbol.h" #include "link/symbol.h"
@@ -60,9 +62,17 @@ static int32_t asl(int32_t value, int32_t shiftamt)
return (uint32_t)value << shiftamt; return (uint32_t)value << shiftamt;
} }
/* This is an "empty"-type stack */ /*
* This is an "empty"-type stack. Apart from the actual values, we also remember
* whether the value is a placeholder inserted for error recovery. This allows
* us to avoid cascading errors.
*
* The best way to think about this is a stack of (value, errorFlag) pairs.
* They are only separated for reasons of memory efficiency.
*/
struct RPNStack { struct RPNStack {
int32_t *buf; int32_t *values;
bool *errorFlags;
size_t size; size_t size;
size_t capacity; size_t capacity;
} stack; } stack;
@@ -70,8 +80,9 @@ struct RPNStack {
static inline void initRPNStack(void) static inline void initRPNStack(void)
{ {
stack.capacity = 64; stack.capacity = 64;
stack.buf = malloc(sizeof(*stack.buf) * stack.capacity); stack.values = malloc(sizeof(*stack.values) * stack.capacity);
if (!stack.buf) stack.errorFlags = malloc(sizeof(*stack.errorFlags) * stack.capacity);
if (!stack.values || !stack.errorFlags)
err(1, "Failed to init RPN stack"); err(1, "Failed to init RPN stack");
} }
@@ -80,7 +91,7 @@ static inline void clearRPNStack(void)
stack.size = 0; stack.size = 0;
} }
static void pushRPN(int32_t value) static void pushRPN(int32_t value, bool comesFromError)
{ {
if (stack.size >= stack.capacity) { if (stack.size >= stack.capacity) {
static const size_t increase_factor = 2; static const size_t increase_factor = 2;
@@ -89,48 +100,59 @@ static void pushRPN(int32_t value)
errx(1, "Overflow in RPN stack resize"); errx(1, "Overflow in RPN stack resize");
stack.capacity *= increase_factor; stack.capacity *= increase_factor;
stack.buf = stack.values =
realloc(stack.buf, sizeof(*stack.buf) * stack.capacity); realloc(stack.values, sizeof(*stack.values) * stack.capacity);
stack.errorFlags =
realloc(stack.errorFlags, sizeof(*stack.errorFlags) * stack.capacity);
/* /*
* Static analysis tools complain that the capacity might become * Static analysis tools complain that the capacity might become
* zero due to overflow, but fail to realize that it's caught by * zero due to overflow, but fail to realize that it's caught by
* the overflow check above. Hence the stringent check below. * the overflow check above. Hence the stringent check below.
*/ */
if (!stack.buf || !stack.capacity) if (!stack.values || !stack.errorFlags || !stack.capacity)
err(1, "Failed to resize RPN stack"); err(1, "Failed to resize RPN stack");
} }
stack.buf[stack.size] = value; stack.values[stack.size] = value;
stack.errorFlags[stack.size] = comesFromError;
stack.size++; stack.size++;
} }
static int32_t popRPN(char const *fileName) // This flag tracks whether the RPN op that is currently being evaluated
// has popped any values with the error flag set.
static bool isError = false;
static int32_t popRPN(struct FileStackNode const *node, uint32_t lineNo)
{ {
if (stack.size == 0) if (stack.size == 0)
errx(1, "%s: Internal error, RPN stack empty", fileName); fatal(node, lineNo, "Internal error, RPN stack empty");
stack.size--; stack.size--;
return stack.buf[stack.size]; isError |= stack.errorFlags[stack.size];
return stack.values[stack.size];
} }
static inline void freeRPNStack(void) static inline void freeRPNStack(void)
{ {
free(stack.buf); free(stack.values);
free(stack.errorFlags);
} }
/* RPN operators */ /* RPN operators */
static uint32_t getRPNByte(uint8_t const **expression, int32_t *size, static uint32_t getRPNByte(uint8_t const **expression, int32_t *size,
char const *fileName) struct FileStackNode const *node, uint32_t lineNo)
{ {
if (!(*size)--) if (!(*size)--)
errx(1, "%s: RPN expression overread", fileName); fatal(node, lineNo, "Internal error, RPN expression overread");
return *(*expression)++; return *(*expression)++;
} }
static struct Symbol const *getSymbol(struct Symbol const * const *symbolList, static struct Symbol const *getSymbol(struct Symbol const * const *symbolList,
uint32_t index) uint32_t index)
{ {
assert(index != -1); /* PC needs to be handled specially, not here */
struct Symbol const *symbol = symbolList[index]; struct Symbol const *symbol = symbolList[index];
/* If the symbol is defined elsewhere... */ /* If the symbol is defined elsewhere... */
@@ -145,12 +167,14 @@ static struct Symbol const *getSymbol(struct Symbol const * const *symbolList,
* @param patch The patch to compute the value of * @param patch The patch to compute the value of
* @param section The section the patch is contained in * @param section The section the patch is contained in
* @return The patch's value * @return The patch's value
* @return isError Set if an error occurred during evaluation, and further
* errors caused by the value should be suppressed.
*/ */
static int32_t computeRPNExpr(struct Patch const *patch, static int32_t computeRPNExpr(struct Patch const *patch,
struct Symbol const * const *fileSymbols) struct Symbol const * const *fileSymbols)
{ {
/* Small shortcut to avoid a lot of repetition */ /* Small shortcut to avoid a lot of repetition */
#define popRPN() popRPN(patch->fileName) #define popRPN() popRPN(patch->src, patch->lineNo)
uint8_t const *expression = patch->rpnExpression; uint8_t const *expression = patch->rpnExpression;
int32_t size = patch->rpnSize; int32_t size = patch->rpnSize;
@@ -159,9 +183,11 @@ static int32_t computeRPNExpr(struct Patch const *patch,
while (size > 0) { while (size > 0) {
enum RPNCommand command = getRPNByte(&expression, &size, enum RPNCommand command = getRPNByte(&expression, &size,
patch->fileName); patch->src, patch->lineNo);
int32_t value; int32_t value;
isError = false;
/* /*
* Friendly reminder: * Friendly reminder:
* Be VERY careful with two `popRPN` in the same expression. * Be VERY careful with two `popRPN` in the same expression.
@@ -187,7 +213,9 @@ static int32_t computeRPNExpr(struct Patch const *patch,
case RPN_DIV: case RPN_DIV:
value = popRPN(); value = popRPN();
if (value == 0) { if (value == 0) {
error("%s: Division by 0", patch->fileName); if (!isError)
error(patch->src, patch->lineNo, "Division by 0");
isError = true;
popRPN(); popRPN();
value = INT32_MAX; value = INT32_MAX;
} else { } else {
@@ -197,7 +225,9 @@ static int32_t computeRPNExpr(struct Patch const *patch,
case RPN_MOD: case RPN_MOD:
value = popRPN(); value = popRPN();
if (value == 0) { if (value == 0) {
error("%s: Modulo by 0", patch->fileName); if (!isError)
error(patch->src, patch->lineNo, "Modulo by 0");
isError = true;
popRPN(); popRPN();
value = 0; value = 0;
} else { } else {
@@ -269,18 +299,20 @@ static int32_t computeRPNExpr(struct Patch const *patch,
value = 0; value = 0;
for (uint8_t shift = 0; shift < 32; shift += 8) for (uint8_t shift = 0; shift < 32; shift += 8)
value |= getRPNByte(&expression, &size, value |= getRPNByte(&expression, &size,
patch->fileName) << shift; patch->src, patch->lineNo) << shift;
symbol = getSymbol(fileSymbols, value); symbol = getSymbol(fileSymbols, value);
if (!symbol) { if (!symbol) {
error("%s: Requested BANK() of symbol \"%s\", which was not found", error(patch->src, patch->lineNo,
patch->fileName, "Requested BANK() of symbol \"%s\", which was not found",
fileSymbols[value]->name); fileSymbols[value]->name);
isError = true;
value = 1; value = 1;
} else if (!symbol->section) { } else if (!symbol->section) {
error("%s: Requested BANK() of non-label symbol \"%s\"", error(patch->src, patch->lineNo,
patch->fileName, "Requested BANK() of non-label symbol \"%s\"",
fileSymbols[value]->name); fileSymbols[value]->name);
isError = true;
value = 1; value = 1;
} else { } else {
value = symbol->section->bank; value = symbol->section->bank;
@@ -289,14 +321,16 @@ static int32_t computeRPNExpr(struct Patch const *patch,
case RPN_BANK_SECT: case RPN_BANK_SECT:
name = (char const *)expression; name = (char const *)expression;
while (getRPNByte(&expression, &size, patch->fileName)) while (getRPNByte(&expression, &size, patch->src, patch->lineNo))
; ;
sect = sect_GetSection(name); sect = sect_GetSection(name);
if (!sect) { if (!sect) {
error("%s: Requested BANK() of section \"%s\", which was not found", error(patch->src, patch->lineNo,
patch->fileName, name); "Requested BANK() of section \"%s\", which was not found",
name);
isError = true;
value = 1; value = 1;
} else { } else {
value = sect->bank; value = sect->bank;
@@ -305,7 +339,9 @@ static int32_t computeRPNExpr(struct Patch const *patch,
case RPN_BANK_SELF: case RPN_BANK_SELF:
if (!patch->pcSection) { if (!patch->pcSection) {
error("%s: PC has no bank outside a section"); error(patch->src, patch->lineNo,
"PC has no bank outside a section");
isError = true;
value = 1; value = 1;
} else { } else {
value = patch->pcSection->bank; value = patch->pcSection->bank;
@@ -314,11 +350,13 @@ static int32_t computeRPNExpr(struct Patch const *patch,
case RPN_HRAM: case RPN_HRAM:
value = popRPN(); value = popRPN();
if (value < 0 if (!isError && (value < 0
|| (value > 0xFF && value < 0xFF00) || (value > 0xFF && value < 0xFF00)
|| value > 0xFFFF) || value > 0xFFFF)) {
error("%s: Value %" PRId32 " is not in HRAM range", error(patch->src, patch->lineNo,
patch->fileName, value); "Value %" PRId32 " is not in HRAM range", value);
isError = true;
}
value &= 0xFF; value &= 0xFF;
break; break;
@@ -327,9 +365,12 @@ static int32_t computeRPNExpr(struct Patch const *patch,
/* Acceptable values are 0x00, 0x08, 0x10, ..., 0x38 /* Acceptable values are 0x00, 0x08, 0x10, ..., 0x38
* They can be easily checked with a bitmask * They can be easily checked with a bitmask
*/ */
if (value & ~0x38) if (value & ~0x38) {
error("%s: Value %" PRId32 " is not a RST vector", if (!isError)
patch->fileName, value); error(patch->src, patch->lineNo,
"Value %" PRId32 " is not a RST vector", value);
isError = true;
}
value |= 0xC7; value |= 0xC7;
break; break;
@@ -337,43 +378,49 @@ static int32_t computeRPNExpr(struct Patch const *patch,
value = 0; value = 0;
for (uint8_t shift = 0; shift < 32; shift += 8) for (uint8_t shift = 0; shift < 32; shift += 8)
value |= getRPNByte(&expression, &size, value |= getRPNByte(&expression, &size,
patch->fileName) << shift; patch->src, patch->lineNo) << shift;
break; break;
case RPN_SYM: case RPN_SYM:
value = 0; value = 0;
for (uint8_t shift = 0; shift < 32; shift += 8) for (uint8_t shift = 0; shift < 32; shift += 8)
value |= getRPNByte(&expression, &size, value |= getRPNByte(&expression, &size,
patch->fileName) << shift; patch->src, patch->lineNo) << shift;
if (value == -1) { /* PC */
if (!patch->pcSection) {
error(patch->src, patch->lineNo,
"PC has no value outside a section");
value = 0;
isError = true;
} else {
value = patch->pcOffset + patch->pcSection->org;
}
} else {
symbol = getSymbol(fileSymbols, value); symbol = getSymbol(fileSymbols, value);
if (!symbol) { if (!symbol) {
error("%s: Unknown symbol \"%s\"", error(patch->src, patch->lineNo,
patch->fileName, "Unknown symbol \"%s\"", fileSymbols[value]->name);
fileSymbols[value]->name); isError = true;
} else if (strcmp(symbol->name, "@")) { } else {
value = symbol->value; value = symbol->value;
/* Symbols attached to sections have offsets */ /* Symbols attached to sections have offsets */
if (symbol->section) if (symbol->section)
value += symbol->section->org; value += symbol->section->org;
} else if (!patch->pcSection) { }
error("%s: PC has no value outside a section",
patch->fileName);
value = 0;
} else {
value = patch->pcOffset + patch->pcSection->org;
} }
break; break;
} }
pushRPN(value); pushRPN(value, isError);
} }
if (stack.size > 1) if (stack.size > 1)
error("%s: RPN stack has %zu entries on exit, not 1", error(patch->src, patch->lineNo,
patch->fileName, stack.size); "RPN stack has %zu entries on exit, not 1", stack.size);
isError = false;
return popRPN(); return popRPN();
#undef popRPN #undef popRPN
@@ -385,27 +432,34 @@ void patch_CheckAssertions(struct Assertion *assert)
initRPNStack(); initRPNStack();
while (assert) { while (assert) {
if (!computeRPNExpr(&assert->patch, int32_t value = computeRPNExpr(&assert->patch,
(struct Symbol const * const *) (struct Symbol const * const *)assert->fileSymbols);
assert->fileSymbols)) { enum AssertionType type = (enum AssertionType)assert->patch.type;
switch ((enum AssertionType)assert->patch.type) {
if (!isError && !value) {
switch (type) {
case ASSERT_FATAL: case ASSERT_FATAL:
fatal("%s: %s", assert->patch.fileName, fatal(assert->patch.src, assert->patch.lineNo, "%s",
assert->message[0] ? assert->message assert->message[0] ? assert->message
: "assert failure"); : "assert failure");
/* Not reached */ /* Not reached */
break; /* Here so checkpatch doesn't complain */ break; /* Here so checkpatch doesn't complain */
case ASSERT_ERROR: case ASSERT_ERROR:
error("%s: %s", assert->patch.fileName, error(assert->patch.src, assert->patch.lineNo, "%s",
assert->message[0] ? assert->message assert->message[0] ? assert->message
: "assert failure"); : "assert failure");
break; break;
case ASSERT_WARN: case ASSERT_WARN:
warnx("%s: %s", assert->patch.fileName, warning(assert->patch.src, assert->patch.lineNo, "%s",
assert->message[0] ? assert->message assert->message[0] ? assert->message
: "assert failure"); : "assert failure");
break; break;
} }
} else if (isError && type == ASSERT_FATAL) {
fatal(assert->patch.src, assert->patch.lineNo,
"couldn't evaluate assertion%s%s",
assert->message[0] ? ": " : "",
assert->message);
} }
struct Assertion *next = assert->next; struct Assertion *next = assert->next;
@@ -441,9 +495,10 @@ static void applyFilePatches(struct Section *section, struct Section *dataSectio
+ patch->pcOffset + 1; + patch->pcOffset + 1;
int16_t jumpOffset = value - address; int16_t jumpOffset = value - address;
if (jumpOffset < -128 || jumpOffset > 127) if (!isError && (jumpOffset < -128 || jumpOffset > 127))
error("%s: jr target out of reach (expected -129 < %" PRId16 " < 128)", error(patch->src, patch->lineNo,
patch->fileName, jumpOffset); "jr target out of reach (expected -129 < %" PRId16 " < 128)",
jumpOffset);
dataSection->data[offset] = jumpOffset & 0xFF; dataSection->data[offset] = jumpOffset & 0xFF;
} else { } else {
/* Patch a certain number of bytes */ /* Patch a certain number of bytes */
@@ -457,11 +512,11 @@ static void applyFilePatches(struct Section *section, struct Section *dataSectio
[PATCHTYPE_LONG] = {4, INT32_MIN, INT32_MAX} [PATCHTYPE_LONG] = {4, INT32_MIN, INT32_MAX}
}; };
if (value < types[patch->type].min if (!isError && (value < types[patch->type].min
|| value > types[patch->type].max) || value > types[patch->type].max))
error("%s: Value %#" PRIx32 "%s is not %u-bit", error(patch->src, patch->lineNo,
patch->fileName, value, "Value %#" PRIx32 "%s is not %u-bit",
value < 0 ? " (maybe negative?)" : "", value, value < 0 ? " (maybe negative?)" : "",
types[patch->type].size * 8U); types[patch->type].size * 8U);
for (uint8_t i = 0; i < types[patch->type].size; i++) { for (uint8_t i = 0; i < types[patch->type].size; i++) {
dataSection->data[offset + i] = value & 0xFF; dataSection->data[offset + i] = value & 0xFF;

View File

@@ -134,7 +134,7 @@ Here is a more complete example:
.Ed .Ed
.Sh BUGS .Sh BUGS
Please report bugs on Please report bugs on
.Lk https://github.com/rednex/rgbds/issues GitHub . .Lk https://github.com/gbdev/rgbds/issues GitHub .
.Sh SEE ALSO .Sh SEE ALSO
.Xr rgbasm 1 , .Xr rgbasm 1 ,
.Xr rgblink 5 , .Xr rgblink 5 ,
@@ -145,4 +145,4 @@ Please report bugs on
.Nm .Nm
was originally written by Carsten S\(/orensen as part of the ASMotor package, and was later packaged in RGBDS by Justin Lloyd. was originally written by Carsten S\(/orensen as part of the ASMotor package, and was later packaged in RGBDS by Justin Lloyd.
It is now maintained by a number of contributors at It is now maintained by a number of contributors at
.Lk https://github.com/rednex/rgbds . .Lk https://github.com/gbdev/rgbds .

View File

@@ -88,4 +88,4 @@ in the linker script.
was originally written by Carsten S\(/orensen as part of the ASMotor package, was originally written by Carsten S\(/orensen as part of the ASMotor package,
and was later packaged in RGBDS by Justin Lloyd. It is now maintained by a and was later packaged in RGBDS by Justin Lloyd. It is now maintained by a
number of contributors at number of contributors at
.Lk https://github.com/rednex/rgbds . .Lk https://github.com/gbdev/rgbds .

View File

@@ -8,9 +8,12 @@
#include <inttypes.h> #include <inttypes.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h>
#include "link/object.h"
#include "link/symbol.h" #include "link/symbol.h"
#include "link/main.h" #include "link/main.h"
#include "extern/err.h" #include "extern/err.h"
#include "hashmap.h" #include "hashmap.h"
@@ -40,11 +43,15 @@ void sym_AddSymbol(struct Symbol *symbol)
/* Check if the symbol already exists */ /* Check if the symbol already exists */
struct Symbol *other = hash_GetElement(symbols, symbol->name); struct Symbol *other = hash_GetElement(symbols, symbol->name);
if (other) if (other) {
errx(1, "\"%s\" both in %s from %s(%" PRId32 ") and in %s from %s(%" PRId32 ")", fprintf(stderr, "error: \"%s\" both in %s from ", symbol->name, symbol->objFileName);
symbol->name, dumpFileStack(symbol->src);
symbol->objFileName, symbol->fileName, symbol->lineNo, fprintf(stderr, "(%" PRIu32 ") and in %s from ",
other->objFileName, other->fileName, other->lineNo); symbol->lineNo, other->objFileName);
dumpFileStack(other->src);
fprintf(stderr, "(%" PRIu32 ")\n", other->lineNo);
exit(1);
}
/* If not, add it */ /* If not, add it */
bool collided = hash_AddElement(symbols, symbol->name, symbol); bool collided = hash_AddElement(symbols, symbol->name, symbol);

View File

@@ -1,7 +1,7 @@
.\" .\"
.\" This file is part of RGBDS. .\" This file is part of RGBDS.
.\" .\"
.\" Copyright (c) 2017-2018, Antonio Nino Diaz and RGBDS contributors. .\" Copyright (c) 2017-2020, Antonio Nino Diaz and RGBDS contributors.
.\" .\"
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
@@ -34,9 +34,42 @@ is a 0terminated string of
; Header ; Header
BYTE ID[4] ; "RGB9" BYTE ID[4] ; "RGB9"
LONG RevisionNumber ; The format's revision number this file uses LONG RevisionNumber ; The format's revision number this file uses.
LONG NumberOfSymbols ; The number of symbols used in this file LONG NumberOfSymbols ; The number of symbols used in this file.
LONG NumberOfSections ; The number of sections used in this file LONG NumberOfSections ; The number of sections used in this file.
; File info
LONG NumberOfNodes ; The number of nodes contained in this file.
REPT NumberOfNodes ; IMPORTANT NOTE: the nodes are actually written in
; **reverse** order, meaningthe node with ID 0 is
; the last one in the file!
LONG ParentID ; ID of the parent node, -1 means this is the root.
LONG ParentLineNo ; Line at which the parent context was exited.
; Meaningless on the root node.
BYTE Type ; 0 = REPT node
; 1 = File node
; 2 = Macro node
IF Type != 0 ; If the node is not a REPT...
STRING Name ; The node's name: either a file name, or macro name
; prefixed by its definition file name.
ELSE ; If the node is a REPT, it also contains the iter
; counts of all the parent REPTs.
LONG Depth ; Size of the array below.
LONG Iter[Depth] ; The number of REPT iterations by increasing depth.
ENDC
ENDR
; Symbols ; Symbols
@@ -48,12 +81,10 @@ REPT NumberOfSymbols ; Number of symbols defined in this object file.
BYTE Type ; 0 = LOCAL symbol only used in this file. BYTE Type ; 0 = LOCAL symbol only used in this file.
; 1 = IMPORT this symbol from elsewhere ; 1 = IMPORT this symbol from elsewhere
; 2 = EXPORT this symbol to other objects. ; 2 = EXPORT this symbol to other objects.
; Bit 7 is independent from the above value, and
; encodes whether the section is unionized
IF (Type & 0x7F) != 1 ; If symbol is defined in this object file. IF (Type & 0x7F) != 1 ; If symbol is defined in this object file.
STRING FileName ; File where the symbol is defined. LONG SourceFile ; File where the symbol is defined.
LONG LineNum ; Line number in the file where the symbol is defined. LONG LineNum ; Line number in the file where the symbol is defined.
@@ -84,6 +115,10 @@ REPT NumberOfSections
; 5 = WRAMX ; 5 = WRAMX
; 6 = SRAM ; 6 = SRAM
; 7 = OAM ; 7 = OAM
; Bits 7 and 6 are independent from the above value:
; Bit 7 encodes whether the section is unionized
; Bit 6 encodes whether the section is a fragment
; Bits 6 and 7 may not be both set at the same time!
LONG Org ; Address to fix this section at. -1 if the linker should LONG Org ; Address to fix this section at. -1 if the linker should
; decide (floating address). ; decide (floating address).
@@ -105,8 +140,10 @@ REPT NumberOfSections
REPT NumberOfPatches REPT NumberOfPatches
STRING SourceFile ; Name of the source file (for printing error LONG SourceFile ; ID of the source file node (for printing
; messages). ; error messages).
LONG LineNo ; Line at which the patch was created.
LONG Offset ; Offset into the section where patch should LONG Offset ; Offset into the section where patch should
; be applied (in bytes). ; be applied (in bytes).
@@ -143,7 +180,9 @@ LONG NumberOfAssertions
REPT NumberOfAssertions REPT NumberOfAssertions
STRING SourceFile ; Name of the source file (for printing the failure). LONG SourceFile ; ID of the source file node (for printing the failure).
LONG LineNo ; Line at which the assertion was created.
LONG Offset ; Offset into the section where the assertion is located. LONG Offset ; Offset into the section where the assertion is located.
@@ -207,7 +246,7 @@ with some bytes being special prefixes for integers and symbols.
.It Li $50 Ta Li BANK(symbol) , .It Li $50 Ta Li BANK(symbol) ,
a a
.Ar LONG .Ar LONG
Symbol ID follows. Symbol ID follows, where -1 means PC
.It Li $51 Ta Li BANK(section_name) , .It Li $51 Ta Li BANK(section_name) ,
a null-terminated string follows. a null-terminated string follows.
.It Li $52 Ta Li Current BANK() .It Li $52 Ta Li Current BANK()
@@ -231,4 +270,4 @@ symbol ID follows.
was originally written by Carsten S\(/orensen as part of the ASMotor package, was originally written by Carsten S\(/orensen as part of the ASMotor package,
and was later packaged in RGBDS by Justin Lloyd. and was later packaged in RGBDS by Justin Lloyd.
It is now maintained by a number of contributors at It is now maintained by a number of contributors at
.Lk https://github.com/rednex/rgbds . .Lk https://github.com/gbdev/rgbds .

View File

@@ -45,4 +45,10 @@ It is now maintained by a number of contributors at
.Lk https://github.com/rednex/rgbds . .Lk https://github.com/rednex/rgbds .
.It .It
2018, codebase relicensed under the MIT license. 2018, codebase relicensed under the MIT license.
.It
2020, repository is moved to the gbdev organisation, at
.Lk https://github.com/gbdev/rgbds .
The
.Lk https://rgbds.gbdev.io
website serving documentation and downloads is created.
.El .El

1
test/asm/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
quote\"file.*

View File

@@ -6,5 +6,5 @@ ERROR: assert.asm(18):
Expected constant expression: 'FloatingBase' is not constant at assembly time Expected constant expression: 'FloatingBase' is not constant at assembly time
ERROR: assert.asm(18): ERROR: assert.asm(18):
Assertion failed Assertion failed
ERROR: assert.asm(21): FATAL: assert.asm(21):
Assertion failed Assertion failed

View File

@@ -1,2 +1,2 @@
ERROR: divzero-instr.asm(2): FATAL: divzero-instr.asm(2):
Division by zero Division by zero

View File

@@ -1,2 +1,2 @@
ERROR: divzero-section-bank.asm(1): FATAL: divzero-section-bank.asm(1):
Division by zero Division by zero

4
test/asm/equs-nest.asm Normal file
View File

@@ -0,0 +1,4 @@
X1 equs "Y1 equs \"\\\"Success!\\\\n\\\"\""
Y1 equs "Z1"
X1
PRINTT Z1

0
test/asm/equs-nest.err Normal file
View File

1
test/asm/equs-nest.out Normal file
View File

@@ -0,0 +1 @@
Success!

View File

@@ -0,0 +1,4 @@
ACT equs "WARN \"First\"\nWARN \"Second\""
ACT
WARN "Third"

View File

@@ -0,0 +1,7 @@
warning: equs-newline.asm(3): [-Wuser]
First
while expanding symbol "ACT"
warning: equs-newline.asm(3): [-Wuser]
Second
warning: equs-newline.asm(4): [-Wuser]
Third

View File

2
test/asm/equs-purge.asm Normal file
View File

@@ -0,0 +1,2 @@
BYE equs "PURGE BYE\nWARN \"Crash?\"\n \n"
BYE

3
test/asm/equs-purge.err Normal file
View File

@@ -0,0 +1,3 @@
warning: equs-purge.asm(2): [-Wuser]
Crash?
while expanding symbol "BYE"

0
test/asm/equs-purge.out Normal file
View File

View File

@@ -1,2 +1,6 @@
recurse EQUS "recurse " recurse EQUS "recurse "
recurse recurse
; FIXME: also handle the following:
; recurse EQUS "recurse"
; recurse

View File

@@ -1,4 +1,4 @@
ERROR: equs-recursion.asm(2): FATAL: equs-recursion.asm(2):
Recursion limit (64) exceeded Recursion limit (64) exceeded
while expanding symbol "recurse" while expanding symbol "recurse"
while expanding symbol "recurse" while expanding symbol "recurse"
@@ -64,3 +64,4 @@ while expanding symbol "recurse"
while expanding symbol "recurse" while expanding symbol "recurse"
while expanding symbol "recurse" while expanding symbol "recurse"
while expanding symbol "recurse" while expanding symbol "recurse"
while expanding symbol "recurse"

1
test/asm/file-sym.asm Normal file
View File

@@ -0,0 +1 @@
PRINTT "{__FILE__}\n"

0
test/asm/file-sym.err Normal file
View File

1
test/asm/file-sym.out Normal file
View File

@@ -0,0 +1 @@
"file-sym.asm"

View File

@@ -1 +1 @@
x<EFBFBD> <EFBFBD>

View File

@@ -1,2 +1,3 @@
ERROR: garbage_char.asm(1): ERROR: garbage_char.asm(1):
Found garbage character: 0xFF Unknown character 0xFF
error: Assembly aborted (1 errors)!

13
test/asm/if-macro.asm Normal file
View File

@@ -0,0 +1,13 @@
m: macro
if 0
WARN "3"
else
WARN "5"
endc
endm
if 1
m
else
WARN "12"
endc

2
test/asm/if-macro.err Normal file
View File

@@ -0,0 +1,2 @@
warning: if-macro.asm(10) -> if-macro.asm::m(5): [-Wuser]
5

0
test/asm/if-macro.out Normal file
View File

View File

@@ -1,2 +1,2 @@
if {@} if "{@}"
endc endc

View File

@@ -1,2 +1,2 @@
ERROR: include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1): FATAL: include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1) -> include-recursion.asm(1):
Recursion limit (64) exceeded Recursion limit (64) exceeded

7
test/asm/jr-@.asm Normal file
View File

@@ -0,0 +1,7 @@
SECTION "fixed", ROM0[0]
jr @
; We need this section to be floating because RGBASM can know the value of PC
; otherwise, leading to different behavior
; FIXME: we rely on this landing at address 2, which isn't *guaranteed*...
SECTION "floating", ROM0
jr @

0
test/asm/jr-@.err Normal file
View File

0
test/asm/jr-@.out Normal file
View File

1
test/asm/jr-@.out.bin Normal file
View File

@@ -0,0 +1 @@
<18><18>

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