Compare commits

...

211 Commits

Author SHA1 Message Date
ISSOtm
ede982b50a Bump patch level appropriately
*HEADDESK*
2020-12-09 20:30:31 +01:00
ISSOtm
319d775c13 Add CI script to create docs on new releases 2020-12-09 16:08:43 +01:00
ISSOtm
44124319a6 Work around old Bison versions not forward-declaring yyparse
Notably, macOS still ships 2.3 (from 2008)...
Should fix #214
2020-12-09 16:00:22 +01:00
ISSOtm
462fd7539c Prohibit nested macros
After discussion (starting there:
https://github.com/gbdev/rgbds/pull/594#issuecomment-706437458
), it was decided that plain nested macros should not be
allowed.
Since #590 is fixed, EQUS can be used as a workaround;
multiline strings (#589) will make that easier on the
user when implemented.
Fixes #588, supersedes and closes #594.
Additionally, closes #388.
2020-12-09 10:44:39 +01:00
ISSOtm
f16e34b804 Fix captures beginning in expansions
Fixes #590
2020-12-09 09:54:55 +01:00
ISSOtm
c3ccdc548e Remove unnecessary flex dep from Dockerfile 2020-12-09 09:42:19 +01:00
ISSOtm
fd721ca480 Document optional RB/RW/RL argument 2020-12-09 09:39:46 +01:00
ISSOtm
eac365aef0 Allow argument to rb, rw and rl to be optional
Fixes #617
2020-12-09 09:31:47 +01:00
ISSOtm
4de6266442 Add explanation of how EXPORT works
Fixes #608
2020-12-08 21:07:24 +01:00
ISSOtm
213d985e17 Fix incorrect "sliced" INCBIN causing memory leaks
Oh, how I miss RAII...
2020-11-29 12:47:53 +01:00
ISSOtm
de76dcb8fb Fix possible segfault from -MT and -MQ
Happened only if `malloc` failed, so...
2020-11-29 12:47:53 +01:00
Eldred Habert
30fb6bde5e Merge pull request #615 from ISSOtm/find-sym
Create specialized symbol finder functions
2020-11-25 15:17:34 +01:00
ISSOtm
4f842a1248 Create specialized symbol finder functions
The old "find symbol with auto scope" function is now three:
- One finds the exact name passed to it, skipping any checks
  This is useful e.g. if such checks were already performed.
- One checks that the name is not scoped, and calls the first.
  This is useful for names that cannot be scoped, such as checking for EQUS.
  Doing this instead of the third should improve performance somehwat, since
  this specific case is hit by the lexer each time an identifier is read.
- The last one checks if the name should be expanded (`.loc` → `Glob.loc`),
  and that the local part is not scoped. This is essentially the old function.
2020-11-21 01:06:17 +01:00
ISSOtm
cc4d455b8a Add test for empty local label component 2020-11-21 00:58:40 +01:00
Eldred Habert
0bb5efebfd Merge pull request #610 from daid/stdin
Allow rgbasm and rgblink to use stdout and stdin as input and output
2020-11-08 01:20:44 +01:00
ISSOtm
b6bf7ae620 Fix RGBLINK incorrectly reading file stack nodes
This caused node IDs to mismatch, yielding possibly corrupted file stacks
Worst part is, the docs mentioned the reading order had to be reversed...
2020-11-04 02:52:06 +01:00
ISSOtm
dc96cc6d1e Add zsh completion scripts
Can't get bash ones to work, but these do.
2020-11-03 23:29:47 +01:00
daid
642daf1a76 Update main.c 2020-11-03 13:33:57 +01:00
daid
84edfb3d88 Update output.c 2020-11-03 13:33:02 +01:00
Daid
7e620bff81 Allow rgbasm and rgblink to use stdout and stdin as input and output 2020-10-26 20:28:15 +01:00
ISSOtm
0c55703438 Improve helpers.h
Use C11 standard _Noreturn instead of attributes (except, of course, MSVC)
Remove unused helpers
Avoid trapping in release builds, in favor of just unreachability
Improve shim when __builtin_* are not available
2020-10-26 15:19:31 +01:00
ISSOtm
6c57ad2226 Have make clean delete parser artifacts
Forgot to update the names there
2020-10-25 16:26:58 +01:00
ISSOtm
9028fb5391 Fix mistakes in RGBDS man pages
As reported by `mandoc -Wall`
2020-10-23 01:02:59 +02:00
ISSOtm
12dc49b60a Make page processor print usage only after reporting all bad opts 2020-10-23 00:40:05 +02:00
Eldred Habert
7e1d20acdf Merge pull request #607 from anderoonies/inline-comment-#537
handle inline c-like comments in lexer
2020-10-19 19:36:49 +02:00
anderoonies
5230104852 documentation for block comments 2020-10-19 18:05:37 +02:00
anderoonies
55be77be69 discard block comments delimited with /* */ 2020-10-15 12:42:53 -04:00
Eldred Habert
42b3a17356 Merge pull request #602 from NieDzejkob/shiftstorm
Report only one error when invalid shift has argument
2020-10-13 15:48:16 +02:00
Jakub Kądziołka
4e1d79081c Improve error message for negative shift arguments 2020-10-13 15:42:16 +02:00
ISSOtm
4ce4fdec71 Mention setting CMAKE_BUILD_TYPE to Release when using CMake
Not *necessary*, but if you don't know better, you should probably use it.
Fixes rgbds-www#9
2020-10-13 11:37:25 +02:00
Eldred Habert
05256946ac Merge pull request #604 from NieDzejkob/narg-overwrite
Don't overwrite symbol when it's not allowed
2020-10-13 10:47:57 +02:00
Eldred Habert
73396166aa Merge pull request #605 from NieDzejkob/invalid-labels
Don't consider difference of invalid labels constant
2020-10-13 10:42:05 +02:00
Jakub Kądziołka
4c5d5c7085 Don't consider difference of invalid labels constant
If a label is defined outside of a section, avoid trying to obtain its
value.
2020-10-12 23:03:14 +02:00
Jakub Kądziołka
045a9e8b93 Report only one error when invalid shift has argument
Not to mention that incrementing a variable in a loop is kinda dumb.
2020-10-12 22:54:20 +02:00
Jakub Kądziołka
4419f0d54f remove dead function: sym_GetDefinedValue 2020-10-12 13:31:21 +02:00
Jakub Kądziołka
b07aa00d5c Don't overwrite symbol when it's not allowed
When a user tried to overwrite a builtin symbol, it would change its
type despite the error, making the second try succeed. This is
problematic, as the location of a builtin symbol cannot be updated.
2020-10-12 12:35:49 +02:00
Eldred Habert
e4f5df1306 Merge pull request #603 from NieDzejkob/rpn-realloc
reserveSpace: don't assume one doubling is enough
2020-10-12 12:26:44 +02:00
Jakub Kądziołka
dc62d60e9b reserveSpace: don't assume one doubling is enough 2020-10-12 11:57:03 +02:00
ISSOtm
71d8aeb4c2 Add CMake defines to enable tracing lexer and parser 2020-10-12 09:02:21 +02:00
Eldred Habert
0836f67d42 Merge pull request #601 from NieDzejkob/utf8decoder
utf8decoder: Use byte-sized byte argument
2020-10-12 01:49:57 +02:00
Eldred Habert
176a57a1e9 Merge pull request #600 from NieDzejkob/stray-shift
Report error when shifting outside a macro
2020-10-12 01:44:10 +02:00
Eldred Habert
0d02355dbf Merge pull request #599 from NieDzejkob/stray-align
Report error when aligning outside of a section
2020-10-12 01:43:35 +02:00
Jakub Kądziołka
6767d11c23 utf8decoder: Use byte-sized byte argument
This prevents passing a negative value out of a signed char by accident.
Also renders some casts in the code superfluous.
2020-10-12 01:22:09 +02:00
ISSOtm
2dd9015dc6 Remove two stale variables from parser.y
They were made useless with the lexer rewrite
2020-10-12 00:52:12 +02:00
Jakub Kądziołka
217c10ddac Report error when shifting outside a macro 2020-10-12 00:47:01 +02:00
Jakub Kądziołka
822e4e7c44 Report error when aligning outside of a section 2020-10-12 00:27:54 +02:00
ISSOtm
0b1d01792d Indicate cur offset in linkerscript "backwards org" message 2020-10-12 00:04:08 +02:00
ISSOtm
01637768cf Rename asmy to more explicit parser
This should make the purpose of that file clearer to newcomers
2020-10-11 21:03:41 +02:00
ISSOtm
6a8ae643d5 Mention that SHIFT updates _NARG
Fixes #598
2020-10-11 02:13:30 +02:00
ISSOtm
fd83d46ba0 Enable master docs update workflow always
There seems to be no way to make the check work, and Actions are disabled
in forks by default anyways.
2020-10-11 02:04:09 +02:00
ISSOtm
914856342c Fix incorrect documentation of accepted sym names
No, they cannot start with a digit!
2020-10-11 01:57:27 +02:00
Eldred Habert
91889fc14a Merge pull request #593 from Rangi42/issue586
Fix #586: Update the `charmaps` hashmap when an existing charmap is resized
2020-10-10 02:15:04 +02:00
Rangi
7c8ec5a5ed Add a test case for charmaps that segfaults prior to this fix 2020-10-09 20:06:02 -04:00
Rangi
effc6788eb Fix #586 segfault: Update the charmaps hashmap when an existing charmap is resized 2020-10-07 13:21:13 -04:00
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
245 changed files with 5602 additions and 9696 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\"/>")
}

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

@@ -0,0 +1,100 @@
#!/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'"
bad_usage=1
;;
esac
done
if [ $bad_usage -ne 0 ]; then
usage
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

@@ -0,0 +1,54 @@
name: "Create release docs"
on:
release:
types:
- created
jobs:
build:
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 ${GITHUB_REF}
- 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 "Create RGBDS ${GITHUB_REF} 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

@@ -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,65 @@
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:
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
LANGUAGES C)
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)
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,25 +23,59 @@ 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)
option(TRACE_PARSER "Trace parser execution" OFF)
option(TRACE_LEXER "Trace lexer execution" 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()
if(MORE_WARNINGS)
add_compile_options(-Werror -Wextra -Wno-type-limits
-Wno-sign-compare -Wvla -Wformat -Wformat-security -Wformat-overflow=2
-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()
if(CMAKE_VERSION VERSION_LESS 3.12) # Use versioning consistent with Makefile
add_definitions(-DBUILD_VERSION_STRING="${PROJECT_VERSION}") # the git revision is used but uses the fallback in an archive
else()
add_compile_definitions(BUILD_VERSION_STRING="${PROJECT_VERSION}") execute_process(COMMAND git describe --tags --dirty --always
endif() 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)
add_subdirectory(src) add_subdirectory(src)
if(TRACE_PARSER)
target_compile_definitions(rgbasm PRIVATE -DYYDEBUG)
endif()
if(TRACE_LEXER)
target_compile_definitions(rgbasm PRIVATE -DLEXER_DEBUG)
endif()

View File

@@ -8,7 +8,6 @@ FROM alpine:latest
RUN apk add --update \ RUN apk add --update \
build-base \ build-base \
byacc \ byacc \
flex \
libpng-dev libpng-dev
COPY . /rgbds COPY . /rgbds
WORKDIR /rgbds WORKDIR /rgbds

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
@@ -52,14 +53,13 @@ RM := rm -rf
all: rgbasm rgblink rgbfix rgbgfx all: rgbasm rgblink rgbfix rgbgfx
rgbasm_obj := \ rgbasm_obj := \
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 \
src/asm/math.o \ src/asm/math.o \
src/asm/parser.o \
src/asm/output.o \ src/asm/output.o \
src/asm/rpn.o \ src/asm/rpn.o \
src/asm/section.o \ src/asm/section.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/parser.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
@@ -137,16 +136,7 @@ clean:
$Q${RM} rgbgfx rgbgfx.exe $Q${RM} rgbgfx rgbgfx.exe
$Qfind src/ -name "*.o" -exec rm {} \; $Qfind src/ -name "*.o" -exec rm {} \;
$Q${RM} rgbshim.sh $Q${RM} rgbshim.sh
$Q${RM} src/asm/asmy.c src/asm/asmy.h $Q${RM} src/asm/parser.c src/asm/parser.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.
@@ -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,87 @@ 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 -DCMAKE_BUILD_TYPE=Release
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/
│   ├── zsh_compl/
│   │   └── ...
│   └── ...
├── 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: * ``zsh_compl`` contains tab completion scripts for use with zsh. Put them somewhere in your ``fpath``, and they should auto-load.
.. code:: sh - ``include/`` - header files for each respective C files in `src`.
make Q= - ``src/`` - source code and manual pages for RGBDS.
This is the complete list of user-defined variables: * 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.
- ``PREFIX``: Location where RGBDS will be installed. Defaults to - ``test/`` - testing framework used to verify that changes to the code don't break or modify the behavior of RGBDS.
``/usr/local``.
- ``bindir``: Location where the binaries will be installed. Defaults to 3. History
``${PREFIX}/bin``. ----------
- ``mandir``: Location where the manpages will be installed. Defaults to - Around 1997, Carsten Sørensen (AKA SurfSmurf) writes ASMotor as a
``${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 +111,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.

49
contrib/zsh_compl/_rgbasm Normal file
View File

@@ -0,0 +1,49 @@
#compdef rgbasm
_rgbasm_warnings() {
local warnings=(
'error:Turn all warnings into errors'
'all:Enable most warning messages'
'extra:Enable extra, possibly unwanted, messages'
'everything:Enable literally everything'
'assert:Warn when WARN-type asserts fail'
'builtin-args:Report incorrect args to built-in funcs'
'div:Warn when dividing the smallest int by -1'
'empty-entry:Warn on empty entries in db, dw, dl args'
'large-constant:Warn on constants too large for a signed 32-bit int'
'long-string:Warn on strings too long'
'obsolete:Warn when using deprecated features'
'shift:Warn when shifting negative values'
'shift-amount:Warn when a shift'\''s operand it negative or \> 32'
'truncation:Warn when implicit truncations lose bits'
'user:Warn when executing the WARN built-in'
)
# TODO: handle `no-` and `error=` somehow?
_describe warning warnings
}
local args=(
# Arguments are listed here in the same order as in the manual, except for the version
'(- : * options)'{-V,--version}'[Print version number]'
'(-E --export-all)'{-E,--export-all}'[Export all symbols]'
'(-h --halt-without-nop)'{-h,--halt-without-nop}'[Avoid outputting a `nop` after `halt`]'
'(-L ---preserve-ld)'{-L,--preserve-ld}'[Prevent auto-optimizing `ld` into `ldh`]'
'(-v --verbose)'{-v,--verbose}'[Print additional messages regarding progression]'
-w'[Disable all warnings]'
'(-b --binary-digits)'{-b,--binary-digits}'+[Change chars for binary constants]:digit spec:'
'(-D --define)'{-D,--define}'+[Define a string symbol]:name + value (default 1):'
'(-g --gfx-chars)'{-g,--gfx-chars}'+[Change chars for gfx constants]:chars spec:'
'(-i --include)'{-i,--include}'+[Add an include directory]:include path:_files -/'
'(-M --dependfile)'{-M,--dependfile}"+[List deps in make format]:output file:_files -g '*.{d,mk}"
'(-o --output)'{-o,--output}'+[Output file]:output file:_files'
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
'(-r --recursion-depth)'{-r,--recursion-depth}'+[Set maximum recursion depth]:depth:'
'(-W --warning)'{-W,--warning}'+[Toggle warning flags]:warning flag:_rgbasm_warnings'
'*'":assembly sources:_files -g '*.asm'"
)
_arguments -s -S : $args

25
contrib/zsh_compl/_rgbfix Normal file
View File

@@ -0,0 +1,25 @@
#compdef rgbfix
local args=(
# Arguments are listed here in the same order as in the manual, except for the version
'(- : * options)'{-V,--version}'[Print version number]'
'(-C --color-only)'{-C,--color-only}'[Mark ROM as GBC-only]'
'(-c --color-compatible)'{-c,--color-compatible}'[Mark ROM as GBC-compatible]'
'(-j --non-japanese)'{-j,--non-japanese}'[Set the non-Japanese region flag]'
'(-s --sgb-compatible)'{-s,--sgb-compatible}'[Set the SGB flag]'
'(-f --fix-spec -v --validate)'{-v,--validate}'[Shorthand for -f lhg]'
'(-f --fix-spec -v --validate)'{-f,--fix-spec}'+[Fix or trash some header values]:fix spec:'
'(-i --game-id)'{-i,--game-id}'+[Set game ID string]:4-char game ID:'
'(-k --new-licensee)'{-k,--new-licensee}'+[Set new licensee string]:2-char licensee ID:'
'(-l --old-licensee)'{-l,--old-licensee}'+[Set old licensee ID]:licensee number:'
'(-m --mbc-type)'{-m,--mbc-type}'+[Set MBC flags]:mbc flags byte:'
'(-n --rom-version)'{-n,--rom-version}'+[Set ROM version]:rom version byte:'
'(-p --pad-value)'{-p,--pad-value}'+[Pad to next valid size using this byte as padding]:padding byte:'
'(-r --ram-size)'{-r,--ram-size}'+[Set RAM size]:ram size byte:'
'(-t --title)'{-t,--title}'+[Set title string]:11-char title string:'
'*'":ROM files:_files -g '*.{gb,sgb,gbc}'"
)
_arguments -s -S : $args

37
contrib/zsh_compl/_rgbgfx Normal file
View File

@@ -0,0 +1,37 @@
#compdef rgbgfx
_depths() {
local depths=(
'1:1bpp'
'2:2bpp (native)'
)
_describe 'bit depth' depths
}
local args=(
# Arguments are listed here in the same order as in the manual, except for the version
'(- : * options)'{-V,--version}'[Print version number]'
'(-a --attr-map -A --output-attr-map)'{-A,--output-attr-map}'[Shortcut for -a <file>.attrmap]'
'(-C --color-curve)'{-C,--color-curve}'[Generate palettes using GBC color curve]'
'(-D --debug)'{-D,--debug}'[Enable debug features]'
'(-f --fix -F --fix-and-save)'{-f,--fix}'[Fix input PNG into an indexed image]'
'(-f --fix -F --fix-and-save)'{-F,--fix-and-save}'[Like -f but also save CLI params within the PNG]'
'(-h --horizontal)'{-h,--horizontal}'[Lay out tiles horizontally instead of vertically]'
'(-m --mirror-tiles)'{-m,--mirror-tiles}'[Eliminate mirrored tiles from output]'
'(-p --palette -P --output-palette)'{-P,--output-palette}'[Shortcut for -p <file>.pal]'
'(-t --tilemap -T --output-tilemap)'{-T,--output-tilemap}'[Shortcut for -t <file>.tilemap]'
'(-u --unique-tiles)'{-u,--unique-tiles}'[Eliminate redundant tiles]'
'(-v --verbose)'{-v,--verbose}'[Enable verbose output]'
'(-a --attr-map -A --output-attr-map)'{-a,--attr-map}'+[Generate a map of tile attributes (mirroring)]:attrmap file:_files'
'(-d --depth)'{-d,--depth}'+[Set bit depth]:bit depth:_depths'
'(-o --output)'{-o,--output}'+[Set output file]:output file:_files'
'(-p --palette -P --output-palette)'{-p,--palette}"+[Output the image's palette in little-endian native RGB555 format]:palette file:_files"
'(-t --tilemap -T --output-tilemap)'{-t,--tilemap}'+[Generate a map of tile indices]:tilemap file:_files'
'(-x --trim-end)'{-x,--trim-end}'+[Trim end of output by this many tiles]:tile count:'
'*'":input png files:_files -g '*.png'"
)
_arguments -s -S : $args

View File

@@ -0,0 +1,22 @@
#compdef rgblink
local args=(
# Arguments are listed here in the same order as in the manual, except for the version
'(- : * options)'{-V,--version}'[Print version number]'
'(-d --dmg)'{-d,--dmg}'[Enable DMG mode (-w + no VRAM banking)]'
'(-t --tiny)'{-t,--tiny}'[Enable tiny mode, disabling ROM banking]'
'(-v --verbose)'{-v,--verbose}'[Enable verbose output]'
'(-w --wramx)'{-w,--wramx}'[Disable WRAM banking]'
'(-l --linkerscript)'{-l,--linkerscript}"+[Use a linker script]:linker script:_files -g '*.link'"
'(-m --map)'{-m,--map}"+[Produce a map file]:map file:_files -g '*.map'"
'(-n --sym)'(-n,--sym)"+[Produce a symbol file]:sym file:_files -g '*.sym'"
'(-O --overlay)'{-O,--overlay}'+[Overlay sections over on top of bin file]:base overlay:_files'
'(-o --output)'{-o,--output}"+[Write ROM image to this file]:rom file:_files -g '*.{gb,sgb,gbc}'"
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
'(-s --smart)'{-s,--smart}'+[!BROKEN! Perform smart linking from this symbol]:symbol name:'
'*'":object files:_files -g '*.o'"
)
_arguments -s -S : $args

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,7 +28,8 @@ 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);
void macro_ShiftCurrentArgs(void); uint32_t macro_UseNewUniqueID(void);
void macro_ShiftCurrentArgs(int32_t count);
uint32_t macro_NbArgs(void); uint32_t macro_NbArgs(void);
#endif #endif

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

@@ -27,7 +27,7 @@ struct Expression {
uint32_t nRPNPatchSize; // Size the expression will take in the obj file uint32_t nRPNPatchSize; // Size the expression will take in the obj file
}; };
/* FIXME: Should be defined in `asmy.h`, but impossible with POSIX Yacc */ /* FIXME: Should be defined in `parser.h`, but impossible with POSIX Yacc */
extern int32_t nPCOffset; extern int32_t nPCOffset;
/* /*
@@ -46,9 +46,11 @@ static inline bool rpn_isSymbol(const struct Expression *expr)
return expr->isSymbol; return expr->isSymbol;
} }
void rpn_Symbol(struct Expression *expr, char *tzSym); void rpn_Symbol(struct Expression *expr, char const *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,23 +113,35 @@ 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_AddMacro(char const *symName, int32_t defLineNo); * Find a symbol by exact name, bypassing expansion checks
*/
struct Symbol *sym_FindExactSymbol(char const *name);
/*
* Find a symbol by exact name; may not be scoped, produces an error if it is
*/
struct Symbol *sym_FindUnscopedSymbol(char const *name);
/*
* Find a symbol, possibly scoped, by name
*/
struct Symbol *sym_FindScopedSymbol(char const *name);
struct Symbol const *sym_GetPC(void);
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);
void sym_Purge(char const *symName); 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,20 @@
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_NESTED_COMMENT, /* Comment-start delimeter in a block comment */
WARNING_SHIFT_AMOUNT, WARNING_OBSOLETE, /* Obsolete things */
WARNING_TRUNCATION, WARNING_SHIFT, /* Shifting undefined behavior */
WARNING_USER, WARNING_SHIFT_AMOUNT, /* Strange shift amount */
WARNING_TRUNCATION, /* Implicit truncation loses some bits */
WARNING_USER, /* User warnings */
NB_WARNINGS, NB_WARNINGS,
@@ -53,7 +55,7 @@ void warning(enum WarningID id, const char *fmt, ...);
* It is also used when the assembler goes into an invalid state (for example, * It is also used when the assembler goes into an invalid state (for example,
* when it fails to allocate memory). * when it fails to allocate memory).
*/ */
noreturn_ void fatalerror(const char *fmt, ...); _Noreturn void fatalerror(const char *fmt, ...);
/* /*
* Used for errors that make it impossible to assemble correctly, but don't * Used for errors that make it impossible to assemble correctly, but don't
@@ -61,6 +63,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

@@ -34,10 +34,10 @@ void vwarn(const char *fmt, va_list ap) format_(printf, 1, 0);
void warnx(const char *fmt, ...) format_(printf, 1, 2); void warnx(const char *fmt, ...) format_(printf, 1, 2);
void vwarnx(const char *fmt, va_list ap) format_(printf, 1, 0); void vwarnx(const char *fmt, va_list ap) format_(printf, 1, 0);
noreturn_ void err(int status, const char *fmt, ...) format_(printf, 2, 3); _Noreturn void err(int status, const char *fmt, ...) format_(printf, 2, 3);
noreturn_ void verr(int status, const char *fmt, va_list ap) format_(printf, 2, 0); _Noreturn void verr(int status, const char *fmt, va_list ap) format_(printf, 2, 0);
noreturn_ void errx(int status, const char *fmt, ...) format_(printf, 2, 3); _Noreturn void errx(int status, const char *fmt, ...) format_(printf, 2, 3);
noreturn_ void verrx(int status, const char *fmt, va_list ap) format_(printf, 2, 0); _Noreturn void verrx(int status, const char *fmt, va_list ap) format_(printf, 2, 0);
#endif /* ERR_IN_LIBC */ #endif /* ERR_IN_LIBC */

View File

@@ -9,6 +9,6 @@
#ifndef EXTERN_UTF8DECODER_H #ifndef EXTERN_UTF8DECODER_H
#define EXTERN_UTF8DECODER_H #define EXTERN_UTF8DECODER_H
uint32_t decode(uint32_t *state, uint32_t *codep, uint32_t byte); uint32_t decode(uint32_t *state, uint32_t *codep, uint8_t byte);
#endif /* EXTERN_UTF8DECODER_H */ #endif /* EXTERN_UTF8DECODER_H */

View File

@@ -33,6 +33,16 @@ typedef struct HashMapEntry *HashMap[HASHMAP_NB_BUCKETS];
*/ */
bool hash_AddElement(HashMap map, char const *key, void *element); bool hash_AddElement(HashMap map, char const *key, void *element);
/**
* Replaces an element with an already-present key in a hashmap.
* @warning Inserting a NULL will make `hash_GetElement`'s return ambiguous!
* @param map The HashMap to replace the element in
* @param key The key with which the element will be stored and retrieved
* @param element The element to replace
* @return True if the element was found and replaced
*/
bool hash_ReplaceElement(HashMap const map, char const *key, void *element);
/** /**
* Removes an element from a hashmap. * Removes an element from a hashmap.
* @param map The HashMap to remove the element from * @param map The HashMap to remove the element from

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
*/ */
@@ -9,25 +9,30 @@
#ifndef HELPERS_H #ifndef HELPERS_H
#define HELPERS_H #define HELPERS_H
#ifdef __GNUC__ // Of course, MSVC does not support C11, so no _Noreturn there...
/* GCC or compatible */ #ifdef _MSC_VER
#define _Noreturn __declspec(noreturn)
#endif
// Ideally, we'd use `__has_attribute` and `__has_builtin`, but these were only introduced in GCC 9
#ifdef __GNUC__ // GCC or compatible
#define format_(archetype, str_index, first_arg) \ #define format_(archetype, str_index, first_arg) \
__attribute__ ((format (archetype, str_index, first_arg))) __attribute__ ((format (archetype, str_index, first_arg)))
#define noreturn_ __attribute__ ((noreturn)) // In release builds, define "unreachable" as such, but trap in debug builds
#define trap_ __builtin_trap() #ifdef NDEBUG
#define unreachable_ __builtin_unreachable
#else
#define unreachable_ __builtin_trap
#endif
#else #else
/* Unsupported, but no need to throw a fit */ // Unsupported, but no need to throw a fit
#define format_(archetype, str_index, first_arg) #define format_(archetype, str_index, first_arg)
#define noreturn_ // This seems to generate similar code to __builtin_unreachable, despite different semantics
#define unused_ // Note that executing this is undefined behavior (declared _Noreturn, but does return)
#define trap_ static inline _Noreturn unreachable_(void) {}
#endif #endif
#ifndef DEVELOP // Macros for stringification
#define DEVELOP 0
#endif
/* 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

@@ -11,7 +11,7 @@
#define PACKAGE_VERSION_MAJOR (0) #define PACKAGE_VERSION_MAJOR (0)
#define PACKAGE_VERSION_MINOR (4) #define PACKAGE_VERSION_MINOR (4)
#define PACKAGE_VERSION_PATCH (1) #define PACKAGE_VERSION_PATCH (2)
const char *get_package_version_string(void); const char *get_package_version_string(void);

View File

@@ -12,28 +12,26 @@ set(common_src
"version.c" "version.c"
) )
BISON_TARGET(ASMy "asm/asmy.y" find_package(BISON REQUIRED)
"${PROJECT_SOURCE_DIR}/src/asm/asmy.c" find_package(PkgConfig)
DEFINES_FILE "${PROJECT_SOURCE_DIR}/src/asm/asmy.h"
)
# Lexer is not present yet if(NOT PKG_CONFIG_FOUND)
if(False) # FLEX_FOUND # fallback to find_package
FLEX_TARGET(Lexer "asm/lexer.l" find_package(PNG REQUIRED)
"${PROJECT_SOURCE_DIR}/src/asm/lexer.c"
)
ADD_FLEX_BISON_DEPENDENCY(Lexer ASMy)
set(Lexer_SOURCE "${FLEX_Lexer_OUTPUTS}")
else() else()
set(Lexer_SOURCE "asm/lexer.c") pkg_check_modules(LIBPNG REQUIRED libpng)
endif() endif()
BISON_TARGET(PARSER "asm/parser.y"
"${PROJECT_SOURCE_DIR}/src/asm/parser.c"
DEFINES_FILE "${PROJECT_SOURCE_DIR}/src/asm/parser.h"
)
set(rgbasm_src set(rgbasm_src
"${BISON_ASMy_OUTPUT_SOURCE}" "${BISON_PARSER_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 PRIVATE "m")
target_link_libraries(rgbasm LINK_PRIVATE "m")
else()
target_link_libraries(rgbasm PRIVATE "m")
endif()
endif() endif()

4
src/asm/.gitignore vendored
View File

@@ -1,2 +1,2 @@
asmy.c /parser.c
asmy.h /parser.h

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,87 @@ 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);
hash_ReplaceElement(charmaps, currentCharmap->name, currentCharmap);
}
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; * The goal is to match the longest mapping possible.
* For that, advance through the trie with each character read.
* If that would lead to a dead end, rewind characters until the last match, and output.
* If no match, read a UTF-8 codepoint and output that.
*/
size_t outputLen = 0;
struct Charnode const *node = &currentCharmap->nodes[0];
struct Charnode const *match = NULL;
size_t rewindDistance = 0;
char *output; for (;;) {
char outchar[8]; /* We still want NULs to reach the `else` path, to give a chance to rewind */
uint8_t c = *input - 1;
int32_t i, match, length; if (*input && node->next[c]) {
uint8_t v, foundCode; input++; /* Consume that char */
rewindDistance++;
output = malloc(strlen(*input)); node = &currentCharmap->nodes[node->next[c]];
if (output == NULL) if (node->isTerminal) {
fatalerror("Not enough memory for buffer"); match = node;
rewindDistance = 0; /* Rewind from after the match */
}
length = 0; } else {
input -= rewindDistance; /* Rewind */
rewindDistance = 0;
node = &currentCharmap->nodes[0];
while (**input) { if (match) { /* Arrived at a dead end with a match found */
charnode = &charmap->nodes[0]; *output++ = match->value;
outputLen++;
match = NULL; /* Reset match for next round */
/* } else if (*input) { /* No match found */
* Find the longest valid match which has been registered in size_t codepointLen = readUTF8Char(output, input);
* charmap, possibly yielding multiple or no matches.
* The longest match is taken, meaning partial matches shorter input += codepointLen; /* OK because UTF-8 has no NUL in multi-byte chars */
* than the longest one are ignored. output += codepointLen;
*/ outputLen += codepointLen;
for (i = match = 0; (v = (*input)[i]);) { }
if (!charnode->next[v])
if (!*input)
break; break;
charnode = charnode->next[v];
i++;
if (charnode->isCode) {
match = i;
foundCode = charnode->code;
}
} }
if (match) {
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 outputLen;
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();
nLineNo = 0;
}
static int32_t popcontext(void)
{
struct sContext *pLastFile, **ppLastFile;
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;
return 0;
} }
} }
return name;
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) void fstk_Dump(struct FileStackNode const *node, uint32_t lineNo)
{ {
struct sContext *pLastFile, **ppLastFile; dumpNodeAndParents(node);
fprintf(stderr, "(%" PRIu32 ")", lineNo);
switch (nCurrentStatus) {
case STAT_isInclude:
/* This is the normal mode, also used when including a file. */
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;
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) void fstk_DumpCurrent(void)
{ {
return popcontext(); if (!contextStack) {
} fputs("at top level", stderr);
return;
/*
* Dump the context stack to stderr
*/
void fstk_Dump(void)
{
const struct sContext *pLastFile;
pLastFile = pFileStack;
while (pLastFile) {
fprintf(stderr, "%s(%" PRId32 ") -> ", pLastFile->tzFileName,
pLastFile->nLine);
pLastFile = pLastFile->pNext;
} }
fstk_Dump(contextStack->fileInfo, lexer_GetLineNo());
fprintf(stderr, "%s(%" PRId32 ")", tzCurrentFileName, nLineNo);
} }
void fstk_DumpToStr(char *buf, size_t buflen) struct FileStackNode *fstk_GetFileStack(void)
{ {
const struct sContext *pLastFile = pFileStack; struct FileStackNode *node = contextStack->fileInfo;
int retcode;
size_t len = buflen;
while (pLastFile) { /* Mark node and all of its parents as referenced if not already so they don't get freed */
retcode = snprintf(&buf[buflen - len], len, "%s(%" PRId32 ") -> ", while (node && !node->referenced) {
pLastFile->tzFileName, pLastFile->nLine); node->ID = -1;
if (retcode < 0) node->referenced = true;
fatalerror("Failed to dump file stack to string: %s", node = node->parent;
strerror(errno));
else if (retcode >= len)
len = 0;
else
len -= retcode;
pLastFile = pLastFile->pNext;
} }
return contextStack->fileInfo;
retcode = snprintf(&buf[buflen - len], len, "%s(%" PRId32 ")",
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");
} }
/* char const *fstk_GetFileName(void)
* Dump the string expansion stack to stderr
*/
void fstk_DumpStringExpansions(void)
{ {
const struct sStringExpansionPos *pExpansion = pCurrentStringExpansion; /* Iterating via the nodes themselves skips nested REPTs */
struct FileStackNode const *node = contextStack->fileInfo;
while (pExpansion) { while (node->type != NODE_FILE)
fprintf(stderr, "while expanding symbol \"%s\"\n", node = node->parent;
pExpansion->tzName); return ((struct FileStackNamedNode const *)node)->name;
pExpansion = pExpansion->pParent; }
void fstk_AddIncludePath(char const *path)
{
if (path[0] == '\0')
return;
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);
if (!str) {
/* Attempt to continue without that path */
error("Failed to allocate new include path: %s\n", strerror(errno));
return;
}
memcpy(str, path, len);
char *end = str + len - 1;
if (*end++ != '/')
*end++ = '/';
*end = '\0';
includePaths[nbIncPaths++] = str;
} }
/* static void printDep(char const *path)
* 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)
* Set up an include file for parsing
*/
void fstk_RunInclude(char *tzFileName)
{ {
char *incPathUsed = ""; if (contextStack->fileInfo->type == NODE_REPT) { /* The context is a REPT block, which may loop */
FILE *f = fstk_FindFile(tzFileName, &incPathUsed); struct FileStackReptNode *fileInfo = (struct FileStackReptNode *)contextStack->fileInfo;
if (f == NULL) { /* If the node is referenced, we can't edit it; duplicate it */
if (oGeneratedMissingIncludes) { if (contextStack->fileInfo->referenced) {
oFailedOnMissingInclude = true; size_t size = sizeof(*fileInfo) + sizeof(fileInfo->iters[0]) * fileInfo->reptDepth;
return; 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;
} }
yyerror("Unable to open included file '%s': %s", tzFileName,
strerror(errno)); fileInfo->iters[0]++;
return; /* 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");
pushcontext(); struct Context *context = contextStack;
nLineNo = 1;
nCurrentStatus = STAT_isInclude;
snprintf(tzCurrentFileName, sizeof(tzCurrentFileName), "%s%s",
incPathUsed, tzFileName);
if (verbose)
printf("Assembling %s\n", tzCurrentFileName);
pCurrentFile = f;
CurrentFlexHandle = yy_create_buffer(pCurrentFile);
yy_switch_to_buffer(CurrentFlexHandle);
/* Dirty hack to give the INCLUDE directive a linefeed */ contextStack = contextStack->parent;
contextDepth--;
yyunput('\n'); lexer_DeleteState(context->lexerState);
nLineNo--; /* 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 a macro 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_RunMacro(char *s, struct MacroArgs *args) static void newContext(struct FileStackNode *fileInfo)
{ {
struct Symbol const *sym = sym_FindSymbol(s); if (++contextDepth >= nMaxRecursionDepth)
int nPrintedChars; fatalerror("Recursion limit (%zu) exceeded\n", nMaxRecursionDepth);
struct Context *context = malloc(sizeof(*context));
if (sym == NULL) { if (!context)
yyerror("Macro \"%s\" not defined", s); 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;
else
error("Unable to open included file '%s': %s\n", path, strerror(errno));
return; return;
} }
if (sym->type != SYM_MACRO) { dbgPrint("Full path: \"%s\"\n", fullPath);
yyerror("\"%s\" is not a macro", s);
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);
macro_SetUniqueID(nMacroCount++); contextStack->lexerState = lexer_OpenFile(fileInfo->name);
/* Minus 1 because there is a newline at the beginning of the buffer */ if (!contextStack->lexerState)
nLineNo = sym->fileLine - 1; fatalerror("Failed to set up lexer for file include\n");
lexer_SetStateAtEOL(contextStack->lexerState);
/* We're back at top-level, so most things are reset */
contextStack->uniqueID = 0;
macro_SetUniqueID(0);
}
void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
{
dbgPrint("Running macro \"%s\"\n", macroName);
struct Symbol *macro = sym_FindExactSymbol(macroName);
if (!macro) {
error("Macro \"%s\" not defined\n", macroName);
return;
}
if (macro->type != SYM_MACRO) {
error("\"%s\" is not a macro\n", macroName);
return;
}
contextStack->macroArgs = macro_GetCurrentArgs();
/* Compute total length of this node's name: <base name>::<macro> */
size_t reptNameLen = 0;
struct FileStackNode const *node = macro->src;
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;
/* TODO: why is `strlen` being used when there's a macro size field? */
CurrentFlexHandle = yy_scan_bytes(pCurrentMacro->macro,
strlen(pCurrentMacro->macro));
yy_switch_to_buffer(CurrentFlexHandle);
} }
/* void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
* Set up a repeat block for parsing
*/
void fstk_RunRept(uint32_t count, int32_t nReptLineNo)
{ {
if (count) { dbgPrint("Running REPT(%" PRIu32 ")\n", count);
static const char *tzReptStr = "::REPT~1"; if (count == 0)
return;
/* For error printing to make sense, fake nLineNo */ uint32_t reptDepth = contextStack->fileInfo->type == NODE_REPT
nCurrentREPTBodyLastLine = nLineNo; ? ((struct FileStackReptNode *)contextStack->fileInfo)->reptDepth
nLineNo = nReptLineNo; : 0;
pushcontext(); struct FileStackReptNode *fileInfo = malloc(sizeof(*fileInfo)
macro_SetUniqueID(nMacroCount++); + (reptDepth + 1) * sizeof(fileInfo->iters[0]));
nCurrentREPTBlockCount = count;
nCurrentStatus = STAT_isREPTBlock;
nCurrentREPTBlockSize = ulNewMacroSize;
pCurrentREPTBlock = tzNewMacro;
nCurrentREPTBodyFirstLine = nReptLineNo + 1;
nLineNo = nReptLineNo;
if (strlen(tzCurrentFileName) + strlen(tzReptStr) > _MAX_PATH) if (!fileInfo) {
fatalerror("Cannot append \"%s\" to file path", error("Failed to alloc file info for REPT: %s\n", strerror(errno));
tzReptStr); return;
strcat(tzCurrentFileName, tzReptStr);
CurrentFlexHandle =
yy_scan_bytes(pCurrentREPTBlock, nCurrentREPTBlockSize);
yy_switch_to_buffer(CurrentFlexHandle);
} }
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)
* Initialize the filestack routines
*/
void fstk_Init(char *pFileName)
{ {
char tzSymFileName[_MAX_PATH + 1 + 2]; struct LexerState *state = lexer_OpenFile(mainPath);
snprintf(tzSymFileName, sizeof(tzSymFileName), "\"%s\"", pFileName); if (!state)
sym_AddString("__FILE__", tzSymFileName); 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);
pFileStack = NULL; if (!context)
if (strcmp(pFileName, "-") == 0) { fatalerror("Failed to allocate memory for main context: %s\n", strerror(errno));
pCurrentFile = stdin; 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;
/*
* Check that max recursion depth won't allow overflowing node `malloc`s
* This assumes that the rept node is larger
*/
#define DEPTH_LIMIT ((SIZE_MAX - sizeof(struct FileStackReptNode)) / sizeof(uint32_t))
if (maxRecursionDepth > DEPTH_LIMIT) {
error("Recursion depth may not be higher than %zu, defaulting to "
EXPAND_AND_STR(DEFAULT_MAX_DEPTH) "\n", DEPTH_LIMIT);
nMaxRecursionDepth = DEFAULT_MAX_DEPTH;
} 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,19 +111,34 @@ 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;
} }
} }
void macro_ShiftCurrentArgs(void) uint32_t macro_UseNewUniqueID(void)
{ {
if (macroArgs->shift != macroArgs->nbArgs) macro_SetUniqueID(++maxUniqueID);
macroArgs->shift++; return maxUniqueID;
}
void macro_ShiftCurrentArgs(int32_t count)
{
if (!macroArgs) {
error("Cannot shift macro arguments outside of a macro\n");
} else if (count < 0) {
error("Cannot shift arguments by negative amount %" PRId32 "\n", count);
} else if (macroArgs->shift < macroArgs->nbArgs) {
macroArgs->shift += count;
if (macroArgs->shift > macroArgs->nbArgs)
macroArgs->shift = macroArgs->nbArgs;
}
} }
uint32_t macro_NbArgs(void) uint32_t macro_NbArgs(void)

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 "parser.h"
#include "extern/err.h" #include "extern/err.h"
#include "extern/getopt.h" #include "extern/getopt.h"
@@ -31,7 +34,9 @@
#include "helpers.h" #include "helpers.h"
#include "version.h" #include "version.h"
extern int yyparse(void); // Old Bison versions (confirmed for 2.3) do not forward-declare `yyparse` in the generated header
// Unfortunately, macOS still ships 2.3, which is from 2008...
int yyparse(void);
size_t cldefines_index; size_t cldefines_index;
size_t cldefines_numindices; size_t cldefines_numindices;
@@ -40,12 +45,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 +70,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 +96,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 +104,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 +117,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 +139,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 +166,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 +197,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 +285,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 +306,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 +395,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'");
@@ -475,7 +436,8 @@ int main(int argc, char *argv[])
/* On first alloc, make an empty str */ /* On first alloc, make an empty str */
tzTargetFileName = tzTargetFileName =
malloc(nTargetFileNameLen + 1); malloc(nTargetFileNameLen + 1);
*tzTargetFileName = '\0'; if (tzTargetFileName)
*tzTargetFileName = '\0';
} else { } else {
tzTargetFileName = tzTargetFileName =
realloc(tzTargetFileName, realloc(tzTargetFileName,
@@ -517,8 +479,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 +489,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 +514,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;
} }
@@ -240,6 +296,11 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
uint8_t rpndata = popbyte(); uint8_t rpndata = popbyte();
switch (rpndata) { switch (rpndata) {
struct Symbol *sym;
uint32_t value;
uint8_t b;
size_t i;
case RPN_CONST: case RPN_CONST:
writebyte(RPN_CONST); writebyte(RPN_CONST);
writebyte(popbyte()); writebyte(popbyte());
@@ -247,13 +308,15 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
writebyte(popbyte()); writebyte(popbyte());
writebyte(popbyte()); writebyte(popbyte());
break; break;
case RPN_SYM:
{
for (unsigned int i = -1; (tzSym[++i] = popbyte()); )
;
struct Symbol *sym = sym_FindSymbol(tzSym);
uint32_t value;
case RPN_SYM:
i = 0;
do {
tzSym[i] = popbyte();
} while (tzSym[i++]);
// The symbol name is always written expanded
sym = sym_FindExactSymbol(tzSym);
if (sym_IsConstant(sym)) { if (sym_IsConstant(sym)) {
writebyte(RPN_CONST); writebyte(RPN_CONST);
value = sym_GetConstantValue(tzSym); value = sym_GetConstantValue(tzSym);
@@ -261,18 +324,22 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
writebyte(RPN_SYM); writebyte(RPN_SYM);
value = getSymbolID(sym); value = getSymbolID(sym);
} }
writebyte(value & 0xFF); writebyte(value & 0xFF);
writebyte(value >> 8); writebyte(value >> 8);
writebyte(value >> 16); writebyte(value >> 16);
writebyte(value >> 24); writebyte(value >> 24);
break; break;
}
case RPN_BANK_SYM: case RPN_BANK_SYM:
{ i = 0;
for (unsigned int i = -1; (tzSym[++i] = popbyte()); ) do {
; tzSym[i] = popbyte();
struct Symbol *sym = sym_FindSymbol(tzSym); } while (tzSym[i++]);
uint32_t value = getSymbolID(sym);
// The symbol name is always written expanded
sym = sym_FindExactSymbol(tzSym);
value = getSymbolID(sym);
writebyte(RPN_BANK_SYM); writebyte(RPN_BANK_SYM);
writebyte(value & 0xFF); writebyte(value & 0xFF);
@@ -280,18 +347,15 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
writebyte(value >> 16); writebyte(value >> 16);
writebyte(value >> 24); writebyte(value >> 24);
break; break;
}
case RPN_BANK_SECT:
{
uint8_t b;
case RPN_BANK_SECT:
writebyte(RPN_BANK_SECT); writebyte(RPN_BANK_SECT);
do { do {
b = popbyte(); b = popbyte();
writebyte(b); writebyte(b);
} while (b != 0); } while (b != 0);
break; break;
}
default: default:
writebyte(rpndata); writebyte(rpndata);
break; break;
@@ -303,31 +367,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 +413,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 +449,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++;
} }
} }
@@ -386,7 +479,11 @@ static void registerExportedSymbol(struct Symbol *symbol, void *arg)
*/ */
void out_WriteObject(void) void out_WriteObject(void)
{ {
FILE *f = fopen(tzObjectname, "wb"); FILE *f;
if (strcmp(tzObjectname, "-") != 0)
f = fopen(tzObjectname, "wb");
else
f = fdopen(1, "wb");
if (!f) if (!f)
err(1, "Couldn't write file '%s'", tzObjectname); err(1, "Couldn't write file '%s'", tzObjectname);
@@ -400,10 +497,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);

File diff suppressed because it is too large Load Diff

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

@@ -12,7 +12,6 @@
.Nm rgbasm .Nm rgbasm
.Nd language documentation .Nd language documentation
.Sh DESCRIPTION .Sh DESCRIPTION
.Pp
This is the full description of the language used by This is the full description of the language used by
.Xr rgbasm 1 . .Xr rgbasm 1 .
The description of the instructions supported by the Game Boy CPU is in The description of the instructions supported by the Game Boy CPU is in
@@ -30,7 +29,6 @@ but any program that processes RGB object files (described in
.Xr rgbds 5 ) .Xr rgbds 5 )
can be used in its place. can be used in its place.
.Sh SYNTAX .Sh SYNTAX
.Pp
The syntax is linebased, just as in any other assembler, meaning that you do one instruction or pseudoop per line: The syntax is linebased, just as in any other assembler, meaning that you do one instruction or pseudoop per line:
.Pp .Pp
.Dl Oo Ar label Oc Oo Ar instruction Oc Oo Ar ;\ comment Oc .Dl Oo Ar label Oc Oo Ar instruction Oc Oo Ar ;\ comment Oc
@@ -47,13 +45,23 @@ The assembler
.Em always .Em always
ignores comments and their contents. ignores comments and their contents.
.Pp .Pp
There are two syntaxes for comments. The most common is that anything that follows a semicolon There are three syntaxes for comments.
The most common is that anything that follows a semicolon
.Ql \&; .Ql \&;
not inside a string, is a comment until the end of the line. not inside a string, is a comment until the end of the line.
The other is that lines beginning with a The second is a block comment, beginning with
.Ql /*
and ending with
.Ql */ .
It can be split across multiple lines, or occur in the middle of an expression:
.Bd -literal -offset indent
X = /* the value of x
should be 3 */ 3
.Ed
The third is that lines beginning with a
.Ql * .Ql *
(not even spaces before it) are ignored. (not even spaces before it) are ignored.
This second syntax is deprecated (will be removed in a future version) and should be replaced with the first one. This third syntax is deprecated (will be removed in a future version) and should be replaced with either of the first two.
.Pp .Pp
Sometimes lines can be too long and it may be necessary to split them. Sometimes lines can be too long and it may be necessary to split them.
To do so, put a backslash at the end of the line: To do so, put a backslash at the end of the line:
@@ -72,7 +80,6 @@ like this:
"world!") "world!")
.Ed .Ed
.Sh EXPRESSIONS .Sh EXPRESSIONS
.Pp
An expression can be composed of many things. An expression can be composed of many things.
Numerical expressions are always evaluated using signed 32-bit math. Numerical expressions are always evaluated using signed 32-bit math.
Zero is considered to be the only "false" number, all non-zero numbers (including negative) are "true". Zero is considered to be the only "false" number, all non-zero numbers (including negative) are "true".
@@ -86,9 +93,7 @@ section.
.Pp .Pp
The instructions in the macro-language generally require constant expressions. The instructions in the macro-language generally require constant expressions.
.Ss Numeric Formats .Ss Numeric Formats
.Pp
There are a number of numeric formats. There are a number of numeric formats.
.Pp
.Bl -column -offset indent "Fixed point (16.16)" "Prefix" .Bl -column -offset indent "Fixed point (16.16)" "Prefix"
.It Sy Format type Ta Sy Prefix Ta Sy Accepted characters .It Sy Format type Ta Sy Prefix Ta Sy Accepted characters
.It Hexadecimal Ta $ Ta 0123456789ABCDEF .It Hexadecimal Ta $ Ta 0123456789ABCDEF
@@ -119,9 +124,7 @@ is equivalent to
.Pp .Pp
You can also use symbols, which are implicitly replaced with their value. You can also use symbols, which are implicitly replaced with their value.
.Ss Operators .Ss Operators
.Pp
A great number of operators you can use in expressions are available (listed from highest to lowest precedence): A great number of operators you can use in expressions are available (listed from highest to lowest precedence):
.Pp
.Bl -column -offset indent "!= == <= >= < >" .Bl -column -offset indent "!= == <= >= < >"
.It Sy Operator Ta Sy Meaning .It Sy Operator Ta Sy Meaning
.It Li \&( \&) Ta Precedence override .It Li \&( \&) Ta Precedence override
@@ -167,7 +170,6 @@ and
.Pp .Pp
! returns 1 if the operand was 0, and 0 otherwise. ! returns 1 if the operand was 0, and 0 otherwise.
.Ss Fixedpoint Expressions .Ss Fixedpoint Expressions
.Pp
Fixed-point numbers are basically normal (32-bit) integers, which count 65536th's instead of entire units, offering better precision than integers but limiting the range of values. Fixed-point numbers are basically normal (32-bit) integers, which count 65536th's instead of entire units, offering better precision than integers but limiting the range of values.
The upper 16 bits are used for the integer part and the lower 16 bits are used for the fraction (65536ths). The upper 16 bits are used for the integer part and the lower 16 bits are used for the fraction (65536ths).
Since they are still akin to integers, you can use them in normal integer expressions, and some integer operators like Since they are still akin to integers, you can use them in normal integer expressions, and some integer operators like
@@ -182,7 +184,6 @@ The following functions are designed to operate with fixed-point numbers:
.EQ .EQ
delim $$ delim $$
.EN .EN
.Pp
.Bl -column -offset indent "ATAN2(x, y)" .Bl -column -offset indent "ATAN2(x, y)"
.It Sy Name Ta Sy Operation .It Sy Name Ta Sy Operation
.It Fn DIV x y Ta $x \[di] y$ .It Fn DIV x y Ta $x \[di] y$
@@ -213,7 +214,6 @@ ANGLE = ANGLE + 256.0 ; 256 = 65536 / table_len, with table_len = 256
ENDR ENDR
.Ed .Ed
.Ss String Expressions .Ss String Expressions
.Pp
The most basic string expression is any number of characters contained in double quotes The most basic string expression is any number of characters contained in double quotes
.Pq Ql \&"for instance" . .Pq Ql \&"for instance" .
The backslash character The backslash character
@@ -222,8 +222,7 @@ is special in that it causes the character following it to be
.Dq escaped , .Dq escaped ,
meaning that it is treated differently from normal. meaning that it is treated differently from normal.
There are a number of escape sequences you can use within a string: There are a number of escape sequences you can use within a string:
.Pp .Bl -column -offset indent "Qo \[rs]1 Qc \[en] Qo \[rs]9 Qc"
.Bl -column -offset indent "'\1' - '\9'"
.It Sy String Ta Sy Meaning .It Sy String Ta Sy Meaning
.It Ql \[rs]\[rs] Ta Produces a backslash .It Ql \[rs]\[rs] Ta Produces a backslash
.It Ql \[rs]" Ta Produces a double quote without terminating .It Ql \[rs]" Ta Produces a double quote without terminating
@@ -278,19 +277,17 @@ The symbol's value is again inserted directly.
.Pp .Pp
The following functions operate on string expressions. The following functions operate on string expressions.
Most of them return a string, however some of these functions actually return an integer and can be used as part of an integer expression! Most of them return a string, however some of these functions actually return an integer and can be used as part of an integer expression!
.Pp
.Bl -column "STRSUB(str, pos, len)" .Bl -column "STRSUB(str, pos, len)"
.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.
.It Fn STRLWR str Ta Converts all characters in Ar str No to lower case and returns the new string. .It Fn STRLWR str Ta Converts all characters in Ar str No to lower case and returns the new string.
.El .El
.Ss Character maps .Ss Character maps
.Pp
When writing text that is meant to be displayed in the Game Boy, the characters used in the source code may have a different encoding than the default of ASCII. When writing text that is meant to be displayed in the Game Boy, the characters used in the source code may have a different encoding than the default of ASCII.
For example, the tiles used for uppercase letters may be placed starting at tile index 128, which makes it difficult to add text strings to the ROM. For example, the tiles used for uppercase letters may be placed starting at tile index 128, which makes it difficult to add text strings to the ROM.
.Pp .Pp
@@ -325,9 +322,7 @@ This means that any string that the code may want to print as debug information
The output value of a mapping can be 0. The output value of a mapping can be 0.
If this happens, the assembler will treat this as the end of the string and the rest of it will be trimmed. If this happens, the assembler will treat this as the end of the string and the rest of it will be trimmed.
.Ss Other functions .Ss Other functions
.Pp
There are a few other functions that do various useful things: There are a few other functions that do various useful things:
.Pp
.Bl -column "DEF(label)" .Bl -column "DEF(label)"
.It Sy Name Ta Sy Operation .It Sy Name Ta Sy Operation
.It Fn BANK arg Ta Returns a bank number. .It Fn BANK arg Ta Returns a bank number.
@@ -356,7 +351,6 @@ String symbols are not expanded within the parentheses.
or 0 if only RGBLINK can compute its value. or 0 if only RGBLINK can compute its value.
.El .El
.Sh SECTIONS .Sh SECTIONS
.Pp
Before you can start writing code, you must define a section. Before you can start writing code, you must define a section.
This tells the assembler what kind of information follows and, if it is code, where to put it. This tells the assembler what kind of information follows and, if it is code, where to put it.
.Pp .Pp
@@ -373,7 +367,6 @@ All other sections must have a unique name, even in different source files, or t
Possible section Possible section
.Ar type Ns s .Ar type Ns s
are as follows: are as follows:
.Pp
.Bl -tag .Bl -tag
.It Ic ROM0 .It Ic ROM0
A ROM section. A ROM section.
@@ -559,7 +552,6 @@ SECTION "VRAM Data",ROMX,BANK[2],ALIGN[4] ;\ align to 16 bytes
.Ed .Ed
.El .El
.Ss Section Stack .Ss Section Stack
.Pp
.Ic POPS .Ic POPS
and and
.Ic PUSHS .Ic PUSHS
@@ -572,7 +564,6 @@ will push the current section context on the section stack.
can then later be used to restore it. can then later be used to restore it.
Useful for defining sections in included files when you don't want to override the section context at the point the file was included. Useful for defining sections in included files when you don't want to override the section context at the point the file was included.
.Ss RAM Code .Ss RAM Code
.Pp
Sometimes you want to have some code in RAM. Sometimes you want to have some code in RAM.
But then you can't simply put it in a RAM section, you have to store it in ROM and copy it to RAM at some point. But then you can't simply put it in a RAM section, you have to store it in ROM and copy it to RAM at some point.
.Pp .Pp
@@ -633,7 +624,6 @@ You cannot nest
.Ic LOAD .Ic LOAD
blocks, nor can you change the current section within them. blocks, nor can you change the current section within them.
.Ss Unionized Sections .Ss Unionized Sections
.Pp
When you're tight on RAM, you may want to define overlapping blocks of variables, as explained in the When you're tight on RAM, you may want to define overlapping blocks of variables, as explained in the
.Sx Unions .Sx Unions
section. section.
@@ -681,6 +671,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:
@@ -722,12 +717,11 @@ and the one from
.Ql bar.o .Ql bar.o
last. last.
.Sh SYMBOLS .Sh SYMBOLS
.Pp
RGBDS supports several types of symbols: RGBDS supports several types of symbols:
.Pp
.Bl -hang .Bl -hang
.It Sy Label .It Sy Label
Numerical symbol designating a memory location. May or may not have a value known at assembly time. Numerical symbol designating a memory location.
May or may not have a value known at assembly time.
.It Sy Constant .It Sy Constant
Numerical symbol whose value has to be known at assembly time. Numerical symbol whose value has to be known at assembly time.
.It Sy Macro .It Sy Macro
@@ -738,12 +732,18 @@ code that can be invoked later.
String symbol that can be evaluated, similarly to a macro. String symbol that can be evaluated, similarly to a macro.
.El .El
.Pp .Pp
Symbol names can contain letters, numbers, underscores, hashes and Symbol names can contain letters, numbers, underscores
.Sq _ ,
hashes
.Sq #
and at signs
.Sq @ . .Sq @ .
However, they must begin with either a letter, a number, or an underscore. However, they must begin with either a letter, or an underscore.
Periods are allowed exclusively for labels, as described below. Periods
.Sq \&.
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.
@@ -841,7 +841,6 @@ str_SIZEOF EQU 259
.Ed .Ed
.Pp .Pp
There are five commands in the RS group of commands: There are five commands in the RS group of commands:
.Pp
.Bl -column "RSSET constexpr" .Bl -column "RSSET constexpr"
.It Sy Command Ta Sy Meaning .It Sy Command Ta Sy Meaning
.It Ic RSRESET Ta Equivalent to Ql RSSET 0 . .It Ic RSRESET Ta Equivalent to Ql RSSET 0 .
@@ -852,6 +851,12 @@ There are five commands in the RS group of commands:
(In practice, this one cannot be used due to a bug). (In practice, this one cannot be used due to a bug).
.El .El
.Pp .Pp
If the argument to
.Ic RB , RW ,
or
.Ic RL
is omitted, it's assumed to be 1.
.Pp
Note that colons Note that colons
.Ql \&: .Ql \&:
following the name are not allowed. following the name are not allowed.
@@ -918,9 +923,28 @@ Note that a single colon
.Ql \&: .Ql \&:
following the macro's name is required. following the macro's name is required.
Macros can't be exported or imported. Macros can't be exported or imported.
.Pp
Plainly nesting macro definitions is not allowed, but this can be worked around using
.Ic EQUS .
This won't work:
.Bd -literal -offset indent
outer: MACRO
inner: MACRO
PRINTT "Hello!\[rs]n"
ENDM
ENDM
.Ed
.Pp
But this will:
.Bd -literal -offset indent
outer: MACRO
definition equs "inner: MACRO\[rs]nPRINTT \[rs]"Hello!\[rs]\[rs]n\[rs]"\[rs]nENDM"
definition
PURGE definition
ENDM
.Ed
.El .El
.Ss Exporting and importing symbols .Ss Exporting and importing symbols
.Pp
Importing and exporting of symbols is a feature that is very useful when your project spans many source files and, for example, you need to jump to a routine defined in another file. Importing and exporting of symbols is a feature that is very useful when your project spans many source files and, for example, you need to jump to a routine defined in another file.
.Pp .Pp
Exporting of symbols has to be done manually, importing is done automatically if Exporting of symbols has to be done manually, importing is done automatically if
@@ -932,15 +956,56 @@ The following will cause
and so on to be accessible to other files during the link process: and so on to be accessible to other files during the link process:
.Dl Ic EXPORT Ar symbol1 Bq , Ar symbol2 , No ... .Dl Ic EXPORT Ar symbol1 Bq , Ar symbol2 , No ...
.Pp .Pp
For example, if you have the following three files:
.Pp
.Ql a.asm :
.Bd -literal -compact
SECTION "a", WRAM0
LabelA:
.Ed
.Pp
.Ql b.asm :
.Bd -literal -compact
SECTION "b", WRAM0
ExportedLabelB1::
ExportedLabelB2:
EXPORT ExportedLabelB2
.Ed
.Pp
.Ql c.asm :
.Bd -literal -compact
SECTION "C", ROM0[0]
dw LabelA
dw ExportedLabelB1
dw ExportedLabelB2
.Ed
.Pp
Then
.Ql c.asm
can use
.Ql ExportedLabelB1
and
.Ql ExportedLabelB2 ,
but not
.Ql LabelA ,
so linking them together will fail:
.Bd -literal
$ rgbasm -o a.o a.asm
$ rgbasm -o b.o b.asm
$ rgbasm -o c.o c.asm
$ rgblink a.o b.o c.o
error: c.asm(2): Unknown symbol "LabelA"
Linking failed with 1 error
.Ed
.Pp
Note also that only exported symbols will appear in symbol and map files produced by
.Xr rgblink 1 .
.Pp
.Ic GLOBAL .Ic GLOBAL
is a deprecated synonym for is a deprecated synonym for
.Ic EXPORT , .Ic EXPORT ,
do not use it. do not use it.
.Pp
Note also that only exported symbols will appear in symbol and map files produced by
.Xr rgblink 1 .
.Ss Purging symbols .Ss Purging symbols
.Pp
.Ic PURGE .Ic PURGE
allows you to completely remove a symbol from the symbol table as if it had never existed. allows you to completely remove a symbol from the symbol table as if it had never existed.
.Em USE WITH EXTREME CAUTION!!! .Em USE WITH EXTREME CAUTION!!!
@@ -959,15 +1024,13 @@ Note that, as an exception, string symbols in the argument list of a
command command
.Em will not be expanded . .Em will not be expanded .
.Ss Predeclared Symbols .Ss Predeclared Symbols
.Pp
The following symbols are defined by the assembler: The following symbols are defined by the assembler:
.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, updated by Ic SHIFT
.It Ic EQU Ta Dv __LINE__ Ta The current line number .It Ic EQU Ta Dv __LINE__ Ta The current line number
.It Ic EQUS Ta Dv __FILE__ Ta The current filename .It Ic EQUS Ta Dv __FILE__ Ta The current filename
.It Ic EQUS Ta Dv __DATE__ Ta Today's date .It Ic EQUS Ta Dv __DATE__ Ta Today's date
@@ -986,7 +1049,6 @@ The following symbols are defined by the assembler:
.El .El
.Sh DEFINING DATA .Sh DEFINING DATA
.Ss Declaring variables in a RAM section .Ss Declaring variables in a RAM section
.Pp
.Ic DS .Ic DS
allocates a number of empty bytes. allocates a number of empty bytes.
This is the preferred method of allocating space in a RAM section. This is the preferred method of allocating space in a RAM section.
@@ -1007,7 +1069,6 @@ In ROM sections, it will be filled with the value passed to the
command-line option, except when using overlays with command-line option, except when using overlays with
.Fl O . .Fl O .
.Ss Defining constant data .Ss Defining constant data
.Pp
.Ic DB .Ic DB
defines a list of bytes that will be stored in the final image. defines a list of bytes that will be stored in the final image.
Ideal for tables and text. Ideal for tables and text.
@@ -1059,7 +1120,6 @@ can be used in a
.Ic SRAM .Ic SRAM
section. section.
.Ss Including binary files .Ss Including binary files
.Pp
You probably have some graphics, level data, etc. you'd like to include. You probably have some graphics, level data, etc. you'd like to include.
Use Use
.Ic INCBIN .Ic INCBIN
@@ -1080,8 +1140,10 @@ 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
.Ss Unions
.Pp .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
Unions allow multiple memory allocations to overlap, like unions in C. Unions allow multiple memory allocations to overlap, like unions in C.
This does not increase the amount of memory available, but allows re-using the same memory region for different purposes. This does not increase the amount of memory available, but allows re-using the same memory region for different purposes.
.Pp .Pp
@@ -1133,7 +1195,6 @@ like commands (see
.Sx Declaring variables in a RAM section ) . .Sx Declaring variables in a RAM section ) .
.Sh THE MACRO LANGUAGE .Sh THE MACRO LANGUAGE
.Ss Invoking macros .Ss Invoking macros
.Pp
You execute the macro by inserting its name. You execute the macro by inserting its name.
.Bd -literal -offset indent .Bd -literal -offset indent
add a,b add a,b
@@ -1195,7 +1256,6 @@ Also, a macro can have inside an
.Sy EQUS .Sy EQUS
which references the same macro, which has the same problem. which references the same macro, which has the same problem.
.Pp .Pp
.Pp
It's possible to pass arguments to macros as well! It's possible to pass arguments to macros as well!
You retrieve the arguments by using the escape sequences You retrieve the arguments by using the escape sequences
.Ic \[rs]1 .Ic \[rs]1
@@ -1269,7 +1329,9 @@ is a special command only available in macros.
Very useful in Very useful in
.Ic REPT .Ic REPT
blocks. blocks.
It will shift the arguments by one to the left. It will shift the arguments by one to the left, and decrease
.Dv _NARG
by 1.
.Ic \[rs]1 .Ic \[rs]1
will get the value of will get the value of
.Ic \[rs]2 , \[rs]2 .Ic \[rs]2 , \[rs]2
@@ -1282,7 +1344,6 @@ This is the only way of accessing the value of arguments from 10 to 256.
.Ic SHIFT .Ic SHIFT
can optionally be given an integer parameter, and will apply the above shifting that number of times. can optionally be given an integer parameter, and will apply the above shifting that number of times.
.Ss Printing things during assembly .Ss Printing things during assembly
.Pp
The next four commands print text and values to the standard output. The next four commands print text and values to the standard output.
Useful for debugging macros, or wherever you may feel the need to tell yourself some important information. Useful for debugging macros, or wherever you may feel the need to tell yourself some important information.
.Bd -literal -offset indent .Bd -literal -offset indent
@@ -1291,7 +1352,6 @@ PRINTI (2 + 3) / 5
PRINTV $FF00 + $F0 PRINTV $FF00 + $F0
PRINTF MUL(3.14, 3987.0) PRINTF MUL(3.14, 3987.0)
.Ed .Ed
.Pp
.Bl -inset .Bl -inset
.It Ic PRINTT .It Ic PRINTT
prints out a string. prints out a string.
@@ -1310,7 +1370,6 @@ prints out a fixed point value.
Be careful that none of those automatically print a line feed; if you need one, use Be careful that none of those automatically print a line feed; if you need one, use
.Ic PRINTT "\[rs]n" . .Ic PRINTT "\[rs]n" .
.Ss Automatically repeating blocks of code .Ss Automatically repeating blocks of code
.Pp
Suppose you want to unroll a time consuming loop without copy-pasting it. Suppose you want to unroll a time consuming loop without copy-pasting it.
.Ic REPT .Ic REPT
is here for that purpose. is here for that purpose.
@@ -1347,7 +1406,6 @@ As in macros, you can also use the escape sequence
.Ic REPT .Ic REPT
blocks can be nested. blocks can be nested.
.Ss Aborting the assembly process .Ss Aborting the assembly process
.Pp
.Ic FAIL .Ic FAIL
and and
.Ic WARN .Ic WARN
@@ -1368,7 +1426,6 @@ If you need to ensure some assumption is correct when compiling, you can use
and and
.Ic STATIC_ASSERT . .Ic STATIC_ASSERT .
Syntax examples are given below: Syntax examples are given below:
.Pp
.Bd -literal -offset indent .Bd -literal -offset indent
Function: Function:
xor a xor a
@@ -1419,11 +1476,10 @@ to be emitted;
.Ic FATAL .Ic FATAL
immediately aborts. immediately aborts.
.Ss Including other source files .Ss Including other source files
.Pp
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 )
@@ -1435,7 +1491,6 @@ calls infinitely (or until you run out of memory, whichever comes first).
INCLUDE "irq.inc" INCLUDE "irq.inc"
.Ed .Ed
.Ss Conditional assembling .Ss Conditional assembling
.Pp
The four commands The four commands
.Ic IF , ELIF , ELSE , .Ic IF , ELIF , ELSE ,
and and
@@ -1485,7 +1540,6 @@ Also, if there is more than one
block, all of them but the first one are ignored. block, all of them but the first one are ignored.
.Sh MISCELLANEOUS .Sh MISCELLANEOUS
.Ss Changing options while assembling .Ss Changing options while assembling
.Pp
.Ic OPT .Ic OPT
can be used to change some of the options during assembling from within the source, instead of defining them on the command-line. can be used to change some of the options during assembling from within the source, instead of defining them on the command-line.
.Pp .Pp
@@ -1515,7 +1569,6 @@ can then later be used to restore them.
Useful if you want to change some options in an include file and you don't want to destroy the options set by the program that included your file. Useful if you want to change some options in an include file and you don't want to destroy the options set by the program that included your file.
The stack's number of entries is limited only by the amount of memory in your machine. The stack's number of entries is limited only by the amount of memory in your machine.
.Ss Requesting alignment .Ss Requesting alignment
.Pp
While While
.Ic ALIGN .Ic ALIGN
as presented in as presented in
@@ -1543,9 +1596,8 @@ is a shorthand for
.Xr rgbds 7 , .Xr rgbds 7 ,
.Xr gbz80 7 .Xr gbz80 7
.Sh HISTORY .Sh HISTORY
.Pp
.Nm .Nm
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); \
@@ -46,22 +46,23 @@ static uint8_t *reserveSpace(struct Expression *expr, uint32_t size)
/* If there isn't enough room to reserve the space, realloc */ /* If there isn't enough room to reserve the space, realloc */
if (!expr->tRPN) if (!expr->tRPN)
expr->nRPNCapacity = 256; /* Initial size */ expr->nRPNCapacity = 256; /* Initial size */
else if (expr->nRPNCapacity >= MAXRPNLEN) while (expr->nRPNCapacity - expr->nRPNLength < size) {
/* if (expr->nRPNCapacity >= MAXRPNLEN)
* To avoid generating humongous object files, cap the /*
* size of RPN expressions * To avoid generating humongous object files, cap the
*/ * size of RPN expressions
fatalerror("RPN expression cannot grow larger than %lu bytes", */
(unsigned long)MAXRPNLEN); fatalerror("RPN expression cannot grow larger than "
else if (expr->nRPNCapacity > MAXRPNLEN / 2) EXPAND_AND_STR(MAXRPNLEN) " bytes\n");
expr->nRPNCapacity = MAXRPNLEN; else if (expr->nRPNCapacity > MAXRPNLEN / 2)
else expr->nRPNCapacity = MAXRPNLEN;
expr->nRPNCapacity *= 2; else
expr->nRPNCapacity *= 2;
}
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;
@@ -103,28 +104,26 @@ void rpn_Number(struct Expression *expr, uint32_t i)
expr->nVal = i; expr->nVal = i;
} }
void rpn_Symbol(struct Expression *expr, char *tzSym) void rpn_Symbol(struct Expression *expr, char const *tzSym)
{ {
struct Symbol *sym = sym_FindSymbol(tzSym); struct Symbol *sym = sym_FindScopedSymbol(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); makeUnknown(expr, sym_IsPC(sym) ? "PC is not constant at assembly time"
makeUnknown(expr, sym_IsPC(sym) : "'%s' is not constant at assembly time", tzSym);
? "PC is not constant at assembly time" sym = sym_Ref(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(sym->name) + 1; /* Don't forget NUL! */
uint8_t *ptr = reserveSpace(expr, nameLen + 1); uint8_t *ptr = reserveSpace(expr, nameLen + 1);
*ptr++ = RPN_SYM; *ptr++ = RPN_SYM;
memcpy(ptr, tzSym, nameLen); memcpy(ptr, sym->name, nameLen);
/* RGBLINK assumes PC is at the byte being computed... */ /* RGBLINK assumes PC is at the byte being computed... */
if (sym_IsPC(sym) && nPCOffset) { if (sym_IsPC(sym) && nPCOffset) {
@@ -145,20 +144,20 @@ 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;
} }
} }
void rpn_BankSymbol(struct Expression *expr, char const *tzSym) void rpn_BankSymbol(struct Expression *expr, char const *tzSym)
{ {
struct Symbol const *sym = sym_FindSymbol(tzSym); struct Symbol const *sym = sym_FindScopedSymbol(tzSym);
/* The @ symbol is treated differently. */ /* The @ symbol is treated differently. */
if (sym_IsPC(sym)) { if (sym_IsPC(sym)) {
@@ -168,24 +167,22 @@ 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 = sym_Ref(tzSym);
if (!sym) assert(sym); // If the symbol didn't exist, it should have been created
/* If the symbol didn't exist, `sym_Ref` created it */
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 */
size_t nameLen = strlen(tzSym) + 1; /* Room for NUL! */ size_t nameLen = strlen(sym->name) + 1; /* Room for NUL! */
uint8_t *ptr = reserveSpace(expr, nameLen + 1); uint8_t *ptr = reserveSpace(expr, nameLen + 1);
*ptr++ = RPN_BANK_SYM; *ptr++ = RPN_BANK_SYM;
memcpy(ptr, tzSym, nameLen); memcpy(ptr, sym->name, nameLen);
} }
} }
} }
@@ -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,30 @@ 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_FindScopedSymbol((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;
struct Section const *section1 = sym_GetSection(sym1);
struct Section const *section2 = sym_GetSection(sym);
return section1 && (section1 == section2);
} }
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 +373,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 +396,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 +408,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 +426,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;
@@ -359,24 +378,25 @@ uint32_t sect_GetOutputOffset(void)
void sect_AlignPC(uint8_t alignment, uint16_t offset) void sect_AlignPC(uint8_t alignment, uint16_t offset)
{ {
checksection();
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 +404,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 +436,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 +514,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 +620,24 @@ void out_PCRelByte(struct Expression *expr)
{ {
checkcodesection(); checkcodesection();
reserveSpace(1); reserveSpace(1);
struct Symbol const *pc = sym_GetPC();
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 +650,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");
free(fullPath);
if (!f) { if (!f) {
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 +679,22 @@ 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, fclose(f);
strerror(errno)); return;
}
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,8 +704,7 @@ 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);
} }
@@ -620,28 +712,31 @@ void out_BinaryFile(char const *s)
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 +748,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 +773,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 +789,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,43 +212,52 @@ 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);
} }
/* struct Symbol *sym_FindExactSymbol(char const *name)
* Find a symbol by name and scope
*/
static struct Symbol *findsymbol(char const *s, struct Symbol const *scope)
{ {
char fullname[MAXSYMLEN + 1]; return hash_GetElement(symbols, name);
}
if (s[0] == '.' && scope) { struct Symbol *sym_FindUnscopedSymbol(char const *name)
fullSymbolName(fullname, sizeof(fullname), s, scope); {
s = fullname; if (strchr(name, '.')) {
error("Expected non-scoped symbol name, not \"%s\"\n", name);
return NULL;
} }
return sym_FindExactSymbol(name);
char const *separator = strchr(s, '.');
if (separator && strchr(separator + 1, '.'))
fatalerror("'%s' is a nonsensical reference to a nested local symbol",
s);
return hash_GetElement(symbols, s);
} }
/* struct Symbol *sym_FindScopedSymbol(char const *name)
* Find a symbol by name, with automatically determined scope
*/
struct Symbol *sym_FindSymbol(char const *symName)
{ {
return findsymbol(symName, symName[0] == '.' ? symbolScope : NULL); char const *dotPtr = strchr(name, '.');
if (dotPtr) {
if (strchr(dotPtr + 1, '.'))
fatalerror("'%s' is a nonsensical reference to a nested local symbol\n",
name);
/* If auto-scoped local label, expand the name */
if (dotPtr == name) { /* Meaning, the name begins with the dot */
char fullname[MAXSYMLEN + 1];
fullSymbolName(fullname, sizeof(fullname), name, labelScope);
return sym_FindExactSymbol(fullname);
}
}
return sym_FindExactSymbol(name);
}
struct Symbol const *sym_GetPC(void)
{
return PCSymbol;
} }
static inline bool isReferenced(struct Symbol const *sym) static inline bool isReferenced(struct Symbol const *sym)
@@ -198,20 +270,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_FindScopedSymbol(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,27 +294,23 @@ 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_GetConstantValue(char const *s) uint32_t sym_GetConstantSymValue(struct Symbol const *sym)
{ {
struct Symbol const *sym = sym_FindSymbol(s); if (sym == PCSymbol)
if (sym == NULL)
yyerror("'%s' not defined", s);
else if (sym == PCSymbol)
return sym_GetPCValue(); return sym_GetPCValue();
else if (!sym_IsConstant(sym)) else if (!sym_IsConstant(sym))
yyerror("\"%s\" does not have a constant value", s); error("\"%s\" does not have a constant value\n", sym->name);
else else
return sym_GetValue(sym); return sym_GetValue(sym);
@@ -249,30 +318,28 @@ uint32_t sym_GetConstantValue(char const *s)
} }
/* /*
* Return a defined symbols value... aborts if not defined yet * Return a constant symbol's value
*/ */
uint32_t sym_GetDefinedValue(char const *s) uint32_t sym_GetConstantValue(char const *s)
{ {
struct Symbol const *sym = sym_FindSymbol(s); struct Symbol const *sym = sym_FindScopedSymbol(s);
if (sym == NULL || !sym_IsDefined(sym)) if (sym == NULL)
yyerror("'%s' not defined", s); error("'%s' not defined\n", s);
else if (!sym_IsNumeric(sym))
yyerror("'%s' is a macro or string symbol", s);
else else
return sym_GetValue(sym); return sym_GetConstantSymValue(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;
} }
/* /*
@@ -282,13 +349,15 @@ void sym_SetCurrentSymbolScope(struct Symbol *newScope)
*/ */
static struct Symbol *createNonrelocSymbol(char const *symbolName) static struct Symbol *createNonrelocSymbol(char const *symbolName)
{ {
struct Symbol *symbol = findsymbol(symbolName, NULL); struct Symbol *symbol = sym_FindExactSymbol(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 +370,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 +394,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;
@@ -342,96 +410,109 @@ struct Symbol *sym_AddString(char const *symName, char const *value)
*/ */
struct Symbol *sym_AddSet(char const *symName, int32_t value) struct Symbol *sym_AddSet(char const *symName, int32_t value)
{ {
struct Symbol *sym = findsymbol(symName, NULL); struct Symbol *sym = sym_FindExactSymbol(symName);
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? */ return sym;
} else {
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 = sym_FindExactSymbol(name);
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;
} }
/* /*
@@ -439,7 +520,7 @@ struct Symbol *sym_AddReloc(char const *symName)
*/ */
void sym_Export(char const *symName) void sym_Export(char const *symName)
{ {
struct Symbol *sym = sym_FindSymbol(symName); struct Symbol *sym = sym_FindScopedSymbol(symName);
/* If the symbol doesn't exist, create a ref that can be purged */ /* If the symbol doesn't exist, create a ref that can be purged */
if (!sym) if (!sym)
@@ -450,14 +531,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
@@ -473,26 +554,20 @@ struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo)
*/ */
struct Symbol *sym_Ref(char const *symName) struct Symbol *sym_Ref(char const *symName)
{ {
struct Symbol *nsym = sym_FindSymbol(symName); struct Symbol *nsym = sym_FindScopedSymbol(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 +592,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 +670,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)
if (decode(&state, &codep, (uint8_t)src[i]) == 1) return "EOF";
fatalerror("invalid UTF-8 character");
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, src[i]) == 1)
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,20 @@ 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_NESTED_COMMENT] = WARNING_ENABLED,
WARNING_DISABLED, /* Strange shift amount */ [WARNING_OBSOLETE] = WARNING_ENABLED,
WARNING_ENABLED, /* Implicit truncation loses some bits */ [WARNING_SHIFT] = WARNING_DISABLED,
WARNING_ENABLED, /* User warnings */ [WARNING_SHIFT_AMOUNT] = WARNING_DISABLED,
[WARNING_TRUNCATION] = WARNING_ENABLED,
[WARNING_USER] = WARNING_ENABLED,
}; };
static enum WarningState warningStates[NB_WARNINGS]; static enum WarningState warningStates[NB_WARNINGS];
@@ -68,11 +70,13 @@ 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",
"large-constant", "large-constant",
"long-string", "long-string",
"nested-comment",
"obsolete", "obsolete",
"shift", "shift",
"shift-amount", "shift-amount",
@@ -92,6 +96,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,
@@ -101,6 +106,7 @@ static uint8_t const _wallCommands[] = {
/* Warnings that are less likely to indicate an error */ /* Warnings that are less likely to indicate an error */
static uint8_t const _wextraCommands[] = { static uint8_t const _wextraCommands[] = {
WARNING_EMPTY_ENTRY, WARNING_EMPTY_ENTRY,
WARNING_NESTED_COMMENT,
META_WARNING_DONE META_WARNING_DONE
}; };
@@ -112,6 +118,7 @@ static uint8_t const _weverythingCommands[] = {
WARNING_EMPTY_ENTRY, WARNING_EMPTY_ENTRY,
WARNING_LARGE_CONSTANT, WARNING_LARGE_CONSTANT,
WARNING_LONG_STR, WARNING_LONG_STR,
WARNING_NESTED_COMMENT,
WARNING_OBSOLETE, WARNING_OBSOLETE,
WARNING_SHIFT, WARNING_SHIFT,
WARNING_SHIFT_AMOUNT, WARNING_SHIFT_AMOUNT,
@@ -195,32 +202,32 @@ 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(); }
void error(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
printDiag(fmt, args, "ERROR: ", ":\n ", NULL);
va_end(args);
nbErrors++; nbErrors++;
} }
void yyerror(const char *fmt, ...) _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);
}
noreturn_ void fatalerror(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
verror(fmt, args, NULL);
va_end(args); va_end(args);
exit(1); exit(1);
@@ -238,24 +245,19 @@ 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;
case WARNING_DEFAULT: case WARNING_DEFAULT:
trap_; unreachable_();
/* Not reached */ /* Not reached */
case WARNING_ENABLED: case WARNING_ENABLED:
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);
} }

8
src/extern/err.c vendored
View File

@@ -34,7 +34,7 @@ void rgbds_vwarnx(const char *fmt, va_list ap)
putc('\n', stderr); putc('\n', stderr);
} }
noreturn_ void rgbds_verr(int status, const char *fmt, va_list ap) _Noreturn void rgbds_verr(int status, const char *fmt, va_list ap)
{ {
fprintf(stderr, "error: "); fprintf(stderr, "error: ");
if (fmt) { if (fmt) {
@@ -46,7 +46,7 @@ noreturn_ void rgbds_verr(int status, const char *fmt, va_list ap)
exit(status); exit(status);
} }
noreturn_ void rgbds_verrx(int status, const char *fmt, va_list ap) _Noreturn void rgbds_verrx(int status, const char *fmt, va_list ap)
{ {
fprintf(stderr, "error"); fprintf(stderr, "error");
if (fmt) { if (fmt) {
@@ -75,7 +75,7 @@ void rgbds_warnx(const char *fmt, ...)
va_end(ap); va_end(ap);
} }
noreturn_ void rgbds_err(int status, const char *fmt, ...) _Noreturn void rgbds_err(int status, const char *fmt, ...)
{ {
va_list ap; va_list ap;
@@ -84,7 +84,7 @@ noreturn_ void rgbds_err(int status, const char *fmt, ...)
va_end(ap); va_end(ap);
} }
noreturn_ void rgbds_errx(int status, const char *fmt, ...) _Noreturn void rgbds_errx(int status, const char *fmt, ...)
{ {
va_list ap; va_list ap;

View File

@@ -38,7 +38,7 @@ static const uint8_t utf8d[] = {
1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* s8 */ 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* s8 */
}; };
uint32_t decode(uint32_t *state, uint32_t *codep, uint32_t byte) uint32_t decode(uint32_t *state, uint32_t *codep, uint8_t byte)
{ {
uint32_t type = utf8d[byte]; uint32_t type = utf8d[byte];

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

@@ -22,12 +22,10 @@ as destination can omit the destination as it is assumed to be register
.Sy A .Sy A
by default. by default.
The following two lines have the same effect: The following two lines have the same effect:
.Pp
.Bd -literal -offset indent .Bd -literal -offset indent
OR A,B OR A,B
OR B OR B
.Ed .Ed
.Pp
.Sh LEGEND .Sh LEGEND
List of abbreviations used in this document. List of abbreviations used in this document.
.Bl -tag .Bl -tag
@@ -1859,4 +1857,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

@@ -26,7 +26,7 @@
The The
.Nm .Nm
program converts PNG images into the Nintendo Game Boy's planar tile format. program converts PNG images into the Nintendo Game Boy's planar tile format.
.Pp
The resulting colors and their palette indices are determined differently depending on the input PNG file: The resulting colors and their palette indices are determined differently depending on the input PNG file:
.Bl -dash -width Ds .Bl -dash -width Ds
.It .It
@@ -39,7 +39,7 @@ If the image has multiple shades that map to the same index, the palette is inst
.It .It
If the image has color (or the grayscale method failed), the colors are sorted from lightest to darkest. If the image has color (or the grayscale method failed), the colors are sorted from lightest to darkest.
.El .El
.Pp
The input image may not contain more colors than the selected bit depth allows. The input image may not contain more colors than the selected bit depth allows.
Transparent pixels are set to palette index 0. Transparent pixels are set to palette index 0.
.Sh ARGUMENTS .Sh ARGUMENTS
@@ -53,7 +53,7 @@ is invalid because it could also be
.Fl Fl version . .Fl Fl version .
The arguments are as follows: The arguments are as follows:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Fl a Ar attrmap, Fl Fl attr-map Ar attrmap .It Fl a Ar attrmap , Fl Fl attr-map Ar attrmap
Generate a file of tile mirroring attributes for OAM or (CGB-only) background tiles. 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. 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 Useful in combination with
@@ -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

@@ -64,6 +64,22 @@ bool hash_AddElement(HashMap map, char const *key, void *element)
return newEntry->next != NULL; return newEntry->next != NULL;
} }
bool hash_ReplaceElement(HashMap const map, char const *key, void *element)
{
HashType hashedKey = hash(key);
struct HashMapEntry *ptr = map[(HalfHashType)hashedKey];
while (ptr) {
if (hashedKey >> HALF_HASH_NB_BITS == ptr->hash
&& !strcmp(ptr->key, key)) {
ptr->content = element;
return true;
}
ptr = ptr->next;
}
return false;
}
bool hash_RemoveElement(HashMap map, char const *key) bool hash_RemoveElement(HashMap map, char const *key)
{ {
HashType hashedKey = hash(key); HashType hashedKey = hash(key);

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;
@@ -439,7 +439,7 @@ void assign_AssignSections(void)
return; return;
} }
trap_; unreachable_();
} }
void assign_Cleanup(void) void assign_Cleanup(void)

View File

@@ -6,11 +6,14 @@
* 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>
#include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
@@ -39,11 +42,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 +100,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);
@@ -76,7 +127,13 @@ FILE *openFile(char const *fileName, char const *mode)
if (!fileName) if (!fileName)
return NULL; return NULL;
FILE *file = fopen(fileName, mode); FILE *file;
if (strcmp(fileName, "-") != 0)
file = fopen(fileName, mode);
else if (mode[0] == 'r')
file = fdopen(0, mode);
else
file = fdopen(1, mode);
if (!file) if (!file)
err(1, "Could not open file \"%s\"", fileName); err(1, "Could not open file \"%s\"", fileName);
@@ -132,7 +189,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 +234,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 +246,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 +291,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,20 +262,25 @@ 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); 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);
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",
fileName, sectName, i); fileName, sectName, i);
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",
fileName, sectName, i); fileName, sectName, i);
@@ -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 = 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;
symbol = getSymbol(fileSymbols, value); if (value == -1) { /* PC */
if (!patch->pcSection) {
if (!symbol) { error(patch->src, patch->lineNo,
error("%s: Unknown symbol \"%s\"", "PC has no value outside a section");
patch->fileName, value = 0;
fileSymbols[value]->name); isError = true;
} else if (strcmp(symbol->name, "@")) { } else {
value = symbol->value; value = patch->pcOffset + patch->pcSection->org;
/* Symbols attached to sections have offsets */ }
if (symbol->section)
value += symbol->section->org;
} else if (!patch->pcSection) {
error("%s: PC has no value outside a section",
patch->fileName);
value = 0;
} else { } else {
value = patch->pcOffset + patch->pcSection->org; symbol = getSymbol(fileSymbols, value);
if (!symbol) {
error(patch->src, patch->lineNo,
"Unknown symbol \"%s\"", fileSymbols[value]->name);
isError = true;
} else {
value = symbol->value;
/* Symbols attached to sections have offsets */
if (symbol->section)
value += symbol->section->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

@@ -66,7 +66,7 @@ Enable DMG mode.
Prohibit the use of sections that doesn't exist on a DMG, such as VRAM bank 1. Prohibit the use of sections that doesn't exist on a DMG, such as VRAM bank 1.
This option automatically enables This option automatically enables
.Fl w . .Fl w .
.It Fl l Ar linker_script, Fl Fl linkerscript Ar linker_script .It Fl l Ar linker_script , Fl Fl linkerscript Ar linker_script
Specify a linker script file that tells the linker how sections must be placed in the ROM. Specify a linker script file that tells the linker how sections must be placed in the ROM.
The attributes assigned in the linker script must be consistent with any assigned in the code. The attributes assigned in the linker script must be consistent with any assigned in the code.
See See
@@ -116,7 +116,7 @@ option!
.Sh EXAMPLES .Sh EXAMPLES
All you need for a basic ROM is an object file, which can be made into a ROM image like so: All you need for a basic ROM is an object file, which can be made into a ROM image like so:
.Pp .Pp
.D1 $ rgblink -o bar.gb foo.o .Dl $ rgblink -o bar.gb foo.o
.Pp .Pp
The resulting The resulting
.Ar bar.gb .Ar bar.gb
@@ -126,15 +126,13 @@ You should use
to fix these so that the program will actually run in a Game Boy: to fix these so that the program will actually run in a Game Boy:
.Pp .Pp
.Dl $ rgbfix -v bar.gb .Dl $ rgbfix -v bar.gb
.Ed
.Pp .Pp
Here is a more complete example: Here is a more complete example:
.Pp .Pp
.Dl $ rgblink -o bin/game.gb -n bin/game.sym -p 0xFF obj/title.o obj/engine.o .Dl $ rgblink -o bin/game.gb -n bin/game.sym -p 0xFF obj/title.o obj/engine.o
.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 +143,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

@@ -19,7 +19,6 @@ They can be lowercase or uppercase, it is ignored.
Any line can contain a comment starting with Any line can contain a comment starting with
.Ql \&; .Ql \&;
that ends at the end of the line: that ends at the end of the line:
.Pp
.Bd -literal -offset indent .Bd -literal -offset indent
ROMX $F ; This is a comment ROMX $F ; This is a comment
"Functions to read array" "Functions to read array"
@@ -86,6 +85,6 @@ in the linker script.
.Sh HISTORY .Sh HISTORY
.Nm .Nm
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.
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

@@ -318,7 +318,7 @@ static void processCommand(enum LinkerScriptCommand command, uint16_t arg,
{ {
switch (command) { switch (command) {
case COMMAND_INVALID: case COMMAND_INVALID:
trap_; unreachable_();
case COMMAND_ORG: case COMMAND_ORG:
break; break;
@@ -331,8 +331,8 @@ static void processCommand(enum LinkerScriptCommand command, uint16_t arg,
} }
if (arg < *pc) if (arg < *pc)
errx(1, "%s(%" PRIu32 "): `%s` cannot be used to go backwards", errx(1, "%s(%" PRIu32 "): `%s` cannot be used to go backwards (currently at $%x)",
linkerScriptName, lineNo, commands[command]); linkerScriptName, lineNo, commands[command], *pc);
*pc = arg; *pc = arg;
} }
@@ -391,12 +391,12 @@ struct SectionPlacement *script_NextSection(void)
switch (parserState) { switch (parserState) {
case PARSER_FIRSTTIME: case PARSER_FIRSTTIME:
trap_; unreachable_();
case PARSER_LINESTART: case PARSER_LINESTART:
switch (token->type) { switch (token->type) {
case TOKEN_INVALID: case TOKEN_INVALID:
trap_; unreachable_();
case TOKEN_EOF: case TOKEN_EOF:
if (!popFile()) if (!popFile())

View File

@@ -108,7 +108,7 @@ static void mergeSections(struct Section *target, struct Section *other, enum Se
break; break;
case SECTION_NORMAL: case SECTION_NORMAL:
trap_; unreachable_();
} }
other->nextu = target->nextu; other->nextu = target->nextu;

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
.\" .\"
@@ -16,9 +16,8 @@ This is the description of the object files used by
.Xr rgbasm 1 .Xr rgbasm 1
and and
.Xr rgblink 1 . .Xr rgblink 1 .
.Em Please note that the specifications may change. .Em Please note that the specifications may change .
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. 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.
.Pp
.Sh FILE STRUCTURE .Sh FILE STRUCTURE
The following types are used: The following types are used:
.Pp .Pp
@@ -29,41 +28,71 @@ is an 8bit integer.
.Ar STRING .Ar STRING
is a 0terminated string of is a 0terminated string of
.Ar BYTE . .Ar BYTE .
.Pp
.Bd -literal .Bd -literal
; 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
REPT NumberOfSymbols ; Number of symbols defined in this object file. REPT NumberOfSymbols ; Number of symbols defined in this object file.
STRING Name ; The name of this symbol. Local symbols are stored STRING Name ; The name of this symbol. Local symbols are stored
; as "Scope.Symbol". ; as "Scope.Symbol".
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.
LONG SectionID ; The section number (of this object file) in which LONG SectionID ; The section number (of this object file) in which
; this symbol is defined. If it doesn't belong to any ; this symbol is defined. If it doesn't belong to any
; specific section (like a constant), this field has ; specific section (like a constant), this field has
; the value -1. ; the value -1.
LONG Value ; The symbols value. It's the offset into that LONG Value ; The symbols value. It's the offset into that
; symbol's section. ; symbol's section.
ENDC ENDC
@@ -84,6 +113,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 +138,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 +178,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.
@@ -180,8 +217,7 @@ The
In the RGB format, RPN expressions are stored as In the RGB format, RPN expressions are stored as
.Ar BYTE Ns s .Ar BYTE Ns s
with some bytes being special prefixes for integers and symbols. with some bytes being special prefixes for integers and symbols.
.Pp .Bl -column -offset indent "Sy String" "Sy String"
.Bl -column -offset indent ".Sy String" ".Sy String"
.It Sy Value Ta Sy Meaning .It Sy Value Ta Sy Meaning
.It Li $00 Ta Li + operator .It Li $00 Ta Li + operator
.It Li $01 Ta Li - operator .It Li $01 Ta Li - operator
@@ -207,7 +243,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()
@@ -220,7 +256,6 @@ integer follows.
.It Li $81 Ta Ar LONG .It Li $81 Ta Ar LONG
symbol ID follows. symbol ID follows.
.El .El
.Pp
.Sh SEE ALSO .Sh SEE ALSO
.Xr rgbasm 1 , .Xr rgbasm 1 ,
.Xr rgblink 1 , .Xr rgblink 1 ,
@@ -231,4 +266,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

@@ -13,7 +13,6 @@
.Nd Rednex Game Boy Development System .Nd Rednex Game Boy Development System
.Sh EXAMPLES .Sh EXAMPLES
To get a working ROM image from a single assembly source file: To get a working ROM image from a single assembly source file:
.Pp
.Bd -literal -offset indent .Bd -literal -offset indent
$ rgbasm \-o bar.o foo.asm $ rgbasm \-o bar.o foo.asm
$ rgblink \-o baz.gb bar.o $ rgblink \-o baz.gb bar.o
@@ -26,7 +25,7 @@ $ rgbfix \-v \-p 0 baz.gb
.Xr rgbds 5 , .Xr rgbds 5 ,
.Xr gbz80 7 .Xr gbz80 7
.Sh HISTORY .Sh HISTORY
.Bl -ohang .Bl -item
.It .It
1997, Carsten S\(/orensen (AKA SurfSmurf) writes ASMotor as a general-purpose 1997, Carsten S\(/orensen (AKA SurfSmurf) writes ASMotor as a general-purpose
assembler/linker system for DOS/Win32. assembler/linker system for DOS/Win32.
@@ -37,12 +36,18 @@ assembly/machine code, and releases this version as RGBDS.
2009, Vegard Nossum adapts the code to be more UNIX-like and releases this 2009, Vegard Nossum adapts the code to be more UNIX-like and releases this
version as rgbds-linux on GitHub. version as rgbds-linux on GitHub.
.It .It
2010, Anthony J. Bentley forks that repository. The fork becomes the reference 2010, Anthony J. Bentley forks that repository.
implementation of rgbds. The fork becomes the reference implementation of rgbds.
.It .It
2017, Bentley's repository is moved to a neutral name. 2017, Bentley's repository is moved to a neutral name.
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/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

@@ -0,0 +1 @@
align 1

View File

@@ -0,0 +1,2 @@
FATAL: align-pc-outside-section.asm(1):
Code generation before SECTION directive

View 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

@@ -0,0 +1,2 @@
/* block comments containing /* throw warnings */
PRINTT "reachable\n"

View File

@@ -0,0 +1,2 @@
warning: block-comment-contents-error.asm(1): [-Wnested-comment]
/* in block comment

View File

@@ -0,0 +1 @@
reachable

View File

@@ -0,0 +1 @@
PRINTT /* block comments must terminate before EOF

View File

@@ -0,0 +1,5 @@
ERROR: block-comment-termination-error.asm(1):
Unterminated block comment
ERROR: block-comment-termination-error.asm(1):
syntax error
error: Assembly aborted (2 errors)!

View File

@@ -0,0 +1,5 @@
PRINTT /* block comments are ignored // ** */ "hi\n"
PRINTT "block (/* ... */) comments at ends of line are fine\n" /* hi */
PRINTT /* block comments
can span multiple lines
*/ "mutliline\n"

View File

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