Compare commits

...

149 Commits

Author SHA1 Message Date
ISSOtm
0759c98d91 Increase version number to 0.3.10 2020-03-21 21:18:14 +01:00
ISSOtm
402ffbf0c5 Add test for version constants 2020-03-21 21:01:25 +01:00
ISSOtm
8191e5eb27 Define version symbols
Major blunder. That warrants a new release on its own...
2020-03-21 19:26:40 +01:00
ISSOtm
323738e7b8 Increase version number to 0.3.9 2019-11-01 17:38:43 +01:00
ISSOtm
a1d132cd35 Regenerate wwwman 2019-11-01 17:35:46 +01:00
Eldred Habert
e93d65d736 Merge pull request #425 from ISSOtm/eexpansion_error
Add info about string expansions in error reports
2019-10-03 10:20:49 +02:00
ISSOtm
0f4d543aeb Have make clean delete all .o files in source directory
This will work better if files are rearranged in the future.
This appears to be POSIX-compliant, so why wasn't it used earlier?
2019-09-25 03:17:36 +02:00
ISSOtm
dab5f59ed9 Fix location of all relevant SECTIONs in tests
If section placement is changed such that those are no longer guaranteed to be
placed at zero, tests would break when they shouldn't.
2019-09-23 01:28:48 +02:00
Eldred Habert
22a6a82642 Merge pull request #419 from dbrotz/fix-blackslash-tab-at-eof
Handle tabs after backslash at end of file
2019-09-23 00:05:21 +02:00
Eldred Habert
7b592eff8a Merge pull request #420 from dbrotz/disallow-null-char
Reject input that contains null characters
2019-09-22 02:43:06 +02:00
Eldred Habert
4be81d9ffd Merge pull request #416 from ISSOtm/makefile
Improve Makefile
2019-09-22 01:55:51 +02:00
ISSOtm
55fbecee49 Add info about string expansions in error reports
This is especially useful when an EQUS expands to another one, to help
track them.
This is done separately from the file stack as the EQUS stack is separate
(which is itself because EQUS are managed *way* differently).
2019-09-12 10:02:24 +02:00
dbrotz
f36a3d5b2a Fix macro and rept buffer overflows
Macro and rept buffers were not always being terminated with newlines
and/or were vulnerable to the final newline being escaped, allowing
buffer overflows to occur. Now, they are terminated with newlines using
the same mechanism as the file buffer.
2019-09-10 03:03:04 -07:00
dbrotz
c5e8e4ff83 Reject input that contains null characters
Null characters in the middle of strings interact badly with the RGBDS
codebase, which assumes null-terminated strings. There is no reason to
support null characters in input source code, so the simplest way to deal
with null characters is to reject them early.
2019-09-09 17:27:56 -07:00
Eldred Habert
ccc666c1e4 Merge pull request #417 from ISSOtm/zero_sections
Allow 0-byte SECTIONs to be fixed anywhere
2019-09-09 23:32:30 +02:00
dbrotz
89eda89838 Handle tabs after backslash at end of file
Commit 6fbb25c added support for tabs between a \ and the newline it escapes,
but yy_create_buffer() was not updated to handle tabs.
2019-09-09 12:25:26 -07:00
Eldred Habert
17b9838c8f Merge pull request #418 from dbrotz/fix-symbol-macro-arg-0
Print useful error message when '\0' is used in a symbol name
2019-09-09 19:37:44 +02:00
dbrotz
889dd83798 Print useful error message when '\0' is used in a symbol name
AppendMacroArg() was passing 0 to sym_FindMacroArg(), which caused an assertion
failure. Now, AppendMacroArg() prints an error message instead.
2019-09-09 09:46:18 -07:00
ISSOtm
38a372f25f Allow 0-byte SECTIONs to be fixed anywhere
They do not take any room, so they can only be used to define symbols at
a given location. I ran into trouble with such a SECTION failing to be
placed where specified, which doesn't make sense.
This way, it also makes sense to have such a SECTION in the middle of
another one, but that should be fine, since there's no actual overlap.
2019-09-09 00:00:36 +02:00
ISSOtm
2e6f5ac679 Fix unary NOT being broken
nVal wasn't being set, this made the operator a no-op on constant expressions
2019-09-08 21:42:17 +02:00
ISSOtm
7a45fc68d9 Fix incorrect evaluation of && and ||
f29d768 forgot to switch these two `expr->nVal` to `src1->nVal`
This won't break anything, since this wasn't published yet.
I checked, and didn't see any other missed changes.

Reported by pret, I confirmed that `1 && 1` evaluated to 0.
2019-09-08 21:12:31 +02:00
ISSOtm
1288737c0d Clean up suffix rules
Generate .c files from .l files instead of directly a .o
Improve yacc and lex header generation dependency
2019-09-07 13:19:32 +00:00
ISSOtm
5902306271 Have make clean delete all generated .o files
Especially important if the file structure changes, to avoid
leaving stale object files in non-fresh repos.
2019-09-07 12:53:30 +00:00
ISSOtm
2fe4521a96 Remove locallex.o reference
The file was deleted in 8e88659, finish eliminating it
2019-09-07 12:53:30 +00:00
ISSOtm
74673436a1 Separate LDFLAGS from CFLAGS 2019-09-07 12:53:29 +00:00
ISSOtm
481748c279 Make CI output Makefile commands
This is good for debugging Makefiles on systems I don't have (eg. macOS)
End users will not be affected by this, and CI provides a persistent log
so clobbering isn't much of an issue.
2019-09-07 12:53:29 +00:00
ISSOtm
9faa5c7a9e Update docs on char escapes in macro args
Fixes #415
2019-09-05 15:40:54 +02:00
ISSOtm
6fbb25c0da Clean up lexer.c
Remove some hardcoded character values
Allow tabs to be used for line continuations
2019-09-05 15:22:24 +02:00
ISSOtm
65fc42cce5 Remove -Wchkp from develop target
This option has been removed in GCC 9, and according to GCC 8.2's man page,
only has an implementation on Intel MRX anyways.
2019-09-05 05:56:11 +02:00
Eldred Habert
736b7727b6 Merge pull request #400 from NieDzejkob/out.pipe-substitute
test/asm: Generate .out.pipe files on the fly
2019-09-04 18:43:00 +02:00
Jakub Kądziołka
fa920f8449 Remove the no-longer-needed .out.pipe files 2019-09-03 23:25:27 +02:00
Jakub Kądziołka
01aa56606f test/asm: special-case include-recursion 2019-09-03 23:25:15 +02:00
Jakub Kądziołka
bddd5bc678 test/asm: Generate .out.pipe files on the fly 2019-09-03 22:46:05 +02:00
Eldred Habert
f6b7e39ce7 Merge pull request #405 from ISSOtm/output_errors
Make RGBDS behave identically whether writing a .o
2019-09-03 10:05:19 +02:00
clach04
1363dd8f34 Add url link to releases page to readme (#413) 2019-09-03 01:51:38 +02:00
Eldred Habert
8214f0c09d Merge pull request #412 from clach04/patch-1
Clarify install instructions on Windows
2019-09-03 00:50:43 +02:00
clach04
ff7c46f5a6 Readme windows install instructions, alternative install location 2019-09-02 10:19:22 -07:00
ISSOtm
b44f5825a5 Make RGBDS behave identically whether writing a .o
Some errors are only tripped in `out_WriteObject`, which was
basically a stub when `-o` wasn't specified. Now, instead,
errors are checked in a separate function before out_WriteFile
2019-09-02 15:04:46 +02:00
Eldred Habert
631910bd67 Merge pull request #395 from ISSOtm/better_error_stack
Improve error stack
2019-09-02 14:33:06 +02:00
ISSOtm
37089ef940 Improve error stack
The old error stack was fairly obtuse and hard to use for debugging.
This improves it notably by ensuring all line numbers are relative
to the file, and not, say, the macro definition.
This is a breaking change if you were parsing the old stack, but
the change should be painless, and the new stack only brings more info.
The syntax is unchanged for files, macros see their name prefixed
with the file they're defined in and a pair of colors, REPT blocks
simply append a '::REPT~n' to the context they're in, where 'n' is
the number of iterations the REPT has done.
This is especially helpful in macro-heavy code such as rgbds-structs.
2019-09-02 14:18:29 +02:00
Eldred Habert
4ef27a0d23 Merge pull request #411 from ISSOtm/recursion_limit
Add a recursion limit
2019-09-02 02:21:16 +02:00
ISSOtm
476ccc9f6b Fix undefined behavior in yyunputstr
Refer to comment at lexer.c:100 for more info
2019-09-02 02:09:59 +02:00
Eldred Habert
89dc14fcaf Merge pull request #408 from ISSOtm/plumbing
Fix memory leaks with macro args
2019-09-01 22:16:27 +02:00
clach04
1ca2f12d24 Update README.rst
Add Windows path note, in preference from adding exes to system locations
2019-09-01 09:51:47 -07:00
ISSOtm
20b2f5ee2f Fix incorrect line numbers with some IF blocks
If a line ended with a string's closing quote, or a newline escape, then
skipping over that line via IF/ELIF/ELSE would fail to count that line,
offsetting the rest of the file.
I have no idea why but for some reason 9829be1 changed *specifically*
`if_skip_to_else` to have incorrect behavior on string endings. The incorrect
behavior on newline escapes seems to have been here since the beginning.
Also added a test to check for both of those behaviors in both functions.

Honestly, it baffles me that nobody ever noticed. I didn't until I started
working on #395.
2019-09-01 03:59:29 +02:00
ISSOtm
3cc67c48cf Add recursion limit info to man and help 2019-08-31 17:34:54 +02:00
ISSOtm
f9a04696f2 Add recursion overflow tests 2019-08-31 17:22:43 +02:00
ISSOtm
e0e8170fe6 Add recursion limit for string expansions
Unlike macros, REPTs and INCLUDEs, this recursion depth is independent.
This is intentional, because string expansions work very differently.

While it's easy to know when a string expansion begins, checking where it
ends is much more complicated, since the expansion's contents are simply
injected back into the lex buffer. Therefore, the depth has to be checked
after lexing took place.
Because of this, the placement of the expansion end check is somewhat
haphazard, but I think it's good. While I have no certainty, all tests
ended with all expansions properly ended, and I couldn't find any pitfalls.

Finally, `pCurrentStringExpansion` has been made global so error printing
can use it to tell the user if an error occurred inside of an expansion.
2019-08-31 15:50:08 +02:00
Eldred Habert
9b40663d54 Merge pull request #409 from rednex/dd-head
Use POSIX-compatible dd(1) instead of head -c.
2019-08-31 12:59:18 +02:00
Anthony J. Bentley
a517f900e4 Use POSIX-compatible dd(1) instead of head -c. 2019-08-30 23:11:28 -06:00
Eldred Habert
350f40300c Merge pull request #403 from dbrotz/multiple-charmaps
Add support for multiple charmaps
2019-08-31 04:44:58 +02:00
dbrotz
d3db5f0d76 Add pushc and popc directives 2019-08-30 19:36:23 -07:00
ISSOtm
7e3af4b3cd Make testing external projects use local RGBDS
Currently, the installed version is used instead, which isn't
consistent with the tests, which use the local version.
2019-08-31 04:17:16 +02:00
ISSOtm
dfb3072381 Fix memory leaks with macro args
REPT blocks nested in macros (and possibly other cases) leaked
memory on every call. Unlike most other memory leaks, which would
be freed at the end of program execution if they were done properly,
those piled up the more compilation went on.
I believe memory usage could have started being fairly high on
large projects following the "one master file INCLUDEs all the rest"
so this may have actually been worth it.
2019-08-31 03:52:42 +02:00
ISSOtm
02191135a0 Fix possible unterminated string
sym_SetMacroArgID used a `sprintf` that could write no \0.
In practice this was benign because %u cannot print 256 chars,
but better future-proof this.
2019-08-31 03:00:26 +02:00
ISSOtm
dc2c97fe0c Comment and improve ParseSymbol and AppendMacroArg 2019-08-31 02:31:46 +02:00
ISSOtm
6068b565f5 Add recursion limit for INCLUDE and macros
(And REPT.)
Not exactly a *recursion* limit, more like a *stack depth* limit,
but calling it "recursion" conveys its purpose better.
The default of 64 is super overkill: even in a a project with
what I believe to be above-average levels of nesting, the
level only peaked at 6.
Keeping in mind the purpose of this is to catch infinite
recursion, which is still caught quickly (in usual cases, anyways),
this default seems sensible.
And it passes tests. What more do you need?
2019-08-31 02:31:42 +02:00
ISSOtm
a21cef7190 Say which macro argument caused an error when one does 2019-08-30 20:57:11 +02:00
dbrotz
461ef6cea5 Don't complain about initializing statics
This error is specific to the way the Linux kernel handles statics.
It does not apply to userspace programs.
2019-08-29 22:48:16 -07:00
dbrotz
e05199ca1e Add support for multiple charmaps
This adds two new directives: newcharmap and setcharmap.
newcharmap creates a new charmap and switches to it.
setcharmap switches to an existing charmap.
2019-08-29 21:54:06 -07:00
Eldred Habert
12d82eb768 Remove extra entry in error stack on macro not defined (#394)
While working on #392, I noticed that the macro-@ test (as well
as the line-continuation test, but for that one see #393)
printed an additional '@(-1)' entry which doesn't make sense.
2019-08-30 02:14:21 +02:00
Eldred Habert
05becf3f4b Merge pull request #381 from NieDzejkob/rgbgfx-curve
rgbgfx: Add an option to take the CGB's color profile into account
2019-08-29 22:19:36 +02:00
Eldred Habert
3cc7981c82 Merge pull request #402 from dbrotz/fix-386
Fix nested if statements that don't have following whitespace
2019-08-29 22:06:10 +02:00
dbrotz
8f287eeef9 Fix nested if statements that don't have following whitespace
When trying to skip over nested if statements, if there was no whitespace
after an "if", then that "if" would not be recognized. That's a problem since
"if(" and "if{" are also valid ways to start an if statement. This change
will make it so that they are recognized correctly.
2019-08-29 12:37:59 -07:00
Eldred Habert
ce05cb5683 Merge pull request #401 from dbrotz/line-cont-test
Get rid of error in line continuation test
2019-08-29 21:23:00 +02:00
dbrotz
17945a7377 Get rid of error in line continuation test
The purpose of the test is to ensure that rgbasm doesn't segfault in this case.
The "Macro '@' not defined" error is unnecessary.
2019-08-29 11:49:17 -07:00
Eldred Habert
3a1b47129e Merge pull request #397 from NieDzejkob/test-local-without-parent
Add a test for a defining local label without a parent
2019-08-29 20:24:36 +02:00
Eldred Habert
6ffa751090 Merge pull request #390 from ISSOtm/print_types
Add "print types" to bracketed symbols
2019-08-29 20:12:32 +02:00
Eldred Habert
c3641321d7 Merge pull request #399 from ISSOtm/allow_dots
Allow periods to continue macro args
2019-08-29 20:09:32 +02:00
Eldred Habert
446173f0cb Merge pull request #387 from ISSOtm/set_doesnt_override_equ
Prevent `SET` from overriding constant symbols
2019-08-29 20:08:08 +02:00
Eldred Habert
e27f381842 Merge pull request #361 from ISSOtm/better_section_overflow
Improve section overflow error message
2019-08-29 20:04:48 +02:00
ISSOtm
a3ee76dddd Allow periods to continue macro args
c75a953 broke my (previously-working) project that defined, via
macros, 'sizeof_.player'.
A test was added to confirm that those are indeed accepted
outside of macros.
2019-08-29 19:51:47 +02:00
ISSOtm
995265c549 Improve testing bracketed symbols
Also test EQU and _RS constants, as well as that EQUS errors out,
and that labels don't work.
2019-08-29 19:16:28 +02:00
Jakub Kądziołka
03629b74d9 Add a test for a defining local label without a parent 2019-08-29 19:14:02 +02:00
Eldred Habert
b069278e98 Merge pull request #384 from dbrotz/fix-local-label-segfault
Check if parent exists for local label reference
2019-08-29 19:05:27 +02:00
Eldred Habert
9738c88f95 Merge pull request #383 from dbrotz/fix-380
Change the precedence of == to match the documentation
2019-08-29 17:14:05 +02:00
ISSOtm
a21ea30be0 Add tests for bracketed symbols 2019-08-29 17:08:54 +02:00
ISSOtm
64752da42d Add "print types" to bracketed symbols
Should partially cover #178 and close #270.
This allows printing numbers in different bases and without the dollar prefix
This is especially useful in macros because the dollar isnt a valid character
for symbol names, requiring heavy `STRSUB` usage.
2019-08-29 14:04:58 +02:00
ISSOtm
e3e18063c6 Prevent SET from overriding constant symbols
Fixes #341
2019-08-27 21:23:36 +02:00
ISSOtm
e400eac42b Improve section overflow error message
When trying to fix a section becoming too large, the size it reached is necessary to know whether to optimize away a few bytes or split it entirely.
This error is also commonly encountered when INCBINing too large a slice of a file, in which case the amount of bytes by which the section is too large is again an useful information
2019-08-20 19:13:01 +02:00
dbrotz
a6bf77718c Check if parent exists for local label reference
If an attempt is made to reference a local label before any non-local label
is defined, raise an error instead of segfaulting.
2019-08-20 09:57:53 -07:00
dbrotz
c2787a9ea9 Change the precedence of == to match the documentation
The documentation states that == has the same precedence as the other
comparison operators.
2019-08-20 08:50:18 -07:00
Antonio Niño Díaz
e33e6e2413 Merge pull request #382 from NieDzejkob/checkpatch-bool-member
checkpatch.conf: Don't complain when bools are used in struct
2019-08-20 14:17:15 +01:00
Jakub Kądziołka
3cb56c5a2e checkpatch.conf: Don't complain when bools are used in struct
In RGBDS's codebase, boolean struct members aren't a problem. See
include/asm/main.h, include/gfx/main.h for examples.
2019-08-20 14:43:31 +02:00
Jakub Kądziołka
91984cb7e7 rgbgfx: Add an option to take the CGB's color profile into account 2019-08-20 14:38:17 +02:00
Antonio Niño Díaz
b8d5dd1824 Merge pull request #366 from dbrotz/fix-313
Fix signed integer overflow issues
2019-08-17 16:09:09 +01:00
Antonio Niño Díaz
88b66f2941 Merge pull request #364 from dbrotz/fix-362
Don't append invalid characters to symbol name
2019-08-17 16:08:27 +01:00
Antonio Niño Díaz
3c7e59b9e1 Merge pull request #374 from Ben10do/pushs-outside-of-section
Allow PUSHS to be used before a section
2019-08-17 16:07:28 +01:00
Ben Hetherington
c4471e3300 Update CONTRIBUTORS.rst
Updated to reflect my current committer identity.
2019-08-16 13:22:19 +01:00
Ben Hetherington
16111f46ef Allow PUSHS to be used before a section
Previously, a PUSHS before a SECTION directive would cause rgbasm to crash when encountering a subsequent POPS.

This is because the subsequently-called out_setCurrentSection() expected the new section to be non-null, which wasn’t the case in this situation. This has been addressed by allowing the ‘null’ section to be set in this function, and only dereferencing it (to set nPC) if a non-null section is to be set.

In practice, this means that PUSHS/POPS can now be used to push/restore a context without a section.
2019-08-16 13:22:19 +01:00
Antonio Niño Díaz
b019b03946 Merge pull request #358 from dbrotz/fix-357
Fix buffer overflow when creating patches with long RPN expressions
2019-07-12 00:49:08 +01:00
Antonio Niño Díaz
605acd24ba Merge pull request #372 from jidoc01/master
Document the feature of input from stdin (resolves #371)
2019-07-12 00:48:25 +01:00
jidoc01
c8630eee95 Document the feature of input from stdin
Since #329, rgbasm can get input from stdin. I updated the manpage
file related to it, which explain the description and usages in
rgbasm.
2019-07-08 23:28:46 +09:00
Antonio Niño Díaz
4040555532 Merge pull request #365 from dbrotz/terminate-bracketed-symbol
Terminate standalone bracketed symbol strings
2019-07-07 11:46:43 +01:00
Antonio Niño Díaz
defb221c98 Merge pull request #360 from jidoc01/master
Improve charmap structure with trie.
2019-07-07 11:46:08 +01:00
jidoc01
e7dc094e56 Improve charmap structure with trie
Charmap's previous structure was using brute-force comparison for
converting the strings in source files. It always compared given
strings to all of the strings in charmap, which was very costly
in huge projects.
For its improvement, I changed its structure into trie, which is
being used in many string-processing areas. It's now much faster
than before.
2019-07-06 19:25:44 +09:00
Antonio Niño Díaz
dfdb107105 Merge pull request #370 from jidoc01/fix_bug
Fix comment bug
2019-07-06 11:10:17 +01:00
Antonio Niño Díaz
9f598dfdb7 Merge pull request #359 from dbrotz/fix-lexer-out-of-bounds
Fix out of bounds array access in lexer
2019-07-06 11:09:43 +01:00
jidoc01
38110a833d Fix comment bug
There is a bug in processing the comments in source files. It's
related to #326. And this bug comes out when you comment something
with the character ';', and include the quotation mark without its
pair in it.

The lastest version of rgbds compiler has a step to parse the given
source to convert its line endings to a unified one, and it
processes quotation marks even before it processes the comments.

I edited a little bit of the source, and it works fine now.
2019-07-05 13:48:24 +09:00
dbrotz
484d15dbb2 Handle unprintable characters more gracefully
* Skip UTF-8 byte order mark at beginning of file
* Error on other unexpected unprintable characters
2019-07-04 17:14:55 -07:00
dbrotz
1decf5d0d4 Fix out of bounds array access in lexer
If the type char is signed, then in the function
yylex_GetFloatMaskAndFloatLen(), *s can have a negative value and be converted
to a negative int32_t which is then used as an array index. It should be
converted to uint8_t instead to ensure that the value is in the bounds of the
tFloatingFirstChar, tFloatingSecondChar, and tFloatingChars arrays.
2019-07-04 17:01:29 -07:00
dbrotz
74e9de1b71 Check for RPN stack overflow in linker 2019-07-04 16:49:23 -07:00
dbrotz
015d2b0830 Fix buffer overflow when creating patches with long RPN expressions
The createpatch() function was using a fixed-size buffer. I've changed it
to be dynamically allocated. I saw that the RPN format used in patches is
slightly different from the one used internally in the assembler, so I
added a new member to the Expression struct to track the patch size.

I've also limited the RPN expression length to 1MB. I realized that the
patch RPN expression could potentially be longer than the internal RPN
expression, so the internal expression would need a limit smaller than
UINT32_MAX. I thought 1MB would be a reasonable limit.
2019-07-04 16:49:09 -07:00
dbrotz
c75a9539ba Don't append invalid characters to symbol name
When a macro arg appears in a symbol name, the contents are appended.
However, the contents of the macro arg were not being validated.
Any character, regardless of whether it was allowed in a symbol name,
would be appended. With this change, the contents of the macro arg
are now validated character by character. The symbol name is considered
to end at the last valid character. The remainder of the macro arg is
treated as though it followed the symbol name in the asm source code.
2019-07-04 16:34:47 -07:00
dbrotz
ca6149abcf Fix signed integer overflow issues
It seemed that the consensus in our discussions of signed integer
overflow, which invokes undefined behavior in C, was that integer
arithmetic should be two's complement and there should be no warning for
overflows. I have implemented that by converting values to unsigned types
when appropriate. These changes will mostly preserve existing behavior,
except for a few cases that were being handled incorrectly before.

The case of dividing INT_MIN by -1 previously resulted in a CPU
exception and program termination. Now, that case is detected and results
in a warning and a value of INT_MIN.

Similarly, INT_MIN % -1 would have resulted in a CPU exception. Since this
is a mathematically valid operation with a result of 0, it now simply
gives that result without a warning.

I noticed that in rpn.c, there were attempts in certain operation handlers
to validate the nVal members of the source expressions even when the
expressions may have been relocatable expressions with meaningless numbers
for the nVal member. This could have caused spurious errors/warnings, so I
made those handlers confirm that isReloc is false before validating nVal.

Also, integer constants that are too large now result in a warning. The
post-conversion values have not been changed, in order to preserve
backward compatibility.
2019-07-04 16:27:31 -07:00
dbrotz
b3120aea25 Terminate standalone bracketed symbol strings
Standalone bracketed symbols like the following weren't being zero-terminated.

X EQUS {Y}

This doesn't apply to bracketed symbols that aren't standalone, but are
instead found in a string. For example, the following works even without this
fix.

X EQUS "{Y}"
2019-07-04 16:01:57 -07:00
Antonio Niño Díaz
54e5bf0f0c Merge pull request #343 from phs/phs/docker
Have a docker file
2019-07-04 23:07:16 +01:00
Antonio Niño Díaz
847cae5b95 Merge pull request #329 from NieDzejkob/allow-stdin-input
Allow using - to indicate input from stdin (resolves #305)
2019-07-04 23:02:42 +01:00
Jakub Kądziołka
df15c97b6e Handle zero-byte files gracefully 2019-07-03 16:38:35 +02:00
Jakub Kądziołka
0d97b58265 Avoid potentially implementation-defined behavior when using a pipe as input 2019-07-03 16:38:00 +02:00
Jakub Kądziołka
f7bc61e874 Automatic tests for input from stdin 2019-07-03 16:05:54 +02:00
Jakub Kądziołka
8d5a53f529 Handle non-seekable input correctly 2019-07-03 15:38:14 +02:00
Jakub Kądziołka
20f9492899 Allow using - to indicate input from stdin 2019-07-03 15:38:14 +02:00
Antonio Niño Díaz
3cd1d46a1b Merge pull request #356 from NieDzejkob/add-narg-test
Add a test for the behavior of NARG after SHIFT
2019-06-16 22:35:37 +01:00
Jakub Kądziołka
88eceec257 Add a test for the behavior of NARG after SHIFT 2019-06-09 12:58:32 +02:00
Antonio Niño Díaz
d00ec024a2 Merge pull request #351 from dbrotz/fix-strsub-strlen
Use code points instead of bytes for STRSUB/STRLEN
2019-06-07 10:31:11 +01:00
Antonio Niño Díaz
0bcd53778a Merge pull request #346 from qguv/tilemap-mirrored-duplicates
gfx: Add mirrored tile check when generating tilemap
2019-06-07 10:29:15 +01:00
Antonio Niño Díaz
7592eaf42b Merge pull request #354 from NieDzejkob/test-runner-stuff
Various test running fixes
2019-06-05 23:23:57 +01:00
Antonio Niño Díaz
12ed9e044a Merge pull request #353 from qguv/checkpatch-path-override
Clarify how to override checkpatch.pl path
2019-06-05 23:15:10 +01:00
Jakub Kądziołka
0a3af87aee Ignore the .git folder of the test repositories
Before this change, doing `git add test` would also add
pokecrystal and the other test repos, even though they
didn't show up on `git status`.
2019-06-05 20:54:21 +02:00
Jakub Kądziołka
4dee999f68 Clean the test repositories before running tests 2019-06-05 20:53:32 +02:00
Jakub Kądziołka
9a4941c794 Allow running the tests from outside of the test folder 2019-06-05 20:52:35 +02:00
Quint Guvernator
2d0fd71159 Clarify how to override checkpatch.pl path 2019-06-04 15:22:45 +02:00
Quint Guvernator
327582be31 Regenerate wwwman 2019-06-04 13:23:33 +02:00
Quint Guvernator
21aea281bd gfx: Add mirrored tile check when generating tilemap 2019-06-04 13:22:59 +02:00
dbrotz
975f85260d Use code points instead of bytes for STRSUB/STRLEN 2019-06-02 16:10:34 -07:00
dbrotz
f29d768989 Set all of expr struct's fields in mergetwoexpressions() 2019-05-31 08:59:50 -07:00
Phil Smith
9bd7ecad4c Have a docker file 2019-05-31 08:26:53 -07:00
Antonio Niño Díaz
cc458a9693 Fix a few checkpatch issues 2019-05-31 12:34:14 +01:00
Antonio Niño Díaz
d2bd9a2368 Merge pull request #337 from dbrotz/one-pass
Use only one pass
2019-05-31 12:10:46 +01:00
dbrotz
b909a5063a Include symbol name in 'symbol too long' error message 2019-05-29 10:56:59 -07:00
Marcus Huderle
b2c1f6122e Properly set all 16 characters in ROM header title 2019-05-18 19:43:54 -05:00
Antonio Niño Díaz
a761e98e18 Run checkpatch against origin/master
The develop branch has been deleted. Remove references to it.
2019-05-10 00:16:27 +01:00
dbrotz
e12e7b2acc Don't assign PC to macro symbols
Macros have nothing to do with the current PC, so this doesn't make any sense.
The value isn't ever used either.
2019-05-09 15:01:06 -07:00
dbrotz
f927c41abb Add test for referencing a symbol before setting it 2019-05-09 14:46:11 -07:00
dbrotz
249acace08 Prevent non-reloc symbol from shadowing reloc symbol 2019-05-09 12:48:10 -07:00
Antonio Niño Díaz
fa37922ca7 Remove develop branch from contributing guide
This branch was meant to contain changes that were considered too risky
to be in master. However, there is not enough activity in the repository
to justify its presence. Both branches are always pointing at the same
commit.

Signed-off-by: Antonio Niño Díaz <antonio_nd@outlook.com>
2019-05-09 18:58:25 +01:00
dbrotz
021990b8e0 Properly check if a symbol's full name is too long 2019-05-05 20:21:55 -07:00
dbrotz
540564694c Add missing space to error message 2019-05-05 18:13:10 -07:00
dbrotz
8da4feb83c Use sym_FindSymbol() where possible 2019-05-05 18:10:05 -07:00
dbrotz
23f5e9dacc Use only one pass 2019-05-05 15:50:56 -07:00
Antonio Niño Díaz
6ff9435e0a Merge pull request #335 from dbrotz/fix-334
Dynamically allocate RPN expression buffer
2019-05-05 23:33:36 +01:00
dbrotz
40006c6152 Make yylex() return int 2019-05-02 19:53:45 -07:00
dbrotz
b256e4c2e3 Dynamically allocate RPN expression buffer 2019-05-02 19:31:26 -07:00
Antonio Niño Díaz
a37a09c09c Merge pull request #328 from NieDzejkob/better-linker-errors
Print location information in linker errors where viable #328
2019-03-10 23:41:14 +01:00
Antonio Niño Díaz
8ece231d8b Merge pull request #327 from NieDzejkob/symbol-length-check
Fix symbol length checking
2019-03-10 23:40:03 +01:00
Jakub Kądziołka
e7de0745ad Improve documentation of the object format 2019-03-04 09:45:14 +01:00
Jakub Kądziołka
7af2d5dfe1 Print location information in linker errors where viable 2019-03-03 22:55:17 +01:00
Jakub Kądziołka
2f2f14bf80 Fix symbol length checking
When the while loop in `ParseSymbol` stops because of the symbol length,
`copied` will have the value of `MAXSYMLEN`, which is obviously not
greater than `MAXSYMLEN`. Changing the condition to `>=` fixes the
issue.

As a bonus, the correct union field will now be used. It shouldn't
matter, but it's technically UB to use a wrong one.
2019-03-02 19:11:53 +01:00
127 changed files with 5917 additions and 4620 deletions

View File

@@ -67,3 +67,9 @@
# Don't complain when files are modified in 'include/asm' # Don't complain when files are modified in 'include/asm'
--ignore MODIFIED_INCLUDE_ASM --ignore MODIFIED_INCLUDE_ASM
# Don't complain when bools are used in structs
--ignore BOOL_MEMBER
# Don't complain about initializing statics (this is specific to the kernel)
--ignore INITIALISED_STATIC

7
.dockerignore Normal file
View File

@@ -0,0 +1,7 @@
# This file is part of RGBDS.
#
# Copyright (c) 2018-2019, Phil Smith and RGBDS contributors.
#
# SPDX-License-Identifier: MIT
.git
docs

6
.gitignore vendored
View File

@@ -6,6 +6,6 @@ rgbgfx
*.exe *.exe
.checkpatch-camelcase.* .checkpatch-camelcase.*
test/pokecrystal/* test/pokecrystal
test/pokered/* test/pokered
test/ucity/* test/ucity

View File

@@ -26,9 +26,6 @@ echo "Running checkpatch.pl..."
fname=$(mktemp) fname=$(mktemp)
rc=0 rc=0
git remote set-branches --add origin develop
git fetch
make CHECKPATCH=checkpatchdir/checkpatch.pl checkpatch > $fname make CHECKPATCH=checkpatchdir/checkpatch.pl checkpatch > $fname
cat $fname cat $fname

View File

@@ -2,8 +2,8 @@ language: c
sudo: required sudo: required
install: install:
- ./.travis-deps.sh - ./.travis-deps.sh
- make - make Q=
- sudo make install - sudo make install Q=
os: os:
- linux - linux
- osx - osx

View File

@@ -65,9 +65,9 @@ copyright and the reference to the MIT License.
1. Fork this repository. 1. Fork this repository.
2. Checkout the ``develop`` branch. 2. Checkout the ``master`` branch.
3. Create a new branch to work on. You could still work on ``develop``, but it's 3. Create a new branch to work on. You could still work on ``master``, but it's
easier that way. easier that way.
4. Compile your changes with ``make develop`` instead of just ``make``. This 4. Compile your changes with ``make develop`` instead of just ``make``. This
@@ -88,9 +88,9 @@ copyright and the reference to the MIT License.
code. By default, the Makefile expects the script (and associate files) to be code. By default, the Makefile expects the script (and associate files) to be
located in ``../linux/scripts/``, but you can place them anywhere you like as located in ``../linux/scripts/``, but you can place them anywhere you like as
long as you specify it when executing the command: long as you specify it when executing the command:
``CHECKPATCH=../path/to/folder make checkpatch``. ``make checkpatch CHECKPATCH=../path/to/folder``.
8. Create a pull request against the branch ``develop``. 8. Create a pull request against the branch ``master``.
9. Be prepared to get some comments about your code and to modify it. Tip: Use 9. Be prepared to get some comments about your code and to modify it. Tip: Use
``git rebase -i origin/develop`` to modify chains of commits. ``git rebase -i origin/master`` to modify chains of commits.

View File

@@ -22,7 +22,7 @@ Main contributors
Other contributors Other contributors
------------------ ------------------
- Ben10do - Ben Hetherington <dev@ben-h.uk>
- Björn Höhrmann <bjoern@hoehrmann.de> - Björn Höhrmann <bjoern@hoehrmann.de>
@@ -30,14 +30,22 @@ Other contributors
- David Brotz <dbrotz007@gmail.com> - David Brotz <dbrotz007@gmail.com>
- Eldred Habert <eldredhabert0@gmail.com>
- The Musl C library <http://www.musl-libc.org> - The Musl C library <http://www.musl-libc.org>
- obskyr <powpowd@gmail.com> - obskyr <powpowd@gmail.com>
- The OpenBSD Project <http://www.openbsd.org> - The OpenBSD Project <http://www.openbsd.org>
- Quint Guvernator <quint@guvernator.net>
- Sanqui <gsanky@gmail.com> - Sanqui <gsanky@gmail.com>
- YamaArashi <shadow962@live.com> - YamaArashi <shadow962@live.com>
- yenatch <yenatch@gmail.com> - yenatch <yenatch@gmail.com>
- phs <phil@philhsmith.com>
- jidoc01 <jidoc01@naver.com>

25
Dockerfile Normal file
View File

@@ -0,0 +1,25 @@
# This file is part of RGBDS.
#
# Copyright (c) 2018-2019, Phil Smith and RGBDS contributors.
#
# SPDX-License-Identifier: MIT
# docker build -t rgbds:vX.X.X-alpine
FROM alpine:latest
RUN apk add --update \
build-base \
byacc \
flex \
libpng-dev
COPY . /rgbds
WORKDIR /rgbds
RUN make Q='' all
FROM alpine:latest
RUN apk add --update \
libpng
COPY --from=0 \
/rgbds/rgbasm \
/rgbds/rgbfix \
/rgbds/rgblink \
/rgbds/rgbgfx \
/bin/

View File

@@ -6,6 +6,9 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
# #
.SUFFIXES:
.SUFFIXES: .h .l .y .c .o
# User-defined variables # User-defined variables
Q := @ Q := @
@@ -33,6 +36,10 @@ CFLAGS := -g
# Non-overridable CFLAGS # Non-overridable CFLAGS
REALCFLAGS := ${CFLAGS} ${WARNFLAGS} -std=c99 -D_POSIX_C_SOURCE=200809L \ REALCFLAGS := ${CFLAGS} ${WARNFLAGS} -std=c99 -D_POSIX_C_SOURCE=200809L \
-Iinclude -DBUILD_VERSION_STRING=\"${VERSION_STRING}\" -Iinclude -DBUILD_VERSION_STRING=\"${VERSION_STRING}\"
# Overridable LDFLAGS
LDFLAGS :=
# Non-overridable LDFLAGS
REALLDFLAGS := ${LDFLAGS} ${WARNFLAGS}
YFLAGS := YFLAGS :=
LFLAGS := --nounistd LFLAGS := --nounistd
@@ -57,12 +64,12 @@ rgbasm_obj := \
src/asm/output.o \ src/asm/output.o \
src/asm/rpn.o \ src/asm/rpn.o \
src/asm/symbol.o \ src/asm/symbol.o \
src/asm/util.o \
src/extern/err.o \ src/extern/err.o \
src/extern/utf8decoder.o \ src/extern/utf8decoder.o \
src/version.o src/version.o
src/asm/asmy.h: src/asm/asmy.c src/asm/globlex.o src/asm/lexer.o src/asm/constexpr.o: src/asm/asmy.h
src/asm/locallex.o src/asm/globlex.o src/asm/lexer.o src/asm/constexpr.o: src/asm/asmy.h
rgblink_obj := \ rgblink_obj := \
src/link/assign.o \ src/link/assign.o \
@@ -79,7 +86,6 @@ rgblink_obj := \
src/extern/err.o \ src/extern/err.o \
src/version.o src/version.o
src/link/parser.h: src/link/parser.c
src/link/lexer.o: src/link/parser.h src/link/lexer.o: src/link/parser.h
rgbfix_obj := \ rgbfix_obj := \
@@ -95,27 +101,32 @@ rgbgfx_obj := \
src/version.o src/version.o
rgbasm: ${rgbasm_obj} rgbasm: ${rgbasm_obj}
$Q${CC} ${REALCFLAGS} -o $@ ${rgbasm_obj} -lm $Q${CC} ${REALLDFLAGS} -o $@ ${rgbasm_obj} -lm
rgblink: ${rgblink_obj} rgblink: ${rgblink_obj}
$Q${CC} ${REALCFLAGS} -o $@ ${rgblink_obj} $Q${CC} ${REALLDFLAGS} -o $@ ${rgblink_obj}
rgbfix: ${rgbfix_obj} rgbfix: ${rgbfix_obj}
$Q${CC} ${REALCFLAGS} -o $@ ${rgbfix_obj} $Q${CC} ${REALLDFLAGS} -o $@ ${rgbfix_obj}
rgbgfx: ${rgbgfx_obj} rgbgfx: ${rgbgfx_obj}
$Q${CC} ${REALCFLAGS} ${PNGLDFLAGS} -o $@ ${rgbgfx_obj} ${PNGLDLIBS} $Q${CC} ${REALLDFLAGS} ${PNGLDFLAGS} -o $@ ${rgbgfx_obj} ${PNGLDLIBS}
# Rules to process files # Rules to process files
# We want the yacc and lex invocations to pass through our rules
.y.o:
.l.o:
# yacc- and lex-generated C files have an accompanying header
.c.h:
$Qtouch $@
.y.c: .y.c:
$Q${YACC} -d ${YFLAGS} -o $@ $< $Q${YACC} -d ${YFLAGS} -o $@ $<
.l.o: .l.c:
$Q${RM} $*.c $Q${LEX} ${LFLAGS} -o $@ $<
$Q${LEX} ${LFLAGS} -o $*.c $<
$Q${CC} ${REALCFLAGS} -c -o $@ $*.c
$Q${RM} $*.c
.c.o: .c.o:
$Q${CC} ${REALCFLAGS} ${PNGCFLAGS} -c -o $@ $< $Q${CC} ${REALCFLAGS} ${PNGCFLAGS} -c -o $@ $<
@@ -124,10 +135,11 @@ rgbgfx: ${rgbgfx_obj}
# for the html documentation. # for the html documentation.
clean: clean:
$Q${RM} rgbasm rgbasm.exe ${rgbasm_obj} $Q${RM} rgbasm rgbasm.exe
$Q${RM} rgblink rgblink.exe ${rgblink_obj} $Q${RM} rgblink rgblink.exe
$Q${RM} rgbfix rgbfix.exe ${rgbfix_obj} $Q${RM} rgbfix rgbfix.exe
$Q${RM} rgbgfx rgbgfx.exe ${rgbgfx_obj} $Q${RM} rgbgfx rgbgfx.exe
$Qfind src/ -name "*.o" -exec rm {} \;
$Q${RM} src/asm/asmy.c src/asm/asmy.h $Q${RM} src/asm/asmy.c src/asm/asmy.h
$Q${RM} src/link/lexer.c src/link/parser.c src/link/parser.h $Q${RM} src/link/lexer.c src/link/parser.c src/link/parser.h
@@ -169,11 +181,11 @@ checkcodebase:
# Target used to check the coding style of the patches from the upstream branch # Target used to check the coding style of the patches from the upstream branch
# to the HEAD. Runs checkpatch once for each commit between the current HEAD and # to the HEAD. Runs checkpatch once for each commit between the current HEAD and
# the first common commit between the HEAD and origin/develop. '.y' and '.l' # the first common commit between the HEAD and origin/master. '.y' and '.l'
# files aren't checked, unfortunately... # files aren't checked, unfortunately...
checkpatch: checkpatch:
$Qeval COMMON_COMMIT=$$(git merge-base HEAD origin/develop); \ $Qeval COMMON_COMMIT=$$(git merge-base HEAD origin/master); \
for commit in `git rev-list $$COMMON_COMMIT..HEAD`; do \ for commit in `git rev-list $$COMMON_COMMIT..HEAD`; do \
echo "[*] Analyzing commit '$$commit'"; \ echo "[*] Analyzing commit '$$commit'"; \
git format-patch --stdout "$$commit~..$$commit" \ git format-patch --stdout "$$commit~..$$commit" \
@@ -202,7 +214,7 @@ wwwman:
develop: develop:
$Qenv make -j WARNFLAGS="-Werror -Wall -Wextra -Wpedantic \ $Qenv make -j WARNFLAGS="-Werror -Wall -Wextra -Wpedantic \
-Wno-sign-compare -Wchkp -Wformat=2 -Wformat-overflow=2 \ -Wno-sign-compare -Wformat=2 -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 \

View File

@@ -24,13 +24,16 @@ found in this repository.
1.1 Windows 1.1 Windows
~~~~~~~~~~~ ~~~~~~~~~~~
Windows builds are available in the releases page on GitHub: 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:
:: ::
https://github.com/rednex/rgbds/releases path %PATH%;C:\Programs\rgbds-0.3.8-win64\win64
Copy the ``.exe`` files to ``C:\Windows\`` or similar. 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 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 compile RGBDS with MinGW or Cygwin by following the instructions to build it on

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,14 @@
<!DOCTYPE html> <!DOCTYPE html>
<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> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<style>
table.head, table.foot { width: 100%; }
td.head-rtitle, td.foot-os { text-align: right; }
td.head-vol { text-align: center; }
div.Pp { margin: 1ex 0ex; }
</style>
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/> <link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
<title>RGBASM(1)</title> <title>RGBASM(1)</title>
</head> </head>
@@ -20,136 +21,122 @@
</tr> </tr>
</table> </table>
<div class="manual-text"> <div class="manual-text">
<h1 class="Sh" title="Sh" id="NAME"><a class="selflink" href="#NAME">NAME</a></h1> <section class="Sh">
<b class="Nm" title="Nm">rgbasm</b> &#x2014; <span class="Nd" title="Nd">Game <h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
Boy assembler</span> <code class="Nm">rgbasm</code> &#x2014;
<h1 class="Sh" title="Sh" id="SYNOPSIS"><a class="selflink" href="#SYNOPSIS">SYNOPSIS</a></h1> <div class="Nd">Game Boy assembler</div>
</section>
<section class="Sh">
<h1 class="Sh" id="SYNOPSIS"><a class="permalink" href="#SYNOPSIS">SYNOPSIS</a></h1>
<table class="Nm"> <table class="Nm">
<tr> <tr>
<td><b class="Nm" title="Nm">rgbasm</b></td> <td><code class="Nm">rgbasm</code></td>
<td>[<span class="Op"><b class="Fl" title="Fl">-EhLVvw</b></span>] <td>[<code class="Fl">-EhLVvw</code>] [<code class="Fl">-b</code>
[<span class="Op"><b class="Fl" title="Fl">-b</b> <var class="Ar">chars</var>] [<code class="Fl">-D</code>
<var class="Ar" title="Ar">chars</var></span>] <var class="Ar">name</var>[=<var class="Ar">value</var>]]
[<span class="Op"><b class="Fl" title="Fl">-D</b> [<code class="Fl">-g</code> <var class="Ar">chars</var>]
<var class="Ar" title="Ar">name</var>[<span class="Op">=<var class="Ar" title="Ar">value</var></span>]</span>] [<code class="Fl">-i</code> <var class="Ar">path</var>]
[<span class="Op"><b class="Fl" title="Fl">-g</b> [<code class="Fl">-M</code> <var class="Ar">dependfile</var>]
<var class="Ar" title="Ar">chars</var></span>] [<code class="Fl">-o</code> <var class="Ar">outfile</var>]
[<span class="Op"><b class="Fl" title="Fl">-i</b> [<code class="Fl">-p</code> <var class="Ar">pad_value</var>]
<var class="Ar" title="Ar">path</var></span>] [<code class="Fl">-r</code> <var class="Ar">recursion_depth</var>]
[<span class="Op"><b class="Fl" title="Fl">-M</b> <var class="Ar">file</var></td>
<var class="Ar" title="Ar">dependfile</var></span>]
[<span class="Op"><b class="Fl" title="Fl">-o</b>
<var class="Ar" title="Ar">outfile</var></span>]
[<span class="Op"><b class="Fl" title="Fl">-p</b>
<var class="Ar" title="Ar">pad_value</var></span>]
<var class="Ar" title="Ar">file</var></td>
</tr> </tr>
</table> </table>
<h1 class="Sh" title="Sh" id="DESCRIPTION"><a class="selflink" href="#DESCRIPTION">DESCRIPTION</a></h1> </section>
The <b class="Nm" title="Nm">rgbasm</b> program creates an object file from an <section class="Sh">
assembly source file. Its arguments are as follows: <h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
The <code class="Nm">rgbasm</code> program creates an 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>. Its
arguments are as follows:
<dl class="Bl-tag"> <dl class="Bl-tag">
<dt class="It-tag">&#x00A0;</dt> <dt><a class="permalink" href="#b"><code class="Fl" id="b">-b</code></a>
<dd class="It-tag">&#x00A0;</dd> <var class="Ar">chars</var></dt>
<dt class="It-tag"><a class="selflink" href="#b"><b class="Fl" title="Fl" id="b">-b</b></a> <dd>Change the two characters used for binary constants. The defaults are
<var class="Ar" title="Ar">chars</var></dt> 01.</dd>
<dd class="It-tag">Change the two characters used for binary constants. The <dt><a class="permalink" href="#D"><code class="Fl" id="D">-D</code></a>
defaults are 01.</dd> <var class="Ar">name</var>[=<var class="Ar">value</var>]</dt>
<dt class="It-tag">&#x00A0;</dt> <dd>Add string symbol to the compiled source code. This is equivalent to
<dd class="It-tag">&#x00A0;</dd> <var class="Ar">name</var> <code class="Cm">EQUS</code>
<dt class="It-tag"><a class="selflink" href="#D"><b class="Fl" title="Fl" id="D">-D</b></a> &#x201C;<var class="Ar">value</var>&#x201D; in code. If a value is not
<var class="Ar" title="Ar">name</var>[<span class="Op">=<var class="Ar" title="Ar">value</var></span>]</dt> specified, a value of 1 is given.</dd>
<dd class="It-tag">Add string symbol to the compiled source code. This is <dt><a class="permalink" href="#E"><code class="Fl" id="E">-E</code></a></dt>
equivalent to <var class="Ar" title="Ar">name</var> <dd>Export all labels, including unreferenced and local labels.</dd>
<b class="Cm" title="Cm">EQUS</b> <dt><a class="permalink" href="#g"><code class="Fl" id="g">-g</code></a>
&#x201C;<var class="Ar" title="Ar">value</var>&#x201D; in code. If a value <var class="Ar">chars</var></dt>
is not specified, a value of 1 is given.</dd> <dd>Change the four characters used for binary constants. The defaults are
<dt class="It-tag">&#x00A0;</dt> 0123.</dd>
<dd class="It-tag">&#x00A0;</dd> <dt><a class="permalink" href="#h"><code class="Fl" id="h">-h</code></a></dt>
<dt class="It-tag"><a class="selflink" href="#E"><b class="Fl" title="Fl" id="E">-E</b></a></dt> <dd>By default, <code class="Nm">rgbasm</code> inserts a &#x2018;nop&#x2019;
<dd class="It-tag">Export all labels, including unreferenced and local instruction immediately after any &#x2018;halt&#x2019; instruction. The
labels.</dd> <code class="Fl">-h</code> option disables this behavior.</dd>
<dt class="It-tag">&#x00A0;</dt> <dt><a class="permalink" href="#i"><code class="Fl" id="i">-i</code></a>
<dd class="It-tag">&#x00A0;</dd> <var class="Ar">path</var></dt>
<dt class="It-tag"><a class="selflink" href="#g"><b class="Fl" title="Fl" id="g">-g</b></a> <dd>Add an include path.</dd>
<var class="Ar" title="Ar">chars</var></dt> <dt><a class="permalink" href="#L"><code class="Fl" id="L">-L</code></a></dt>
<dd class="It-tag">Change the four characters used for binary constants. The <dd>Disable the optimization that turns loads of the form <b class="Sy">LD
defaults are 0123.</dd> [$FF00+n8],A</b> into the opcode <b class="Sy">LDH [$FF00+n8],A</b> in
<dt class="It-tag">&#x00A0;</dt> order to have full control of the result in the final ROM.</dd>
<dd class="It-tag">&#x00A0;</dd> <dt><a class="permalink" href="#M"><code class="Fl" id="M">-M</code></a>
<dt class="It-tag"><a class="selflink" href="#h"><b class="Fl" title="Fl" id="h">-h</b></a></dt> <var class="Ar">dependfile</var></dt>
<dd class="It-tag">By default, <b class="Nm" title="Nm">rgbasm</b> inserts a <dd>Print <a class="Xr">make(1)</a> dependencies to
&#x2018;nop&#x2019; instruction immediately after any &#x2018;halt&#x2019; <var class="Ar">dependfile</var>.</dd>
instruction. The <b class="Fl" title="Fl">-h</b> option disables this <dt><a class="permalink" href="#o"><code class="Fl" id="o">-o</code></a>
behavior.</dd> <var class="Ar">outfile</var></dt>
<dt class="It-tag">&#x00A0;</dt> <dd>Write an object file to the given filename.</dd>
<dd class="It-tag">&#x00A0;</dd> <dt><a class="permalink" href="#p"><code class="Fl" id="p">-p</code></a>
<dt class="It-tag"><a class="selflink" href="#i"><b class="Fl" title="Fl" id="i">-i</b></a> <var class="Ar">pad_value</var></dt>
<var class="Ar" title="Ar">path</var></dt> <dd>When padding an image, pad with this value. The default is 0x00.</dd>
<dd class="It-tag">Add an include path.</dd> <dt><a class="permalink" href="#r"><code class="Fl" id="r">-r</code></a>
<dt class="It-tag">&#x00A0;</dt> <var class="Ar">recursion_depth</var></dt>
<dd class="It-tag">&#x00A0;</dd> <dd>Specifies the recursion depth at which RGBASM will assume being in an
<dt class="It-tag"><a class="selflink" href="#L"><b class="Fl" title="Fl" id="L">-L</b></a></dt> infinite loop.</dd>
<dd class="It-tag">Disable the optimization that turns loads of the form <dt><a class="permalink" href="#V"><code class="Fl" id="V">-V</code></a></dt>
<b class="Sy" title="Sy">LD [$FF00+n8],A</b> into the opcode <dd>Print the version of the program and exit.</dd>
<b class="Sy" title="Sy">LDH [$FF00+n8],A</b> in order to have full <dt><a class="permalink" href="#v"><code class="Fl" id="v">-v</code></a></dt>
control of the result in the final ROM.</dd> <dd>Be verbose.</dd>
<dt class="It-tag">&#x00A0;</dt> <dt><a class="permalink" href="#w"><code class="Fl" id="w">-w</code></a></dt>
<dd class="It-tag">&#x00A0;</dd> <dd>Disable warning output.</dd>
<dt class="It-tag"><a class="selflink" href="#M"><b class="Fl" title="Fl" id="M">-M</b></a>
<var class="Ar" title="Ar">dependfile</var></dt>
<dd class="It-tag">Print <a class="Xr" title="Xr">make(1)</a> dependencies to
<var class="Ar" title="Ar">dependfile</var>.</dd>
<dt class="It-tag">&#x00A0;</dt>
<dd class="It-tag">&#x00A0;</dd>
<dt class="It-tag"><a class="selflink" href="#o"><b class="Fl" title="Fl" id="o">-o</b></a>
<var class="Ar" title="Ar">outfile</var></dt>
<dd class="It-tag">Write an object file to the given filename.</dd>
<dt class="It-tag">&#x00A0;</dt>
<dd class="It-tag">&#x00A0;</dd>
<dt class="It-tag"><a class="selflink" href="#p"><b class="Fl" title="Fl" id="p">-p</b></a>
<var class="Ar" title="Ar">pad_value</var></dt>
<dd class="It-tag">When padding an image, pad with this value. The default is
0x00.</dd>
<dt class="It-tag">&#x00A0;</dt>
<dd class="It-tag">&#x00A0;</dd>
<dt class="It-tag"><a class="selflink" href="#V"><b class="Fl" title="Fl" id="V">-V</b></a></dt>
<dd class="It-tag">Print the version of the program and exit.</dd>
<dt class="It-tag">&#x00A0;</dt>
<dd class="It-tag">&#x00A0;</dd>
<dt class="It-tag"><a class="selflink" href="#v"><b class="Fl" title="Fl" id="v">-v</b></a></dt>
<dd class="It-tag">Be verbose.</dd>
<dt class="It-tag">&#x00A0;</dt>
<dd class="It-tag">&#x00A0;</dd>
<dt class="It-tag"><a class="selflink" href="#w"><b class="Fl" title="Fl" id="w">-w</b></a></dt>
<dd class="It-tag">Disable warning output.</dd>
</dl> </dl>
<h1 class="Sh" title="Sh" id="EXAMPLES"><a class="selflink" href="#EXAMPLES">EXAMPLES</a></h1> </section>
Assembling a basic source file is simple: <section class="Sh">
<div class="Pp"></div> <h1 class="Sh" id="EXAMPLES"><a class="permalink" href="#EXAMPLES">EXAMPLES</a></h1>
<div class="Bd" style="margin-left: 5.00ex;"> You can assemble a source file in two ways. Straight forward way:
<pre class="Li"> <div class="Bd Pp Bd-indent">
<pre>
$ rgbasm -o bar.o foo.asm $ rgbasm -o bar.o foo.asm
</pre> </pre>
</div> </div>
<div class="Pp"></div> <p class="Pp">Pipes way:</p>
The resulting object file is not yet a usable ROM image &#x2014; it must first <div class="Bd Pp Bd-indent">
be run through <a class="Xr" title="Xr">rgblink(1)</a> and <pre>
<a class="Xr" title="Xr">rgbfix(1)</a>. $ cat foo.asm | rgbasm -o bar.o -
<h1 class="Sh" title="Sh" id="SEE_ALSO"><a class="selflink" href="#SEE_ALSO">SEE $ rgbasm -o bar.o - &lt; foo.asm
</pre>
</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">rgblink(1)</a> and
<a class="Xr">rgbfix(1)</a>.</p>
</section>
<section class="Sh">
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
ALSO</a></h1> ALSO</a></h1>
<a class="Xr" title="Xr">rgbasm(5)</a>, <a class="Xr" title="Xr">rgbfix(1)</a>, <a class="Xr">rgbasm(5)</a>, <a class="Xr">rgbfix(1)</a>,
<a class="Xr" title="Xr">rgblink(1)</a>, <a class="Xr">rgblink(1)</a>, <a class="Xr">rgbds(5)</a>,
<a class="Xr" title="Xr">rgbds(5)</a>, <a class="Xr" title="Xr">rgbds(7)</a>, <a class="Xr">rgbds(7)</a>, <a class="Xr">gbz80(7)</a>
<a class="Xr" title="Xr">gbz80(7)</a> </section>
<h1 class="Sh" title="Sh" id="HISTORY"><a class="selflink" href="#HISTORY">HISTORY</a></h1> <section class="Sh">
<b class="Nm" title="Nm">rgbasm</b> was originally written by Carsten <h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
S&#x00F8;rensen as part of the ASMotor package, and was later packaged in <code class="Nm">rgbasm</code> was originally written by Carsten S&#x00F8;rensen
RGBDS by Justin Lloyd. It is now maintained by a number of contributors at as part of the ASMotor package, and was later packaged in RGBDS by Justin
<a class="Lk" title="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.</div> 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"> <table class="foot">
<tr> <tr>
<td class="foot-date">February 24, 2018</td> <td class="foot-date">July 8, 2019</td>
<td class="foot-os">RGBDS Manual</td> <td class="foot-os">RGBDS Manual</td>
</tr> </tr>
</table> </table>

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,14 @@
<!DOCTYPE html> <!DOCTYPE html>
<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> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<style>
table.head, table.foot { width: 100%; }
td.head-rtitle, td.foot-os { text-align: right; }
td.head-vol { text-align: center; }
div.Pp { margin: 1ex 0ex; }
</style>
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/> <link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
<title>RGBDS(5)</title> <title>RGBDS(5)</title>
</head> </head>
@@ -20,27 +21,29 @@
</tr> </tr>
</table> </table>
<div class="manual-text"> <div class="manual-text">
<h1 class="Sh" title="Sh" id="NAME"><a class="selflink" href="#NAME">NAME</a></h1> <section class="Sh">
<b class="Nm" title="Nm">rgbds</b> &#x2014; <span class="Nd" title="Nd">object <h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
file format documentation</span> <code class="Nm">rgbds</code> &#x2014;
<h1 class="Sh" title="Sh" id="DESCRIPTION"><a class="selflink" href="#DESCRIPTION">DESCRIPTION</a></h1> <div class="Nd">object file format documentation</div>
This is the description of the object files used by </section>
<a class="Xr" title="Xr">rgbasm(1)</a> and <section class="Sh">
<a class="Xr" title="Xr">rgblink(1)</a>. Please, note that the specifications <h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
may change. This toolchain is in development and new features may require This is the description of the object files used by <a class="Xr">rgbasm(1)</a>
adding more information to the current format, or modifying some fields, which and <a class="Xr">rgblink(1)</a>. Please, note that the specifications may
would break compatibility with older versions. change. This toolchain is in development and new features may require adding
<h1 class="Sh" title="Sh" id="FILE_STRUCTURE"><a class="selflink" href="#FILE_STRUCTURE">FILE 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> STRUCTURE</a></h1>
The following types are used: The following types are used:
<div class="Pp"></div> <p class="Pp"><var class="Ar">LONG</var> is a 32&#x2010;bit integer stored in
<var class="Ar" title="Ar">LONG</var> is a 32&#x2010;bit integer stored in little&#x2010;endian format (Intel). <var class="Ar">BYTE</var> is an
little&#x2010;endian format (Intel). <var class="Ar" title="Ar">BYTE</var> is 8&#x2010;bit integer. <var class="Ar">STRING</var> is a 0&#x2010;terminated
an 8&#x2010;bit integer. <var class="Ar" title="Ar">STRING</var> is a string of <var class="Ar">BYTE</var>.</p>
0&#x2010;terminated string of <var class="Ar" title="Ar">BYTE</var>. <div class="Bd Pp">
<div class="Pp"></div> <pre>
<div class="Bd" style="margin-left: 0.00ex;">
<pre class="Li">
; Header ; Header
BYTE ID[4] ; &quot;RGB6&quot; BYTE ID[4] ; &quot;RGB6&quot;
@@ -55,7 +58,7 @@ REPT NumberOfSymbols ; Number of symbols defined in this object file.
; as &quot;Scope.Symbol&quot;. ; as &quot;Scope.Symbol&quot;.
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 (unused). ; 1 = IMPORT this symbol from elsewhere
; 2 = EXPORT this symbol to other objects. ; 2 = EXPORT this symbol to other objects.
IF Type != 1 ; If symbol is defined in this object file. IF Type != 1 ; If symbol is defined in this object file.
@@ -137,7 +140,8 @@ REPT NumberOfSections
ENDR ENDR
</pre> </pre>
</div> </div>
<h2 class="Ss" title="Ss" id="RPN_DATA"><a class="selflink" href="#RPN_DATA">RPN <section class="Ss">
<h2 class="Ss" id="RPN_DATA"><a class="permalink" href="#RPN_DATA">RPN
DATA</a></h2> DATA</a></h2>
Expressions in the object file are stored as RPN. This is an expression of the 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; form &#x201C;2 5 +&#x201D;. This will first push the value &#x201C;2&#x201D;
@@ -146,159 +150,160 @@ Expressions in the object file are stored as RPN. This is an expression of the
effectively replacing the two top arguments with their sum. In the RGB format, effectively replacing the two top arguments with their sum. In the RGB format,
RPN expressions are stored as BYTEs with some bytes being special prefixes for RPN expressions are stored as BYTEs with some bytes being special prefixes for
integers and symbols. integers and symbols.
<table class="Bl-column" style="margin-left: 6.00ex;"> <table class="Bl-column Bd-indent">
<colgroup> <tr>
<col style="width: 15.00ex;"/> <td><b class="Sy">Value</b></td>
<col style="min-width: 10.00ex;"/> <td><b class="Sy">Meaning</b></td>
</colgroup>
<tr class="It-column">
<td class="It-column"><b class="Sy" title="Sy">Value</b></td>
<td class="It-column"><b class="Sy" title="Sy">Meaning</b></td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$00"><code class="Li" id="$00">$00</code></a></td> <td><a class="permalink" href="#$00"><code class="Li" id="$00">$00</code></a></td>
<td class="It-column"><a class="selflink" href="#+_operator"><code class="Li" id="+_operator">+ <td><a class="permalink" href="#+_operator"><code class="Li" id="+_operator">+
operator</code></a></td> operator</code></a></td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$01"><code class="Li" id="$01">$01</code></a></td> <td><a class="permalink" href="#$01"><code class="Li" id="$01">$01</code></a></td>
<td class="It-column"><a class="selflink" href="#-_operator"><code class="Li" id="-_operator">- <td><a class="permalink" href="#-_operator"><code class="Li" id="-_operator">-
operator</code></a></td> operator</code></a></td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$02"><code class="Li" id="$02">$02</code></a></td> <td><a class="permalink" href="#$02"><code class="Li" id="$02">$02</code></a></td>
<td class="It-column"><a class="selflink" href="#*_operator"><code class="Li" id="*_operator">* <td><a class="permalink" href="#*_operator"><code class="Li" id="*_operator">*
operator</code></a></td> operator</code></a></td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$03"><code class="Li" id="$03">$03</code></a></td> <td><a class="permalink" href="#$03"><code class="Li" id="$03">$03</code></a></td>
<td class="It-column"><a class="selflink" href="#/_operator"><code class="Li" id="/_operator">/ <td><a class="permalink" href="#/_operator"><code class="Li" id="/_operator">/
operator</code></a></td> operator</code></a></td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$04"><code class="Li" id="$04">$04</code></a></td> <td><a class="permalink" href="#$04"><code class="Li" id="$04">$04</code></a></td>
<td class="It-column"><a class="selflink" href="#%_operator"><code class="Li" id="%_operator">% <td><a class="permalink" href="#__operator"><code class="Li" id="__operator">%
operator</code></a></td> operator</code></a></td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$05"><code class="Li" id="$05">$05</code></a></td> <td><a class="permalink" href="#$05"><code class="Li" id="$05">$05</code></a></td>
<td class="It-column"><a class="selflink" href="#unary_-"><code class="Li" id="unary_-">unary <td><a class="permalink" href="#unary_-"><code class="Li" id="unary_-">unary
-</code></a></td> -</code></a></td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$10"><code class="Li" id="$10">$10</code></a></td> <td><a class="permalink" href="#$10"><code class="Li" id="$10">$10</code></a></td>
<td class="It-column">| <td>|
<a class="selflink" href="#operator"><code class="Li" id="operator">operator</code></a></td> <a class="permalink" href="#operator"><code class="Li" id="operator">operator</code></a></td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$11"><code class="Li" id="$11">$11</code></a></td> <td><a class="permalink" href="#$11"><code class="Li" id="$11">$11</code></a></td>
<td class="It-column"><a class="selflink" href="#&amp;_operator"><code class="Li" id="&amp;_operator">&amp; <td><a class="permalink" href="#&amp;_operator"><code class="Li" id="&amp;_operator">&amp;
operator</code></a></td> operator</code></a></td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$12"><code class="Li" id="$12">$12</code></a></td> <td><a class="permalink" href="#$12"><code class="Li" id="$12">$12</code></a></td>
<td class="It-column"><a class="selflink" href="#^_operator"><code class="Li" id="^_operator">^ <td><a class="permalink" href="#__operator_2"><code class="Li" id="__operator_2">^
operator</code></a></td> operator</code></a></td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$13"><code class="Li" id="$13">$13</code></a></td> <td><a class="permalink" href="#$13"><code class="Li" id="$13">$13</code></a></td>
<td class="It-column"><a class="selflink" href="#unary_~"><code class="Li" id="unary_~">unary <td><a class="permalink" href="#unary_~"><code class="Li" id="unary_~">unary
~</code></a></td> ~</code></a></td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$21"><code class="Li" id="$21">$21</code></a></td> <td><a class="permalink" href="#$21"><code class="Li" id="$21">$21</code></a></td>
<td class="It-column"><a class="selflink" href="#&amp;&amp;_comparison"><code class="Li" id="&amp;&amp;_comparison">&amp;&amp; <td><a class="permalink" href="#&amp;&amp;_comparison"><code class="Li" id="&amp;&amp;_comparison">&amp;&amp;
comparison</code></a></td> comparison</code></a></td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$22"><code class="Li" id="$22">$22</code></a></td> <td><a class="permalink" href="#$22"><code class="Li" id="$22">$22</code></a></td>
<td class="It-column"><a class="selflink" href="#||_comparison"><code class="Li" id="||_comparison">|| <td><a class="permalink" href="#___comparison"><code class="Li" id="___comparison">||
comparison</code></a></td> comparison</code></a></td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$23"><code class="Li" id="$23">$23</code></a></td> <td><a class="permalink" href="#$23"><code class="Li" id="$23">$23</code></a></td>
<td class="It-column"><a class="selflink" href="#unary"><code class="Li" id="unary">unary</code></a>!</td> <td><a class="permalink" href="#unary"><code class="Li" id="unary">unary</code></a>!</td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$30"><code class="Li" id="$30">$30</code></a></td> <td><a class="permalink" href="#$30"><code class="Li" id="$30">$30</code></a></td>
<td class="It-column"><a class="selflink" href="#==_comparison"><code class="Li" id="==_comparison">== <td><a class="permalink" href="#==_comparison"><code class="Li" id="==_comparison">==
comparison</code></a></td> comparison</code></a></td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$31"><code class="Li" id="$31">$31</code></a></td> <td><a class="permalink" href="#$31"><code class="Li" id="$31">$31</code></a></td>
<td class="It-column"><a class="selflink" href="#!=_comparison"><code class="Li" id="!=_comparison">!= <td><a class="permalink" href="#!=_comparison"><code class="Li" id="!=_comparison">!=
comparison</code></a></td> comparison</code></a></td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$32"><code class="Li" id="$32">$32</code></a></td> <td><a class="permalink" href="#$32"><code class="Li" id="$32">$32</code></a></td>
<td class="It-column"><a class="selflink" href="#&gt;_comparison"><code class="Li" id="&gt;_comparison">&gt; <td><a class="permalink" href="#__comparison"><code class="Li" id="__comparison">&gt;
comparison</code></a></td> comparison</code></a></td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$33"><code class="Li" id="$33">$33</code></a></td> <td><a class="permalink" href="#$33"><code class="Li" id="$33">$33</code></a></td>
<td class="It-column"><a class="selflink" href="#&lt;_comparison"><code class="Li" id="&lt;_comparison">&lt; <td><a class="permalink" href="#__comparison_2"><code class="Li" id="__comparison_2">&lt;
comparison</code></a></td> comparison</code></a></td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$34"><code class="Li" id="$34">$34</code></a></td> <td><a class="permalink" href="#$34"><code class="Li" id="$34">$34</code></a></td>
<td class="It-column"><a class="selflink" href="#&gt;=_comparison"><code class="Li" id="&gt;=_comparison">&gt;= <td><a class="permalink" href="#_=_comparison"><code class="Li" id="_=_comparison">&gt;=
comparison</code></a></td> comparison</code></a></td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$35"><code class="Li" id="$35">$35</code></a></td> <td><a class="permalink" href="#$35"><code class="Li" id="$35">$35</code></a></td>
<td class="It-column"><a class="selflink" href="#&lt;=_comparison"><code class="Li" id="&lt;=_comparison">&lt;= <td><a class="permalink" href="#_=_comparison_2"><code class="Li" id="_=_comparison_2">&lt;=
comparison</code></a></td> comparison</code></a></td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$40"><code class="Li" id="$40">$40</code></a></td> <td><a class="permalink" href="#$40"><code class="Li" id="$40">$40</code></a></td>
<td class="It-column"><a class="selflink" href="#&lt;&lt;_comparison"><code class="Li" id="&lt;&lt;_comparison">&lt;&lt; <td><a class="permalink" href="#___operator"><code class="Li" id="___operator">&lt;&lt;
comparison</code></a></td> operator</code></a></td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$41"><code class="Li" id="$41">$41</code></a></td> <td><a class="permalink" href="#$41"><code class="Li" id="$41">$41</code></a></td>
<td class="It-column"><a class="selflink" href="#&gt;&gt;_comparison"><code class="Li" id="&gt;&gt;_comparison">&gt;&gt; <td><a class="permalink" href="#___operator_2"><code class="Li" id="___operator_2">&gt;&gt;
comparison</code></a></td> operator</code></a></td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$50"><code class="Li" id="$50">$50</code></a></td> <td><a class="permalink" href="#$50"><code class="Li" id="$50">$50</code></a></td>
<td class="It-column"><a class="selflink" href="#BANK(symbol),"><code class="Li" id="BANK(symbol),">BANK(symbol),</code></a> <td><a class="permalink" href="#BANK(symbol),"><code class="Li" id="BANK(symbol),">BANK(symbol),</code></a>
a <var class="Ar" title="Ar">LONG</var> Symbol ID follows.</td> a <var class="Ar">LONG</var> Symbol ID follows.</td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$51"><code class="Li" id="$51">$51</code></a></td> <td><a class="permalink" href="#$51"><code class="Li" id="$51">$51</code></a></td>
<td class="It-column"><a class="selflink" href="#BANK(section_name),"><code class="Li" id="BANK(section_name),">BANK(section_name),</code></a> <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> a null-terminated string follows.</td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$52"><code class="Li" id="$52">$52</code></a></td> <td><a class="permalink" href="#$52"><code class="Li" id="$52">$52</code></a></td>
<td class="It-column"><a class="selflink" href="#Current_BANK()"><code class="Li" id="Current_BANK()">Current <td><a class="permalink" href="#Current_BANK()"><code class="Li" id="Current_BANK()">Current
BANK()</code></a>.</td> BANK()</code></a>.</td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$60"><code class="Li" id="$60">$60</code></a></td> <td><a class="permalink" href="#$60"><code class="Li" id="$60">$60</code></a></td>
<td class="It-column"><a class="selflink" href="#HRAMCheck."><code class="Li" id="HRAMCheck.">HRAMCheck.</code></a> <td><a class="permalink" href="#HRAMCheck."><code class="Li" id="HRAMCheck.">HRAMCheck.</code></a>
Check if the value is in HRAM, AND it with 0xFF.</td> Check if the value is in HRAM, AND it with 0xFF.</td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$80"><code class="Li" id="$80">$80</code></a></td> <td><a class="permalink" href="#$80"><code class="Li" id="$80">$80</code></a></td>
<td class="It-column"><var class="Ar" title="Ar">LONG</var> integer <td><var class="Ar">LONG</var> integer follows.</td>
follows.</td>
</tr> </tr>
<tr class="It-column"> <tr>
<td class="It-column"><a class="selflink" href="#$81"><code class="Li" id="$81">$81</code></a></td> <td><a class="permalink" href="#$81"><code class="Li" id="$81">$81</code></a></td>
<td class="It-column"><var class="Ar" title="Ar">LONG</var> Symbol ID <td><var class="Ar">LONG</var> Symbol ID follows.</td>
follows.</td>
</tr> </tr>
</table> </table>
<h1 class="Sh" title="Sh" id="SEE_ALSO"><a class="selflink" href="#SEE_ALSO">SEE </section>
</section>
<section class="Sh">
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
ALSO</a></h1> ALSO</a></h1>
<a class="Xr" title="Xr">rgbasm(1)</a>, <a class="Xr" title="Xr">rgblink(1)</a>, <a class="Xr">rgbasm(1)</a>, <a class="Xr">rgblink(1)</a>,
<a class="Xr" title="Xr">rgbds(7)</a>, <a class="Xr" title="Xr">gbz80(7)</a> <a class="Xr">rgbds(7)</a>, <a class="Xr">gbz80(7)</a>
<h1 class="Sh" title="Sh" id="HISTORY"><a class="selflink" href="#HISTORY">HISTORY</a></h1> </section>
<b class="Nm" title="Nm">rgbds</b> was originally written by Carsten <section class="Sh">
S&#x00F8;rensen as part of the ASMotor package, and was later packaged in <h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
RGBDS by Justin Lloyd. It is now maintained by a number of contributors at <code class="Nm">rgbds</code> was originally written by Carsten S&#x00F8;rensen
<a class="Lk" title="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.</div> 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"> <table class="foot">
<tr> <tr>
<td class="foot-date">January 26, 2018</td> <td class="foot-date">January 26, 2018</td>

View File

@@ -1,13 +1,14 @@
<!DOCTYPE html> <!DOCTYPE html>
<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> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<style>
table.head, table.foot { width: 100%; }
td.head-rtitle, td.foot-os { text-align: right; }
td.head-vol { text-align: center; }
div.Pp { margin: 1ex 0ex; }
</style>
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/> <link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
<title>RGBDS(7)</title> <title>RGBDS(7)</title>
</head> </head>
@@ -20,46 +21,52 @@
</tr> </tr>
</table> </table>
<div class="manual-text"> <div class="manual-text">
<h1 class="Sh" title="Sh" id="NAME"><a class="selflink" href="#NAME">NAME</a></h1> <section class="Sh">
<b class="Nm" title="Nm">rgbds</b> &#x2014; <span class="Nd" title="Nd">Rednex <h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
Game Boy Development System</span> <code class="Nm">rgbds</code> &#x2014;
<h1 class="Sh" title="Sh" id="EXAMPLES"><a class="selflink" href="#EXAMPLES">EXAMPLES</a></h1> <div class="Nd">Rednex Game Boy Development System</div>
</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: To get a working ROM image from a single assembly source file:
<div class="Pp"></div> <div class="Bd Pp Bd-indent">
<div class="Bd" style="margin-left: 5.00ex;"> <pre>
<pre class="Li">
$ rgbasm -o bar.o foo.asm $ rgbasm -o bar.o foo.asm
$ rgblink -o baz.gb bar.o $ rgblink -o baz.gb bar.o
$ rgbfix -v -p 0 baz.gb $ rgbfix -v -p 0 baz.gb
</pre> </pre>
</div> </div>
<h1 class="Sh" title="Sh" id="SEE_ALSO"><a class="selflink" href="#SEE_ALSO">SEE </section>
<section class="Sh">
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
ALSO</a></h1> ALSO</a></h1>
<a class="Xr" title="Xr">rgbasm(1)</a>, <a class="Xr" title="Xr">rgbfix(1)</a>, <a class="Xr">rgbasm(1)</a>, <a class="Xr">rgbfix(1)</a>,
<a class="Xr" title="Xr">rgblink(1)</a>, <a class="Xr">rgblink(1)</a>, <a class="Xr">rgbds(5)</a>,
<a class="Xr" title="Xr">rgbds(5)</a>, <a class="Xr" title="Xr">gbz80(7)</a> <a class="Xr">gbz80(7)</a>
<h1 class="Sh" title="Sh" id="HISTORY"><a class="selflink" href="#HISTORY">HISTORY</a></h1> </section>
<section class="Sh">
<h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
<dl class="Bl-ohang"> <dl class="Bl-ohang">
<dt class="It-ohang"></dt> <dt></dt>
<dd class="It-ohang">1997, Carsten S&#x00F8;rensen (AKA SurfSmurf) writes <dd>1997, Carsten S&#x00F8;rensen (AKA SurfSmurf) writes ASMotor as a
ASMotor as a general-purpose assembler/linker system for DOS/Win32.</dd> general-purpose assembler/linker system for DOS/Win32.</dd>
<dt class="It-ohang"></dt> <dt></dt>
<dd class="It-ohang">1999, Justin Lloyd (AKA Otaku no Zoku) adapts ASMotor to <dd>1999, Justin Lloyd (AKA Otaku no Zoku) adapts ASMotor to read and produce
read and produce GBZ80 assembly/machine code, and releases this version as GBZ80 assembly/machine code, and releases this version as RGBDS.</dd>
RGBDS.</dd> <dt></dt>
<dt class="It-ohang"></dt> <dd>2009, Vegard Nossum adapts the code to be more UNIX-like and releases this
<dd class="It-ohang">2009, Vegard Nossum adapts the code to be more UNIX-like version as rgbds-linux on GitHub.</dd>
and releases this version as rgbds-linux on GitHub.</dd> <dt></dt>
<dt class="It-ohang"></dt> <dd>2010, Anthony J. Bentley forks that repository. The fork becomes the
<dd class="It-ohang">2010, Anthony J. Bentley forks that repository. The fork reference implementation of rgbds.</dd>
becomes the reference implementation of rgbds.</dd> <dt></dt>
<dt class="It-ohang"></dt> <dd>2017, Bentley's repository is moved to a neutral name. It is now
<dd class="It-ohang">2017, Bentley's repository is moved to a neutral name. It maintained by a number of contributors at
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>
<a class="Lk" title="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.</dd> <dt></dt>
<dt class="It-ohang"></dt> <dd>2018, codebase relicensed under the MIT license.</dd>
<dd class="It-ohang">2018, codebase relicensed under the MIT license.</dd>
</dl> </dl>
</section>
</div> </div>
<table class="foot"> <table class="foot">
<tr> <tr>

View File

@@ -1,13 +1,14 @@
<!DOCTYPE html> <!DOCTYPE html>
<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> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<style>
table.head, table.foot { width: 100%; }
td.head-rtitle, td.foot-os { text-align: right; }
td.head-vol { text-align: center; }
div.Pp { margin: 1ex 0ex; }
</style>
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/> <link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
<title>RGBFIX(1)</title> <title>RGBFIX(1)</title>
</head> </head>
@@ -20,189 +21,158 @@
</tr> </tr>
</table> </table>
<div class="manual-text"> <div class="manual-text">
<h1 class="Sh" title="Sh" id="NAME"><a class="selflink" href="#NAME">NAME</a></h1> <section class="Sh">
<b class="Nm" title="Nm">rgbfix</b> &#x2014; <span class="Nd" title="Nd">Game <h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
Boy checksum fixer</span> <code class="Nm">rgbfix</code> &#x2014;
<h1 class="Sh" title="Sh" id="SYNOPSIS"><a class="selflink" href="#SYNOPSIS">SYNOPSIS</a></h1> <div class="Nd">Game Boy checksum fixer</div>
</section>
<section class="Sh">
<h1 class="Sh" id="SYNOPSIS"><a class="permalink" href="#SYNOPSIS">SYNOPSIS</a></h1>
<table class="Nm"> <table class="Nm">
<tr> <tr>
<td><b class="Nm" title="Nm">rgbfix</b></td> <td><code class="Nm">rgbfix</code></td>
<td>[<span class="Op"><b class="Fl" title="Fl">-CcjsVv</b></span>] <td>[<code class="Fl">-CcjsVv</code>] [<code class="Fl">-f</code>
[<span class="Op"><b class="Fl" title="Fl">-f</b> <var class="Ar">fix_spec</var>] [<code class="Fl">-i</code>
<var class="Ar" title="Ar">fix_spec</var></span>] <var class="Ar">game_id</var>] [<code class="Fl">-k</code>
[<span class="Op"><b class="Fl" title="Fl">-i</b> <var class="Ar">licensee_str</var>] [<code class="Fl">-l</code>
<var class="Ar" title="Ar">game_id</var></span>] <var class="Ar">licensee_id</var>] [<code class="Fl">-m</code>
[<span class="Op"><b class="Fl" title="Fl">-k</b> <var class="Ar">mbc_type</var>] [<code class="Fl">-n</code>
<var class="Ar" title="Ar">licensee_str</var></span>] <var class="Ar">rom_version</var>] [<code class="Fl">-p</code>
[<span class="Op"><b class="Fl" title="Fl">-l</b> <var class="Ar">pad_value</var>] [<code class="Fl">-r</code>
<var class="Ar" title="Ar">licensee_id</var></span>] <var class="Ar">ram_size</var>] [<code class="Fl">-t</code>
[<span class="Op"><b class="Fl" title="Fl">-m</b> <var class="Ar">title_str</var>] <var class="Ar">file</var></td>
<var class="Ar" title="Ar">mbc_type</var></span>]
[<span class="Op"><b class="Fl" title="Fl">-n</b>
<var class="Ar" title="Ar">rom_version</var></span>]
[<span class="Op"><b class="Fl" title="Fl">-p</b>
<var class="Ar" title="Ar">pad_value</var></span>]
[<span class="Op"><b class="Fl" title="Fl">-r</b>
<var class="Ar" title="Ar">ram_size</var></span>]
[<span class="Op"><b class="Fl" title="Fl">-t</b>
<var class="Ar" title="Ar">title_str</var></span>]
<var class="Ar" title="Ar">file</var></td>
</tr> </tr>
</table> </table>
<h1 class="Sh" title="Sh" id="DESCRIPTION"><a class="selflink" href="#DESCRIPTION">DESCRIPTION</a></h1> </section>
The <b class="Nm" title="Nm">rgbfix</b> program changes headers of Game Boy ROM <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 filetype operations, such as truncation. The images. It also performs other filetype operations, such as truncation. The
arguments are as follows: arguments are as follows:
<dl class="Bl-tag"> <dl class="Bl-tag">
<dt class="It-tag">&#x00A0;</dt> <dt><a class="permalink" href="#C"><code class="Fl" id="C">-C</code></a></dt>
<dd class="It-tag">&#x00A0;</dd> <dd>Set the Game Boy Color&#x2013;only flag: <span class="Ad">0x143</span> =
<dt class="It-tag"><a class="selflink" href="#C"><b class="Fl" title="Fl" id="C">-C</b></a></dt> 0xC0. If both this and the <code class="Fl">-c</code> flag are set, this
<dd class="It-tag">Set the Game Boy Color&#x2013;only flag: takes precedence.</dd>
<i class="Ad">0x143</i> = 0xC0. If both this and the <dt><a class="permalink" href="#c"><code class="Fl" id="c">-c</code></a></dt>
<b class="Fl" title="Fl">-c</b> flag are set, this takes precedence.</dd> <dd>Set the Game Boy Color&#x2013;compatible flag:
<dt class="It-tag">&#x00A0;</dt> <span class="Ad">0x143</span> = 0x80. If both this and the
<dd class="It-tag">&#x00A0;</dd> <code class="Fl">-C</code> flag are set, <code class="Fl">-C</code> takes
<dt class="It-tag"><a class="selflink" href="#c"><b class="Fl" title="Fl" id="c">-c</b></a></dt> precedence.</dd>
<dd class="It-tag">Set the Game Boy Color&#x2013;compatible flag: <dt><a class="permalink" href="#f"><code class="Fl" id="f">-f</code></a>
<i class="Ad">0x143</i> = 0x80. If both this and the <var class="Ar">fix_spec</var></dt>
<b class="Fl" title="Fl">-C</b> flag are set, <dd>Fix certain header values that the Game Boy checks for correctness.
<b class="Fl" title="Fl">-C</b> takes precedence.</dd> Alternatively, intentionally trash these values by writing their binary
<dt class="It-tag">&#x00A0;</dt> inverse instead. <var class="Ar">fix_spec</var> is a string containing any
<dd class="It-tag">&#x00A0;</dd> combination of the following characters:
<dt class="It-tag"><a class="selflink" href="#f"><b class="Fl" title="Fl" id="f">-f</b></a> <p class="Pp"></p>
<var class="Ar" title="Ar">fix_spec</var></dt> <dl class="Bl-tag Bl-compact">
<dd class="It-tag">Fix certain header values that the Game Boy checks for <dt><a class="permalink" href="#l"><code class="Cm" id="l">l</code></a></dt>
correctness. Alternatively, intentionally trash these values by writing <dd>Fix the Nintendo logo
their binary inverse instead. <var class="Ar" title="Ar">fix_spec</var> is (<span class="Ad">0x104</span>&#x2013;<span class="Ad">0x133</span>).</dd>
a string containing any combination of the following characters: <dt><a class="permalink" href="#L"><code class="Cm" id="L">L</code></a></dt>
<div class="Pp"></div> <dd>Trash the Nintendo logo.</dd>
<dl class="Bl-tag Bl-compact" style="margin-left: 5.40ex;"> <dt><a class="permalink" href="#h"><code class="Cm" id="h">h</code></a></dt>
<dt class="It-tag" style="margin-left: -5.40ex;"><a class="selflink" href="#l"><b class="Cm" title="Cm" id="l">l</b></a></dt> <dd>Fix the header checksum (<span class="Ad">0x14D</span>).</dd>
<dd class="It-tag">Fix the Nintendo logo <dt><a class="permalink" href="#H"><code class="Cm" id="H">H</code></a></dt>
(<i class="Ad">0x104</i>&#x2013;<i class="Ad">0x133</i>).</dd> <dd>Trash the header checksum.</dd>
<dt class="It-tag" style="margin-left: -5.40ex;"><a class="selflink" href="#L"><b class="Cm" title="Cm" id="L">L</b></a></dt> <dt><a class="permalink" href="#g"><code class="Cm" id="g">g</code></a></dt>
<dd class="It-tag">Trash the Nintendo logo.</dd> <dd>Fix the global checksum
<dt class="It-tag" style="margin-left: -5.40ex;"><a class="selflink" href="#h"><b class="Cm" title="Cm" id="h">h</b></a></dt> (<span class="Ad">0x14E</span>&#x2013;<span class="Ad">0x14F</span>).</dd>
<dd class="It-tag">Fix the header checksum (<i class="Ad">0x14D</i>).</dd> <dt><a class="permalink" href="#G"><code class="Cm" id="G">G</code></a></dt>
<dt class="It-tag" style="margin-left: -5.40ex;"><a class="selflink" href="#H"><b class="Cm" title="Cm" id="H">H</b></a></dt> <dd>Trash the global checksum.</dd>
<dd class="It-tag">Trash the header checksum.</dd>
<dt class="It-tag" style="margin-left: -5.40ex;"><a class="selflink" href="#g"><b class="Cm" title="Cm" id="g">g</b></a></dt>
<dd class="It-tag">Fix the global checksum
(<i class="Ad">0x14E</i>&#x2013;<i class="Ad">0x14F</i>).</dd>
<dt class="It-tag" style="margin-left: -5.40ex;"><a class="selflink" href="#G"><b class="Cm" title="Cm" id="G">G</b></a></dt>
<dd class="It-tag">Trash the global checksum.</dd>
</dl> </dl>
</dd> </dd>
<dt class="It-tag">&#x00A0;</dt> <dt><a class="permalink" href="#i"><code class="Fl" id="i">-i</code></a>
<dd class="It-tag">&#x00A0;</dd> <var class="Ar">game_id</var></dt>
<dt class="It-tag"><a class="selflink" href="#i"><b class="Fl" title="Fl" id="i">-i</b></a> <dd>Set the game ID string
<var class="Ar" title="Ar">game_id</var></dt> (<span class="Ad">0x13F</span>&#x2013;<span class="Ad">0x142</span>) to a
<dd class="It-tag">Set the game ID string given string of exactly 4 characters. If both this and the title are set,
(<i class="Ad">0x13F</i>&#x2013;<i class="Ad">0x142</i>) to a given string the game ID will overwrite the overlapping portion of the title.</dd>
of exactly 4 characters. If both this and the title are set, the game ID <dt><a class="permalink" href="#j"><code class="Fl" id="j">-j</code></a></dt>
will overwrite the overlapping portion of the title.</dd> <dd>Set the non-Japanese region flag: <span class="Ad">0x14A</span> = 1.</dd>
<dt class="It-tag">&#x00A0;</dt> <dt><a class="permalink" href="#k"><code class="Fl" id="k">-k</code></a>
<dd class="It-tag">&#x00A0;</dd> <var class="Ar">licensee_str</var></dt>
<dt class="It-tag"><a class="selflink" href="#j"><b class="Fl" title="Fl" id="j">-j</b></a></dt> <dd>Set the new licensee string
<dd class="It-tag">Set the non-Japanese region flag: <i class="Ad">0x14A</i> = (<span class="Ad">0x144</span>&#x2013;<span class="Ad">0x145</span>) to a
1.</dd> given string, truncated to at most two characters.</dd>
<dt class="It-tag">&#x00A0;</dt> <dt><a class="permalink" href="#l_2"><code class="Fl" id="l_2">-l</code></a>
<dd class="It-tag">&#x00A0;</dd> <var class="Ar">licensee_id</var></dt>
<dt class="It-tag"><a class="selflink" href="#k"><b class="Fl" title="Fl" id="k">-k</b></a> <dd>Set the old licensee code, <span class="Ad">0x14B</span>, to a given value
<var class="Ar" title="Ar">licensee_str</var></dt> from 0 to 0xFF. This value is deprecated and should be set to 0x33 in all
<dd class="It-tag">Set the new licensee string new software.</dd>
(<i class="Ad">0x144</i>&#x2013;<i class="Ad">0x145</i>) to a given <dt><a class="permalink" href="#m"><code class="Fl" id="m">-m</code></a>
string, truncated to at most two characters.</dd> <var class="Ar">mbc_type</var></dt>
<dt class="It-tag">&#x00A0;</dt> <dd>Set the MBC type, <span class="Ad">0x147</span>, to a given value from 0
<dd class="It-tag">&#x00A0;</dd> to 0xFF.</dd>
<dt class="It-tag"><a class="selflink" href="#l"><b class="Fl" title="Fl" id="l">-l</b></a> <dt><a class="permalink" href="#n"><code class="Fl" id="n">-n</code></a>
<var class="Ar" title="Ar">licensee_id</var></dt> <var class="Ar">rom_version</var></dt>
<dd class="It-tag">Set the old licensee code, <i class="Ad">0x14B</i>, to a <dd>Set the ROM version, <span class="Ad">0x14C</span>, to a given value from
given value from 0 to 0xFF. This value is deprecated and should be set to 0 to 0xFF.</dd>
0x33 in all new software.</dd> <dt><a class="permalink" href="#p"><code class="Fl" id="p">-p</code></a>
<dt class="It-tag">&#x00A0;</dt> <var class="Ar">pad_value</var></dt>
<dd class="It-tag">&#x00A0;</dd> <dd>Pad the image to a valid size with a given pad value from 0 to 0xFF.
<dt class="It-tag"><a class="selflink" href="#m"><b class="Fl" title="Fl" id="m">-m</b></a> <code class="Nm">rgbfix</code> will automatically pick a size from 32KiB,
<var class="Ar" title="Ar">mbc_type</var></dt> 64KiB, 128KiB, ..., 8192KiB and give a warning thereafter. The cartridge
<dd class="It-tag">Set the MBC type, <i class="Ad">0x147</i>, to a given value size byte (<span class="Ad">0x148</span>) will be changed to reflect this
from 0 to 0xFF.</dd> new size.</dd>
<dt class="It-tag">&#x00A0;</dt> <dt><a class="permalink" href="#r"><code class="Fl" id="r">-r</code></a>
<dd class="It-tag">&#x00A0;</dd> <var class="Ar">ram_size</var></dt>
<dt class="It-tag"><a class="selflink" href="#n"><b class="Fl" title="Fl" id="n">-n</b></a> <dd>Set the RAM size, <span class="Ad">0x149</span>, to a given value from 0
<var class="Ar" title="Ar">rom_version</var></dt> to 0xFF.</dd>
<dd class="It-tag">Set the ROM version, <i class="Ad">0x14C</i>, to a given <dt><a class="permalink" href="#s"><code class="Fl" id="s">-s</code></a></dt>
value from 0 to 0xFF.</dd> <dd>Set the SGB flag: <span class="Ad">0x146</span> = 3.</dd>
<dt class="It-tag">&#x00A0;</dt> <dt><a class="permalink" href="#t"><code class="Fl" id="t">-t</code></a>
<dd class="It-tag">&#x00A0;</dd> <var class="Ar">title</var></dt>
<dt class="It-tag"><a class="selflink" href="#p"><b class="Fl" title="Fl" id="p">-p</b></a> <dd>Set the title string
<var class="Ar" title="Ar">pad_value</var></dt> (<span class="Ad">0x134</span>&#x2013;<span class="Ad">0x143</span>) to a
<dd class="It-tag">Pad the image to a valid size with a given pad value from 0 given string, truncated to at most 16 characters. It is recommended to use
to 0xFF. <b class="Nm" title="Nm">rgbfix</b> will automatically pick a 15 characters instead, to avoid clashing with the CGB flag
size from 32KiB, 64KiB, 128KiB, ..., 8192KiB and give a warning (<code class="Fl">-c</code> or <code class="Fl">-C</code>). If both this
thereafter. The cartridge size byte (<i class="Ad">0x148</i>) will be and the game ID are set, the game ID will overwrite the overlapping
changed to reflect this new size.</dd> portion of the title.</dd>
<dt class="It-tag">&#x00A0;</dt> <dt><a class="permalink" href="#V"><code class="Fl" id="V">-V</code></a></dt>
<dd class="It-tag">&#x00A0;</dd> <dd>Print the version of the program and exit.</dd>
<dt class="It-tag"><a class="selflink" href="#r"><b class="Fl" title="Fl" id="r">-r</b></a> <dt><a class="permalink" href="#v"><code class="Fl" id="v">-v</code></a></dt>
<var class="Ar" title="Ar">ram_size</var></dt> <dd>Equivalent to <code class="Fl">-f</code> <code class="Cm">lhg</code>.</dd>
<dd class="It-tag">Set the RAM size, <i class="Ad">0x149</i>, to a given value
from 0 to 0xFF.</dd>
<dt class="It-tag">&#x00A0;</dt>
<dd class="It-tag">&#x00A0;</dd>
<dt class="It-tag"><a class="selflink" href="#s"><b class="Fl" title="Fl" id="s">-s</b></a></dt>
<dd class="It-tag">Set the SGB flag: <i class="Ad">0x146</i> = 3.</dd>
<dt class="It-tag">&#x00A0;</dt>
<dd class="It-tag">&#x00A0;</dd>
<dt class="It-tag"><a class="selflink" href="#t"><b class="Fl" title="Fl" id="t">-t</b></a>
<var class="Ar" title="Ar">title</var></dt>
<dd class="It-tag">Set the title string
(<i class="Ad">0x134</i>&#x2013;<i class="Ad">0x143</i>) 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
(<b class="Fl" title="Fl">-c</b> or <b class="Fl" title="Fl">-C</b>). If
both this and the game ID are set, the game ID will overwrite the
overlapping portion of the title.</dd>
<dt class="It-tag">&#x00A0;</dt>
<dd class="It-tag">&#x00A0;</dd>
<dt class="It-tag"><a class="selflink" href="#V"><b class="Fl" title="Fl" id="V">-V</b></a></dt>
<dd class="It-tag">Print the version of the program and exit.</dd>
<dt class="It-tag">&#x00A0;</dt>
<dd class="It-tag">&#x00A0;</dd>
<dt class="It-tag"><a class="selflink" href="#v"><b class="Fl" title="Fl" id="v">-v</b></a></dt>
<dd class="It-tag">Equivalent to <b class="Fl" title="Fl">-f</b>
<b class="Cm" title="Cm">lhg</b>.</dd>
</dl> </dl>
<h1 class="Sh" title="Sh" id="EXAMPLES"><a class="selflink" href="#EXAMPLES">EXAMPLES</a></h1> </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 Most values in the ROM header are only cosmetic. The bare minimum requirements
for a workable image are checksums, the Nintendo logo, and (if needed) the for a workable image are checksums, 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 CGB/SGB flags. It is a good idea to pad the image to a valid size as well
(&#x201C;valid&#x201D; meaning a multiple of 32KiB). (&#x201C;valid&#x201D; meaning a multiple of 32KiB).
<div class="Pp"></div> <p class="Pp">The following will make a plain, no-color Game Boy game without
The following will make a plain, no-color Game Boy game without checking for a checking for a valid size:</p>
valid size: <p class="Pp"></p>
<div class="Pp"></div> <div class="Bd Bd-indent">$ rgbfix -v foo.gb</div>
<div class="D1">$ rgbfix -v foo.gb</div> <p class="Pp">The following will make a SGB-enabled, color-enabled game with a
<div class="Pp"></div> title of &#x201C;foobar&#x201D;, and pad it to a multiple of 32KiB. (The
The following will make a SGB-enabled, color-enabled game with a title of Game Boy itself does not use the title, but some emulators or ROM managers
&#x201C;foobar&#x201D;, and pad it to a multiple of 32KiB. (The Game Boy might.)</p>
itself does not use the title, but some emulators or ROM managers might.) <p class="Pp"></p>
<div class="Pp"></div> <div class="Bd Bd-indent">$ rgbfix -vcs -l 0x33 -p 0 -t foobar baz.gb</div>
<div class="D1">$ rgbfix -vcs -l 0x33 -p 0 -t foobar baz.gb</div> <p class="Pp">The following will duplicate the header (sans global checksum) of
<div class="Pp"></div> the game &#x201C;Survival Kids&#x201D;:</p>
The following will duplicate the header (sans global checksum) of the game <p class="Pp"></p>
&#x201C;Survival Kids&#x201D;: <div class="Bd Bd-indent">$ rgbfix -cjsv -k A4 -l 0x33 -m 0x1B -p 0xFF -r 3 -t
<div class="Pp"></div>
<div class="D1">$ rgbfix -cjsv -k A4 -l 0x33 -m 0x1B -p 0xFF -r 3 -t
SURVIVALKIDAVKE SurvivalKids.gbc</div> SURVIVALKIDAVKE SurvivalKids.gbc</div>
<h1 class="Sh" title="Sh" id="SEE_ALSO"><a class="selflink" href="#SEE_ALSO">SEE </section>
<section class="Sh">
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
ALSO</a></h1> ALSO</a></h1>
<a class="Xr" title="Xr">rgbasm(1)</a>, <a class="Xr" title="Xr">rgblink(1)</a>, <a class="Xr">rgbasm(1)</a>, <a class="Xr">rgblink(1)</a>,
<a class="Xr" title="Xr">rgbds(7)</a> <a class="Xr">rgbds(7)</a>
<h1 class="Sh" title="Sh" id="HISTORY"><a class="selflink" href="#HISTORY">HISTORY</a></h1> </section>
<b class="Nm" title="Nm">rgbfix</b> was originally released by Carsten <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 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 in RGBDS by Justin Lloyd. It is now maintained by a number of contributors at
<a class="Lk" title="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.</div> <a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.
</section>
</div>
<table class="foot"> <table class="foot">
<tr> <tr>
<td class="foot-date">March 11, 2018</td> <td class="foot-date">March 11, 2018</td>

View File

@@ -1,13 +1,14 @@
<!DOCTYPE html> <!DOCTYPE html>
<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> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<style>
table.head, table.foot { width: 100%; }
td.head-rtitle, td.foot-os { text-align: right; }
td.head-vol { text-align: center; }
div.Pp { margin: 1ex 0ex; }
</style>
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/> <link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
<title>RGBGFX(1)</title> <title>RGBGFX(1)</title>
</head> </head>
@@ -20,151 +21,155 @@
</tr> </tr>
</table> </table>
<div class="manual-text"> <div class="manual-text">
<h1 class="Sh" title="Sh" id="NAME"><a class="selflink" href="#NAME">NAME</a></h1> <section class="Sh">
<b class="Nm" title="Nm">rgbgfx</b> &#x2014; <span class="Nd" title="Nd">Game <h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
Boy graphics converter</span> <code class="Nm">rgbgfx</code> &#x2014;
<h1 class="Sh" title="Sh" id="SYNOPSIS"><a class="selflink" href="#SYNOPSIS">SYNOPSIS</a></h1> <div class="Nd">Game Boy graphics converter</div>
</section>
<section class="Sh">
<h1 class="Sh" id="SYNOPSIS"><a class="permalink" href="#SYNOPSIS">SYNOPSIS</a></h1>
<table class="Nm"> <table class="Nm">
<tr> <tr>
<td><b class="Nm" title="Nm">rgbgfx</b></td> <td><code class="Nm">rgbgfx</code></td>
<td>[<span class="Op"><b class="Fl" title="Fl">-DfFhPTVv</b></span>] <td>[<code class="Fl">-ADfFhmPTuVv</code>] [<code class="Fl">-o</code>
[<span class="Op"><b class="Fl" title="Fl">-o</b> <var class="Ar">outfile</var>] [<code class="Fl">-a</code>
<var class="Ar" title="Ar">outfile</var></span>] <var class="Ar">attrmap</var>] [<code class="Fl">-d</code>
[<span class="Op"><b class="Fl" title="Fl">-d</b> <var class="Ar">depth</var>] [<code class="Fl">-p</code>
<var class="Ar" title="Ar">depth</var></span>] <var class="Ar">palfile</var>] [<code class="Fl">-t</code>
[<span class="Op"><b class="Fl" title="Fl">-p</b> <var class="Ar">tilemap</var>] [<code class="Fl">-x</code>
<var class="Ar" title="Ar">palfile</var></span>] <var class="Ar">tiles</var>] <var class="Ar">file</var></td>
[<span class="Op"><b class="Fl" title="Fl">-t</b>
<var class="Ar" title="Ar">mapfile</var></span>]
[<span class="Op"><b class="Fl" title="Fl">-x</b>
<var class="Ar" title="Ar">tiles</var></span>]
<var class="Ar" title="Ar">file</var></td>
</tr> </tr>
</table> </table>
<h1 class="Sh" title="Sh" id="DESCRIPTION"><a class="selflink" href="#DESCRIPTION">DESCRIPTION</a></h1> </section>
The <b class="Nm" title="Nm">rgbgfx</b> program converts PNG images into the <section class="Sh">
Nintendo Game Boy's planar tile format. <h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
<div style="height: 1.00em;">&#x00A0;</div> The <code class="Nm">rgbgfx</code> program converts PNG images into the Nintendo
The resulting colors and their palette indices are determined differently Game Boy's planar tile format.
depending on the input PNG file: <p class="Pp">The resulting colors and their palette indices are determined
differently depending on the input PNG file:</p>
<ul class="Bl-dash"> <ul class="Bl-dash">
<li class="It-dash">If the file has an embedded palette, that palette's color <li>If the file has an embedded palette, that palette's color and order are
and order are used.</li> used.</li>
<li class="It-dash">If not, and the image only contains shades of gray, rgbgfx <li>If not, and the image only contains shades of gray, rgbgfx maps them to
maps them to the indices appropriate for each shade. Any undetermined the indices appropriate for each shade. Any undetermined indices are set
indices are set to respective default shades of gray. For example: if the to respective default shades of gray. For example: if the bit depth is 2
bit depth is 2 and the image contains light gray and black, they become and the image contains light gray and black, they become the second and
the second and fourth colors - and the first and third colors get set to fourth colors - and the first and third colors get set to default white
default white and dark gray. If the image has multiple shades that map to and dark gray. If the image has multiple shades that map to the same
the same index, the palette is instead determined as if the image had index, the palette is instead determined as if the image had color.</li>
color.</li> <li>If the image has color (or the grayscale method failed), the colors are
<li class="It-dash">If the image has color (or the grayscale method failed), sorted from lightest to darkest.</li>
the colors are sorted from lightest to darkest.</li>
</ul> </ul>
<div style="height: 1.00em;">&#x00A0;</div> <p class="Pp">The input image may not contain more colors than the selected bit
The input image may not contain more colors than the selected bit depth allows. depth allows. Transparent pixels are set to palette index 0.</p>
Transparent pixels are set to palette index 0. </section>
<h1 class="Sh" title="Sh" id="ARGUMENTS"><a class="selflink" href="#ARGUMENTS">ARGUMENTS</a></h1> <section class="Sh">
<h1 class="Sh" id="ARGUMENTS"><a class="permalink" href="#ARGUMENTS">ARGUMENTS</a></h1>
<dl class="Bl-tag"> <dl class="Bl-tag">
<dt class="It-tag">&#x00A0;</dt> <dt><a class="permalink" href="#a"><code class="Fl" id="a">-a</code></a>
<dd class="It-tag">&#x00A0;</dd> <var class="Ar">attrmap</var></dt>
<dt class="It-tag"><a class="selflink" href="#D"><b class="Fl" title="Fl" id="D">-D</b></a></dt> <dd>Generate a file of tile mirroring attributes for OAM or (CGB-only)
<dd class="It-tag">Debug features are enabled.</dd> background tiles. For each tile in the input file, a byte is written
<dt class="It-tag">&#x00A0;</dt> representing the dimensions that the associated tile in the output file
<dd class="It-tag">&#x00A0;</dd> should be mirrored. Useful in combination with <code class="Fl">-m</code>
<dt class="It-tag"><a class="selflink" href="#f"><b class="Fl" title="Fl" id="f">-f</b></a></dt> to keep track the mirror direction of mirrored duplicate tiles.</dd>
<dd class="It-tag">Fix the input PNG file to be a correctly indexed <dt><a class="permalink" href="#A"><code class="Fl" id="A">-A</code></a></dt>
image.</dd> <dd>Same as <code class="Fl">-a</code>, but the attrmap file output name is
<dt class="It-tag">&#x00A0;</dt> made by taking the input filename, removing the file extension, and
<dd class="It-tag">&#x00A0;</dd> appending <span class="Pa">.attrmap</span>.</dd>
<dt class="It-tag"><a class="selflink" href="#F"><b class="Fl" title="Fl" id="F">-F</b></a></dt> <dt><a class="permalink" href="#C"><code class="Fl" id="C">-C</code></a></dt>
<dd class="It-tag">Same as <b class="Fl" title="Fl">-f</b>, but additionally, <dd>Use the color curve of the Game Boy Color when generating palettes.</dd>
the supplied command line parameters are saved within the PNG and will be <dt><a class="permalink" href="#D"><code class="Fl" id="D">-D</code></a></dt>
loaded and automatically used next time.</dd> <dd>Debug features are enabled.</dd>
<dt class="It-tag">&#x00A0;</dt> <dt><a class="permalink" href="#f"><code class="Fl" id="f">-f</code></a></dt>
<dd class="It-tag">&#x00A0;</dd> <dd>Fix the input PNG file to be a correctly indexed image.</dd>
<dt class="It-tag"><a class="selflink" href="#d"><b class="Fl" title="Fl" id="d">-d</b></a> <dt><a class="permalink" href="#F"><code class="Fl" id="F">-F</code></a></dt>
<var class="Ar" title="Ar">depth</var></dt> <dd>Same as <code class="Fl">-f</code>, but additionally, the supplied command
<dd class="It-tag">The bit depth of the output image (either 1 or 2). By line parameters are saved within the PNG and will be loaded and
default, the bit depth is 2 (two bits per pixel).</dd> automatically used next time.</dd>
<dt class="It-tag">&#x00A0;</dt> <dt><a class="permalink" href="#d"><code class="Fl" id="d">-d</code></a>
<dd class="It-tag">&#x00A0;</dd> <var class="Ar">depth</var></dt>
<dt class="It-tag"><a class="selflink" href="#h"><b class="Fl" title="Fl" id="h">-h</b></a></dt> <dd>The bit depth of the output image (either 1 or 2). By default, the bit
<dd class="It-tag">Lay out tiles horizontally rather than vertically.</dd> depth is 2 (two bits per pixel).</dd>
<dt class="It-tag">&#x00A0;</dt> <dt><a class="permalink" href="#h"><code class="Fl" id="h">-h</code></a></dt>
<dd class="It-tag">&#x00A0;</dd> <dd>Lay out tiles horizontally rather than vertically.</dd>
<dt class="It-tag"><a class="selflink" href="#o"><b class="Fl" title="Fl" id="o">-o</b></a> <dt><a class="permalink" href="#m"><code class="Fl" id="m">-m</code></a></dt>
<var class="Ar" title="Ar">outfile</var></dt> <dd>Truncate tiles by checking for tiles that are mirrored versions of others
<dd class="It-tag">The name of the output file.</dd> and omitting these from the output file. Useful with tilemaps and attrmaps
<dt class="It-tag">&#x00A0;</dt> together to keep track of the duplicated tiles and the dimension mirrored.
<dd class="It-tag">&#x00A0;</dd> Tiles are checked for horizontal, vertical, and horizontal-vertical
<dt class="It-tag"><a class="selflink" href="#p"><b class="Fl" title="Fl" id="p">-p</b></a> mirroring. Implies <code class="Fl">-u</code>.</dd>
<var class="Ar" title="Ar">palfile</var></dt> <dt><a class="permalink" href="#o"><code class="Fl" id="o">-o</code></a>
<dd class="It-tag">Output the image's palette in standard GBC palette format - <var class="Ar">outfile</var></dt>
bytes (8 bytes for two bits per pixel, 4 bytes for one bit per pixel) <dd>The name of the output file.</dd>
containing the RGB15 values in little-endian byte order. If the palette <dt><a class="permalink" href="#p"><code class="Fl" id="p">-p</code></a>
contains too few colors, the remaining entries are set to black.</dd> <var class="Ar">palfile</var></dt>
<dt class="It-tag">&#x00A0;</dt> <dd>Output the image's palette in standard GBC palette format - bytes (8 bytes
<dd class="It-tag">&#x00A0;</dd> for two bits per pixel, 4 bytes for one bit per pixel) containing the
<dt class="It-tag"><a class="selflink" href="#P"><b class="Fl" title="Fl" id="P">-P</b></a></dt> RGB15 values in little-endian byte order. If the palette contains too few
<dd class="It-tag">Same as <b class="Fl" title="Fl">-p</b>, but the palette colors, the remaining entries are set to black.</dd>
file output name is made by taking the input PNG file's filename, removing <dt><a class="permalink" href="#P"><code class="Fl" id="P">-P</code></a></dt>
the file extension, and appending <i class="Pa" title="Pa">.pal</i>.</dd> <dd>Same as <code class="Fl">-p</code>, but the palette file output name is
<dt class="It-tag">&#x00A0;</dt> made by taking the input PNG file's filename, removing the file extension,
<dd class="It-tag">&#x00A0;</dd> and appending <span class="Pa">.pal</span>.</dd>
<dt class="It-tag"><a class="selflink" href="#t"><b class="Fl" title="Fl" id="t">-t</b></a> <dt><a class="permalink" href="#t"><code class="Fl" id="t">-t</code></a>
<var class="Ar" title="Ar">mapfile</var></dt> <var class="Ar">tilemap</var></dt>
<dd class="It-tag">If any tiles are the same, don't place the repeat tiles in <dd>Generate a file of tile indices. For each tile in the input file, a byte
the output file, and make a tilemap file.</dd> is written representing the index of the associated tile in the output
<dt class="It-tag">&#x00A0;</dt> file. Useful in combination with <code class="Fl">-u</code> or
<dd class="It-tag">&#x00A0;</dd> <code class="Fl">-m</code> to keep track of duplicate tiles.</dd>
<dt class="It-tag"><a class="selflink" href="#T"><b class="Fl" title="Fl" id="T">-T</b></a></dt> <dt><a class="permalink" href="#T"><code class="Fl" id="T">-T</code></a></dt>
<dd class="It-tag">Same as <b class="Fl" title="Fl">-t</b>, but the tilemap <dd>Same as <code class="Fl">-t</code>, but the tilemap file output name is
file output name is made by taking the input filename, removing the file made by taking the input filename, removing the file extension, and
extension, and appending <i class="Pa" title="Pa">.tilemap</i>.</dd> appending <span class="Pa">.tilemap</span>.</dd>
<dt class="It-tag">&#x00A0;</dt> <dt><a class="permalink" href="#u"><code class="Fl" id="u">-u</code></a></dt>
<dd class="It-tag">&#x00A0;</dd> <dd>Truncate tiles by checking for tiles that are exact duplicates of others
<dt class="It-tag"><a class="selflink" href="#u"><b class="Fl" title="Fl" id="u">-u</b></a></dt> and omitting these from the output file. Useful with tilemaps to keep
<dd class="It-tag">Truncate repeated tiles. Useful with tilemaps.</dd> track of the duplicated tiles.</dd>
<dt class="It-tag">&#x00A0;</dt> <dt><a class="permalink" href="#V"><code class="Fl" id="V">-V</code></a></dt>
<dd class="It-tag">&#x00A0;</dd> <dd>Print the version of the program and exit.</dd>
<dt class="It-tag"><a class="selflink" href="#V"><b class="Fl" title="Fl" id="V">-V</b></a></dt> <dt><a class="permalink" href="#v"><code class="Fl" id="v">-v</code></a></dt>
<dd class="It-tag">Print the version of the program and exit.</dd> <dd>Verbose. Print errors when the command line parameters and the parameters
<dt class="It-tag">&#x00A0;</dt> in the PNG file don't match.</dd>
<dd class="It-tag">&#x00A0;</dd> <dt><a class="permalink" href="#x"><code class="Fl" id="x">-x</code></a>
<dt class="It-tag"><a class="selflink" href="#v"><b class="Fl" title="Fl" id="v">-v</b></a></dt> <var class="Ar">tiles</var></dt>
<dd class="It-tag">Verbose. Print errors when the command line parameters and <dd>Trim the end of the output file by this many tiles.</dd>
the parameters in the PNG file don't match.</dd>
<dt class="It-tag">&#x00A0;</dt>
<dd class="It-tag">&#x00A0;</dd>
<dt class="It-tag"><a class="selflink" href="#x"><b class="Fl" title="Fl" id="x">-x</b></a>
<var class="Ar" title="Ar">tiles</var></dt>
<dd class="It-tag">Trim the end of the output file by this many tiles.</dd>
</dl> </dl>
<h1 class="Sh" title="Sh" id="EXAMPLES"><a class="selflink" href="#EXAMPLES">EXAMPLES</a></h1> </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 The following will take a PNG file with a bit depth of 1, 2, or 8, and output
planar 2bpp data: planar 2bpp data:
<div class="Pp"></div> <p class="Pp"></p>
<div class="D1">$ rgbgfx -o out.2bpp in.png</div> <div class="Bd Bd-indent">$ rgbgfx -o out.2bpp in.png</div>
<div class="Pp"></div> <p class="Pp">The following creates a planar 2bpp file with only unique tiles,
The following creates a planar 2bpp file with only unique tiles, and its tilemap and its tilemap <span class="Pa">out.tilemap</span>:</p>
<i class="Pa" title="Pa">out.tilemap</i>: <p class="Pp"></p>
<div class="Pp"></div> <div class="Bd Bd-indent">$ rgbgfx -T -u -o out.2bpp in.png</div>
<div class="D1">$ rgbgfx -T -u -o out.2bpp in.png</div> <p class="Pp">The following creates a planar 2bpp file with only unique tiles
<div class="Pp"></div> (accounting for tile mirroring) and its associated tilemap
The following will do nothing: <span class="Pa">out.tilemap</span> and attrmap
<div class="Pp"></div> <span class="Pa">out.attrmap</span>:</p>
<div class="D1">$ rgbgfx in.png</div> <p class="Pp"></p>
<h1 class="Sh" title="Sh" id="SEE_ALSO"><a class="selflink" href="#SEE_ALSO">SEE <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="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
ALSO</a></h1> ALSO</a></h1>
<a class="Xr" title="Xr">rgbds(7)</a>, <a class="Xr" title="Xr">rgbasm(1)</a>, <a class="Xr">rgbds(7)</a>, <a class="Xr">rgbasm(1)</a>,
<a class="Xr" title="Xr">rgblink(1)</a>, <a class="Xr">rgblink(1)</a>, <a class="Xr">rgbfix(1)</a>,
<a class="Xr" title="Xr">rgbfix(1)</a>, <a class="Xr" title="Xr">gbz80(7)</a> <a class="Xr">gbz80(7)</a>
<h1 class="Sh" title="Sh" id="HISTORY"><a class="selflink" href="#HISTORY">HISTORY</a></h1> </section>
<b class="Nm" title="Nm">rgbgfx</b> was created by <section class="Sh">
<span class="An" title="An">stag019</span> to be included in RGBDS. It is now <h1 class="Sh" id="HISTORY"><a class="permalink" href="#HISTORY">HISTORY</a></h1>
maintained by a number of contributors at <code class="Nm">rgbgfx</code> was created by <span class="An">stag019</span> to
<a class="Lk" title="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.</div> 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"> <table class="foot">
<tr> <tr>
<td class="foot-date">January 26, 2018</td> <td class="foot-date">January 26, 2018</td>

View File

@@ -1,13 +1,14 @@
<!DOCTYPE html> <!DOCTYPE html>
<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> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<style>
table.head, table.foot { width: 100%; }
td.head-rtitle, td.foot-os { text-align: right; }
td.head-vol { text-align: center; }
div.Pp { margin: 1ex 0ex; }
</style>
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/> <link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
<title>RGBLINK(1)</title> <title>RGBLINK(1)</title>
</head> </head>
@@ -20,137 +21,113 @@
</tr> </tr>
</table> </table>
<div class="manual-text"> <div class="manual-text">
<h1 class="Sh" title="Sh" id="NAME"><a class="selflink" href="#NAME">NAME</a></h1> <section class="Sh">
<b class="Nm" title="Nm">rgblink</b> &#x2014; <span class="Nd" title="Nd">Game <h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
Boy linker</span> <code class="Nm">rgblink</code> &#x2014;
<h1 class="Sh" title="Sh" id="SYNOPSIS"><a class="selflink" href="#SYNOPSIS">SYNOPSIS</a></h1> <div class="Nd">Game Boy linker</div>
</section>
<section class="Sh">
<h1 class="Sh" id="SYNOPSIS"><a class="permalink" href="#SYNOPSIS">SYNOPSIS</a></h1>
<table class="Nm"> <table class="Nm">
<tr> <tr>
<td><b class="Nm" title="Nm">rgblink</b></td> <td><code class="Nm">rgblink</code></td>
<td>[<span class="Op"><b class="Fl" title="Fl">-dtVw</b></span>] <td>[<code class="Fl">-dtVw</code>] [<code class="Fl">-m</code>
[<span class="Op"><b class="Fl" title="Fl">-m</b> <var class="Ar">mapfile</var>] [<code class="Fl">-n</code>
<var class="Ar" title="Ar">mapfile</var></span>] <var class="Ar">symfile</var>] [<code class="Fl">-O</code>
[<span class="Op"><b class="Fl" title="Fl">-n</b> <var class="Ar">overlayfile</var>] [<code class="Fl">-o</code>
<var class="Ar" title="Ar">symfile</var></span>] <var class="Ar">outfile</var>] [<code class="Fl">-p</code>
[<span class="Op"><b class="Fl" title="Fl">-O</b> <var class="Ar">pad_value</var>] [<code class="Fl">-s</code>
<var class="Ar" title="Ar">overlayfile</var></span>] <var class="Ar">symbol</var>] [<code class="Fl">-l</code>
[<span class="Op"><b class="Fl" title="Fl">-o</b> <var class="Ar">linkerscript</var>] <var class="Ar">file ...</var></td>
<var class="Ar" title="Ar">outfile</var></span>]
[<span class="Op"><b class="Fl" title="Fl">-p</b>
<var class="Ar" title="Ar">pad_value</var></span>]
[<span class="Op"><b class="Fl" title="Fl">-s</b>
<var class="Ar" title="Ar">symbol</var></span>]
[<span class="Op"><b class="Fl" title="Fl">-l</b>
<var class="Ar" title="Ar">linkerscript</var></span>]
<var class="Ar" title="Ar">file ...</var></td>
</tr> </tr>
</table> </table>
<h1 class="Sh" title="Sh" id="DESCRIPTION"><a class="selflink" href="#DESCRIPTION">DESCRIPTION</a></h1> </section>
The <b class="Nm" title="Nm">rgblink</b> program links objects created by <section class="Sh">
<a class="Xr" title="Xr">rgbasm(1)</a> into a single Game Boy ROM file. <h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
<div class="Pp"></div> The <code class="Nm">rgblink</code> program links objects created by
By default, ROM0 sections created by the assembler are placed in the 16KiB bank <a class="Xr">rgbasm(1)</a> into a single Game Boy ROM file.
0, and ROMX sections are placed in any bank except bank 0. If your ROM will <p class="Pp">By default, ROM0 sections created by the assembler are placed in
only be 32KiB, you can use the <b class="Fl" title="Fl">-t</b> option to the 16KiB bank 0, and ROMX sections are placed in any bank except bank 0. If
override this. your ROM will only be 32KiB, you can use the <code class="Fl">-t</code>
<div class="Pp"></div> option to override this.</p>
Similarly, WRAM0 sections are placed in the first 4KiB of WRAM bank 0 and WRAMX <p class="Pp">Similarly, WRAM0 sections are placed in the first 4KiB of WRAM
sections are placed in any bank except bank 0. If your ROM doesn't use banked bank 0 and WRAMX sections are placed in any bank except bank 0. If your ROM
WRAM you can use option <b class="Fl" title="Fl">-w</b> option to override doesn't use banked WRAM you can use option <code class="Fl">-w</code> option
this. to override this.</p>
<div class="Pp"></div> <p class="Pp">Also, if your ROM is designed for DMG, you can make sure that you
Also, if your ROM is designed for DMG, you can make sure that you don't use any don't use any prohibited section by using the option
prohibited section by using the option <b class="Fl" title="Fl">-d</b>, which <code class="Fl">-d</code>, which implies <code class="Fl">-w</code> but
implies <b class="Fl" title="Fl">-w</b> but also prohibits the use of VRAM also prohibits the use of VRAM bank 1.</p>
bank 1. <p class="Pp">The arguments are as follows:</p>
<div class="Pp"></div>
The arguments are as follows:
<dl class="Bl-tag"> <dl class="Bl-tag">
<dt class="It-tag">&#x00A0;</dt> <dt><a class="permalink" href="#m"><code class="Fl" id="m">-m</code></a>
<dd class="It-tag">&#x00A0;</dd> <var class="Ar">mapfile</var></dt>
<dt class="It-tag"><a class="selflink" href="#m"><b class="Fl" title="Fl" id="m">-m</b></a> <dd>Write a mapfile to the given filename.</dd>
<var class="Ar" title="Ar">mapfile</var></dt> <dt><a class="permalink" href="#n"><code class="Fl" id="n">-n</code></a>
<dd class="It-tag">Write a mapfile to the given filename.</dd> <var class="Ar">symfile</var></dt>
<dt class="It-tag">&#x00A0;</dt> <dd>Write a symbol file to the given filename.</dd>
<dd class="It-tag">&#x00A0;</dd> <dt><a class="permalink" href="#O"><code class="Fl" id="O">-O</code></a>
<dt class="It-tag"><a class="selflink" href="#n"><b class="Fl" title="Fl" id="n">-n</b></a> <var class="Ar">overlayfile</var></dt>
<var class="Ar" title="Ar">symfile</var></dt> <dd>The ROM image to overlay sections over. When an overlay ROM is provided,
<dd class="It-tag">Write a symbol file to the given filename.</dd> all sections must be fixed. This may be used to patch an existing
<dt class="It-tag">&#x00A0;</dt> binary.</dd>
<dd class="It-tag">&#x00A0;</dd> <dt><a class="permalink" href="#o"><code class="Fl" id="o">-o</code></a>
<dt class="It-tag"><a class="selflink" href="#O"><b class="Fl" title="Fl" id="O">-O</b></a> <var class="Ar">outfile</var></dt>
<var class="Ar" title="Ar">overlayfile</var></dt> <dd>Write ROM image to the given filename.</dd>
<dd class="It-tag">The ROM image to overlay sections over. When an overlay ROM <dt><a class="permalink" href="#p"><code class="Fl" id="p">-p</code></a>
is provided, all sections must be fixed. This may be used to patch an <var class="Ar">pad_value</var></dt>
existing binary.</dd> <dd>When padding an image, pad with this value. The default is 0x00.</dd>
<dt class="It-tag">&#x00A0;</dt> <dt><a class="permalink" href="#s"><code class="Fl" id="s">-s</code></a>
<dd class="It-tag">&#x00A0;</dd> <var class="Ar">symbol</var></dt>
<dt class="It-tag"><a class="selflink" href="#o"><b class="Fl" title="Fl" id="o">-o</b></a> <dd>???</dd>
<var class="Ar" title="Ar">outfile</var></dt> <dt><a class="permalink" href="#w"><code class="Fl" id="w">-w</code></a></dt>
<dd class="It-tag">Write ROM image to the given filename.</dd> <dd>Expand the WRAM0 section size from 4KiB to the full 8KiB assigned to WRAM
<dt class="It-tag">&#x00A0;</dt> and prohibit the use of WRAMX sections.</dd>
<dd class="It-tag">&#x00A0;</dd> <dt><a class="permalink" href="#d"><code class="Fl" id="d">-d</code></a></dt>
<dt class="It-tag"><a class="selflink" href="#p"><b class="Fl" title="Fl" id="p">-p</b></a> <dd>Enable DMG mode. Prohibit the use of sections that doesn't exist on a DMG,
<var class="Ar" title="Ar">pad_value</var></dt> such as WRAMX and VRAM bank 1. This option automatically enables
<dd class="It-tag">When padding an image, pad with this value. The default is <code class="Fl">-w</code>.</dd>
0x00.</dd> <dt><a class="permalink" href="#t"><code class="Fl" id="t">-t</code></a></dt>
<dt class="It-tag">&#x00A0;</dt> <dd>Expand the ROM0 section size from 16KiB to the full 32KiB assigned to ROM
<dd class="It-tag">&#x00A0;</dd> and prohibit the use of ROMX sections. Useful for ROMs that fit in 32
<dt class="It-tag"><a class="selflink" href="#s"><b class="Fl" title="Fl" id="s">-s</b></a> KiB.</dd>
<var class="Ar" title="Ar">symbol</var></dt> <dt><a class="permalink" href="#l"><code class="Fl" id="l">-l</code></a>
<dd class="It-tag">???</dd> <var class="Ar">linkerscript</var></dt>
<dt class="It-tag">&#x00A0;</dt> <dd>Specify a linkerscript file that tells the linker how sections must be
<dd class="It-tag">&#x00A0;</dd> placed in the ROM. This file has priority over the attributes assigned in
<dt class="It-tag"><a class="selflink" href="#w"><b class="Fl" title="Fl" id="w">-w</b></a></dt> the source code, but they have to be consistent. See
<dd class="It-tag">Expand the WRAM0 section size from 4KiB to the full 8KiB <a class="Xr">rgblink(5)</a> for more information about its format.</dd>
assigned to WRAM and prohibit the use of WRAMX sections.</dd> <dt><a class="permalink" href="#V"><code class="Fl" id="V">-V</code></a></dt>
<dt class="It-tag">&#x00A0;</dt> <dd>Print the version of the program and exit.</dd>
<dd class="It-tag">&#x00A0;</dd>
<dt class="It-tag"><a class="selflink" href="#d"><b class="Fl" title="Fl" id="d">-d</b></a></dt>
<dd class="It-tag">Enable DMG mode. Prohibit the use of sections that doesn't
exist on a DMG, such as WRAMX and VRAM bank 1. This option automatically
enables <b class="Fl" title="Fl">-w</b>.</dd>
<dt class="It-tag">&#x00A0;</dt>
<dd class="It-tag">&#x00A0;</dd>
<dt class="It-tag"><a class="selflink" href="#t"><b class="Fl" title="Fl" id="t">-t</b></a></dt>
<dd class="It-tag">Expand the ROM0 section size from 16KiB to the full 32KiB
assigned to ROM and prohibit the use of ROMX sections. Useful for ROMs
that fit in 32 KiB.</dd>
<dt class="It-tag">&#x00A0;</dt>
<dd class="It-tag">&#x00A0;</dd>
<dt class="It-tag"><a class="selflink" href="#l"><b class="Fl" title="Fl" id="l">-l</b></a>
<var class="Ar" title="Ar">linkerscript</var></dt>
<dd class="It-tag">Specify a linkerscript file that tells the linker how
sections must be placed in the ROM. This file has priority over the
attributes assigned in the source code, but they have to be consistent.
See <a class="Xr" title="Xr">rgblink(5)</a> for more information about its
format.</dd>
<dt class="It-tag">&#x00A0;</dt>
<dd class="It-tag">&#x00A0;</dd>
<dt class="It-tag"><a class="selflink" href="#V"><b class="Fl" title="Fl" id="V">-V</b></a></dt>
<dd class="It-tag">Print the version of the program and exit.</dd>
</dl> </dl>
<h1 class="Sh" title="Sh" id="EXAMPLES"><a class="selflink" href="#EXAMPLES">EXAMPLES</a></h1> </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 All you need for a basic ROM is an object file, which can be made into a ROM
image like so: image like so:
<div class="Pp"></div> <p class="Pp"></p>
<div class="D1">$ rgblink -o bar.gb foo.o</div> <div class="Bd Bd-indent">$ rgblink -o bar.gb foo.o</div>
<div class="Pp"></div> <p class="Pp">The resulting bar.gb will not have correct checksums (unless you
The resulting bar.gb will not have correct checksums (unless you put them in the put them in the assembly source). You should use <a class="Xr">rgbfix(1)</a>
assembly source). You should use <a class="Xr" title="Xr">rgbfix(1)</a> to fix to fix these so that the program will actually run in a Game Boy:</p>
these so that the program will actually run in a Game Boy: <p class="Pp"></p>
<div class="Pp"></div> <div class="Bd Bd-indent">$ rgbfix -v bar.gb</div>
<div class="D1">$ rgbfix -v bar.gb</div> </section>
<h1 class="Sh" title="Sh" id="SEE_ALSO"><a class="selflink" href="#SEE_ALSO">SEE <section class="Sh">
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
ALSO</a></h1> ALSO</a></h1>
<a class="Xr" title="Xr">rgbasm(1)</a>, <a class="Xr" title="Xr">rgblink(5)</a>, <a class="Xr">rgbasm(1)</a>, <a class="Xr">rgblink(5)</a>,
<a class="Xr" title="Xr">rgbfix(1)</a>, <a class="Xr" title="Xr">rgbds(5)</a>, <a class="Xr">rgbfix(1)</a>, <a class="Xr">rgbds(5)</a>,
<a class="Xr" title="Xr">rgbds(7)</a> <a class="Xr">rgbds(7)</a>
<h1 class="Sh" title="Sh" id="HISTORY"><a class="selflink" href="#HISTORY">HISTORY</a></h1> </section>
<b class="Nm" title="Nm">rgblink</b> was originally written by Carsten <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 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 RGBDS by Justin Lloyd. It is now maintained by a number of contributors at
<a class="Lk" title="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.</div> <a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.
</section>
</div>
<table class="foot"> <table class="foot">
<tr> <tr>
<td class="foot-date">January 26, 2018</td> <td class="foot-date">January 26, 2018</td>

View File

@@ -1,13 +1,14 @@
<!DOCTYPE html> <!DOCTYPE html>
<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> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<style>
table.head, table.foot { width: 100%; }
td.head-rtitle, td.foot-os { text-align: right; }
td.head-vol { text-align: center; }
div.Pp { margin: 1ex 0ex; }
</style>
<link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/> <link rel="stylesheet" href="mandoc.css" type="text/css" media="all"/>
<title>RGBLINK(5)</title> <title>RGBLINK(5)</title>
</head> </head>
@@ -20,23 +21,24 @@
</tr> </tr>
</table> </table>
<div class="manual-text"> <div class="manual-text">
<h1 class="Sh" title="Sh" id="NAME"><a class="selflink" href="#NAME">NAME</a></h1> <section class="Sh">
<b class="Nm" title="Nm">rgblink</b> &#x2014; <h1 class="Sh" id="NAME"><a class="permalink" href="#NAME">NAME</a></h1>
<span class="Nd" title="Nd">linkerscript file format</span> <code class="Nm">rgblink</code> &#x2014;
<h1 class="Sh" title="Sh" id="DESCRIPTION"><a class="selflink" href="#DESCRIPTION">DESCRIPTION</a></h1> <div class="Nd">linkerscript file format</div>
</section>
<section class="Sh">
<h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1>
The linkerscript is an external file that allows the user to specify the order The linkerscript is an external file that allows the user to specify the order
of sections without the need for doing so before assembling each object file. of sections without the need for doing so before assembling each object file.
<div class="Pp"></div> <p class="Pp">The placement of sections specified in the linkerscript is done
The placement of sections specified in the linkerscript is done before the before the sections whose placement is defined in the source code.</p>
sections whose placement is defined in the source code. <p class="Pp">A linkerscript consists on a series of banks followed by a list of
<div class="Pp"></div> sections and, optionally, commands. They can be lowercase or uppercase, it
A linkerscript consists on a series of banks followed by a list of sections and, is ignored. Any line can contain a comment starting with
optionally, commands. They can be lowercase or uppercase, it is ignored. Any &#x2018;<code class="Li">;</code>&#x2019; that ends at the end of the
line can contain a comment starting with line:</p>
&#x2018;<code class="Li">;</code>&#x2019; that ends at the end of the line: <div class="Bd Pp Bd-indent">
<div class="Pp"></div> <pre>
<div class="Bd" style="margin-left: 5.00ex;">
<pre class="Li">
ROMX $F ; This is a comment ROMX $F ; This is a comment
&quot;Functions to read array&quot; &quot;Functions to read array&quot;
ALIGN 8 ALIGN 8
@@ -46,55 +48,53 @@ WRAMX 2
&quot;Some variables&quot; &quot;Some variables&quot;
</pre> </pre>
</div> </div>
<div class="Pp"></div> <p class="Pp">Numbers can be in decimal or hexadecimal format (the prefix is
Numbers can be in decimal or hexadecimal format (the prefix is &#x2018;<code class="Li">$</code>&#x2019;). It is an error if any section
&#x2018;<code class="Li">$</code>&#x2019;). It is an error if any section name name or command are found before setting a bank.</p>
or command are found before setting a bank. <p class="Pp">Files can be included by using the <var class="Ar">INCLUDE</var>
<div class="Pp"></div>
Files can be included by using the <var class="Ar" title="Ar">INCLUDE</var>
keyword followed by a string with the path of the file that has to be keyword followed by a string with the path of the file that has to be
included. included.</p>
<div class="Pp"></div> <p class="Pp">The possible bank types are: <b class="Sy">ROM0</b>,
The possible bank types are: <b class="Sy" title="Sy">ROM0</b>, <b class="Sy">ROMX</b>, <b class="Sy">VRAM</b>, <b class="Sy">WRAM0</b>,
<b class="Sy" title="Sy">ROMX</b>, <b class="Sy" title="Sy">VRAM</b>, <b class="Sy">WRAMX</b>, <b class="Sy">OAM</b> and <b class="Sy">HRAM</b>.
<b class="Sy" title="Sy">WRAM0</b>, <b class="Sy" title="Sy">WRAMX</b>, Types <b class="Sy">ROMX</b>, <b class="Sy">VRAM</b>,
<b class="Sy" title="Sy">OAM</b> and <b class="Sy" title="Sy">HRAM</b>. Types <b class="Sy">WRAMX</b> and <b class="Sy">SRAM</b> are banked, which means
<b class="Sy" title="Sy">ROMX</b>, <b class="Sy" title="Sy">VRAM</b>, that it is needed to specify a bank after the type.</p>
<b class="Sy" title="Sy">WRAMX</b> and <b class="Sy" title="Sy">SRAM</b> are <p class="Pp">When a new bank statement is found, sections found after it will
banked, which means that it is needed to specify a bank after the type. be placed right from the beginning of that bank. If the linkerscript
<div class="Pp"></div> switches to a different bank and then it comes back to the previous one it
When a new bank statement is found, sections found after it will be placed right will continue from the last address that was used.</p>
from the beginning of that bank. If the linkerscript switches to a different <p class="Pp">The only two commands are <var class="Ar">ORG</var> and
bank and then it comes back to the previous one it will continue from the last <var class="Ar">ALIGN</var>:</p>
address that was used.
<div class="Pp"></div>
The only two commands are <var class="Ar" title="Ar">ORG</var> and
<var class="Ar" title="Ar">ALIGN</var>:
<ul class="Bl-bullet"> <ul class="Bl-bullet">
<li class="It-bullet"><var class="Ar" title="Ar">ORG</var> sets the address in <li><var class="Ar">ORG</var> sets the address in which new sections will be
which new sections will be placed. It can not be lower than the current placed. It can not be lower than the current address.</li>
address.</li> <li><var class="Ar">ALIGN</var> will increase the address until it is aligned
<li class="It-bullet"><var class="Ar" title="Ar">ALIGN</var> will increase the to the specified boundary (it tries to set to 0 the number of bits
address until it is aligned to the specified boundary (it tries to set to specified after the command: <b class="Sy">ALIGN 8</b> will align to
0 the number of bits specified after the command: $100).</li>
<b class="Sy" title="Sy">ALIGN 8</b> will align to $100).</li>
</ul> </ul>
<div class="Pp"></div> <p class="Pp">Note: The bank, alignment, address and type of sections can be
Note: The bank, alignment, address and type of sections can be specified both in specified both in the source code and in the linkerscript. For a section to
the source code and in the linkerscript. For a section to be able to be placed be able to be placed with the linkerscript the bank must be left unassigned
with the linkerscript the bank must be left unassigned in the source code or in the source code or be the same as the one specified in the linkerscript.
be the same as the one specified in the linkerscript. The address and The address and alignment musn't be set.</p>
alignment musn't be set. </section>
<h1 class="Sh" title="Sh" id="SEE_ALSO"><a class="selflink" href="#SEE_ALSO">SEE <section class="Sh">
<h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE
ALSO</a></h1> ALSO</a></h1>
<a class="Xr" title="Xr">rgbasm(1)</a>, <a class="Xr" title="Xr">rgblink(1)</a>, <a class="Xr">rgbasm(1)</a>, <a class="Xr">rgblink(1)</a>,
<a class="Xr" title="Xr">rgbfix(1)</a>, <a class="Xr" title="Xr">rgbds(5)</a>, <a class="Xr">rgbfix(1)</a>, <a class="Xr">rgbds(5)</a>,
<a class="Xr" title="Xr">rgbds(7)</a> <a class="Xr">rgbds(7)</a>
<h1 class="Sh" title="Sh" id="HISTORY"><a class="selflink" href="#HISTORY">HISTORY</a></h1> </section>
<b class="Nm" title="Nm">rgblink</b> was originally written by Carsten <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 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 RGBDS by Justin Lloyd. It is now maintained by a number of contributors at
<a class="Lk" title="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.</div> <a class="Lk" href="https://github.com/rednex/rgbds">https://github.com/rednex/rgbds</a>.
</section>
</div>
<table class="foot"> <table class="foot">
<tr> <tr>
<td class="foot-date">January 27, 2018</td> <td class="foot-date">January 27, 2018</td>

View File

@@ -28,7 +28,6 @@
extern int32_t nLineNo; extern int32_t nLineNo;
extern uint32_t nTotalLines; extern uint32_t nTotalLines;
extern uint32_t nPC; extern uint32_t nPC;
extern uint32_t nPass;
extern uint32_t nIFDepth; extern uint32_t nIFDepth;
extern bool skipElif; extern bool skipElif;
extern uint32_t nUnionDepth; extern uint32_t nUnionDepth;
@@ -40,6 +39,7 @@ extern struct sSymbol *tHashedSymbols[HASHSIZE];
extern struct sSymbol *pPCSymbol; extern struct sSymbol *pPCSymbol;
extern bool oDontExpandStrings; extern bool oDontExpandStrings;
size_t symvaluetostring(char *dest, size_t maxLength, char *sym); 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

@@ -13,14 +13,30 @@
#define MAXCHARMAPS 512 #define MAXCHARMAPS 512
#define CHARMAPLENGTH 16 #define CHARMAPLENGTH 16
#define MAXCHARNODES (MAXCHARMAPS * CHARMAPLENGTH + 1)
struct Charmap { /*
int32_t count; * A node for trie structure.
char input[MAXCHARMAPS][CHARMAPLENGTH + 1]; */
char output[MAXCHARMAPS]; struct Charnode {
uint8_t code; /* the value in a key-value pair. */
uint8_t isCode; /* has one 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. */
}; };
int32_t readUTF8Char(char *destination, char *source); 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. */
struct Charmap *next; /* next charmap in hash table bucket */
};
void charmap_InitMain(void);
struct Charmap *charmap_New(const char *name, const char *baseName);
void charmap_Set(const char *name);
void charmap_Push(void);
void charmap_Pop(void);
int32_t charmap_Add(char *input, uint8_t output); int32_t charmap_Add(char *input, uint8_t output);
int32_t charmap_Convert(char **input); int32_t charmap_Convert(char **input);

View File

@@ -33,15 +33,20 @@ struct sContext {
char *pREPTBlock; char *pREPTBlock;
uint32_t nREPTBlockCount; uint32_t nREPTBlockCount;
uint32_t nREPTBlockSize; uint32_t nREPTBlockSize;
int32_t nREPTBodyFirstLine;
int32_t nREPTBodyLastLine;
}; };
extern unsigned int nMaxRecursionDepth;
void fstk_RunInclude(char *tzFileName); void fstk_RunInclude(char *tzFileName);
void fstk_RunMacroArg(int32_t s); void fstk_RunMacroArg(int32_t s);
void fstk_Init(char *s); void fstk_Init(char *s);
void fstk_Dump(void); void fstk_Dump(void);
void fstk_DumpStringExpansions(void);
void fstk_AddIncludePath(char *s); void fstk_AddIncludePath(char *s);
uint32_t fstk_RunMacro(char *s); uint32_t fstk_RunMacro(char *s);
void fstk_RunRept(uint32_t count); void fstk_RunRept(uint32_t count, int32_t nReptLineNo);
FILE *fstk_FindFile(char *fname, char **incPathUsed); FILE *fstk_FindFile(char *fname, char **incPathUsed);
int32_t fstk_GetLine(void); int32_t fstk_GetLine(void);

View File

@@ -31,7 +31,7 @@ struct yy_buffer_state {
/* Address where the data is initially written after a safety margin */ /* Address where the data is initially written after a safety margin */
char *pBufferStart; char *pBufferStart;
char *pBuffer; char *pBuffer;
uint32_t nBufferSize; size_t nBufferSize;
uint32_t oAtLineStart; uint32_t oAtLineStart;
}; };
@@ -40,6 +40,13 @@ enum eLexerState {
LEX_STATE_MACROARGS LEX_STATE_MACROARGS
}; };
struct sStringExpansionPos {
char *tzName;
char *pBuffer;
char *pBufferPos;
struct sStringExpansionPos *pParent;
};
#define INITIAL 0 #define INITIAL 0
#define macroarg 3 #define macroarg 3
@@ -62,14 +69,16 @@ void lex_FloatDeleteSecondRange(uint32_t id, uint16_t start, uint16_t end);
void lex_Init(void); void lex_Init(void);
void lex_AddStrings(const struct sLexInitString *lex); void lex_AddStrings(const struct sLexInitString *lex);
void lex_SetBuffer(char *buffer, uint32_t len); void lex_SetBuffer(char *buffer, uint32_t len);
void lex_BeginStringExpansion(const char *tzName);
int yywrap(void); int yywrap(void);
uint32_t yylex(void); int yylex(void);
void yyunput(char c); void yyunput(char c);
void yyunputstr(char *s); void yyunputstr(const char *s);
void yyskipbytes(uint32_t count); void yyskipbytes(uint32_t count);
void yyunputbytes(uint32_t count); void yyunputbytes(uint32_t count);
extern YY_BUFFER_STATE pCurrentBuffer; extern YY_BUFFER_STATE pCurrentBuffer;
extern struct sStringExpansionPos *pCurrentStringExpansion;
void upperstring(char *s); void upperstring(char *s);
void lowerstring(char *s); void lowerstring(char *s);

View File

@@ -11,6 +11,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include "helpers.h" #include "helpers.h"

View File

@@ -40,6 +40,7 @@ void out_AbsByteGroup(char *s, int32_t length);
void out_RelByte(struct Expression *expr); void out_RelByte(struct Expression *expr);
void out_RelWord(struct Expression *expr); void out_RelWord(struct Expression *expr);
void out_PCRelByte(struct Expression *expr); void out_PCRelByte(struct Expression *expr);
void out_CheckErrors(void);
void out_WriteObject(void); void out_WriteObject(void);
void out_Skip(int32_t skip); void out_Skip(int32_t skip);
void out_BinaryFile(char *s); void out_BinaryFile(char *s);

View File

@@ -11,17 +11,19 @@
#include <stdint.h> #include <stdint.h>
#define MAXRPNLEN 1048576
struct Expression { struct Expression {
int32_t nVal; int32_t nVal;
uint8_t tRPN[256]; uint8_t *tRPN;
uint32_t nRPNCapacity;
uint32_t nRPNLength; uint32_t nRPNLength;
uint32_t nRPNPatchSize;
uint32_t nRPNOut; uint32_t nRPNOut;
uint32_t isReloc; uint32_t isReloc;
uint32_t isPCRel;
}; };
uint32_t rpn_isReloc(const struct Expression *expr); uint32_t rpn_isReloc(const struct Expression *expr);
uint32_t rpn_isPCRelative(const struct Expression *expr);
void rpn_Symbol(struct Expression *expr, char *tzSym); void rpn_Symbol(struct Expression *expr, char *tzSym);
void rpn_Number(struct Expression *expr, uint32_t i); void rpn_Number(struct Expression *expr, uint32_t i);
void rpn_LOGNOT(struct Expression *expr, const struct Expression *src); void rpn_LOGNOT(struct Expression *expr, const struct Expression *src);
@@ -69,7 +71,8 @@ uint16_t rpn_PopByte(struct Expression *expr);
void rpn_BankSymbol(struct Expression *expr, char *tzSym); void rpn_BankSymbol(struct Expression *expr, char *tzSym);
void rpn_BankSection(struct Expression *expr, char *tzSectionName); void rpn_BankSection(struct Expression *expr, char *tzSectionName);
void rpn_BankSelf(struct Expression *expr); void rpn_BankSelf(struct Expression *expr);
void rpn_Reset(struct Expression *expr); void rpn_Init(struct Expression *expr);
void rpn_Free(struct Expression *expr);
void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src); void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src);
#endif /* RGBDS_ASM_RPN_H */ #endif /* RGBDS_ASM_RPN_H */

View File

@@ -38,8 +38,8 @@ struct sSymbol {
#define SYMF_SET 0x004 #define SYMF_SET 0x004
/* Symbol should be exported */ /* Symbol should be exported */
#define SYMF_EXPORT 0x008 #define SYMF_EXPORT 0x008
/* Symbol is imported, it's value is unknown */ /* Symbol referenced in RPN expression */
#define SYMF_IMPORT 0x010 #define SYMF_REF 0x010
/* Symbol is a local symbol */ /* Symbol is a local symbol */
#define SYMF_LOCAL 0x020 #define SYMF_LOCAL 0x020
/* Symbol has been defined, not only referenced */ /* Symbol has been defined, not only referenced */
@@ -51,10 +51,8 @@ struct sSymbol {
/* Symbol has a constant value, will not be changed during linking */ /* Symbol has a constant value, will not be changed during linking */
#define SYMF_CONST 0x200 #define SYMF_CONST 0x200
uint32_t calchash(char *s); uint32_t sym_CalcHash(const char *s);
void sym_SetExportAll(uint8_t set); void sym_SetExportAll(uint8_t set);
void sym_PrepPass1(void);
void sym_PrepPass2(void);
void sym_AddLocalReloc(char *tzSym); void sym_AddLocalReloc(char *tzSym);
void sym_AddReloc(char *tzSym); void sym_AddReloc(char *tzSym);
void sym_Export(char *tzSym); void sym_Export(char *tzSym);
@@ -65,23 +63,21 @@ void sym_AddNewMacroArg(char *s);
void sym_SaveCurrentMacroArgs(char *save[]); void sym_SaveCurrentMacroArgs(char *save[]);
void sym_RestoreCurrentMacroArgs(char *save[]); void sym_RestoreCurrentMacroArgs(char *save[]);
void sym_UseNewMacroArgs(void); void sym_UseNewMacroArgs(void);
void sym_FreeCurrentMacroArgs(void);
void sym_AddEqu(char *tzSym, int32_t value); void sym_AddEqu(char *tzSym, int32_t value);
void sym_AddSet(char *tzSym, int32_t value); void sym_AddSet(char *tzSym, int32_t value);
void sym_Init(void); void sym_Init(void);
uint32_t sym_GetConstantValue(char *s); uint32_t sym_GetConstantValue(char *s);
uint32_t sym_isConstant(char *s); uint32_t sym_isConstant(char *s);
struct sSymbol *sym_FindSymbol(char *tzName); struct sSymbol *sym_FindSymbol(char *tzName);
void sym_Global(char *tzSym);
char *sym_FindMacroArg(int32_t i); char *sym_FindMacroArg(int32_t i);
char *sym_GetStringValue(char *tzSym); char *sym_GetStringValue(char *tzSym);
void sym_UseCurrentMacroArgs(void); void sym_UseCurrentMacroArgs(void);
void sym_SetMacroArgID(uint32_t nMacroCount); void sym_SetMacroArgID(uint32_t nMacroCount);
uint32_t sym_isString(char *tzSym); uint32_t sym_isString(char *tzSym);
void sym_AddMacro(char *tzSym); void sym_AddMacro(char *tzSym, int32_t nDefLineNo);
void sym_Ref(char *tzSym);
void sym_ShiftCurrentMacroArgs(void); void sym_ShiftCurrentMacroArgs(void);
void sym_AddString(char *tzSym, char *tzValue); void sym_AddString(char *tzSym, char *tzValue);
uint32_t sym_GetValue(char *s);
uint32_t sym_GetDefinedValue(char *s); uint32_t sym_GetDefinedValue(char *s);
uint32_t sym_isDefined(char *tzName); uint32_t sym_isDefined(char *tzName);
void sym_Purge(char *tzName); void sym_Purge(char *tzName);

17
include/asm/util.h Normal file
View File

@@ -0,0 +1,17 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#ifndef RGBDS_UTIL_H
#define RGBDS_UTIL_H
#include <stdint.h>
uint32_t calchash(const char *s);
int32_t readUTF8Char(char *dest, char *src);
#endif /* RGBDS_UTIL_H */

View File

@@ -12,14 +12,24 @@
#include <stdint.h> #include <stdint.h>
#include "gfx/main.h" #include "gfx/main.h"
#define XFLIP 0x40
#define YFLIP 0x20
void raw_to_gb(const struct RawIndexedImage *raw_image, struct GBImage *gb); void raw_to_gb(const struct RawIndexedImage *raw_image, struct GBImage *gb);
void output_file(const struct Options *opts, const struct GBImage *gb); void output_file(const struct Options *opts, const struct GBImage *gb);
int get_tile_index(uint8_t *tile, uint8_t **tiles, int num_tiles, int get_tile_index(uint8_t *tile, uint8_t **tiles, int num_tiles,
int tile_size); int tile_size);
void create_tilemap(const struct Options *opts, struct GBImage *gb, uint8_t reverse_bits(uint8_t b);
struct Tilemap *tilemap); void xflip(uint8_t *tile, uint8_t *tile_xflip, int tile_size);
void yflip(uint8_t *tile, uint8_t *tile_yflip, int tile_size);
int get_mirrored_tile_index(uint8_t *tile, uint8_t **tiles, int num_tiles,
int tile_size, int *flags);
void create_mapfiles(const struct Options *opts, struct GBImage *gb,
struct Mapfile *tilemap, struct Mapfile *attrmap);
void output_tilemap_file(const struct Options *opts, void output_tilemap_file(const struct Options *opts,
const struct Tilemap *tilemap); const struct Mapfile *tilemap);
void output_attrmap_file(const struct Options *opts,
const struct Mapfile *attrmap);
void output_palette_file(const struct Options *opts, void output_palette_file(const struct Options *opts,
const struct RawIndexedImage *raw_image); const struct RawIndexedImage *raw_image);

View File

@@ -21,10 +21,14 @@ struct Options {
bool hardfix; bool hardfix;
bool fix; bool fix;
bool horizontal; bool horizontal;
bool mirror;
bool unique; bool unique;
bool colorcurve;
int trim; int trim;
char *mapfile; char *tilemapfile;
bool mapout; bool tilemapout;
char *attrmapfile;
bool attrmapout;
char *palfile; char *palfile;
bool palout; bool palout;
char *outfile; char *outfile;
@@ -40,8 +44,10 @@ struct RGBColor {
struct ImageOptions { struct ImageOptions {
bool horizontal; bool horizontal;
int trim; int trim;
char *mapfile; char *tilemapfile;
bool mapout; bool tilemapout;
char *attrmapfile;
bool attrmapout;
char *palfile; char *palfile;
bool palout; bool palout;
}; };
@@ -71,7 +77,7 @@ struct GBImage {
int trim; int trim;
}; };
struct Tilemap { struct Mapfile {
uint8_t *data; uint8_t *data;
int size; int size;
}; };

View File

@@ -15,7 +15,7 @@ void sym_Init(void);
void sym_CreateSymbol(char *tzName, int32_t nValue, int32_t nBank, void sym_CreateSymbol(char *tzName, int32_t nValue, int32_t nBank,
char *tzObjFileName, char *tzFileName, char *tzObjFileName, char *tzFileName,
uint32_t nFileLine); uint32_t nFileLine);
int32_t sym_GetValue(char *tzName); int32_t sym_GetValue(struct sPatch *pPatch, char *tzName);
int32_t sym_GetBank(char *tzName); int32_t sym_GetBank(struct sPatch *pPatch, char *tzName);
#endif /* RGBDS_LINK_SYMBOL_H */ #endif /* RGBDS_LINK_SYMBOL_H */

View File

@@ -11,7 +11,7 @@
#define PACKAGE_VERSION_MAJOR (0) #define PACKAGE_VERSION_MAJOR (0)
#define PACKAGE_VERSION_MINOR (3) #define PACKAGE_VERSION_MINOR (3)
#define PACKAGE_VERSION_PATCH (8) #define PACKAGE_VERSION_PATCH (10)
const char *get_package_version_string(void); const char *get_package_version_string(void);

View File

@@ -1,7 +1,7 @@
/* /*
* This file is part of RGBDS. * This file is part of RGBDS.
* *
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors. * Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@@ -9,6 +9,7 @@
%{ %{
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -25,6 +26,9 @@
#include "asm/output.h" #include "asm/output.h"
#include "asm/rpn.h" #include "asm/rpn.h"
#include "asm/symbol.h" #include "asm/symbol.h"
#include "asm/util.h"
#include "extern/utf8decoder.h"
#include "common.h" #include "common.h"
#include "linkdefs.h" #include "linkdefs.h"
@@ -73,7 +77,8 @@ static void bankrangecheck(char *name, uint32_t secttype, int32_t org,
out_NewAbsSection(name, secttype, org, bank); out_NewAbsSection(name, secttype, org, bank);
} }
size_t symvaluetostring(char *dest, size_t maxLength, char *sym) size_t symvaluetostring(char *dest, size_t maxLength, char *sym,
const char *mode)
{ {
size_t length; size_t length;
@@ -81,6 +86,9 @@ size_t symvaluetostring(char *dest, size_t maxLength, char *sym)
char *src = sym_GetStringValue(sym); char *src = sym_GetStringValue(sym);
size_t i; size_t i;
if (mode)
yyerror("Print types are only allowed for numbers");
for (i = 0; src[i] != 0; i++) { for (i = 0; src[i] != 0; i++) {
if (i >= maxLength) if (i >= maxLength)
fatalerror("Symbol value too long to fit buffer"); fatalerror("Symbol value too long to fit buffer");
@@ -92,8 +100,25 @@ size_t symvaluetostring(char *dest, size_t maxLength, char *sym)
} else { } else {
uint32_t value = sym_GetConstantValue(sym); uint32_t value = sym_GetConstantValue(sym);
int32_t fullLength = snprintf(dest, maxLength + 1, "$%X", int32_t fullLength;
/* Special cheat for binary */
if (mode && !mode[0]) {
char binary[33]; /* 32 bits + 1 terminator */
char *write_ptr = binary + 32;
fullLength = 0;
binary[32] = 0;
do {
*(--write_ptr) = (value & 1) + '0';
value >>= 1;
fullLength++;
} while(value);
strncpy(dest, write_ptr, maxLength + 1);
} else {
fullLength = snprintf(dest, maxLength + 1,
mode ? : "$%X",
value); value);
}
if (fullLength < 0) { if (fullLength < 0) {
fatalerror("snprintf encoding error"); fatalerror("snprintf encoding error");
@@ -249,14 +274,13 @@ static void copymacro(void)
src = pCurrentBuffer->pBuffer; src = pCurrentBuffer->pBuffer;
ulNewMacroSize = len; ulNewMacroSize = len;
tzNewMacro = (char *)malloc(ulNewMacroSize+2); tzNewMacro = (char *)malloc(ulNewMacroSize + 1);
if (tzNewMacro == NULL) if (tzNewMacro == NULL)
fatalerror("Not enough memory for MACRO definition."); fatalerror("Not enough memory for MACRO definition.");
uint32_t i; uint32_t i;
tzNewMacro[ulNewMacroSize] = '\n'; tzNewMacro[ulNewMacroSize] = 0;
tzNewMacro[ulNewMacroSize+1] = 0;
for (i = 0; i < ulNewMacroSize; i += 1) { for (i = 0; i < ulNewMacroSize; i += 1) {
tzNewMacro[i] = src[i]; tzNewMacro[i] = src[i];
if (src[i] == '\n') if (src[i] == '\n')
@@ -266,16 +290,21 @@ static void copymacro(void)
yyskipbytes(ulNewMacroSize + 4); yyskipbytes(ulNewMacroSize + 4);
} }
static bool endsIf(char c)
{
return isWhiteSpace(c) || c == '(' || c == '{';
}
static uint32_t isIf(char *s) static uint32_t isIf(char *s)
{ {
return (strncasecmp(s, "IF", 2) == 0) return (strncasecmp(s, "IF", 2) == 0)
&& isWhiteSpace(s[-1]) && isWhiteSpace(s[2]); && isWhiteSpace(s[-1]) && endsIf(s[2]);
} }
static uint32_t isElif(char *s) static uint32_t isElif(char *s)
{ {
return (strncasecmp(s, "ELIF", 4) == 0) return (strncasecmp(s, "ELIF", 4) == 0)
&& isWhiteSpace(s[-1]) && isWhiteSpace(s[4]); && isWhiteSpace(s[-1]) && endsIf(s[4]);
} }
static uint32_t isElse(char *s) static uint32_t isElse(char *s)
@@ -324,20 +353,14 @@ static void if_skip_to_else(void)
src++; src++;
} }
} else { } else {
switch (*src) { if (*src == '\"') {
case '\\':
src += 2;
break;
case '\"':
src += 2;
inString = false; inString = false;
break; } else if (*src == '\\') {
/* Escaped quotes don't end the string */
default: if (*++src != '\"')
src++; src--;
break;
} }
src++;
} }
} }
@@ -375,21 +398,14 @@ static void if_skip_to_endc(void)
src++; src++;
} }
} else { } else {
switch (*src) { if (*src == '\"') {
case '\\':
src += 2;
break;
case '\"':
src++;
inString = false; inString = false;
break; } else if (*src == '\\') {
/* Escaped quotes don't end the string */
default: if (*++src != '\"')
src++; src--;
break;
} }
src++;
} }
} }
@@ -431,6 +447,85 @@ static void updateUnion(void)
pPCSymbol->nValue = unionStart[unionIndex]; pPCSymbol->nValue = unionStart[unionIndex];
} }
static size_t strlenUTF8(const char *s)
{
size_t len = 0;
uint32_t state = 0;
uint32_t codep = 0;
while (*s) {
switch (decode(&state, &codep, (uint8_t)*s)) {
case 1:
fatalerror("STRLEN: Invalid UTF-8 character");
break;
case 0:
len++;
break;
}
s++;
}
/* Check for partial code point. */
if (state != 0)
fatalerror("STRLEN: Invalid UTF-8 character");
return len;
}
static void strsubUTF8(char *dest, const char *src, uint32_t pos, uint32_t len)
{
size_t srcIndex = 0;
size_t destIndex = 0;
uint32_t state = 0;
uint32_t codep = 0;
uint32_t curPos = 1;
uint32_t curLen = 0;
if (pos < 1) {
warning("STRSUB: Position starts at 1");
pos = 1;
}
/* Advance to starting position in source string. */
while (src[srcIndex] && curPos < pos) {
switch (decode(&state, &codep, (uint8_t)src[srcIndex])) {
case 1:
fatalerror("STRSUB: Invalid UTF-8 character");
break;
case 0:
curPos++;
break;
}
srcIndex++;
}
if (!src[srcIndex])
warning("STRSUB: Position %lu is past the end of the string",
(unsigned long)pos);
/* Copy from source to destination. */
while (src[srcIndex] && destIndex < MAXSTRLEN && curLen < len) {
switch (decode(&state, &codep, (uint8_t)src[srcIndex])) {
case 1:
fatalerror("STRSUB: Invalid UTF-8 character");
break;
case 0:
curLen++;
break;
}
dest[destIndex++] = src[srcIndex++];
}
if (curLen < len)
warning("STRSUB: Length too big: %lu", (unsigned long)len);
/* Check for partial code point. */
if (state != 0)
fatalerror("STRSUB: Invalid UTF-8 character");
dest[destIndex] = 0;
}
%} %}
%union %union
@@ -456,8 +551,8 @@ static void updateUnion(void)
%token <tzString> T_STRING %token <tzString> T_STRING
%left <nConstValue> T_OP_LOGICNOT %left <nConstValue> T_OP_LOGICNOT
%left <nConstValue> T_OP_LOGICOR T_OP_LOGICAND T_OP_LOGICEQU %left <nConstValue> T_OP_LOGICOR T_OP_LOGICAND
%left <nConstValue> T_OP_LOGICGT T_OP_LOGICLT T_OP_LOGICGE T_OP_LOGICLE T_OP_LOGICNE %left <nConstValue> T_OP_LOGICGT T_OP_LOGICLT T_OP_LOGICGE T_OP_LOGICLE T_OP_LOGICNE T_OP_LOGICEQU
%left <nConstValue> T_OP_ADD T_OP_SUB %left <nConstValue> T_OP_ADD T_OP_SUB
%left <nConstValue> T_OP_OR T_OP_XOR T_OP_AND %left <nConstValue> T_OP_OR T_OP_XOR T_OP_AND
%left <nConstValue> T_OP_SHL T_OP_SHR %left <nConstValue> T_OP_SHL T_OP_SHR
@@ -510,6 +605,10 @@ static void updateUnion(void)
%token T_POP_UNION T_POP_NEXTU T_POP_ENDU %token T_POP_UNION T_POP_NEXTU T_POP_ENDU
%token T_POP_INCBIN T_POP_REPT %token T_POP_INCBIN T_POP_REPT
%token T_POP_CHARMAP %token T_POP_CHARMAP
%token T_POP_NEWCHARMAP
%token T_POP_SETCHARMAP
%token T_POP_PUSHC
%token T_POP_POPC
%token T_POP_SHIFT %token T_POP_SHIFT
%token T_POP_ENDR %token T_POP_ENDR
%token T_POP_FAIL %token T_POP_FAIL
@@ -663,6 +762,10 @@ simple_pseudoop : include
| endu | endu
| incbin | incbin
| charmap | charmap
| newcharmap
| setcharmap
| pushc
| popc
| rept | rept
| shift | shift
| fail | fail
@@ -712,15 +815,17 @@ shift : T_POP_SHIFT { sym_ShiftCurrentMacroArgs(); }
rept : T_POP_REPT uconst rept : T_POP_REPT uconst
{ {
uint32_t nDefinitionLineNo = nLineNo;
copyrept(); copyrept();
fstk_RunRept($2); fstk_RunRept($2, nDefinitionLineNo);
} }
; ;
macrodef : T_LABEL ':' T_POP_MACRO macrodef : T_LABEL ':' T_POP_MACRO
{ {
int32_t nDefinitionLineNo = nLineNo;
copymacro(); copymacro();
sym_AddMacro($1); sym_AddMacro($1, nDefinitionLineNo);
} }
; ;
@@ -799,7 +904,7 @@ ds : T_POP_DS uconst
; ;
db : T_POP_DB constlist_8bit_entry comma constlist_8bit { db : T_POP_DB constlist_8bit_entry comma constlist_8bit {
if ((nPass == 1) && (nListCountEmpty > 0)) { if (nListCountEmpty > 0) {
warning("Empty entry in list of 8-bit elements (treated as 0)."); warning("Empty entry in list of 8-bit elements (treated as 0).");
} }
} }
@@ -807,7 +912,7 @@ db : T_POP_DB constlist_8bit_entry comma constlist_8bit {
; ;
dw : T_POP_DW constlist_16bit_entry comma constlist_16bit { dw : T_POP_DW constlist_16bit_entry comma constlist_16bit {
if ((nPass == 1) && (nListCountEmpty > 0)) { if (nListCountEmpty > 0) {
warning("Empty entry in list of 16-bit elements (treated as 0)."); warning("Empty entry in list of 16-bit elements (treated as 0).");
} }
} }
@@ -815,7 +920,7 @@ dw : T_POP_DW constlist_16bit_entry comma constlist_16bit {
; ;
dl : T_POP_DL constlist_32bit_entry comma constlist_32bit { dl : T_POP_DL constlist_32bit_entry comma constlist_32bit {
if ((nPass == 1) && (nListCountEmpty > 0)) { if (nListCountEmpty > 0) {
warning("Empty entry in list of 32-bit elements (treated as 0)."); warning("Empty entry in list of 32-bit elements (treated as 0).");
} }
} }
@@ -852,7 +957,6 @@ import_list_entry : T_ID
* This is done automatically if the label isn't found * This is done automatically if the label isn't found
* in the list of defined symbols. * in the list of defined symbols.
*/ */
if (nPass == 1)
warning("IMPORT is a deprecated keyword with no effect: %s", $1); warning("IMPORT is a deprecated keyword with no effect: %s", $1);
} }
; ;
@@ -879,7 +983,7 @@ global_list : global_list_entry
global_list_entry : T_ID global_list_entry : T_ID
{ {
sym_Global($1); sym_Export($1);
} }
; ;
@@ -927,30 +1031,48 @@ charmap : T_POP_CHARMAP string comma string
} }
; ;
newcharmap : T_POP_NEWCHARMAP T_ID
{
charmap_New($2, NULL);
}
| T_POP_NEWCHARMAP T_ID comma T_ID
{
charmap_New($2, $4);
}
;
setcharmap : T_POP_SETCHARMAP T_ID
{
charmap_Set($2);
}
;
pushc : T_POP_PUSHC { charmap_Push(); }
;
popc : T_POP_POPC { charmap_Pop(); }
;
printt : T_POP_PRINTT string printt : T_POP_PRINTT string
{ {
if (nPass == 1)
printf("%s", $2); printf("%s", $2);
} }
; ;
printv : T_POP_PRINTV const printv : T_POP_PRINTV const
{ {
if (nPass == 1)
printf("$%X", constexpr_GetConstantValue(&$2)); printf("$%X", constexpr_GetConstantValue(&$2));
} }
; ;
printi : T_POP_PRINTI const printi : T_POP_PRINTI const
{ {
if (nPass == 1)
printf("%d", constexpr_GetConstantValue(&$2)); printf("%d", constexpr_GetConstantValue(&$2));
} }
; ;
printf : T_POP_PRINTF const printf : T_POP_PRINTF const
{ {
if (nPass == 1)
math_Print(constexpr_GetConstantValue(&$2)); math_Print(constexpr_GetConstantValue(&$2));
} }
; ;
@@ -1126,20 +1248,17 @@ relocconst : T_ID
struct Expression sTemp, sOffset; struct Expression sTemp, sOffset;
rpn_Symbol(&sTemp, $1); rpn_Symbol(&sTemp, $1);
sTemp.nVal = sym_GetValue($1);
rpn_Number(&sOffset, nPCOffset); rpn_Number(&sOffset, nPCOffset);
rpn_SUB(&$$, &sTemp, &sOffset); rpn_SUB(&$$, &sTemp, &sOffset);
} else { } else {
rpn_Symbol(&$$, $1); rpn_Symbol(&$$, $1);
$$.nVal = sym_GetValue($1);
} }
} }
| T_NUMBER | T_NUMBER
{ {
rpn_Number(&$$, $1); rpn_Number(&$$, $1);
$$.nVal = $1;
} }
| string | string
{ {
@@ -1149,7 +1268,6 @@ relocconst : T_ID
free(s); free(s);
rpn_Number(&$$, r); rpn_Number(&$$, r);
$$.nVal = r;
} }
| T_OP_LOGICNOT relocconst %prec NEG { rpn_LOGNOT(&$$, &$2); } | T_OP_LOGICNOT relocconst %prec NEG { rpn_LOGNOT(&$$, &$2); }
| relocconst T_OP_LOGICOR relocconst { rpn_LOGOR(&$$, &$1, &$3); } | relocconst T_OP_LOGICOR relocconst { rpn_LOGOR(&$$, &$1, &$3); }
@@ -1179,12 +1297,10 @@ relocconst : T_ID
{ {
/* '@' is also a T_ID, it is handled here. */ /* '@' is also a T_ID, it is handled here. */
rpn_BankSymbol(&$$, $3); rpn_BankSymbol(&$$, $3);
$$.nVal = 0;
} }
| T_OP_BANK '(' string ')' | T_OP_BANK '(' string ')'
{ {
rpn_BankSection(&$$, $3); rpn_BankSection(&$$, $3);
$$.nVal = 0;
} }
| T_OP_DEF { | T_OP_DEF {
oDontExpandStrings = true; oDontExpandStrings = true;
@@ -1260,7 +1376,7 @@ relocconst : T_ID
else else
rpn_Number(&$$, 0); rpn_Number(&$$, 0);
} }
| T_OP_STRLEN '(' string ')' { rpn_Number(&$$, strlen($3)); } | T_OP_STRLEN '(' string ')' { rpn_Number(&$$, strlenUTF8($3)); }
| '(' relocconst ')' { $$ = $2; } | '(' relocconst ')' { $$ = $2; }
; ;
@@ -1338,7 +1454,7 @@ const : T_ID { constexpr_Symbol(&$$, $1); }
else else
constexpr_Number(&$$, 0); constexpr_Number(&$$, 0);
} }
| T_OP_STRLEN '(' string ')' { constexpr_Number(&$$, strlen($3)); } | T_OP_STRLEN '(' string ')' { constexpr_Number(&$$, strlenUTF8($3)); }
| '(' const ')' { $$ = $2; } | '(' const ')' { $$ = $2; }
; ;
@@ -1349,14 +1465,7 @@ string : T_STRING
} }
| T_OP_STRSUB '(' string comma uconst comma uconst ')' | T_OP_STRSUB '(' string comma uconst comma uconst ')'
{ {
uint32_t len = $7; strsubUTF8($$, $3, $5, $7);
if (len > MAXSTRLEN) {
warning("STRSUB: Length too big: %u", len);
len = MAXSTRLEN;
}
if (snprintf($$, len + 1, "%s", $3 + $5 - 1) > MAXSTRLEN)
warning("STRSUB: String too long '%s'", $$);
} }
| T_OP_STRCAT '(' string comma string ')' | T_OP_STRCAT '(' string comma string ')'
{ {
@@ -1637,7 +1746,6 @@ z80_jp : T_Z80_JP const_16bit
| T_Z80_JP T_MODE_HL_IND | T_Z80_JP T_MODE_HL_IND
{ {
out_AbsByte(0xE9); out_AbsByte(0xE9);
if (nPass == 1)
warning("'JP [HL]' is obsolete, use 'JP HL' instead."); warning("'JP [HL]' is obsolete, use 'JP HL' instead.");
} }
| T_Z80_JP T_MODE_HL | T_Z80_JP T_MODE_HL
@@ -1665,7 +1773,6 @@ z80_ldi : T_Z80_LDI T_MODE_HL_IND comma T_MODE_A
| T_Z80_LDI T_MODE_A comma T_MODE_HL | T_Z80_LDI T_MODE_A comma T_MODE_HL
{ {
out_AbsByte(0x0A | (2 << 4)); out_AbsByte(0x0A | (2 << 4));
if (nPass == 1)
warning("'LDI A,HL' is obsolete, use 'LDI A,[HL]' or 'LD A,[HL+] instead."); warning("'LDI A,HL' is obsolete, use 'LDI A,[HL]' or 'LD A,[HL+] instead.");
} }
| T_Z80_LDI T_MODE_A comma T_MODE_HL_IND | T_Z80_LDI T_MODE_A comma T_MODE_HL_IND
@@ -1681,7 +1788,6 @@ z80_ldd : T_Z80_LDD T_MODE_HL_IND comma T_MODE_A
| T_Z80_LDD T_MODE_A comma T_MODE_HL | T_Z80_LDD T_MODE_A comma T_MODE_HL
{ {
out_AbsByte(0x0A | (3 << 4)); out_AbsByte(0x0A | (3 << 4));
if (nPass == 1)
warning("'LDD A,HL' is obsolete, use 'LDD A,[HL]' or 'LD A,[HL-] instead."); warning("'LDD A,HL' is obsolete, use 'LDD A,[HL]' or 'LD A,[HL-] instead.");
} }
| T_Z80_LDD T_MODE_A comma T_MODE_HL_IND | T_Z80_LDD T_MODE_A comma T_MODE_HL_IND
@@ -1772,6 +1878,7 @@ z80_ld_mem : T_Z80_LD op_mem_ind comma T_MODE_SP
(!rpn_isReloc(&$2)) && ($2.nVal >= 0xFF00)) { (!rpn_isReloc(&$2)) && ($2.nVal >= 0xFF00)) {
out_AbsByte(0xE0); out_AbsByte(0xE0);
out_AbsByte($2.nVal & 0xFF); out_AbsByte($2.nVal & 0xFF);
rpn_Free(&$2);
} else { } else {
out_AbsByte(0xEA); out_AbsByte(0xEA);
out_RelWord(&$2); out_RelWord(&$2);
@@ -1826,12 +1933,14 @@ z80_ld_a : T_Z80_LD reg_r comma T_MODE_C_IND
(!rpn_isReloc(&$4)) && ($4.nVal >= 0xFF00)) { (!rpn_isReloc(&$4)) && ($4.nVal >= 0xFF00)) {
out_AbsByte(0xF0); out_AbsByte(0xF0);
out_AbsByte($4.nVal & 0xFF); out_AbsByte($4.nVal & 0xFF);
rpn_Free(&$4);
} else { } else {
out_AbsByte(0xFA); out_AbsByte(0xFA);
out_RelWord(&$4); out_RelWord(&$4);
} }
} else { } else {
yyerror("Destination operand must be A"); yyerror("Destination operand must be A");
rpn_Free(&$4);
} }
} }
; ;
@@ -1964,6 +2073,7 @@ z80_rst : T_Z80_RST const_8bit
yyerror("Invalid address $%x for RST", $2.nVal); yyerror("Invalid address $%x for RST", $2.nVal);
else else
out_AbsByte(0xC7 | $2.nVal); out_AbsByte(0xC7 | $2.nVal);
rpn_Free(&$2);
} }
; ;

View File

@@ -6,6 +6,7 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
@@ -15,134 +16,281 @@
#include "asm/charmap.h" #include "asm/charmap.h"
#include "asm/main.h" #include "asm/main.h"
#include "asm/output.h" #include "asm/output.h"
#include "asm/util.h"
#include "extern/utf8decoder.h" #define CHARMAP_HASH_SIZE (1 << 9)
struct Charmap globalCharmap = {0}; struct CharmapStackEntry {
struct Charmap *charmap;
struct CharmapStackEntry *next;
};
int32_t readUTF8Char(char *dest, char *src) static struct Charmap *tHashedCharmaps[CHARMAP_HASH_SIZE];
static struct Charmap *mainCharmap;
static struct Charmap *currentCharmap;
struct CharmapStackEntry *charmapStack;
static void warnSectionCharmap(void)
{ {
uint32_t state; static bool warned = false;
uint32_t codep;
int32_t i;
for (i = 0, state = 0;; i++) { if (warned)
if (decode(&state, &codep, (uint8_t)src[i]) == 1) return;
fatalerror("invalid UTF-8 character");
dest[i] = src[i]; warning("Using 'charmap' within a section when the current charmap is 'main' is deprecated");
warned = true;
if (state == 0) {
dest[++i] = '\0';
return i;
} }
static uint32_t charmap_CalcHash(const char *s)
{
return calchash(s) % CHARMAP_HASH_SIZE;
} }
static struct Charmap **charmap_Get(const char *name)
{
struct Charmap **ppCharmap = &tHashedCharmaps[charmap_CalcHash(name)];
while (*ppCharmap != NULL && strcmp((*ppCharmap)->name, name))
ppCharmap = &(*ppCharmap)->next;
return ppCharmap;
}
static void CopyNode(struct Charmap *dest,
const struct Charmap *src,
int nodeIdx)
{
dest->nodes[nodeIdx].code = src->nodes[nodeIdx].code;
dest->nodes[nodeIdx].isCode = src->nodes[nodeIdx].isCode;
for (int i = 0; i < 256; i++)
if (src->nodes[nodeIdx].next[i])
dest->nodes[nodeIdx].next[i] = dest->nodes +
(src->nodes[nodeIdx].next[i] - src->nodes);
}
struct Charmap *charmap_New(const char *name, const char *baseName)
{
struct Charmap *pBase = NULL;
if (baseName != NULL) {
struct Charmap **ppBase = charmap_Get(baseName);
if (*ppBase == NULL) {
yyerror("Base charmap '%s' doesn't exist", baseName);
return NULL;
}
pBase = *ppBase;
}
struct Charmap **ppCharmap = charmap_Get(name);
if (*ppCharmap != NULL) {
yyerror("Charmap '%s' already exists", name);
return NULL;
}
*ppCharmap = calloc(1, sizeof(struct Charmap));
if (*ppCharmap == NULL)
fatalerror("Not enough memory for charmap");
struct Charmap *pCharmap = *ppCharmap;
snprintf(pCharmap->name, sizeof(pCharmap->name), "%s", name);
if (pBase != NULL) {
pCharmap->charCount = pBase->charCount;
pCharmap->nodeCount = pBase->nodeCount;
for (int i = 0; i < MAXCHARNODES; i++)
CopyNode(pCharmap, pBase, i);
}
currentCharmap = pCharmap;
return pCharmap;
}
void charmap_Set(const char *name)
{
struct Charmap **ppCharmap = charmap_Get(name);
if (*ppCharmap == NULL) {
yyerror("Charmap '%s' doesn't exist", name);
return;
}
currentCharmap = *ppCharmap;
}
void charmap_Push(void)
{
struct CharmapStackEntry *stackEntry;
stackEntry = malloc(sizeof(struct CharmapStackEntry));
if (stackEntry == NULL)
fatalerror("No memory for charmap stack");
stackEntry->charmap = currentCharmap;
stackEntry->next = charmapStack;
charmapStack = stackEntry;
}
void charmap_Pop(void)
{
if (charmapStack == NULL)
fatalerror("No entries in the charmap stack");
struct CharmapStackEntry *top = charmapStack;
currentCharmap = top->charmap;
charmapStack = top->next;
free(top);
}
void charmap_InitMain(void)
{
mainCharmap = charmap_New("main", NULL);
} }
int32_t charmap_Add(char *input, uint8_t output) int32_t charmap_Add(char *input, uint8_t output)
{ {
int32_t i; int32_t i;
size_t input_length; uint8_t v;
char temp1i[CHARMAPLENGTH + 1], temp2i[CHARMAPLENGTH + 1];
char temp1o = 0, temp2o = 0;
struct Charmap *charmap; struct Charmap *charmap;
struct Charnode *curr_node, *temp_node;
if (pCurrentSection) { /*
* If the user tries to define a character mapping inside a section
* and the current global charmap is the "main" one, then a local
* section charmap will be created or modified instead of the global
* one. In other words, the local section charmap can override the
* main global one, but not the others.
*/
if (pCurrentSection && currentCharmap == mainCharmap) {
warnSectionCharmap();
if (pCurrentSection->charmap) { if (pCurrentSection->charmap) {
charmap = pCurrentSection->charmap; charmap = pCurrentSection->charmap;
} else { } else {
charmap = calloc(1, sizeof(struct Charmap)); charmap = calloc(1, sizeof(struct Charmap));
if (charmap == NULL) if (charmap == NULL)
fatalerror("Not enough memory for charmap"); fatalerror("Not enough memory for charmap");
pCurrentSection->charmap = charmap; pCurrentSection->charmap = charmap;
} }
} else { } else {
charmap = &globalCharmap; charmap = currentCharmap;
} }
if (nPass == 2) if (charmap->charCount >= MAXCHARMAPS || strlen(input) > CHARMAPLENGTH)
return charmap->count;
if (charmap->count > MAXCHARMAPS || strlen(input) > CHARMAPLENGTH)
return -1; return -1;
input_length = strlen(input); curr_node = &charmap->nodes[0];
if (input_length > 1) {
i = 0; for (i = 0; (v = (uint8_t)input[i]); i++) {
while (i < charmap->count + 1) { if (curr_node->next[v]) {
if (input_length > strlen(charmap->input[i])) { curr_node = curr_node->next[v];
memcpy(temp1i, charmap->input[i],
CHARMAPLENGTH + 1);
memcpy(charmap->input[i], input, input_length);
temp1o = charmap->output[i];
charmap->output[i] = output;
i++;
break;
}
i++;
}
while (i < charmap->count + 1) {
memcpy(temp2i, charmap->input[i], CHARMAPLENGTH + 1);
memcpy(charmap->input[i], temp1i, CHARMAPLENGTH + 1);
memcpy(temp1i, temp2i, CHARMAPLENGTH + 1);
temp2o = charmap->output[i];
charmap->output[i] = temp1o;
temp1o = temp2o;
i++;
}
memcpy(charmap->input[charmap->count + 1], temp1i,
CHARMAPLENGTH + 1);
charmap->output[charmap->count + 1] = temp1o;
} else { } else {
memcpy(charmap->input[charmap->count], input, input_length); temp_node = &charmap->nodes[charmap->nodeCount + 1];
charmap->output[charmap->count] = output;
curr_node->next[v] = temp_node;
curr_node = temp_node;
++charmap->nodeCount;
} }
return ++charmap->count; }
/* prevent duplicated keys by accepting only first key-value pair. */
if (curr_node->isCode)
return charmap->charCount;
curr_node->code = output;
curr_node->isCode = 1;
return ++charmap->charCount;
} }
int32_t charmap_Convert(char **input) int32_t charmap_Convert(char **input)
{ {
struct Charmap *charmap; struct Charmap *charmap;
struct Charnode *charnode;
char outchar[CHARMAPLENGTH + 1]; char *output;
char *buffer; char outchar[8];
int32_t i, j, length;
if (pCurrentSection && pCurrentSection->charmap) int32_t i, match, length;
uint8_t v, foundCode;
/*
* If there is a local section charmap and the current global charmap
* is the "main" one, the local one is used. Otherwise, the global
* one is used. In other words, the local section charmap can override
* the main global one, but not the others.
*/
if (pCurrentSection &&
pCurrentSection->charmap &&
currentCharmap == mainCharmap)
charmap = pCurrentSection->charmap; charmap = pCurrentSection->charmap;
else else
charmap = &globalCharmap; charmap = currentCharmap;
buffer = malloc(strlen(*input)); output = malloc(strlen(*input));
if (buffer == NULL) if (output == NULL)
fatalerror("Not enough memory for buffer"); fatalerror("Not enough memory for buffer");
length = 0; length = 0;
while (**input) { while (**input) {
j = 0; charnode = &charmap->nodes[0];
for (i = 0; i < charmap->count; i++) {
j = strlen(charmap->input[i]); /*
if (memcmp(*input, charmap->input[i], j) == 0) { * find the longest valid match which has been registered in charmap.
outchar[0] = charmap->output[i]; * note that there could be either multiple matches or no match.
outchar[1] = 0; * and it possibly takes the longest match between them,
* which means that it ignores partial matches shorter than the longest one.
*/
for (i = match = 0; (v = (*input)[i]);) {
if (!charnode->next[v])
break; break;
charnode = charnode->next[v];
i++;
if (charnode->isCode) {
match = i;
foundCode = charnode->code;
} }
j = 0;
} }
if (!j) if (match) {
j = readUTF8Char(outchar, *input); output[length] = foundCode;
if (!outchar[0]) { length += 1;
buffer[length++] = 0;
} else { } else {
for (i = 0; outchar[i]; i++) /*
buffer[length++] = outchar[i]; * 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;
} }
*input += j;
length += match;
} }
*input = buffer;
*input += match;
}
*input = output;
return length; return length;
} }

View File

@@ -66,7 +66,7 @@ void constexpr_UnaryOp(struct ConstExpression *expr,
result = value; result = value;
break; break;
case T_OP_SUB: case T_OP_SUB:
result = -value; result = -(uint32_t)value;
break; break;
case T_OP_NOT: case T_OP_NOT:
result = ~value; result = ~value;
@@ -155,10 +155,10 @@ void constexpr_BinaryOp(struct ConstExpression *expr,
result = value1 != value2; result = value1 != value2;
break; break;
case T_OP_ADD: case T_OP_ADD:
result = value1 + value2; result = (uint32_t)value1 + (uint32_t)value2;
break; break;
case T_OP_SUB: case T_OP_SUB:
result = value1 - value2; result = (uint32_t)value1 - (uint32_t)value2;
break; break;
case T_OP_XOR: case T_OP_XOR:
result = value1 ^ value2; result = value1 ^ value2;
@@ -181,7 +181,7 @@ void constexpr_BinaryOp(struct ConstExpression *expr,
fatalerror("Shift by too big value: %d", fatalerror("Shift by too big value: %d",
value2); value2);
result = value1 << value2; result = (uint32_t)value1 << value2;
break; break;
case T_OP_SHR: case T_OP_SHR:
if (value2 < 0) if (value2 < 0)
@@ -194,16 +194,24 @@ void constexpr_BinaryOp(struct ConstExpression *expr,
result = value1 >> value2; result = value1 >> value2;
break; break;
case T_OP_MUL: case T_OP_MUL:
result = value1 * value2; result = (uint32_t)value1 * (uint32_t)value2;
break; break;
case T_OP_DIV: case T_OP_DIV:
if (value2 == 0) if (value2 == 0)
fatalerror("Division by zero"); fatalerror("Division by zero");
if (value1 == INT32_MIN && value2 == -1) {
warning("Division of min value by -1");
result = INT32_MIN;
} else {
result = value1 / value2; result = value1 / value2;
}
break; break;
case T_OP_MOD: case T_OP_MOD:
if (value2 == 0) if (value2 == 0)
fatalerror("Division by zero"); fatalerror("Division by zero");
if (value1 == INT32_MIN && value2 == -1)
result = 0;
else
result = value1 % value2; result = value1 % value2;
break; break;
case T_OP_FDIV: case T_OP_FDIV:

View File

@@ -28,6 +28,8 @@
#include "types.h" #include "types.h"
static struct sContext *pFileStack; static struct sContext *pFileStack;
static unsigned int nFileStackDepth;
unsigned int nMaxRecursionDepth;
static struct sSymbol *pCurrentMacro; static struct sSymbol *pCurrentMacro;
static YY_BUFFER_STATE CurrentFlexHandle; static YY_BUFFER_STATE CurrentFlexHandle;
static FILE *pCurrentFile; static FILE *pCurrentFile;
@@ -40,6 +42,8 @@ static uint32_t nMacroCount;
static char *pCurrentREPTBlock; static char *pCurrentREPTBlock;
static uint32_t nCurrentREPTBlockSize; static uint32_t nCurrentREPTBlockSize;
static uint32_t nCurrentREPTBlockCount; static uint32_t nCurrentREPTBlockCount;
static int32_t nCurrentREPTBodyFirstLine;
static int32_t nCurrentREPTBodyLastLine;
uint32_t ulMacroReturnValue; uint32_t ulMacroReturnValue;
@@ -51,6 +55,8 @@ uint32_t ulMacroReturnValue;
#define STAT_isMacroArg 2 #define STAT_isMacroArg 2
#define STAT_isREPTBlock 3 #define STAT_isREPTBlock 3
/* Max context stack size */
/* /*
* Context push and pop * Context push and pop
*/ */
@@ -58,6 +64,9 @@ static void pushcontext(void)
{ {
struct sContext **ppFileStack; struct sContext **ppFileStack;
if (++nFileStackDepth > nMaxRecursionDepth)
fatalerror("Recursion limit (%d) exceeded", nMaxRecursionDepth);
ppFileStack = &pFileStack; ppFileStack = &pFileStack;
while (*ppFileStack) while (*ppFileStack)
ppFileStack = &((*ppFileStack)->pNext); ppFileStack = &((*ppFileStack)->pNext);
@@ -86,6 +95,8 @@ static void pushcontext(void)
(*ppFileStack)->pREPTBlock = pCurrentREPTBlock; (*ppFileStack)->pREPTBlock = pCurrentREPTBlock;
(*ppFileStack)->nREPTBlockSize = nCurrentREPTBlockSize; (*ppFileStack)->nREPTBlockSize = nCurrentREPTBlockSize;
(*ppFileStack)->nREPTBlockCount = nCurrentREPTBlockCount; (*ppFileStack)->nREPTBlockCount = nCurrentREPTBlockCount;
(*ppFileStack)->nREPTBodyFirstLine = nCurrentREPTBodyFirstLine;
(*ppFileStack)->nREPTBodyLastLine = nCurrentREPTBodyLastLine;
break; break;
default: default:
fatalerror("%s: Internal error.", __func__); fatalerror("%s: Internal error.", __func__);
@@ -100,6 +111,11 @@ static int32_t popcontext(void)
if (nCurrentStatus == STAT_isREPTBlock) { if (nCurrentStatus == STAT_isREPTBlock) {
if (--nCurrentREPTBlockCount) { if (--nCurrentREPTBlockCount) {
char *pREPTIterationWritePtr;
unsigned long nREPTIterationNo;
int nNbCharsWritten;
int nNbCharsLeft;
yy_delete_buffer(CurrentFlexHandle); yy_delete_buffer(CurrentFlexHandle);
CurrentFlexHandle = CurrentFlexHandle =
yy_scan_bytes(pCurrentREPTBlock, yy_scan_bytes(pCurrentREPTBlock,
@@ -108,6 +124,29 @@ static int32_t popcontext(void)
sym_UseCurrentMacroArgs(); sym_UseCurrentMacroArgs();
sym_SetMacroArgID(nMacroCount++); sym_SetMacroArgID(nMacroCount++);
sym_UseNewMacroArgs(); sym_UseNewMacroArgs();
/* 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 0;
} }
} }
@@ -128,13 +167,9 @@ static int32_t popcontext(void)
if (nCurrentStatus == STAT_isInclude) if (nCurrentStatus == STAT_isInclude)
fclose(pCurrentFile); fclose(pCurrentFile);
if (nCurrentStatus == STAT_isMacro) { if (nCurrentStatus == STAT_isMacro
sym_FreeCurrentMacroArgs(); || nCurrentStatus == STAT_isREPTBlock)
nLineNo += 1; nLineNo++;
}
if (nCurrentStatus == STAT_isREPTBlock)
nLineNo += 1;
CurrentFlexHandle = pLastFile->FlexHandle; CurrentFlexHandle = pLastFile->FlexHandle;
strcpy((char *)tzCurrentFileName, (char *)pLastFile->tzFileName); strcpy((char *)tzCurrentFileName, (char *)pLastFile->tzFileName);
@@ -153,11 +188,16 @@ static int32_t popcontext(void)
pCurrentREPTBlock = pLastFile->pREPTBlock; pCurrentREPTBlock = pLastFile->pREPTBlock;
nCurrentREPTBlockSize = pLastFile->nREPTBlockSize; nCurrentREPTBlockSize = pLastFile->nREPTBlockSize;
nCurrentREPTBlockCount = pLastFile->nREPTBlockCount; nCurrentREPTBlockCount = pLastFile->nREPTBlockCount;
nCurrentREPTBodyFirstLine = pLastFile->nREPTBodyFirstLine;
/* + 1 to account for the `ENDR` line */
nLineNo = pLastFile->nREPTBodyLastLine + 1;
break; break;
default: default:
fatalerror("%s: Internal error.", __func__); fatalerror("%s: Internal error.", __func__);
} }
nFileStackDepth--;
free(*ppLastFile); free(*ppLastFile);
*ppLastFile = NULL; *ppLastFile = NULL;
yy_switch_to_buffer(CurrentFlexHandle); yy_switch_to_buffer(CurrentFlexHandle);
@@ -222,6 +262,20 @@ void fstk_Dump(void)
fprintf(stderr, "%s(%d)", tzCurrentFileName, nLineNo); fprintf(stderr, "%s(%d)", tzCurrentFileName, nLineNo);
} }
/*
* Dump the string expansion stack to stderr
*/
void fstk_DumpStringExpansions(void)
{
const struct sStringExpansionPos *pExpansion = pCurrentStringExpansion;
while (pExpansion) {
fprintf(stderr, "while expanding symbol \"%s\"\n",
pExpansion->tzName);
pExpansion = pExpansion->pParent;
}
}
/* /*
* Extra includepath stuff * Extra includepath stuff
*/ */
@@ -317,19 +371,23 @@ void fstk_RunInclude(char *tzFileName)
uint32_t fstk_RunMacro(char *s) uint32_t fstk_RunMacro(char *s)
{ {
struct sSymbol *sym = sym_FindMacro(s); struct sSymbol *sym = sym_FindMacro(s);
int nPrintedChars;
if (sym == NULL) if (sym == NULL || sym->pMacro == NULL)
return 0; return 0;
pushcontext(); pushcontext();
sym_SetMacroArgID(nMacroCount++); sym_SetMacroArgID(nMacroCount++);
nLineNo = -1; /* Minus 1 because there is a newline at the beginning of the buffer */
nLineNo = sym->nFileLine - 1;
sym_UseNewMacroArgs(); sym_UseNewMacroArgs();
nCurrentStatus = STAT_isMacro; nCurrentStatus = STAT_isMacro;
strcpy(tzCurrentFileName, s); nPrintedChars = snprintf(tzCurrentFileName, _MAX_PATH + 1,
"%s::%s", sym->tzFileName, s);
if (sym->pMacro == NULL) if (nPrintedChars > _MAX_PATH) {
return 0; popcontext();
fatalerror("File name + macro name is too large to fit into buffer");
}
pCurrentMacro = sym; pCurrentMacro = sym;
CurrentFlexHandle = yy_scan_bytes(pCurrentMacro->pMacro, CurrentFlexHandle = yy_scan_bytes(pCurrentMacro->pMacro,
@@ -385,17 +443,30 @@ void fstk_RunString(char *s)
/* /*
* Set up a repeat block for parsing * Set up a repeat block for parsing
*/ */
void fstk_RunRept(uint32_t count) void fstk_RunRept(uint32_t count, int32_t nReptLineNo)
{ {
if (count) { if (count) {
pushcontext(); static const char *tzReptStr = "::REPT~1";
/* For error printing to make sense, fake nLineNo */
nCurrentREPTBodyLastLine = nLineNo;
nLineNo = nReptLineNo;
sym_UseCurrentMacroArgs(); sym_UseCurrentMacroArgs();
pushcontext();
sym_SetMacroArgID(nMacroCount++); sym_SetMacroArgID(nMacroCount++);
sym_UseNewMacroArgs(); sym_UseNewMacroArgs();
nCurrentREPTBlockCount = count; nCurrentREPTBlockCount = count;
nCurrentStatus = STAT_isREPTBlock; nCurrentStatus = STAT_isREPTBlock;
nCurrentREPTBlockSize = ulNewMacroSize; nCurrentREPTBlockSize = ulNewMacroSize;
pCurrentREPTBlock = tzNewMacro; pCurrentREPTBlock = tzNewMacro;
nCurrentREPTBodyFirstLine = nReptLineNo + 1;
nLineNo = nReptLineNo;
if (strlen(tzCurrentFileName) + strlen(tzReptStr) > _MAX_PATH)
fatalerror("Cannot append \"%s\" to file path",
tzReptStr);
strcat(tzCurrentFileName, tzReptStr);
CurrentFlexHandle = CurrentFlexHandle =
yy_scan_bytes(pCurrentREPTBlock, nCurrentREPTBlockSize); yy_scan_bytes(pCurrentREPTBlock, nCurrentREPTBlockSize);
yy_switch_to_buffer(CurrentFlexHandle); yy_switch_to_buffer(CurrentFlexHandle);
@@ -405,23 +476,26 @@ void fstk_RunRept(uint32_t count)
/* /*
* Initialize the filestack routines * Initialize the filestack routines
*/ */
void fstk_Init(char *s) void fstk_Init(char *pFileName)
{ {
char tzFileName[_MAX_PATH + 1];
char tzSymFileName[_MAX_PATH + 1 + 2]; char tzSymFileName[_MAX_PATH + 1 + 2];
snprintf(tzSymFileName, sizeof(tzSymFileName), "\"%s\"", s); snprintf(tzSymFileName, sizeof(tzSymFileName), "\"%s\"", pFileName);
sym_AddString("__FILE__", tzSymFileName); sym_AddString("__FILE__", tzSymFileName);
strcpy(tzFileName, s);
pFileStack = NULL; pFileStack = NULL;
pCurrentFile = fopen(tzFileName, "rb"); if (strcmp(pFileName, "-") == 0) {
pCurrentFile = stdin;
} else {
pCurrentFile = fopen(pFileName, "rb");
if (pCurrentFile == NULL) if (pCurrentFile == NULL)
err(1, "Unable to open file '%s'", tzFileName); err(1, "Unable to open file '%s'", pFileName);
}
nFileStackDepth = 0;
nMacroCount = 0; nMacroCount = 0;
nCurrentStatus = STAT_isInclude; nCurrentStatus = STAT_isInclude;
snprintf(tzCurrentFileName, _MAX_PATH + 1, "%s", tzFileName); snprintf(tzCurrentFileName, _MAX_PATH + 1, "%s", pFileName);
CurrentFlexHandle = yy_create_buffer(pCurrentFile); CurrentFlexHandle = yy_create_buffer(pCurrentFile);
yy_switch_to_buffer(CurrentFlexHandle); yy_switch_to_buffer(CurrentFlexHandle);
nLineNo = 1; nLineNo = 1;

View File

@@ -71,8 +71,9 @@ typedef int32_t(*x2bin) (char ch);
static int32_t ascii2bin(char *s) static int32_t ascii2bin(char *s)
{ {
int32_t radix = 10; char *start = s;
int32_t result = 0; uint32_t radix = 10;
uint32_t result = 0;
x2bin convertfunc = char2bin; x2bin convertfunc = char2bin;
switch (*s) { switch (*s) {
@@ -101,6 +102,9 @@ static int32_t ascii2bin(char *s)
break; break;
} }
const uint32_t max_q = UINT32_MAX / radix;
const uint32_t max_r = UINT32_MAX % radix;
if (*s == '\0') { if (*s == '\0') {
/* /*
* There are no digits after the radix prefix * There are no digits after the radix prefix
@@ -108,15 +112,39 @@ static int32_t ascii2bin(char *s)
*/ */
yyerror("Invalid integer constant"); yyerror("Invalid integer constant");
} else if (radix == 4) { } else if (radix == 4) {
int32_t size = 0;
int32_t c; int32_t c;
while (*s != '\0') { while (*s != '\0') {
c = convertfunc(*s++); c = convertfunc(*s++);
result = result * 2 + ((c & 2) << 7) + (c & 1); 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("Graphics constant '%s' is too long",
start);
} }
} else { } else {
while (*s != '\0') bool overflow = false;
result = result * radix + convertfunc(*s++);
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("Integer constant '%s' is too large",
start);
} }
return result; return result;
@@ -124,19 +152,19 @@ static int32_t ascii2bin(char *s)
uint32_t ParseFixedPoint(char *s, uint32_t size) uint32_t ParseFixedPoint(char *s, uint32_t size)
{ {
uint32_t i = 0, dot = 0; uint32_t i;
uint32_t dot = 0;
while (size && dot != 2) { for (i = 0; i < size; i++) {
if (s[i] == '.') if (s[i] == '.') {
dot += 1; dot++;
if (dot < 2) { if (dot == 2)
size -= 1; break;
i += 1;
} }
} }
yyunputbytes(size); yyskipbytes(i);
yylval.nConstValue = (int32_t)(atof(s) * 65536); yylval.nConstValue = (int32_t)(atof(s) * 65536);
@@ -147,66 +175,124 @@ uint32_t ParseNumber(char *s, uint32_t size)
{ {
char dest[256]; char dest[256];
if (size > 255)
fatalerror("Number token too long");
strncpy(dest, s, size); strncpy(dest, s, size);
dest[size] = 0; dest[size] = 0;
yylval.nConstValue = ascii2bin(dest); yylval.nConstValue = ascii2bin(dest);
yyskipbytes(size);
return 1; 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 *AppendMacroArg(char whichArg, char *dest, size_t *destIndex)
{
char *marg;
if (whichArg == '@')
marg = sym_FindMacroArg(-1);
else if (whichArg >= '1' && whichArg <= '9')
marg = sym_FindMacroArg(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) uint32_t ParseSymbol(char *src, uint32_t size)
{ {
char dest[MAXSYMLEN + 1]; char dest[MAXSYMLEN + 1];
int32_t copied = 0, size_backup = size; size_t srcIndex = 0;
size_t destIndex = 0;
char *rest = NULL;
while (size && copied < MAXSYMLEN) { while (srcIndex < size) {
if (*src == '\\') { char ch = src[srcIndex++];
char *marg;
src += 1; if (ch == '\\') {
size -= 1; /*
* 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++];
if (*src == '@') { rest = AppendMacroArg(ch, dest, &destIndex);
marg = sym_FindMacroArg(-1); /* If the symbol's end was in the middle of the token */
} else if (*src >= '0' && *src <= '9') { if (rest)
marg = sym_FindMacroArg(*src - '0'); break;
} else { } else {
fatalerror("Malformed ID"); if (destIndex >= MAXSYMLEN)
return 0;
}
src += 1;
size -= 1;
if (marg) {
while (*marg)
dest[copied++] = *marg++;
}
} else {
dest[copied++] = *src++;
size -= 1;
}
}
if (copied > MAXSYMLEN)
fatalerror("Symbol too long"); fatalerror("Symbol too long");
dest[destIndex++] = ch;
}
}
dest[copied] = 0; 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 && sym_isString(dest)) { if (!oDontExpandStrings && sym_isString(dest)) {
char *s; char *s;
yyskipbytes(size_backup); lex_BeginStringExpansion(dest);
/* Feed the symbol's contents into the buffer */
yyunputstr(s = sym_GetStringValue(dest)); yyunputstr(s = sym_GetStringValue(dest));
/* Lines inserted this way shall not increase nLineNo */
while (*s) { while (*s) {
if (*s++ == '\n') if (*s++ == '\n')
nLineNo -= 1; nLineNo--;
} }
return 0; return 0;
} }
strcpy(yylval.tzString, dest); strcpy(yylval.tzSym, dest);
return 1; return 1;
} }
@@ -221,9 +307,9 @@ uint32_t PutMacroArg(char *src, uint32_t size)
if (s != NULL) if (s != NULL)
yyunputstr(s); yyunputstr(s);
else else
yyerror("Macro argument not defined"); yyerror("Macro argument '\\%c' not defined", src[1]);
} else { } else {
yyerror("Invalid macro argument"); yyerror("Invalid macro argument '\\%c'", src[1]);
} }
return 0; return 0;
} }
@@ -401,6 +487,10 @@ const struct sLexInitString lexer_strings[] = {
{"incbin", T_POP_INCBIN}, {"incbin", T_POP_INCBIN},
{"charmap", T_POP_CHARMAP}, {"charmap", T_POP_CHARMAP},
{"newcharmap", T_POP_NEWCHARMAP},
{"setcharmap", T_POP_SETCHARMAP},
{"pushc", T_POP_PUSHC},
{"popc", T_POP_POPC},
{"fail", T_POP_FAIL}, {"fail", T_POP_FAIL},
{"warn", T_POP_WARN}, {"warn", T_POP_WARN},

View File

@@ -1,7 +1,7 @@
/* /*
* This file is part of RGBDS. * This file is part of RGBDS.
* *
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors. * Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@@ -38,6 +38,8 @@ struct sLexString {
#define SAFETYMARGIN 1024 #define SAFETYMARGIN 1024
#define BOM_SIZE 3
struct sLexFloat tLexFloat[32]; struct sLexFloat tLexFloat[32];
struct sLexString *tLexHash[LEXHASHSIZE]; struct sLexString *tLexHash[LEXHASHSIZE];
YY_BUFFER_STATE pCurrentBuffer; YY_BUFFER_STATE pCurrentBuffer;
@@ -49,6 +51,12 @@ uint32_t tFloatingChars[256];
uint32_t nFloating; uint32_t nFloating;
enum eLexerState lexerstate = LEX_STATE_NORMAL; enum eLexerState lexerstate = LEX_STATE_NORMAL;
struct sStringExpansionPos *pCurrentStringExpansion;
static unsigned int nNbStringExpansions;
/* UTF-8 byte order mark */
static const unsigned char bom[BOM_SIZE] = { 0xEF, 0xBB, 0xBF };
void upperstring(char *s) void upperstring(char *s)
{ {
while (*s) { while (*s) {
@@ -83,17 +91,49 @@ void yyunput(char c)
*(--pLexBuffer) = c; *(--pLexBuffer) = c;
} }
void yyunputstr(char *s) void yyunputstr(const char *s)
{ {
int32_t i, len; int32_t len;
len = strlen(s); len = strlen(s);
if (pLexBuffer - len < pLexBufferRealStart) /*
* It would be undefined behavior to subtract `len` from pLexBuffer and
* potentially have it point outside of pLexBufferRealStart's buffer,
* this is why the check is done this way.
* Refer to https://github.com/rednex/rgbds/pull/411#discussion_r319779797
*/
if (pLexBuffer - pLexBufferRealStart < len)
fatalerror("Buffer safety margin exceeded"); fatalerror("Buffer safety margin exceeded");
for (i = len - 1; i >= 0; i--) pLexBuffer -= len;
*(--pLexBuffer) = s[i];
memcpy(pLexBuffer, s, len);
}
/*
* Marks that a new string expansion with name `tzName` ends here
* Enforces recursion depth
*/
void lex_BeginStringExpansion(const char *tzName)
{
if (++nNbStringExpansions > nMaxRecursionDepth)
fatalerror("Recursion limit (%d) exceeded", nMaxRecursionDepth);
struct sStringExpansionPos *pNewStringExpansion =
malloc(sizeof(*pNewStringExpansion));
char *tzNewExpansionName = strdup(tzName);
if (!pNewStringExpansion || !tzNewExpansionName)
fatalerror("Could not allocate memory to expand '%s'",
tzName);
pNewStringExpansion->tzName = tzNewExpansionName;
pNewStringExpansion->pBuffer = pLexBufferRealStart;
pNewStringExpansion->pBufferPos = pLexBuffer;
pNewStringExpansion->pParent = pCurrentStringExpansion;
pCurrentStringExpansion = pNewStringExpansion;
} }
void yy_switch_to_buffer(YY_BUFFER_STATE buf) void yy_switch_to_buffer(YY_BUFFER_STATE buf)
@@ -118,13 +158,34 @@ void yy_delete_buffer(YY_BUFFER_STATE buf)
* 2. The buffer is terminated with 0 * 2. The buffer is terminated with 0
* 3. nBufferSize is the size without the terminator * 3. nBufferSize is the size without the terminator
*/ */
static void yy_buffer_append(YY_BUFFER_STATE buf, uint32_t capacity, char c) static void yy_buffer_append(YY_BUFFER_STATE buf, size_t capacity, char c)
{ {
assert(buf->pBuffer[buf->nBufferSize] == 0); assert(buf->pBufferStart[buf->nBufferSize] == 0);
assert(buf->nBufferSize + 1 < capacity); assert(buf->nBufferSize + 1 < capacity);
buf->pBuffer[buf->nBufferSize++] = c; buf->pBufferStart[buf->nBufferSize++] = c;
buf->pBuffer[buf->nBufferSize] = 0; buf->pBufferStart[buf->nBufferSize] = 0;
}
static void yy_buffer_append_newlines(YY_BUFFER_STATE buf, size_t capacity)
{
/* Add newline if file doesn't end with one */
if (buf->nBufferSize == 0
|| buf->pBufferStart[buf->nBufferSize - 1] != '\n')
yy_buffer_append(buf, capacity, '\n');
/* Add newline if \ will eat the last newline */
if (buf->nBufferSize >= 2) {
size_t pos = buf->nBufferSize - 2;
/* Skip spaces and tabs */
while (pos > 0 && (buf->pBufferStart[pos] == ' '
|| buf->pBufferStart[pos] == '\t'))
pos--;
if (buf->pBufferStart[pos] == '\\')
yy_buffer_append(buf, capacity, '\n');
}
} }
YY_BUFFER_STATE yy_scan_bytes(char *mem, uint32_t size) YY_BUFFER_STATE yy_scan_bytes(char *mem, uint32_t size)
@@ -134,7 +195,9 @@ YY_BUFFER_STATE yy_scan_bytes(char *mem, uint32_t size)
if (pBuffer == NULL) if (pBuffer == NULL)
fatalerror("%s: Out of memory!", __func__); fatalerror("%s: Out of memory!", __func__);
pBuffer->pBufferRealStart = malloc(size + 1 + SAFETYMARGIN); size_t capacity = size + 3; /* space for 2 newlines and terminator */
pBuffer->pBufferRealStart = malloc(capacity + SAFETYMARGIN);
if (pBuffer->pBufferRealStart == NULL) if (pBuffer->pBufferRealStart == NULL)
fatalerror("%s: Out of memory for buffer!", __func__); fatalerror("%s: Out of memory for buffer!", __func__);
@@ -142,9 +205,10 @@ YY_BUFFER_STATE yy_scan_bytes(char *mem, uint32_t size)
pBuffer->pBufferStart = pBuffer->pBufferRealStart + SAFETYMARGIN; pBuffer->pBufferStart = pBuffer->pBufferRealStart + SAFETYMARGIN;
pBuffer->pBuffer = pBuffer->pBufferRealStart + SAFETYMARGIN; pBuffer->pBuffer = pBuffer->pBufferRealStart + SAFETYMARGIN;
memcpy(pBuffer->pBuffer, mem, size); memcpy(pBuffer->pBuffer, mem, size);
pBuffer->nBufferSize = size;
pBuffer->oAtLineStart = 1;
pBuffer->pBuffer[size] = 0; pBuffer->pBuffer[size] = 0;
pBuffer->nBufferSize = size;
yy_buffer_append_newlines(pBuffer, capacity);
pBuffer->oAtLineStart = 1;
return pBuffer; return pBuffer;
} }
@@ -156,71 +220,119 @@ YY_BUFFER_STATE yy_create_buffer(FILE *f)
if (pBuffer == NULL) if (pBuffer == NULL)
fatalerror("%s: Out of memory!", __func__); fatalerror("%s: Out of memory!", __func__);
uint32_t size; size_t size = 0, capacity = -1;
char *buf = NULL;
/*
* Check if we can get the file size without implementation-defined
* behavior:
*
* From ftell(3p):
* [On error], ftell() and ftello() shall return 1, and set errno to
* indicate the error.
*
* The ftell() and ftello() functions shall fail if: [...]
* ESPIPE The file descriptor underlying stream is associated with a
* pipe, FIFO, or socket.
*
* From fseek(3p):
* The behavior of fseek() on devices which are incapable of seeking
* is implementation-defined.
*/
if (ftell(f) != -1) {
fseek(f, 0, SEEK_END); fseek(f, 0, SEEK_END);
size = ftell(f); capacity = ftell(f);
fseek(f, 0, SEEK_SET); rewind(f);
}
// If ftell errored or the block above wasn't executed
if (capacity == -1)
capacity = 4096;
// Handle 0-byte files gracefully
else if (capacity == 0)
capacity = 1;
while (!feof(f)) {
if (buf == NULL || size >= capacity) {
if (buf)
capacity *= 2;
/* Give extra room for 2 newlines and terminator */ /* Give extra room for 2 newlines and terminator */
uint32_t capacity = size + 3; buf = realloc(buf, capacity + SAFETYMARGIN + 3);
pBuffer->pBufferRealStart = malloc(capacity + SAFETYMARGIN); if (buf == NULL)
fatalerror("%s: Out of memory for buffer!",
__func__);
}
if (pBuffer->pBufferRealStart == NULL) char *bufpos = buf + SAFETYMARGIN + size;
fatalerror("%s: Out of memory for buffer!", __func__); size_t read_count = fread(bufpos, 1, capacity - size, f);
pBuffer->pBufferStart = pBuffer->pBufferRealStart + SAFETYMARGIN; if (read_count == 0 && !feof(f))
pBuffer->pBuffer = pBuffer->pBufferRealStart + SAFETYMARGIN; fatalerror("%s: fread error", __func__);
size = fread(pBuffer->pBuffer, sizeof(uint8_t), size, f); size += read_count;
}
pBuffer->pBufferRealStart = buf;
pBuffer->pBufferStart = buf + SAFETYMARGIN;
pBuffer->pBuffer = buf + SAFETYMARGIN;
pBuffer->pBuffer[size] = 0; pBuffer->pBuffer[size] = 0;
pBuffer->nBufferSize = size; pBuffer->nBufferSize = size;
/* This is added here to make the buffer scaling above easy to express,
* while taking the newline space into account
* for the yy_buffer_append_newlines() call below.
*/
capacity += 3;
/* Skip UTF-8 byte order mark. */
if (pBuffer->nBufferSize >= BOM_SIZE
&& !memcmp(pBuffer->pBuffer, bom, BOM_SIZE))
pBuffer->pBuffer += BOM_SIZE;
/* Convert all line endings to LF and spaces */ /* Convert all line endings to LF and spaces */
char *mem = pBuffer->pBuffer; char *mem = pBuffer->pBuffer;
uint32_t instring = 0; int32_t lineCount = 0;
while (*mem) { while (*mem) {
if (*mem == '\"')
instring = 1 - instring;
if ((mem[0] == '\\') && (mem[1] == '\"' || mem[1] == '\\')) { if ((mem[0] == '\\') && (mem[1] == '\"' || mem[1] == '\\')) {
mem += 2; mem += 2;
} else if (instring) {
mem += 1;
} else { } else {
/* LF CR and CR LF */ /* LF CR and CR LF */
if (((mem[0] == 10) && (mem[1] == 13)) if (((mem[0] == '\n') && (mem[1] == '\r'))
|| ((mem[0] == 13) && (mem[1] == 10))) { || ((mem[0] == '\r') && (mem[1] == '\n'))) {
mem[0] = ' '; *mem++ = ' ';
mem[1] = '\n'; *mem++ = '\n';
mem += 2; lineCount++;
/* LF and CR */ /* LF and CR */
} else if ((mem[0] == 10) || (mem[0] == 13)) { } else if ((mem[0] == '\n') || (mem[0] == '\r')) {
mem[0] = '\n'; *mem++ = '\n';
mem += 1; lineCount++;
} else { } else {
mem += 1; mem++;
} }
} }
} }
if (mem != pBuffer->pBuffer + size) {
nLineNo = lineCount + 1;
fatalerror("Found null character");
}
/* Remove comments */ /* Remove comments */
mem = pBuffer->pBuffer; mem = pBuffer->pBuffer;
instring = 0; bool instring = false;
while (*mem) { while (*mem) {
if (*mem == '\"') if (*mem == '\"')
instring = 1 - instring; instring = !instring;
if ((mem[0] == '\\') && (mem[1] == '\"' || mem[1] == '\\')) { if ((mem[0] == '\\') && (mem[1] == '\"' || mem[1] == '\\')) {
mem += 2; mem += 2;
} else if (instring) { } else if (instring) {
mem += 1; mem++;
} else { } else {
/* Comments that start with ; anywhere in a line */ /* Comments that start with ; anywhere in a line */
if (*mem == ';') { if (*mem == ';') {
@@ -228,31 +340,16 @@ YY_BUFFER_STATE yy_create_buffer(FILE *f)
*mem++ = ' '; *mem++ = ' ';
/* Comments that start with * at the start of a line */ /* Comments that start with * at the start of a line */
} else if ((mem[0] == '\n') && (mem[1] == '*')) { } else if ((mem[0] == '\n') && (mem[1] == '*')) {
mem += 1; mem++;
while (!((*mem == '\n') || (*mem == '\0'))) while (!((*mem == '\n') || (*mem == '\0')))
*mem++ = ' '; *mem++ = ' ';
} else { } else {
mem += 1; mem++;
} }
} }
} }
/* Add newline if file doesn't end with one */ yy_buffer_append_newlines(pBuffer, capacity);
if (size == 0 || pBuffer->pBuffer[size - 1] != '\n')
yy_buffer_append(pBuffer, capacity, '\n');
/* Add newline if \ will eat the last newline */
if (pBuffer->nBufferSize >= 2) {
size_t pos = pBuffer->nBufferSize - 2;
/* Skip spaces */
while (pos > 0 && pBuffer->pBuffer[pos] == ' ')
pos--;
if (pBuffer->pBuffer[pos] == '\\')
yy_buffer_append(pBuffer, capacity, '\n');
}
pBuffer->oAtLineStart = 1; pBuffer->oAtLineStart = 1;
return pBuffer; return pBuffer;
} }
@@ -376,6 +473,9 @@ void lex_Init(void)
nLexMaxLength = 0; nLexMaxLength = 0;
nFloating = 0; nFloating = 0;
pCurrentStringExpansion = NULL;
nNbStringExpansions = 0;
} }
void lex_AddStrings(const struct sLexInitString *lex) void lex_AddStrings(const struct sLexInitString *lex)
@@ -428,17 +528,17 @@ void yylex_GetFloatMaskAndFloatLen(uint32_t *pnFloatMask, uint32_t *pnFloatLen)
char *s = pLexBuffer; char *s = pLexBuffer;
uint32_t nOldFloatMask = 0; uint32_t nOldFloatMask = 0;
uint32_t nFloatMask = tFloatingFirstChar[(int32_t)*s]; uint32_t nFloatMask = tFloatingFirstChar[(uint8_t)*s];
if (nFloatMask != 0) { if (nFloatMask != 0) {
s++; s++;
nOldFloatMask = nFloatMask; nOldFloatMask = nFloatMask;
nFloatMask &= tFloatingSecondChar[(int32_t)*s]; nFloatMask &= tFloatingSecondChar[(uint8_t)*s];
while (nFloatMask != 0) { while (nFloatMask != 0) {
s++; s++;
nOldFloatMask = nFloatMask; nOldFloatMask = nFloatMask;
nFloatMask &= tFloatingChars[(int32_t)*s]; nFloatMask &= tFloatingChars[(uint8_t)*s];
} }
} }
@@ -552,6 +652,7 @@ size_t yylex_ReadBracketedSymbol(char *dest, size_t index)
char ch; char ch;
size_t i = 0; size_t i = 0;
size_t length, maxLength; size_t length, maxLength;
const char *mode = NULL;
for (ch = *pLexBuffer; for (ch = *pLexBuffer;
ch != '}' && ch != '"' && ch != '\n'; ch != '}' && ch != '"' && ch != '\n';
@@ -565,16 +666,42 @@ size_t yylex_ReadBracketedSymbol(char *dest, size_t index)
i += length; i += length;
else else
fatalerror("Illegal character escape '%c'", ch); fatalerror("Illegal character escape '%c'", ch);
} else if (ch == ':' && !mode) { /* Only grab 1st colon */
/* Use a whitelist of modes, which does prevent the
* use of some features such as precision,
* but also avoids a security flaw
*/
const char *acceptedModes = "bxXd";
/* Binary isn't natively supported,
* so it's handled differently
*/
static const char * const formatSpecifiers[] = {
"", "%x", "%X", "%d"
};
/* Prevent reading out of bounds! */
const char *designatedMode;
if (i != 1)
fatalerror("Print types are exactly 1 character long");
designatedMode = strchr(acceptedModes, sym[i - 1]);
if (!designatedMode)
fatalerror("Illegal print type '%c'",
sym[i - 1]);
mode = formatSpecifiers[designatedMode - acceptedModes];
/* Begin writing the symbol again */
i = 0;
} else { } else {
yylex_SymbolWriteChar(sym, i++, ch); yylex_SymbolWriteChar(sym, i++, ch);
} }
} }
/* Properly terminate the string */
yylex_SymbolWriteChar(sym, i, 0); yylex_SymbolWriteChar(sym, i, 0);
/* It's assumed we're writing to a T_STRING */ /* It's assumed we're writing to a T_STRING */
maxLength = MAXSTRLEN - index; maxLength = MAXSTRLEN - index;
length = symvaluetostring(&dest[index], maxLength, sym); length = symvaluetostring(&dest[index], maxLength, sym, mode);
if (*pLexBuffer == '}') if (*pLexBuffer == '}')
pLexBuffer++; pLexBuffer++;
@@ -681,10 +808,10 @@ scanagain:
* endings: "\r\n" is replaced by " \n" before the lexer has the * endings: "\r\n" is replaced by " \n" before the lexer has the
* opportunity to see it. * opportunity to see it.
*/ */
if (pLexBuffer[1] == ' ') { if (pLexBuffer[1] == ' ' || pLexBuffer[1] == '\t') {
pLexBuffer += 2; pLexBuffer += 2;
while (1) { while (1) {
if (*pLexBuffer == ' ') { if (*pLexBuffer == ' ' || *pLexBuffer == '\t') {
pLexBuffer++; pLexBuffer++;
} else if (*pLexBuffer == '\n') { } else if (*pLexBuffer == '\n') {
pLexBuffer++; pLexBuffer++;
@@ -731,7 +858,9 @@ scanagain:
return T_STRING; return T_STRING;
} else if (*pLexBuffer == '{') { } else if (*pLexBuffer == '{') {
pLexBuffer++; pLexBuffer++;
yylex_ReadBracketedSymbol(yylval.tzString, 0); size_t len = yylex_ReadBracketedSymbol(yylval.tzString,
0);
yylval.tzString[len] = 0;
return T_STRING; return T_STRING;
} }
@@ -740,10 +869,22 @@ scanagain:
* numeric literal, string, or bracketed symbol, so just return * numeric literal, string, or bracketed symbol, so just return
* the ASCII character. * the ASCII character.
*/ */
if (*pLexBuffer == '\n') unsigned char ch = *pLexBuffer++;
if (ch == '\n')
AtLineStart = 1; AtLineStart = 1;
return *pLexBuffer++; /*
* Check for invalid unprintable characters.
* They may not be readily apparent in a text editor,
* so this is useful for identifying encoding problems.
*/
if (ch != 0
&& ch != '\n'
&& !(ch >= 0x20 && ch <= 0x7E))
fatalerror("Found garbage character: 0x%02X", ch);
return ch;
} }
if (pLongestFixed == NULL || nFloatLen > pLongestFixed->nNameLength) { if (pLongestFixed == NULL || nFloatLen > pLongestFixed->nNameLength) {
@@ -760,8 +901,6 @@ scanagain:
goto scanagain; goto scanagain;
} }
pLexBuffer += nFloatLen;
if (token->nToken == T_ID && linestart) if (token->nToken == T_ID && linestart)
return T_LABEL; return T_LABEL;
else else
@@ -811,6 +950,7 @@ static uint32_t yylex_MACROARGS(void)
ch = '}'; ch = '}';
break; break;
case ' ': case ' ':
case '\t':
/* /*
* Look for line continuation character after a * Look for line continuation character after a
* series of spaces. This is also useful for * series of spaces. This is also useful for
@@ -819,7 +959,8 @@ static uint32_t yylex_MACROARGS(void)
* opportunity to see it. * opportunity to see it.
*/ */
while (1) { while (1) {
if (*pLexBuffer == ' ') { if (*pLexBuffer == ' '
|| *pLexBuffer == '\t') {
pLexBuffer++; pLexBuffer++;
} else if (*pLexBuffer == '\n') { } else if (*pLexBuffer == '\n') {
pLexBuffer++; pLexBuffer++;
@@ -879,14 +1020,32 @@ static uint32_t yylex_MACROARGS(void)
fatalerror("Internal error in %s", __func__); fatalerror("Internal error in %s", __func__);
} }
uint32_t yylex(void) int yylex(void)
{ {
int returnedChar;
switch (lexerstate) { switch (lexerstate) {
case LEX_STATE_NORMAL: case LEX_STATE_NORMAL:
return yylex_NORMAL(); returnedChar = yylex_NORMAL();
break;
case LEX_STATE_MACROARGS: case LEX_STATE_MACROARGS:
return yylex_MACROARGS(); returnedChar = yylex_MACROARGS();
break;
default: default:
fatalerror("%s: Internal error.", __func__); fatalerror("%s: Internal error.", __func__);
} }
/* Check if string expansions were fully read */
while (pCurrentStringExpansion
&& pCurrentStringExpansion->pBuffer == pLexBufferRealStart
&& pCurrentStringExpansion->pBufferPos <= pLexBuffer) {
struct sStringExpansionPos *pParent =
pCurrentStringExpansion->pParent;
free(pCurrentStringExpansion->tzName);
free(pCurrentStringExpansion);
pCurrentStringExpansion = pParent;
nNbStringExpansions--;
}
return returnedChar;
} }

View File

@@ -21,6 +21,7 @@
#include "asm/lexer.h" #include "asm/lexer.h"
#include "asm/output.h" #include "asm/output.h"
#include "asm/main.h" #include "asm/main.h"
#include "asm/charmap.h"
#include "extern/err.h" #include "extern/err.h"
@@ -37,7 +38,7 @@ char **cldefines;
clock_t nStartClock, nEndClock; clock_t nStartClock, nEndClock;
int32_t nLineNo; int32_t nLineNo;
uint32_t nTotalLines, nPass, nPC, nIFDepth, nUnionDepth, nErrors; uint32_t nTotalLines, nPC, nIFDepth, nUnionDepth, nErrors;
bool skipElif; bool skipElif;
uint32_t unionStart[128], unionSize[128]; uint32_t unionStart[128], unionSize[128];
@@ -238,12 +239,13 @@ static void opt_ParseDefines(void)
*/ */
void verror(const char *fmt, va_list args) void verror(const char *fmt, va_list args)
{ {
fprintf(stderr, "ERROR: "); fputs("ERROR: ", stderr);
fstk_Dump(); fstk_Dump();
fprintf(stderr, ":\n "); fputs(":\n ", stderr);
vfprintf(stderr, fmt, args); vfprintf(stderr, fmt, args);
fprintf(stderr, "\n"); fputc('\n', stderr);
nErrors += 1; fstk_DumpStringExpansions();
nErrors++;
} }
void yyerror(const char *fmt, ...) void yyerror(const char *fmt, ...)
@@ -275,11 +277,12 @@ void warning(const char *fmt, ...)
va_start(args, fmt); va_start(args, fmt);
fprintf(stderr, "warning: "); fputs("warning: ", stderr);
fstk_Dump(); fstk_Dump();
fprintf(stderr, ":\n "); fputs(":\n ", stderr);
vfprintf(stderr, fmt, args); vfprintf(stderr, fmt, args);
fprintf(stderr, "\n"); fputc('\n', stderr);
fstk_DumpStringExpansions();
va_end(args); va_end(args);
} }
@@ -288,7 +291,8 @@ static void print_usage(void)
{ {
printf( printf(
"usage: rgbasm [-EhLVvw] [-b chars] [-Dname[=value]] [-g chars] [-i path]\n" "usage: rgbasm [-EhLVvw] [-b chars] [-Dname[=value]] [-g chars] [-i path]\n"
" [-M dependfile] [-o outfile] [-p pad_value] file.asm\n"); " [-M dependfile] [-o outfile] [-p pad_value]\n"
" [-r recursion_depth] file.asm\n");
exit(1); exit(1);
} }
@@ -315,6 +319,8 @@ int main(int argc, char *argv[])
/* yydebug=1; */ /* yydebug=1; */
nMaxRecursionDepth = 64;
DefaultOptions.gbgfx[0] = '0'; DefaultOptions.gbgfx[0] = '0';
DefaultOptions.gbgfx[1] = '1'; DefaultOptions.gbgfx[1] = '1';
DefaultOptions.gbgfx[2] = '2'; DefaultOptions.gbgfx[2] = '2';
@@ -332,7 +338,7 @@ int main(int argc, char *argv[])
newopt = CurrentOptions; newopt = CurrentOptions;
while ((ch = getopt(argc, argv, "b:D:Eg:hi:LM:o:p:Vvw")) != -1) { while ((ch = getopt(argc, argv, "b:D:Eg:hi:LM:o:p:r:Vvw")) != -1) {
switch (ch) { switch (ch) {
case 'b': case 'b':
if (strlen(optarg) == 2) { if (strlen(optarg) == 2) {
@@ -386,6 +392,12 @@ int main(int argc, char *argv[])
errx(1, "Argument for option 'p' must be between 0 and 0xFF"); errx(1, "Argument for option 'p' must be between 0 and 0xFF");
break; break;
case 'r':
nMaxRecursionDepth = strtoul(optarg, &ep, 0);
if (optarg[0] == '\0' || *ep != '\0')
errx(1, "Invalid argument for option 'r'");
break;
case 'V': case 'V':
printf("rgbasm %s\n", get_package_version_string()); printf("rgbasm %s\n", get_package_version_string());
exit(0); exit(0);
@@ -432,21 +444,18 @@ int main(int argc, char *argv[])
skipElif = true; skipElif = true;
nUnionDepth = 0; nUnionDepth = 0;
nPC = 0; nPC = 0;
nPass = 1;
nErrors = 0; nErrors = 0;
sym_PrepPass1(); sym_Init();
sym_SetExportAll(CurrentOptions.exportall); sym_SetExportAll(CurrentOptions.exportall);
fstk_Init(tzMainfile); fstk_Init(tzMainfile);
opt_ParseDefines(); opt_ParseDefines();
charmap_InitMain();
if (CurrentOptions.verbose)
printf("Pass 1...\n");
yy_set_state(LEX_STATE_NORMAL); yy_set_state(LEX_STATE_NORMAL);
opt_SetCurrentOptions(&DefaultOptions); opt_SetCurrentOptions(&DefaultOptions);
if (yyparse() != 0 || nErrors != 0) if (yyparse() != 0 || nErrors != 0)
errx(1, "Assembly aborted in pass 1 (%ld errors)!", nErrors); errx(1, "Assembly aborted (%ld errors)!", nErrors);
if (nIFDepth != 0) if (nIFDepth != 0)
errx(1, "Unterminated IF construct (%ld levels)!", nIFDepth); errx(1, "Unterminated IF construct (%ld levels)!", nIFDepth);
@@ -456,27 +465,6 @@ int main(int argc, char *argv[])
nUnionDepth); nUnionDepth);
} }
nTotalLines = 0;
nLineNo = 1;
nIFDepth = 0;
skipElif = true;
nUnionDepth = 0;
nPC = 0;
nPass = 2;
nErrors = 0;
sym_PrepPass2();
out_PrepPass2();
fstk_Init(tzMainfile);
yy_set_state(LEX_STATE_NORMAL);
opt_SetCurrentOptions(&DefaultOptions);
opt_ParseDefines();
if (CurrentOptions.verbose)
printf("Pass 2...\n");
if (yyparse() != 0 || nErrors != 0)
errx(1, "Assembly aborted in pass 2 (%ld errors)!", nErrors);
double timespent; double timespent;
nEndClock = clock(); nEndClock = clock();
@@ -491,6 +479,10 @@ int main(int argc, char *argv[])
printf("(%d lines/minute)\n", printf("(%d lines/minute)\n",
(int)(60 / timespent * nTotalLines)); (int)(60 / timespent * nTotalLines));
} }
out_CheckErrors();
/* If no path specified, don't write file */
if (tzObjectname != NULL)
out_WriteObject(); out_WriteObject();
return 0; return 0;
} }

View File

@@ -1,7 +1,7 @@
/* /*
* This file is part of RGBDS. * This file is part of RGBDS.
* *
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors. * Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@@ -10,6 +10,7 @@
* Outputs an objectfile * Outputs an objectfile
*/ */
#include <assert.h>
#include <errno.h> #include <errno.h>
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
@@ -259,37 +260,37 @@ static void writesection(struct Section *pSect, FILE *f)
*/ */
static void writesymbol(struct sSymbol *pSym, FILE *f) static void writesymbol(struct sSymbol *pSym, FILE *f)
{ {
char symname[MAXSYMLEN * 2 + 1];
uint32_t type; uint32_t type;
uint32_t offset; uint32_t offset;
int32_t sectid; int32_t sectid;
if (pSym->nType & SYMF_IMPORT) { if (!(pSym->nType & SYMF_DEFINED)) {
/* Symbol should be imported */ type = SYM_IMPORT;
strcpy(symname, pSym->tzName); } else if (pSym->nType & SYMF_EXPORT) {
type = SYM_EXPORT;
} else {
type = SYM_LOCAL;
}
switch (type) {
case SYM_LOCAL:
offset = pSym->nValue;
sectid = getsectid(pSym->pSection);
break;
case SYM_IMPORT:
offset = 0; offset = 0;
sectid = -1; sectid = -1;
type = SYM_IMPORT; break;
} else { case SYM_EXPORT:
strcpy(symname, pSym->tzName);
if (pSym->nType & SYMF_EXPORT) {
/* Symbol should be exported */
type = SYM_EXPORT;
offset = pSym->nValue; offset = pSym->nValue;
if (pSym->nType & SYMF_CONST) if (pSym->nType & SYMF_CONST)
sectid = -1; sectid = -1;
else else
sectid = getsectid(pSym->pSection); sectid = getsectid(pSym->pSection);
} else { break;
/* Symbol is local to this file */
type = SYM_LOCAL;
offset = pSym->nValue;
sectid = getsectid(pSym->pSection);
}
} }
fputstring(symname, f); fputstring(pSym->tzName, f);
fputc(type, f); fputc(type, f);
if (type != SYM_IMPORT) { if (type != SYM_IMPORT) {
@@ -311,7 +312,7 @@ static uint32_t addsymbol(struct sSymbol *pSym)
struct PatchSymbol *pPSym, **ppPSym; struct PatchSymbol *pPSym, **ppPSym;
uint32_t hash; uint32_t hash;
hash = calchash(pSym->tzName); hash = sym_CalcHash(pSym->tzName);
ppPSym = &(tHashedPatchSymbols[hash]); ppPSym = &(tHashedPatchSymbols[hash]);
while ((*ppPSym) != NULL) { while ((*ppPSym) != NULL) {
@@ -383,10 +384,15 @@ void createpatch(uint32_t type, struct Expression *expr)
{ {
struct Patch *pPatch; struct Patch *pPatch;
uint16_t rpndata; uint16_t rpndata;
uint8_t rpnexpr[2048]; uint8_t *rpnexpr;
char tzSym[512]; char tzSym[512];
uint32_t rpnptr = 0, symptr; uint32_t rpnptr = 0, symptr;
rpnexpr = malloc(expr->nRPNPatchSize);
if (rpnexpr == NULL)
fatalerror("No memory for patch RPN expression");
pPatch = allocpatch(); pPatch = allocpatch();
pPatch->nType = type; pPatch->nType = type;
strcpy(pPatch->tzFilename, tzCurrentFileName); strcpy(pPatch->tzFilename, tzCurrentFileName);
@@ -468,12 +474,11 @@ void createpatch(uint32_t type, struct Expression *expr)
} }
} }
pPatch->pRPN = malloc(rpnptr); assert(rpnptr == expr->nRPNPatchSize);
if (pPatch->pRPN != NULL) {
memcpy(pPatch->pRPN, rpnexpr, rpnptr); pPatch->pRPN = rpnexpr;
pPatch->nRPNSize = rpnptr; pPatch->nRPNSize = rpnptr;
} }
}
/* /*
* A quick check to see if we have an initialized section * A quick check to see if we have an initialized section
@@ -507,8 +512,9 @@ static void checksectionoverflow(uint32_t delta_size)
{ {
uint32_t maxsize = getmaxsectionsize(pCurrentSection->nType, uint32_t maxsize = getmaxsectionsize(pCurrentSection->nType,
pCurrentSection->pzName); pCurrentSection->pzName);
uint32_t new_size = pCurrentSection->nPC + delta_size;
if (pCurrentSection->nPC + delta_size > maxsize) { if (new_size > maxsize) {
/* /*
* This check is here to trap broken code that generates * This check is here to trap broken code that generates
* sections that are too big and to prevent the assembler from * sections that are too big and to prevent the assembler from
@@ -516,8 +522,36 @@ static void checksectionoverflow(uint32_t delta_size)
* memory. * memory.
* The real check must be done at the linking stage. * The real check must be done at the linking stage.
*/ */
fatalerror("Section '%s' is too big (max size = 0x%X bytes).", fatalerror("Section '%s' is too big (max size = 0x%X bytes, reached 0x%X).",
pCurrentSection->pzName, maxsize); pCurrentSection->pzName, maxsize, new_size);
}
}
/*
* Check for errors that could happen while writing an object file
* This is important as out_WriteObject is skipped entirely when `-o` is omitted
* Therefore, errors such as memory allocations still should be handled in
* out_WriteObject and not here
*/
void out_CheckErrors(void)
{
/* Local symbols cannot be imported from elsewhere */
struct PatchSymbol *pSym = pPatchSymbols;
while (pSym) {
struct sSymbol *pSymbol = pSym->pSymbol;
if (!(pSymbol->nType & SYMF_DEFINED)
&& pSymbol->nType & SYMF_LOCAL) {
char *name = pSymbol->tzName;
char *localPtr = strchr(name, '.');
if (localPtr)
name = localPtr;
errx(1, "%s(%u) : '%s' not defined",
pSymbol->tzFileName, pSymbol->nFileLine, name);
}
pSym = pSym->pNext;
} }
} }
@@ -527,20 +561,15 @@ static void checksectionoverflow(uint32_t delta_size)
void out_WriteObject(void) void out_WriteObject(void)
{ {
FILE *f; FILE *f;
struct PatchSymbol *pSym;
struct Section *pSect;
addexports(); addexports();
/* If no path specified, don't write file */
if (tzObjectname == NULL)
return;
f = fopen(tzObjectname, "wb"); f = fopen(tzObjectname, "wb");
if (f == NULL) if (f == NULL)
fatalerror("Couldn't write file '%s'\n", tzObjectname); fatalerror("Couldn't write file '%s'\n", tzObjectname);
struct PatchSymbol *pSym;
struct Section *pSect;
fwrite(RGBDS_OBJECT_VERSION_STRING, 1, fwrite(RGBDS_OBJECT_VERSION_STRING, 1,
strlen(RGBDS_OBJECT_VERSION_STRING), f); strlen(RGBDS_OBJECT_VERSION_STRING), f);
@@ -562,22 +591,6 @@ void out_WriteObject(void)
fclose(f); fclose(f);
} }
/*
* Prepare for pass #2
*/
void out_PrepPass2(void)
{
struct Section *pSect;
pSect = pSectionList;
while (pSect) {
pSect->nPC = 0;
pSect = pSect->pNext;
}
pCurrentSection = NULL;
pSectionStack = NULL;
}
/* /*
* Set the objectfilename * Set the objectfilename
*/ */
@@ -586,10 +599,6 @@ void out_SetFileName(char *s)
tzObjectname = s; tzObjectname = s;
if (CurrentOptions.verbose) if (CurrentOptions.verbose)
printf("Output filename %s\n", s); printf("Output filename %s\n", s);
pSectionList = NULL;
pCurrentSection = NULL;
pPatchSymbols = NULL;
} }
/* /*
@@ -636,7 +645,6 @@ struct Section *out_FindSection(char *pzName, uint32_t secttype, int32_t org,
pSect->pNext = NULL; pSect->pNext = NULL;
pSect->pPatches = NULL; pSect->pPatches = NULL;
pSect->charmap = NULL; pSect->charmap = NULL;
pPatchSymbols = NULL;
/* It is only needed to allocate memory for ROM sections. */ /* It is only needed to allocate memory for ROM sections. */
if (secttype == SECT_ROM0 || secttype == SECT_ROMX) { if (secttype == SECT_ROM0 || secttype == SECT_ROMX) {
@@ -662,7 +670,7 @@ void out_SetCurrentSection(struct Section *pSect)
fatalerror("Cannot change the section within a UNION"); fatalerror("Cannot change the section within a UNION");
pCurrentSection = pSect; pCurrentSection = pSect;
nPC = pSect->nPC; nPC = (pSect != NULL) ? pSect->nPC : 0;
pPCSymbol->nValue = nPC; pPCSymbol->nValue = nPC;
pPCSymbol->pSection = pCurrentSection; pPCSymbol->pSection = pCurrentSection;
@@ -705,9 +713,7 @@ void out_AbsByteBypassCheck(int32_t b)
{ {
checksectionoverflow(1); checksectionoverflow(1);
b &= 0xFF; b &= 0xFF;
if (nPass == 2)
pCurrentSection->tData[nPC] = b; pCurrentSection->tData[nPC] = b;
pCurrentSection->nPC += 1; pCurrentSection->nPC += 1;
nPC += 1; nPC += 1;
pPCSymbol->nValue += 1; pPCSymbol->nValue += 1;
@@ -772,17 +778,15 @@ void out_RelByte(struct Expression *expr)
checkcodesection(); checkcodesection();
checksectionoverflow(1); checksectionoverflow(1);
if (rpn_isReloc(expr)) { if (rpn_isReloc(expr)) {
if (nPass == 2) {
pCurrentSection->tData[nPC] = 0; pCurrentSection->tData[nPC] = 0;
createpatch(PATCH_BYTE, expr); createpatch(PATCH_BYTE, expr);
}
pCurrentSection->nPC += 1; pCurrentSection->nPC += 1;
nPC += 1; nPC += 1;
pPCSymbol->nValue += 1; pPCSymbol->nValue += 1;
} else { } else {
out_AbsByte(expr->nVal); out_AbsByte(expr->nVal);
} }
rpn_Reset(expr); rpn_Free(expr);
} }
/* /*
@@ -793,10 +797,8 @@ void out_AbsWord(int32_t b)
checkcodesection(); checkcodesection();
checksectionoverflow(2); checksectionoverflow(2);
b &= 0xFFFF; b &= 0xFFFF;
if (nPass == 2) {
pCurrentSection->tData[nPC] = b & 0xFF; pCurrentSection->tData[nPC] = b & 0xFF;
pCurrentSection->tData[nPC + 1] = b >> 8; pCurrentSection->tData[nPC + 1] = b >> 8;
}
pCurrentSection->nPC += 2; pCurrentSection->nPC += 2;
nPC += 2; nPC += 2;
pPCSymbol->nValue += 2; pPCSymbol->nValue += 2;
@@ -808,24 +810,19 @@ void out_AbsWord(int32_t b)
*/ */
void out_RelWord(struct Expression *expr) void out_RelWord(struct Expression *expr)
{ {
uint32_t b;
checkcodesection(); checkcodesection();
checksectionoverflow(2); checksectionoverflow(2);
b = expr->nVal & 0xFFFF;
if (rpn_isReloc(expr)) { if (rpn_isReloc(expr)) {
if (nPass == 2) { pCurrentSection->tData[nPC] = 0;
pCurrentSection->tData[nPC] = b & 0xFF; pCurrentSection->tData[nPC + 1] = 0;
pCurrentSection->tData[nPC + 1] = b >> 8;
createpatch(PATCH_WORD_L, expr); createpatch(PATCH_WORD_L, expr);
}
pCurrentSection->nPC += 2; pCurrentSection->nPC += 2;
nPC += 2; nPC += 2;
pPCSymbol->nValue += 2; pPCSymbol->nValue += 2;
} else { } else {
out_AbsWord(expr->nVal); out_AbsWord(expr->nVal);
} }
rpn_Reset(expr); rpn_Free(expr);
} }
/* /*
@@ -835,12 +832,10 @@ void out_AbsLong(int32_t b)
{ {
checkcodesection(); checkcodesection();
checksectionoverflow(sizeof(int32_t)); checksectionoverflow(sizeof(int32_t));
if (nPass == 2) {
pCurrentSection->tData[nPC] = b & 0xFF; pCurrentSection->tData[nPC] = b & 0xFF;
pCurrentSection->tData[nPC + 1] = b >> 8; pCurrentSection->tData[nPC + 1] = b >> 8;
pCurrentSection->tData[nPC + 2] = b >> 16; pCurrentSection->tData[nPC + 2] = b >> 16;
pCurrentSection->tData[nPC + 3] = b >> 24; pCurrentSection->tData[nPC + 3] = b >> 24;
}
pCurrentSection->nPC += 4; pCurrentSection->nPC += 4;
nPC += 4; nPC += 4;
pPCSymbol->nValue += 4; pPCSymbol->nValue += 4;
@@ -852,26 +847,21 @@ void out_AbsLong(int32_t b)
*/ */
void out_RelLong(struct Expression *expr) void out_RelLong(struct Expression *expr)
{ {
int32_t b;
checkcodesection(); checkcodesection();
checksectionoverflow(4); checksectionoverflow(4);
b = expr->nVal;
if (rpn_isReloc(expr)) { if (rpn_isReloc(expr)) {
if (nPass == 2) { pCurrentSection->tData[nPC] = 0;
pCurrentSection->tData[nPC] = b & 0xFF; pCurrentSection->tData[nPC + 1] = 0;
pCurrentSection->tData[nPC + 1] = b >> 8; pCurrentSection->tData[nPC + 2] = 0;
pCurrentSection->tData[nPC + 2] = b >> 16; pCurrentSection->tData[nPC + 3] = 0;
pCurrentSection->tData[nPC + 3] = b >> 24;
createpatch(PATCH_LONG_L, expr); createpatch(PATCH_LONG_L, expr);
}
pCurrentSection->nPC += 4; pCurrentSection->nPC += 4;
nPC += 4; nPC += 4;
pPCSymbol->nValue += 4; pPCSymbol->nValue += 4;
} else { } else {
out_AbsLong(expr->nVal); out_AbsLong(expr->nVal);
} }
rpn_Reset(expr); rpn_Free(expr);
} }
/* /*
@@ -884,15 +874,13 @@ void out_PCRelByte(struct Expression *expr)
checksectionoverflow(1); checksectionoverflow(1);
/* Always let the linker calculate the offset. */ /* Always let the linker calculate the offset. */
if (nPass == 2) {
pCurrentSection->tData[nPC] = 0; pCurrentSection->tData[nPC] = 0;
createpatch(PATCH_BYTE_JR, expr); createpatch(PATCH_BYTE_JR, expr);
}
pCurrentSection->nPC += 1; pCurrentSection->nPC += 1;
nPC += 1; nPC += 1;
pPCSymbol->nValue += 1; pPCSymbol->nValue += 1;
rpn_Reset(expr); rpn_Free(expr);
} }
/* /*
@@ -915,13 +903,12 @@ void out_BinaryFile(char *s)
checkcodesection(); checkcodesection();
checksectionoverflow(fsize); checksectionoverflow(fsize);
if (nPass == 2) {
int32_t dest = nPC; int32_t dest = nPC;
int32_t todo = fsize; int32_t todo = fsize;
while (todo--) while (todo--)
pCurrentSection->tData[dest++] = fgetc(f); pCurrentSection->tData[dest++] = fgetc(f);
}
pCurrentSection->nPC += fsize; pCurrentSection->nPC += fsize;
nPC += fsize; nPC += fsize;
pPCSymbol->nValue += fsize; pPCSymbol->nValue += fsize;
@@ -958,13 +945,12 @@ void out_BinaryFileSlice(char *s, int32_t start_pos, int32_t length)
checkcodesection(); checkcodesection();
checksectionoverflow(length); checksectionoverflow(length);
if (nPass == 2) {
int32_t dest = nPC; int32_t dest = nPC;
int32_t todo = length; int32_t todo = length;
while (todo--) while (todo--)
pCurrentSection->tData[dest++] = fgetc(f); pCurrentSection->tData[dest++] = fgetc(f);
}
pCurrentSection->nPC += length; pCurrentSection->nPC += length;
nPC += length; nPC += length;
pPCSymbol->nValue += length; pPCSymbol->nValue += length;

View File

@@ -1,11 +1,11 @@
.\" .\"
.\" This file is part of RGBDS. .\" This file is part of RGBDS.
.\" .\"
.\" Copyright (c) 2010-2018, Anthony J. Bentley and RGBDS contributors. .\" Copyright (c) 2010-2019, Anthony J. Bentley and RGBDS contributors.
.\" .\"
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd February 24, 2018 .Dd July 8, 2019
.Dt RGBASM 1 .Dt RGBASM 1
.Os RGBDS Manual .Os RGBDS Manual
.Sh NAME .Sh NAME
@@ -21,11 +21,18 @@
.Op Fl M Ar dependfile .Op Fl M Ar dependfile
.Op Fl o Ar outfile .Op Fl o Ar outfile
.Op Fl p Ar pad_value .Op Fl p Ar pad_value
.Op Fl r Ar recursion_depth
.Ar file .Ar file
.Sh DESCRIPTION .Sh DESCRIPTION
The The
.Nm .Nm
program creates an object file from an assembly source file. program creates an object file from an assembly source file.
The input
.Ar file
can be a file path, or
.Cm \-
denoting
.Cm stdin .
Its arguments are as follows: Its arguments are as follows:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Fl b Ar chars .It Fl b Ar chars
@@ -71,6 +78,8 @@ Write an object file to the given filename.
.It Fl p Ar pad_value .It Fl p Ar pad_value
When padding an image, pad with this value. When padding an image, pad with this value.
The default is 0x00. The default is 0x00.
.It Fl r Ar recursion_depth
Specifies the recursion depth at which RGBASM will assume being in an infinite loop.
.It Fl V .It Fl V
Print the version of the program and exit. Print the version of the program and exit.
.It Fl v .It Fl v
@@ -79,12 +88,20 @@ Be verbose.
Disable warning output. Disable warning output.
.El .El
.Sh EXAMPLES .Sh EXAMPLES
Assembling a basic source file is simple: You can assemble a source file in two ways.
Straight forward way:
.Pp .Pp
.Bd -literal -offset indent .Bd -literal -offset indent
$ rgbasm -o bar.o foo.asm $ rgbasm -o bar.o foo.asm
.Ed .Ed
.Pp .Pp
Pipes way:
.Pp
.Bd -literal -offset indent
$ cat foo.asm | rgbasm -o bar.o -
$ rgbasm -o bar.o - < foo.asm
.Ed
.Pp
The resulting object file is not yet a usable ROM image \(em it must first be The resulting object file is not yet a usable ROM image \(em it must first be
run through run through
.Xr rgblink 1 .Xr rgblink 1

View File

@@ -455,8 +455,7 @@ String equates can't be exported or imported.
.Sy Important note : .Sy Important note :
An EQUS can be expanded to a string that contains another EQUS An EQUS can be expanded to a string that contains another EQUS
and it will be expanded as well. and it will be expanded as well.
This means that, if you aren't careful, you may trap the assembler into an If this creates an infinite loop, RGBASM will error out once a certain depth is reached. See the -r command-line option.
infinite loop if there's a circular dependency in the expansions.
Also, a MACRO can have inside an EQUS which references the same MACRO, which has Also, a MACRO can have inside an EQUS which references the same MACRO, which has
the same problem. the same problem.
.Pp .Pp
@@ -584,16 +583,15 @@ If you want to use the rest, you need to use the keyword
.Ic SHIFT . .Ic SHIFT .
.Pp .Pp
Line continuations work as usual inside macros or lists of arguments of macros. Line continuations work as usual inside macros or lists of arguments of macros.
Strings, however, are a bit trickier. However, some characters need to be escaped, as in the following example:
The following example shows how to use strings as arguments for a macro:
.Pp .Pp
.Bd -literal -offset indent .Bd -literal -offset indent
PrintMacro : MACRO PrintMacro : MACRO
PRINTT \[rs]1 PRINTT \[rs]1
ENDM ENDM
PrintMacro STRCAT(\[rs]"Hello\[rs]"\[rs], \[rs] PrintMacro STRCAT("Hello"\[rs], \[rs]
\[rs]" world\[rs]\[rs]n\[rs]") " world\[rs]\[rs]n")
.Ed .Ed
.Pp .Pp
.Ic SHIFT .Ic SHIFT
@@ -1079,7 +1077,20 @@ within a string.
This will examine the type of the symbol and insert its value accordingly. This will examine the type of the symbol and insert its value accordingly.
If symbol is a string symbol, the symbols value is simply copied. If symbol is a string symbol, the symbols value is simply copied.
If it's a numeric symbol, the value is converted to hexadecimal notation and If it's a numeric symbol, the value is converted to hexadecimal notation and
inserted as a string. inserted as a string with a dollar prepended.
.Pp
It's possible to change the way numeric symbols are converted by specifying
a print type like so:
.Sy {d:symbol}
Valid print types are:
.Bl -column -offset indent
.It Sy Print type Ta Sy Format Ta Sy Example
.It Li d Ta Decimal Ta 42
.It Li x Ta Lowercase hexadecimal Ta 2a
.It Li X Ta Uppercase hexadecimal Ta 2A
.It Li b Ta Binary Ta 101010
.Pp
Note that print types should only be used with numeric values, not strings.
.Pp .Pp
HINT: The HINT: The
.Sy {symbol} .Sy {symbol}
@@ -1127,9 +1138,30 @@ CHARMAP "&iacute", 20
CHARMAP "A", 128 CHARMAP "A", 128
.Ed .Ed
.Pp .Pp
It is possible to create multiple character maps and then switch between them
as desired. This can be used to encode debug information in ASCII and use
a different encoding for other purposes, for example. Initially, there is
one character map called
.Sy main
and it is automatically selected as the current character map from the
beginning. There is also a character map stack that can be used to save and
restore which character map is currently active.
.Bl -column "NEWCHARMAP name, basename"
.It Sy Command Ta Sy Meaning
.It Ic NEWCHARMAP Ar name Ta Creates a new, empty character map called
.Ic name .
.It Ic NEWCHARMAP Ar name , basename Ta Creates a new character map called
. Ic name ,
copied from character map
.Ic basename .
.It Ic SETCHARMAP Ar name Ta Switch to character map Ic name .
.It Ic PUSHC Ta Push the current character map onto the stack.
.It Ic POPC Ta Pop a character map off the stack and switch to it.
.El
.Pp
.Sy Note: .Sy Note:
Character maps affect all strings in the file from the point in which they are Character maps affect all strings in the file from the point in which they are
defined. defined, until switching to a different character map.
This means that any string that the code may want to print as debug information This means that any string that the code may want to print as debug information
will also be affected by it. will also be affected by it.
.Pp .Pp

View File

@@ -10,6 +10,7 @@
* Controls RPN expressions for objectfiles * Controls RPN expressions for objectfiles
*/ */
#include <assert.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@@ -24,12 +25,39 @@
void mergetwoexpressions(struct Expression *expr, const struct Expression *src1, void mergetwoexpressions(struct Expression *expr, const struct Expression *src1,
const struct Expression *src2) const struct Expression *src2)
{ {
*expr = *src1; assert(src1->tRPN != NULL && src2->tRPN != NULL);
memcpy(&(expr->tRPN[expr->nRPNLength]), src2->tRPN, src2->nRPNLength);
expr->nRPNLength += src2->nRPNLength; if (src1->nRPNLength + src2->nRPNLength > MAXRPNLEN)
expr->isReloc |= src2->isReloc; fatalerror("RPN expression is too large");
expr->isPCRel |= src2->isPCRel;
uint32_t len = src1->nRPNLength + src2->nRPNLength;
expr->nVal = 0;
expr->tRPN = src1->tRPN;
if (src1->nRPNCapacity >= len) {
expr->nRPNCapacity = src1->nRPNCapacity;
} else {
uint32_t cap1 = src1->nRPNCapacity;
uint32_t cap2 = src2->nRPNCapacity;
uint32_t cap = (cap1 > cap2) ? cap1 : cap2;
if (len > cap)
cap = (cap <= MAXRPNLEN / 2) ? cap * 2 : MAXRPNLEN;
expr->nRPNCapacity = cap;
expr->tRPN = realloc(expr->tRPN, expr->nRPNCapacity);
if (expr->tRPN == NULL)
fatalerror("No memory for RPN expression");
}
memcpy(expr->tRPN + src1->nRPNLength, src2->tRPN, src2->nRPNLength);
free(src2->tRPN);
expr->nRPNLength = len;
expr->nRPNPatchSize = src1->nRPNPatchSize + src2->nRPNPatchSize;
expr->nRPNOut = 0;
expr->isReloc = src1->isReloc || src2->isReloc;
} }
#define joinexpr() mergetwoexpressions(expr, src1, src2) #define joinexpr() mergetwoexpressions(expr, src1, src2)
@@ -37,20 +65,46 @@ void mergetwoexpressions(struct Expression *expr, const struct Expression *src1,
/* /*
* Add a byte to the RPN expression * Add a byte to the RPN expression
*/ */
void pushbyte(struct Expression *expr, int b) void pushbyte(struct Expression *expr, uint8_t b)
{ {
expr->tRPN[expr->nRPNLength++] = b & 0xFF; if (expr->nRPNLength == expr->nRPNCapacity) {
if (expr->nRPNCapacity == 0)
expr->nRPNCapacity = 256;
else if (expr->nRPNCapacity == MAXRPNLEN)
fatalerror("RPN expression is too large");
else if (expr->nRPNCapacity > MAXRPNLEN / 2)
expr->nRPNCapacity = MAXRPNLEN;
else
expr->nRPNCapacity *= 2;
expr->tRPN = realloc(expr->tRPN, expr->nRPNCapacity);
if (expr->tRPN == NULL)
fatalerror("No memory for RPN expression");
}
expr->tRPN[expr->nRPNLength++] = b;
} }
/* /*
* Reset the RPN module * Init the RPN expression
*/ */
void rpn_Reset(struct Expression *expr) void rpn_Init(struct Expression *expr)
{ {
expr->tRPN = NULL;
expr->nRPNCapacity = 0;
expr->nRPNLength = 0; expr->nRPNLength = 0;
expr->nRPNPatchSize = 0;
expr->nRPNOut = 0; expr->nRPNOut = 0;
expr->isReloc = 0; expr->isReloc = 0;
expr->isPCRel = 0; }
/*
* Free the RPN expression
*/
void rpn_Free(struct Expression *expr)
{
free(expr->tRPN);
rpn_Init(expr);
} }
/* /*
@@ -72,45 +126,32 @@ uint32_t rpn_isReloc(const struct Expression *expr)
return expr->isReloc; return expr->isReloc;
} }
/*
* Determine if the current expression can be pc-relative
*/
uint32_t rpn_isPCRelative(const struct Expression *expr)
{
return expr->isPCRel;
}
/* /*
* Add symbols, constants and operators to expression * Add symbols, constants and operators to expression
*/ */
void rpn_Number(struct Expression *expr, uint32_t i) void rpn_Number(struct Expression *expr, uint32_t i)
{ {
rpn_Reset(expr); rpn_Init(expr);
pushbyte(expr, RPN_CONST); pushbyte(expr, RPN_CONST);
pushbyte(expr, i); pushbyte(expr, i);
pushbyte(expr, i >> 8); pushbyte(expr, i >> 8);
pushbyte(expr, i >> 16); pushbyte(expr, i >> 16);
pushbyte(expr, i >> 24); pushbyte(expr, i >> 24);
expr->nVal = i; expr->nVal = i;
expr->nRPNPatchSize += 5;
} }
void rpn_Symbol(struct Expression *expr, char *tzSym) void rpn_Symbol(struct Expression *expr, char *tzSym)
{ {
if (!sym_isConstant(tzSym)) { if (!sym_isConstant(tzSym)) {
const struct sSymbol *psym; rpn_Init(expr);
sym_Ref(tzSym);
rpn_Reset(expr);
psym = sym_FindSymbol(tzSym);
if (psym == NULL || psym->pSection == pCurrentSection
|| psym->pSection == NULL)
expr->isPCRel = 1;
expr->isReloc = 1; expr->isReloc = 1;
pushbyte(expr, RPN_SYM); pushbyte(expr, RPN_SYM);
while (*tzSym) while (*tzSym)
pushbyte(expr, *tzSym++); pushbyte(expr, *tzSym++);
pushbyte(expr, 0); pushbyte(expr, 0);
expr->nRPNPatchSize += 5;
} else { } else {
rpn_Number(expr, sym_GetConstantValue(tzSym)); rpn_Number(expr, sym_GetConstantValue(tzSym));
} }
@@ -118,7 +159,7 @@ void rpn_Symbol(struct Expression *expr, char *tzSym)
void rpn_BankSelf(struct Expression *expr) void rpn_BankSelf(struct Expression *expr)
{ {
rpn_Reset(expr); rpn_Init(expr);
/* /*
* This symbol is not really relocatable, but this makes the assembler * This symbol is not really relocatable, but this makes the assembler
@@ -127,6 +168,7 @@ void rpn_BankSelf(struct Expression *expr)
expr->isReloc = 1; expr->isReloc = 1;
pushbyte(expr, RPN_BANK_SELF); pushbyte(expr, RPN_BANK_SELF);
expr->nRPNPatchSize++;
} }
void rpn_BankSymbol(struct Expression *expr, char *tzSym) void rpn_BankSymbol(struct Expression *expr, char *tzSym)
@@ -138,19 +180,14 @@ void rpn_BankSymbol(struct Expression *expr, char *tzSym)
} }
if (!sym_isConstant(tzSym)) { if (!sym_isConstant(tzSym)) {
rpn_Reset(expr); rpn_Init(expr);
sym_Ref(tzSym);
/*
* Check that the symbol exists by evaluating and discarding the
* value.
*/
sym_GetValue(tzSym);
expr->isReloc = 1; expr->isReloc = 1;
pushbyte(expr, RPN_BANK_SYM); pushbyte(expr, RPN_BANK_SYM);
while (*tzSym) while (*tzSym)
pushbyte(expr, *tzSym++); pushbyte(expr, *tzSym++);
pushbyte(expr, 0); pushbyte(expr, 0);
expr->nRPNPatchSize += 5;
} else { } else {
yyerror("BANK argument must be a relocatable identifier"); yyerror("BANK argument must be a relocatable identifier");
} }
@@ -158,7 +195,7 @@ void rpn_BankSymbol(struct Expression *expr, char *tzSym)
void rpn_BankSection(struct Expression *expr, char *tzSectionName) void rpn_BankSection(struct Expression *expr, char *tzSectionName)
{ {
rpn_Reset(expr); rpn_Init(expr);
/* /*
* This symbol is not really relocatable, but this makes the assembler * This symbol is not really relocatable, but this makes the assembler
@@ -167,37 +204,48 @@ void rpn_BankSection(struct Expression *expr, char *tzSectionName)
expr->isReloc = 1; expr->isReloc = 1;
pushbyte(expr, RPN_BANK_SECT); pushbyte(expr, RPN_BANK_SECT);
while (*tzSectionName) expr->nRPNPatchSize++;
while (*tzSectionName) {
pushbyte(expr, *tzSectionName++); pushbyte(expr, *tzSectionName++);
expr->nRPNPatchSize++;
}
pushbyte(expr, 0); pushbyte(expr, 0);
expr->nRPNPatchSize++;
} }
void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src) void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src)
{ {
*expr = *src; *expr = *src;
pushbyte(expr, RPN_HRAM); pushbyte(expr, RPN_HRAM);
expr->nRPNPatchSize++;
} }
void rpn_LOGNOT(struct Expression *expr, const struct Expression *src) void rpn_LOGNOT(struct Expression *expr, const struct Expression *src)
{ {
*expr = *src; *expr = *src;
expr->nVal = !expr->nVal;
pushbyte(expr, RPN_LOGUNNOT); pushbyte(expr, RPN_LOGUNNOT);
expr->nRPNPatchSize++;
} }
void rpn_LOGOR(struct Expression *expr, const struct Expression *src1, void rpn_LOGOR(struct Expression *expr, const struct Expression *src1,
const struct Expression *src2) const struct Expression *src2)
{ {
joinexpr(); joinexpr();
expr->nVal = (expr->nVal || src2->nVal); expr->nVal = (src1->nVal || src2->nVal);
pushbyte(expr, RPN_LOGOR); pushbyte(expr, RPN_LOGOR);
expr->nRPNPatchSize++;
} }
void rpn_LOGAND(struct Expression *expr, const struct Expression *src1, void rpn_LOGAND(struct Expression *expr, const struct Expression *src1,
const struct Expression *src2) const struct Expression *src2)
{ {
joinexpr(); joinexpr();
expr->nVal = (expr->nVal && src2->nVal); expr->nVal = (src1->nVal && src2->nVal);
pushbyte(expr, RPN_LOGAND); pushbyte(expr, RPN_LOGAND);
expr->nRPNPatchSize++;
} }
void rpn_HIGH(struct Expression *expr, const struct Expression *src) void rpn_HIGH(struct Expression *expr, const struct Expression *src)
@@ -221,6 +269,8 @@ void rpn_HIGH(struct Expression *expr, const struct Expression *src)
pushbyte(expr, 0); pushbyte(expr, 0);
pushbyte(expr, RPN_AND); pushbyte(expr, RPN_AND);
expr->nRPNPatchSize += 12;
} }
void rpn_LOW(struct Expression *expr, const struct Expression *src) void rpn_LOW(struct Expression *expr, const struct Expression *src)
@@ -236,94 +286,107 @@ void rpn_LOW(struct Expression *expr, const struct Expression *src)
pushbyte(expr, 0); pushbyte(expr, 0);
pushbyte(expr, RPN_AND); pushbyte(expr, RPN_AND);
expr->nRPNPatchSize += 6;
} }
void rpn_LOGEQU(struct Expression *expr, const struct Expression *src1, void rpn_LOGEQU(struct Expression *expr, const struct Expression *src1,
const struct Expression *src2) const struct Expression *src2)
{ {
joinexpr(); joinexpr();
expr->nVal = (expr->nVal == src2->nVal); expr->nVal = (src1->nVal == src2->nVal);
pushbyte(expr, RPN_LOGEQ); pushbyte(expr, RPN_LOGEQ);
expr->nRPNPatchSize++;
} }
void rpn_LOGGT(struct Expression *expr, const struct Expression *src1, void rpn_LOGGT(struct Expression *expr, const struct Expression *src1,
const struct Expression *src2) const struct Expression *src2)
{ {
joinexpr(); joinexpr();
expr->nVal = (expr->nVal > src2->nVal); expr->nVal = (src1->nVal > src2->nVal);
pushbyte(expr, RPN_LOGGT); pushbyte(expr, RPN_LOGGT);
expr->nRPNPatchSize++;
} }
void rpn_LOGLT(struct Expression *expr, const struct Expression *src1, void rpn_LOGLT(struct Expression *expr, const struct Expression *src1,
const struct Expression *src2) const struct Expression *src2)
{ {
joinexpr(); joinexpr();
expr->nVal = (expr->nVal < src2->nVal); expr->nVal = (src1->nVal < src2->nVal);
pushbyte(expr, RPN_LOGLT); pushbyte(expr, RPN_LOGLT);
expr->nRPNPatchSize++;
} }
void rpn_LOGGE(struct Expression *expr, const struct Expression *src1, void rpn_LOGGE(struct Expression *expr, const struct Expression *src1,
const struct Expression *src2) const struct Expression *src2)
{ {
joinexpr(); joinexpr();
expr->nVal = (expr->nVal >= src2->nVal); expr->nVal = (src1->nVal >= src2->nVal);
pushbyte(expr, RPN_LOGGE); pushbyte(expr, RPN_LOGGE);
expr->nRPNPatchSize++;
} }
void rpn_LOGLE(struct Expression *expr, const struct Expression *src1, void rpn_LOGLE(struct Expression *expr, const struct Expression *src1,
const struct Expression *src2) const struct Expression *src2)
{ {
joinexpr(); joinexpr();
expr->nVal = (expr->nVal <= src2->nVal); expr->nVal = (src1->nVal <= src2->nVal);
pushbyte(expr, RPN_LOGLE); pushbyte(expr, RPN_LOGLE);
expr->nRPNPatchSize++;
} }
void rpn_LOGNE(struct Expression *expr, const struct Expression *src1, void rpn_LOGNE(struct Expression *expr, const struct Expression *src1,
const struct Expression *src2) const struct Expression *src2)
{ {
joinexpr(); joinexpr();
expr->nVal = (expr->nVal != src2->nVal); expr->nVal = (src1->nVal != src2->nVal);
pushbyte(expr, RPN_LOGNE); pushbyte(expr, RPN_LOGNE);
expr->nRPNPatchSize++;
} }
void rpn_ADD(struct Expression *expr, const struct Expression *src1, void rpn_ADD(struct Expression *expr, const struct Expression *src1,
const struct Expression *src2) const struct Expression *src2)
{ {
joinexpr(); joinexpr();
expr->nVal = (expr->nVal + src2->nVal); expr->nVal = ((uint32_t)src1->nVal + (uint32_t)src2->nVal);
pushbyte(expr, RPN_ADD); pushbyte(expr, RPN_ADD);
expr->nRPNPatchSize++;
} }
void rpn_SUB(struct Expression *expr, const struct Expression *src1, void rpn_SUB(struct Expression *expr, const struct Expression *src1,
const struct Expression *src2) const struct Expression *src2)
{ {
joinexpr(); joinexpr();
expr->nVal = (expr->nVal - src2->nVal); expr->nVal = ((uint32_t)src1->nVal - (uint32_t)src2->nVal);
pushbyte(expr, RPN_SUB); pushbyte(expr, RPN_SUB);
expr->nRPNPatchSize++;
} }
void rpn_XOR(struct Expression *expr, const struct Expression *src1, void rpn_XOR(struct Expression *expr, const struct Expression *src1,
const struct Expression *src2) const struct Expression *src2)
{ {
joinexpr(); joinexpr();
expr->nVal = (expr->nVal ^ src2->nVal); expr->nVal = (src1->nVal ^ src2->nVal);
pushbyte(expr, RPN_XOR); pushbyte(expr, RPN_XOR);
expr->nRPNPatchSize++;
} }
void rpn_OR(struct Expression *expr, const struct Expression *src1, void rpn_OR(struct Expression *expr, const struct Expression *src1,
const struct Expression *src2) const struct Expression *src2)
{ {
joinexpr(); joinexpr();
expr->nVal = (expr->nVal | src2->nVal); expr->nVal = (src1->nVal | src2->nVal);
pushbyte(expr, RPN_OR); pushbyte(expr, RPN_OR);
expr->nRPNPatchSize++;
} }
void rpn_AND(struct Expression *expr, const struct Expression *src1, void rpn_AND(struct Expression *expr, const struct Expression *src1,
const struct Expression *src2) const struct Expression *src2)
{ {
joinexpr(); joinexpr();
expr->nVal = (expr->nVal & src2->nVal); expr->nVal = (src1->nVal & src2->nVal);
pushbyte(expr, RPN_AND); pushbyte(expr, RPN_AND);
expr->nRPNPatchSize++;
} }
void rpn_SHL(struct Expression *expr, const struct Expression *src1, void rpn_SHL(struct Expression *expr, const struct Expression *src1,
@@ -331,6 +394,7 @@ void rpn_SHL(struct Expression *expr, const struct Expression *src1,
{ {
joinexpr(); joinexpr();
if (!expr->isReloc) {
if (src1->nVal < 0) if (src1->nVal < 0)
warning("Left shift of negative value: %d", src1->nVal); warning("Left shift of negative value: %d", src1->nVal);
@@ -339,58 +403,86 @@ void rpn_SHL(struct Expression *expr, const struct Expression *src1,
else if (src2->nVal >= 32) else if (src2->nVal >= 32)
fatalerror("Shift by too big value: %d", src2->nVal); fatalerror("Shift by too big value: %d", src2->nVal);
expr->nVal = (expr->nVal << src2->nVal); expr->nVal = ((uint32_t)src1->nVal << src2->nVal);
}
pushbyte(expr, RPN_SHL); pushbyte(expr, RPN_SHL);
expr->nRPNPatchSize++;
} }
void rpn_SHR(struct Expression *expr, const struct Expression *src1, void rpn_SHR(struct Expression *expr, const struct Expression *src1,
const struct Expression *src2) const struct Expression *src2)
{ {
joinexpr(); joinexpr();
if (!expr->isReloc) {
if (src2->nVal < 0) if (src2->nVal < 0)
fatalerror("Shift by negative value: %d", src2->nVal); fatalerror("Shift by negative value: %d", src2->nVal);
else if (src2->nVal >= 32) else if (src2->nVal >= 32)
fatalerror("Shift by too big value: %d", src2->nVal); fatalerror("Shift by too big value: %d", src2->nVal);
expr->nVal = (expr->nVal >> src2->nVal); expr->nVal = (src1->nVal >> src2->nVal);
}
pushbyte(expr, RPN_SHR); pushbyte(expr, RPN_SHR);
expr->nRPNPatchSize++;
} }
void rpn_MUL(struct Expression *expr, const struct Expression *src1, void rpn_MUL(struct Expression *expr, const struct Expression *src1,
const struct Expression *src2) const struct Expression *src2)
{ {
joinexpr(); joinexpr();
expr->nVal = (expr->nVal * src2->nVal); expr->nVal = ((uint32_t)src1->nVal * (uint32_t)src2->nVal);
pushbyte(expr, RPN_MUL); pushbyte(expr, RPN_MUL);
expr->nRPNPatchSize++;
} }
void rpn_DIV(struct Expression *expr, const struct Expression *src1, void rpn_DIV(struct Expression *expr, const struct Expression *src1,
const struct Expression *src2) const struct Expression *src2)
{ {
joinexpr(); joinexpr();
if (!expr->isReloc) {
if (src2->nVal == 0) if (src2->nVal == 0)
fatalerror("Division by zero"); fatalerror("Division by zero");
expr->nVal = (expr->nVal / src2->nVal); if (src1->nVal == INT32_MIN && src2->nVal == -1) {
warning("Division of min value by -1");
expr->nVal = INT32_MIN;
} else {
expr->nVal = (src1->nVal / src2->nVal);
}
}
pushbyte(expr, RPN_DIV); pushbyte(expr, RPN_DIV);
expr->nRPNPatchSize++;
} }
void rpn_MOD(struct Expression *expr, const struct Expression *src1, void rpn_MOD(struct Expression *expr, const struct Expression *src1,
const struct Expression *src2) const struct Expression *src2)
{ {
joinexpr(); joinexpr();
if (!expr->isReloc) {
if (src2->nVal == 0) if (src2->nVal == 0)
fatalerror("Division by zero"); fatalerror("Division by zero");
expr->nVal = (expr->nVal % src2->nVal); if (src1->nVal == INT32_MIN && src2->nVal == -1)
expr->nVal = 0;
else
expr->nVal = (src1->nVal % src2->nVal);
}
pushbyte(expr, RPN_MOD); pushbyte(expr, RPN_MOD);
expr->nRPNPatchSize++;
} }
void rpn_UNNEG(struct Expression *expr, const struct Expression *src) void rpn_UNNEG(struct Expression *expr, const struct Expression *src)
{ {
*expr = *src; *expr = *src;
expr->nVal = -expr->nVal; expr->nVal = -(uint32_t)expr->nVal;
pushbyte(expr, RPN_UNSUB); pushbyte(expr, RPN_UNSUB);
expr->nRPNPatchSize++;
} }
void rpn_UNNOT(struct Expression *expr, const struct Expression *src) void rpn_UNNOT(struct Expression *expr, const struct Expression *src)
@@ -398,4 +490,5 @@ void rpn_UNNOT(struct Expression *expr, const struct Expression *src)
*expr = *src; *expr = *src;
expr->nVal = ~expr->nVal; expr->nVal = ~expr->nVal;
pushbyte(expr, RPN_UNNOT); pushbyte(expr, RPN_UNNOT);
expr->nRPNPatchSize++;
} }

View File

@@ -22,6 +22,7 @@
#include "asm/main.h" #include "asm/main.h"
#include "asm/mymath.h" #include "asm/mymath.h"
#include "asm/output.h" #include "asm/output.h"
#include "asm/util.h"
#include "extern/err.h" #include "extern/err.h"
@@ -68,7 +69,7 @@ int32_t Callback_NARG(unused_ struct sSymbol *sym)
uint32_t i = 0; uint32_t i = 0;
while (currentmacroargs[i] && i < MAXMACROARGS) while (currentmacroargs[i] && i < MAXMACROARGS)
i += 1; i++;
return i; return i;
} }
@@ -90,16 +91,11 @@ static int32_t getvaluefield(struct sSymbol *sym)
} }
/* /*
* Calculate the hash value for a string * Calculate the hash value for a symbol name
*/ */
uint32_t calchash(char *s) uint32_t sym_CalcHash(const char *s)
{ {
uint32_t hash = 5381; return calchash(s) % HASHSIZE;
while (*s != 0)
hash = (hash * 33) ^ (*s++);
return hash % HASHSIZE;
} }
/* /*
@@ -123,7 +119,7 @@ struct sSymbol *createsymbol(char *s)
struct sSymbol **ppsym; struct sSymbol **ppsym;
uint32_t hash; uint32_t hash;
hash = calchash(s); hash = sym_CalcHash(s);
ppsym = &(tHashedSymbols[hash]); ppsym = &(tHashedSymbols[hash]);
while ((*ppsym) != NULL) while ((*ppsym) != NULL)
@@ -154,12 +150,15 @@ struct sSymbol *createsymbol(char *s)
* Creates the full name of a local symbol in a given scope, by prepending * Creates the full name of a local symbol in a given scope, by prepending
* the name with the parent symbol's name. * the name with the parent symbol's name.
*/ */
static size_t fullSymbolName(char *output, size_t outputSize, char *localName, static void fullSymbolName(char *output, size_t outputSize, char *localName,
const struct sSymbol *scope) const struct sSymbol *scope)
{ {
const struct sSymbol *parent = scope->pScope ? scope->pScope : scope; const struct sSymbol *parent = scope->pScope ? scope->pScope : scope;
int n = snprintf(output, outputSize, "%s%s", parent->tzName, localName);
return snprintf(output, outputSize, "%s%s", parent->tzName, localName); if (n >= (int)outputSize)
fatalerror("Symbol name is too long: '%s%s'",
parent->tzName, localName);
} }
/* /*
@@ -184,7 +183,7 @@ struct sSymbol **findpsymbol(char *s, struct sSymbol *scope)
s); s);
} }
hash = calchash(s); hash = sym_CalcHash(s);
ppsym = &(tHashedSymbols[hash]); ppsym = &(tHashedSymbols[hash]);
while ((*ppsym) != NULL) { while ((*ppsym) != NULL) {
@@ -207,7 +206,7 @@ struct sSymbol *findsymbol(char *s, struct sSymbol *scope)
} }
/* /*
* Find a symbol by name and scope * Find a symbol by name, with automatically determined scope
*/ */
struct sSymbol *sym_FindSymbol(char *tzName) struct sSymbol *sym_FindSymbol(char *tzName)
{ {
@@ -256,14 +255,7 @@ void sym_Purge(char *tzName)
*/ */
uint32_t sym_isConstDefined(char *tzName) uint32_t sym_isConstDefined(char *tzName)
{ {
struct sSymbol *psym, *pscope; struct sSymbol *psym = sym_FindSymbol(tzName);
if (*tzName == '.')
pscope = pScope;
else
pscope = NULL;
psym = findsymbol(tzName, pscope);
if (psym && (psym->nType & SYMF_DEFINED)) { if (psym && (psym->nType & SYMF_DEFINED)) {
uint32_t mask = SYMF_EQU | SYMF_SET | SYMF_MACRO | SYMF_STRING; uint32_t mask = SYMF_EQU | SYMF_SET | SYMF_MACRO | SYMF_STRING;
@@ -280,19 +272,9 @@ uint32_t sym_isConstDefined(char *tzName)
uint32_t sym_isDefined(char *tzName) uint32_t sym_isDefined(char *tzName)
{ {
struct sSymbol *psym, *pscope; struct sSymbol *psym = sym_FindSymbol(tzName);
if (*tzName == '.') return (psym && (psym->nType & SYMF_DEFINED));
pscope = pScope;
else
pscope = NULL;
psym = findsymbol(tzName, pscope);
if (psym && (psym->nType & SYMF_DEFINED))
return 1;
else
return 0;
} }
/* /*
@@ -300,21 +282,9 @@ uint32_t sym_isDefined(char *tzName)
*/ */
uint32_t sym_isConstant(char *s) uint32_t sym_isConstant(char *s)
{ {
struct sSymbol *psym, *pscope; struct sSymbol *psym = sym_FindSymbol(s);
if (*s == '.') return (psym && (psym->nType & SYMF_CONST));
pscope = pScope;
else
pscope = NULL;
psym = findsymbol(s, pscope);
if (psym != NULL) {
if (psym->nType & SYMF_CONST)
return 1;
}
return 0;
} }
/* /*
@@ -337,14 +307,7 @@ char *sym_GetStringValue(char *tzSym)
*/ */
uint32_t sym_GetConstantValue(char *s) uint32_t sym_GetConstantValue(char *s)
{ {
struct sSymbol *psym, *pscope; struct sSymbol *psym = sym_FindSymbol(s);
if (*s == '.')
pscope = pScope;
else
pscope = NULL;
psym = findsymbol(s, pscope);
if (psym != NULL) { if (psym != NULL) {
if (psym->nType & SYMF_CONST) if (psym->nType & SYMF_CONST)
@@ -358,63 +321,12 @@ uint32_t sym_GetConstantValue(char *s)
return 0; return 0;
} }
/*
* Return a symbols value... "estimated" if not defined yet
*/
uint32_t sym_GetValue(char *s)
{
struct sSymbol *psym, *pscope;
if (*s == '.')
pscope = pScope;
else
pscope = NULL;
psym = findsymbol(s, pscope);
if (psym != NULL) {
if (psym->nType & SYMF_DEFINED) {
if (psym->nType & (SYMF_MACRO | SYMF_STRING))
yyerror("'%s' is a macro or string symbol", s);
return getvaluefield(psym);
}
if (nPass == 2) {
/*
* Assume undefined symbols are imported from
* somwehere else
*/
psym->nType |= SYMF_IMPORT;
}
/* 0x80 seems like a good default value... */
return 0x80;
}
if (nPass == 1) {
createsymbol(s);
return 0x80;
}
yyerror("'%s' not defined", s);
return 0;
}
/* /*
* Return a defined symbols value... aborts if not defined yet * Return a defined symbols value... aborts if not defined yet
*/ */
uint32_t sym_GetDefinedValue(char *s) uint32_t sym_GetDefinedValue(char *s)
{ {
struct sSymbol *psym, *pscope; struct sSymbol *psym = sym_FindSymbol(s);
if (*s == '.')
pscope = pScope;
else
pscope = NULL;
psym = findsymbol(s, pscope);
if (psym != NULL) { if (psym != NULL) {
if ((psym->nType & SYMF_DEFINED)) { if ((psym->nType & SYMF_DEFINED)) {
@@ -448,7 +360,7 @@ void sym_ShiftCurrentMacroArgs(void)
int32_t i; int32_t i;
free(currentmacroargs[0]); free(currentmacroargs[0]);
for (i = 0; i < MAXMACROARGS - 1; i += 1) for (i = 0; i < MAXMACROARGS - 1; i++)
currentmacroargs[i] = currentmacroargs[i + 1]; currentmacroargs[i] = currentmacroargs[i + 1];
currentmacroargs[MAXMACROARGS - 1] = NULL; currentmacroargs[MAXMACROARGS - 1] = NULL;
@@ -471,7 +383,8 @@ void sym_UseNewMacroArgs(void)
{ {
int32_t i; int32_t i;
for (i = 0; i <= MAXMACROARGS; i += 1) { for (i = 0; i <= MAXMACROARGS; i++) {
free(currentmacroargs[i]);
currentmacroargs[i] = newmacroargs[i]; currentmacroargs[i] = newmacroargs[i];
newmacroargs[i] = NULL; newmacroargs[i] = NULL;
} }
@@ -481,25 +394,19 @@ void sym_SaveCurrentMacroArgs(char *save[])
{ {
int32_t i; int32_t i;
for (i = 0; i <= MAXMACROARGS; i += 1) for (i = 0; i <= MAXMACROARGS; i++) {
save[i] = currentmacroargs[i]; save[i] = currentmacroargs[i];
currentmacroargs[i] = NULL;
}
} }
void sym_RestoreCurrentMacroArgs(char *save[]) void sym_RestoreCurrentMacroArgs(char *save[])
{ {
int32_t i; int32_t i;
for (i = 0; i <= MAXMACROARGS; i += 1) for (i = 0; i <= MAXMACROARGS; i++) {
currentmacroargs[i] = save[i];
}
void sym_FreeCurrentMacroArgs(void)
{
int32_t i;
for (i = 0; i <= MAXMACROARGS; i += 1) {
free(currentmacroargs[i]); free(currentmacroargs[i]);
currentmacroargs[i] = NULL; currentmacroargs[i] = save[i];
} }
} }
@@ -508,7 +415,7 @@ void sym_AddNewMacroArg(char *s)
int32_t i = 0; int32_t i = 0;
while (i < MAXMACROARGS && newmacroargs[i] != NULL) while (i < MAXMACROARGS && newmacroargs[i] != NULL)
i += 1; i++;
if (i < MAXMACROARGS) { if (i < MAXMACROARGS) {
if (s) if (s)
@@ -524,7 +431,7 @@ void sym_SetMacroArgID(uint32_t nMacroCount)
{ {
char s[256]; char s[256];
snprintf(s, sizeof(s), "_%u", nMacroCount); snprintf(s, sizeof(s) - 1, "_%u", nMacroCount);
newmacroargs[MAXMACROARGS] = strdup(s); newmacroargs[MAXMACROARGS] = strdup(s);
} }
@@ -532,7 +439,7 @@ void sym_UseCurrentMacroArgs(void)
{ {
int32_t i; int32_t i;
for (i = 1; i <= MAXMACROARGS; i += 1) for (i = 1; i <= MAXMACROARGS; i++)
sym_AddNewMacroArg(sym_FindMacroArg(i)); sym_AddNewMacroArg(sym_FindMacroArg(i));
} }
@@ -545,23 +452,36 @@ struct sSymbol *sym_FindMacro(char *s)
} }
/* /*
* Add an equated symbol * Create a symbol that will be non-relocatable and ensure that it
* hasn't already been defined or referenced in a context that would
* require that it be relocatable
*/ */
void sym_AddEqu(char *tzSym, int32_t value) static struct sSymbol *createNonrelocSymbol(char *tzSym)
{ {
if ((nPass == 1) || ((nPass == 2) && (sym_isDefined(tzSym) == 0))) {
/* only add equated symbols in pass 1 */
struct sSymbol *nsym = findsymbol(tzSym, NULL); struct sSymbol *nsym = findsymbol(tzSym, NULL);
if (nsym != NULL) { if (nsym != NULL) {
if (nsym->nType & SYMF_DEFINED) { if (nsym->nType & SYMF_DEFINED) {
yyerror("'%s' already defined in %s(%d)", tzSym, yyerror("'%s' already defined at %s(%u)",
nsym->tzFileName, nsym->nFileLine); tzSym, nsym->tzFileName, nsym->nFileLine);
} else if (nsym->nType & SYMF_REF) {
yyerror("'%s' already referenced at %s(%u)",
tzSym, nsym->tzFileName, nsym->nFileLine);
} }
} else { } else {
nsym = createsymbol(tzSym); nsym = createsymbol(tzSym);
} }
return nsym;
}
/*
* Add an equated symbol
*/
void sym_AddEqu(char *tzSym, int32_t value)
{
struct sSymbol *nsym = createNonrelocSymbol(tzSym);
if (nsym) { if (nsym) {
nsym->nValue = value; nsym->nValue = value;
nsym->nType |= SYMF_EQU | SYMF_DEFINED | SYMF_CONST; nsym->nType |= SYMF_EQU | SYMF_DEFINED | SYMF_CONST;
@@ -569,7 +489,6 @@ void sym_AddEqu(char *tzSym, int32_t value)
updateSymbolFilename(nsym); updateSymbolFilename(nsym);
} }
} }
}
/* /*
* Add a string equated symbol. * Add a string equated symbol.
@@ -585,16 +504,7 @@ void sym_AddEqu(char *tzSym, int32_t value)
*/ */
void sym_AddString(char *tzSym, char *tzValue) void sym_AddString(char *tzSym, char *tzValue)
{ {
struct sSymbol *nsym = findsymbol(tzSym, NULL); struct sSymbol *nsym = createNonrelocSymbol(tzSym);
if (nsym != NULL) {
if (nsym->nType & SYMF_DEFINED) {
yyerror("'%s' already defined in %s(%d)",
tzSym, nsym->tzFileName, nsym->nFileLine);
}
} else {
nsym = createsymbol(tzSym);
}
if (nsym) { if (nsym) {
nsym->pMacro = malloc(strlen(tzValue) + 1); nsym->pMacro = malloc(strlen(tzValue) + 1);
@@ -617,11 +527,7 @@ uint32_t sym_isString(char *tzSym)
{ {
const struct sSymbol *pSym = findsymbol(tzSym, NULL); const struct sSymbol *pSym = findsymbol(tzSym, NULL);
if (pSym != NULL) { return (pSym && (pSym->nType & SYMF_STRING));
if (pSym->nType & SYMF_STRING)
return 1;
}
return 0;
} }
/* /*
@@ -631,8 +537,25 @@ void sym_AddSet(char *tzSym, int32_t value)
{ {
struct sSymbol *nsym = findsymbol(tzSym, NULL); struct sSymbol *nsym = findsymbol(tzSym, NULL);
if (nsym == NULL) { if (nsym != NULL) {
/* Symbol hasn been found, create */ if (nsym->nType & SYMF_DEFINED) {
if (!(nsym->nType & SYMF_CONST))
yyerror("'%s' already defined as non-constant at %s(%u)",
tzSym,
nsym->tzFileName,
nsym->nFileLine);
else if (!(nsym->nType & SYMF_SET))
yyerror("'%s' already defined as constant at %s(%u)",
tzSym,
nsym->tzFileName,
nsym->nFileLine);
} else if (nsym->nType & SYMF_REF) {
yyerror("'%s' already referenced at %s(%u)",
tzSym,
nsym->tzFileName,
nsym->nFileLine);
}
} else {
nsym = createsymbol(tzSym); nsym = createsymbol(tzSym);
} }
@@ -650,9 +573,6 @@ void sym_AddSet(char *tzSym, int32_t value)
void sym_AddLocalReloc(char *tzSym) void sym_AddLocalReloc(char *tzSym)
{ {
if (pScope) { if (pScope) {
if (strlen(tzSym) + strlen(pScope->tzName) > MAXSYMLEN)
fatalerror("Symbol too long");
char fullname[MAXSYMLEN + 1]; char fullname[MAXSYMLEN + 1];
fullSymbolName(fullname, sizeof(fullname), tzSym, pScope); fullSymbolName(fullname, sizeof(fullname), tzSym, pScope);
@@ -669,10 +589,6 @@ void sym_AddLocalReloc(char *tzSym)
void sym_AddReloc(char *tzSym) void sym_AddReloc(char *tzSym)
{ {
struct sSymbol *scope = NULL; struct sSymbol *scope = NULL;
if ((nPass == 1)
|| ((nPass == 2) && (sym_isDefined(tzSym) == 0))) {
/* only add reloc symbols in pass 1 */
struct sSymbol *nsym; struct sSymbol *nsym;
char *localPtr = strchr(tzSym, '.'); char *localPtr = strchr(tzSym, '.');
@@ -721,7 +637,7 @@ void sym_AddReloc(char *tzSym)
updateSymbolFilename(nsym); updateSymbolFilename(nsym);
} }
}
pScope = findsymbol(tzSym, scope); pScope = findsymbol(tzSym, scope);
} }
@@ -734,10 +650,6 @@ void sym_AddReloc(char *tzSym)
*/ */
int32_t sym_IsRelocDiffDefined(char *tzSym1, char *tzSym2) int32_t sym_IsRelocDiffDefined(char *tzSym1, char *tzSym2)
{ {
/* Do nothing the first pass. */
if (nPass != 2)
return 1;
const struct sSymbol *nsym1 = sym_FindSymbol(tzSym1); const struct sSymbol *nsym1 = sym_FindSymbol(tzSym1);
const struct sSymbol *nsym2 = sym_FindSymbol(tzSym2); const struct sSymbol *nsym2 = sym_FindSymbol(tzSym2);
@@ -781,8 +693,6 @@ int32_t sym_IsRelocDiffDefined(char *tzSym1, char *tzSym2)
*/ */
void sym_Export(char *tzSym) void sym_Export(char *tzSym)
{ {
if (nPass == 1) {
/* only export symbols in pass 1 */
struct sSymbol *nsym = sym_FindSymbol(tzSym); struct sSymbol *nsym = sym_FindSymbol(tzSym);
if (nsym == NULL) if (nsym == NULL)
@@ -790,139 +700,69 @@ void sym_Export(char *tzSym)
if (nsym) if (nsym)
nsym->nType |= SYMF_EXPORT; nsym->nType |= SYMF_EXPORT;
} else {
const struct sSymbol *nsym = sym_FindSymbol(tzSym);
if (nsym != NULL) {
if (nsym->nType & SYMF_DEFINED)
return;
}
yyerror("'%s' not defined", tzSym);
}
}
/*
* Globalize a symbol (export if defined, import if not)
*/
void sym_Global(char *tzSym)
{
if (nPass == 2) {
/* only globalize symbols in pass 2 */
struct sSymbol *nsym = sym_FindSymbol(tzSym);
if ((nsym == NULL) || ((nsym->nType & SYMF_DEFINED) == 0)) {
if (nsym == NULL)
nsym = createsymbol(tzSym);
if (nsym)
nsym->nType |= SYMF_IMPORT;
} else {
if (nsym)
nsym->nType |= SYMF_EXPORT;
}
}
} }
/* /*
* Add a macro definition * Add a macro definition
*/ */
void sym_AddMacro(char *tzSym) void sym_AddMacro(char *tzSym, int32_t nDefLineNo)
{ {
if ((nPass == 1) || ((nPass == 2) && (sym_isDefined(tzSym) == 0))) { struct sSymbol *nsym = createNonrelocSymbol(tzSym);
/* only add macros in pass 1 */
struct sSymbol *nsym;
nsym = findsymbol(tzSym, NULL);
if (nsym != NULL) {
if (nsym->nType & SYMF_DEFINED) {
yyerror("'%s' already defined in %s(%d)",
tzSym, nsym->tzFileName,
nsym->nFileLine);
}
} else {
nsym = createsymbol(tzSym);
}
if (nsym) { if (nsym) {
nsym->nValue = nPC;
nsym->nType |= SYMF_MACRO | SYMF_DEFINED; nsym->nType |= SYMF_MACRO | SYMF_DEFINED;
nsym->pScope = NULL; nsym->pScope = NULL;
nsym->ulMacroSize = ulNewMacroSize; nsym->ulMacroSize = ulNewMacroSize;
nsym->pMacro = tzNewMacro; nsym->pMacro = tzNewMacro;
updateSymbolFilename(nsym); updateSymbolFilename(nsym);
} /*
* The symbol is created at the line after the `endm`,
* override this with the actual definition line
*/
nsym->nFileLine = nDefLineNo;
} }
} }
/* /*
* Set whether to export all relocable symbols by default * Flag that a symbol is referenced in an RPN expression
* and create it if it doesn't exist yet
*/
void sym_Ref(char *tzSym)
{
struct sSymbol *nsym = sym_FindSymbol(tzSym);
if (nsym == NULL) {
char fullname[MAXSYMLEN + 1];
int isLocal = 0;
if (*tzSym == '.') {
if (!pScope)
fatalerror("Local label reference '%s' in main scope",
tzSym);
fullSymbolName(fullname, sizeof(fullname), tzSym,
pScope);
tzSym = fullname;
isLocal = 1;
}
nsym = createsymbol(tzSym);
if (nsym && isLocal)
nsym->nType |= SYMF_LOCAL;
}
if (nsym)
nsym->nType |= SYMF_REF;
}
/*
* Set whether to export all relocatable symbols by default
*/ */
void sym_SetExportAll(uint8_t set) void sym_SetExportAll(uint8_t set)
{ {
exportall = set; exportall = set;
} }
/*
* Prepare for pass #1
*/
void sym_PrepPass1(void)
{
sym_Init();
}
/*
* Prepare for pass #2
*/
void sym_PrepPass2(void)
{
int32_t i;
for (i = 0; i < HASHSIZE; i += 1) {
struct sSymbol **ppSym = &(tHashedSymbols[i]);
while (*ppSym) {
uint32_t mask = SYMF_SET | SYMF_STRING | SYMF_EQU;
if ((*ppSym)->nType & mask) {
struct sSymbol *pTemp;
pTemp = (*ppSym)->pNext;
free(*ppSym);
*ppSym = pTemp;
} else {
ppSym = &((*ppSym)->pNext);
}
}
}
pScope = NULL;
pPCSymbol->nValue = 0;
sym_AddString("__TIME__", SavedTIME);
sym_AddString("__DATE__", SavedDATE);
sym_AddString("__ISO_8601_LOCAL__", SavedTIMESTAMP_ISO8601_LOCAL);
sym_AddString("__ISO_8601_UTC__", SavedTIMESTAMP_ISO8601_UTC);
sym_AddString("__UTC_DAY__", SavedDAY);
sym_AddString("__UTC_MONTH__", SavedMONTH);
sym_AddString("__UTC_YEAR__", SavedYEAR);
sym_AddString("__UTC_HOUR__", SavedHOUR);
sym_AddString("__UTC_MINUTE__", SavedMINUTE);
sym_AddString("__UTC_SECOND__", SavedSECOND);
sym_AddEqu("__RGBDS_MAJOR__", PACKAGE_VERSION_MAJOR);
sym_AddEqu("__RGBDS_MINOR__", PACKAGE_VERSION_MINOR);
sym_AddEqu("__RGBDS_PATCH__", PACKAGE_VERSION_PATCH);
sym_AddSet("_RS", 0);
sym_AddEqu("_NARG", 0);
p_NARGSymbol = findsymbol("_NARG", NULL);
p_NARGSymbol->Callback = Callback_NARG;
sym_AddEqu("__LINE__", 0);
p__LINE__Symbol = findsymbol("__LINE__", NULL);
p__LINE__Symbol->Callback = Callback__LINE__;
math_DefinePI();
}
/* /*
* Initialize the symboltable * Initialize the symboltable
*/ */
@@ -931,12 +771,12 @@ void sym_Init(void)
int32_t i; int32_t i;
time_t now; time_t now;
for (i = 0; i < MAXMACROARGS; i += 1) { for (i = 0; i < MAXMACROARGS; i++) {
currentmacroargs[i] = NULL; currentmacroargs[i] = NULL;
newmacroargs[i] = NULL; newmacroargs[i] = NULL;
} }
for (i = 0; i < HASHSIZE; i += 1) for (i = 0; i < HASHSIZE; i++)
tHashedSymbols[i] = NULL; tHashedSymbols[i] = NULL;
sym_AddReloc("@"); sym_AddReloc("@");
@@ -948,6 +788,10 @@ void sym_Init(void)
p__LINE__Symbol = findsymbol("__LINE__", NULL); p__LINE__Symbol = findsymbol("__LINE__", NULL);
p__LINE__Symbol->Callback = Callback__LINE__; p__LINE__Symbol->Callback = Callback__LINE__;
sym_AddEqu("__RGBDS_MAJOR__", PACKAGE_VERSION_MAJOR);
sym_AddEqu("__RGBDS_MINOR__", PACKAGE_VERSION_MINOR);
sym_AddEqu("__RGBDS_PATCH__", PACKAGE_VERSION_PATCH);
sym_AddSet("_RS", 0); sym_AddSet("_RS", 0);
if (time(&now) != -1) { if (time(&now) != -1) {

46
src/asm/util.c Normal file
View File

@@ -0,0 +1,46 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2019, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#include <stdint.h>
#include "asm/main.h"
#include "asm/util.h"
#include "extern/utf8decoder.h"
/*
* Calculate the hash value for a string
*/
uint32_t calchash(const char *s)
{
uint32_t hash = 5381;
while (*s != 0)
hash = (hash * 33) ^ (*s++);
return hash;
}
int32_t readUTF8Char(char *dest, char *src)
{
uint32_t state;
uint32_t codep;
int32_t i;
for (i = 0, state = 0;; i++) {
if (decode(&state, &codep, (uint8_t)src[i]) == 1)
fatalerror("invalid UTF-8 character");
dest[i] = src[i];
if (state == 0) {
dest[++i] = '\0';
return i;
}
}
}

View File

@@ -268,10 +268,7 @@ int main(int argc, char *argv[])
* characters may conflict with the title. * characters may conflict with the title.
*/ */
int n = snprintf((char *)header + 0x34, 16, "%s", title); strncpy((char *)header + 0x34, title, 16);
for (int i = 16; i > n; i--)
header[0x34 + i] = '\0';
} }
if (setid) { if (setid) {

View File

@@ -85,8 +85,87 @@ int get_tile_index(uint8_t *tile, uint8_t **tiles, int num_tiles, int tile_size)
return -1; return -1;
} }
void create_tilemap(const struct Options *opts, struct GBImage *gb, uint8_t reverse_bits(uint8_t b)
struct Tilemap *tilemap) {
uint8_t rev = 0;
rev |= (b & 0x80) >> 7;
rev |= (b & 0x40) >> 5;
rev |= (b & 0x20) >> 3;
rev |= (b & 0x10) >> 1;
rev |= (b & 0x08) << 1;
rev |= (b & 0x04) << 3;
rev |= (b & 0x02) << 5;
rev |= (b & 0x01) << 7;
return rev;
}
void xflip(uint8_t *tile, uint8_t *tile_xflip, int tile_size)
{
int i;
for (i = 0; i < tile_size; i++)
tile_xflip[i] = reverse_bits(tile[i]);
}
void yflip(uint8_t *tile, uint8_t *tile_yflip, int tile_size)
{
int i;
for (i = 0; i < tile_size; i++)
tile_yflip[i] = tile[(tile_size - i - 1) ^ (depth - 1)];
}
/*
* get_mirrored_tile_index looks for `tile` in tile array `tiles`, also
* checking x-, y-, and xy-mirrored versions of `tile`. If one is found,
* `*flags` is set according to the type of mirroring and the index of the
* matched tile is returned. If no match is found, -1 is returned.
*/
int get_mirrored_tile_index(uint8_t *tile, uint8_t **tiles, int num_tiles,
int tile_size, int *flags)
{
int index;
uint8_t *tile_xflip;
uint8_t *tile_yflip;
index = get_tile_index(tile, tiles, num_tiles, tile_size);
if (index >= 0) {
*flags = 0;
return index;
}
tile_yflip = malloc(tile_size);
yflip(tile, tile_yflip, tile_size);
index = get_tile_index(tile_yflip, tiles, num_tiles, tile_size);
if (index >= 0) {
*flags = YFLIP;
free(tile_yflip);
return index;
}
tile_xflip = malloc(tile_size);
xflip(tile, tile_xflip, tile_size);
index = get_tile_index(tile_xflip, tiles, num_tiles, tile_size);
if (index >= 0) {
*flags = XFLIP;
free(tile_yflip);
free(tile_xflip);
return index;
}
yflip(tile_xflip, tile_yflip, tile_size);
index = get_tile_index(tile_yflip, tiles, num_tiles, tile_size);
if (index >= 0)
*flags = XFLIP | YFLIP;
free(tile_yflip);
free(tile_xflip);
return index;
}
void create_mapfiles(const struct Options *opts, struct GBImage *gb,
struct Mapfile *tilemap, struct Mapfile *attrmap)
{ {
int i, j; int i, j;
int gb_i; int gb_i;
@@ -94,6 +173,7 @@ void create_tilemap(const struct Options *opts, struct GBImage *gb,
int max_tiles; int max_tiles;
int num_tiles; int num_tiles;
int index; int index;
int flags;
int gb_size; int gb_size;
uint8_t *tile; uint8_t *tile;
uint8_t **tiles; uint8_t **tiles;
@@ -109,19 +189,33 @@ void create_tilemap(const struct Options *opts, struct GBImage *gb,
tiles = calloc(max_tiles, sizeof(uint8_t *)); tiles = calloc(max_tiles, sizeof(uint8_t *));
num_tiles = 0; num_tiles = 0;
if (*opts->tilemapfile) {
tilemap->data = calloc(max_tiles, sizeof(uint8_t)); tilemap->data = calloc(max_tiles, sizeof(uint8_t));
tilemap->size = 0; tilemap->size = 0;
}
if (*opts->attrmapfile) {
attrmap->data = calloc(max_tiles, sizeof(uint8_t));
attrmap->size = 0;
}
gb_i = 0; gb_i = 0;
while (gb_i < gb_size) { while (gb_i < gb_size) {
flags = 0;
tile = malloc(tile_size); tile = malloc(tile_size);
for (i = 0; i < tile_size; i++) { for (i = 0; i < tile_size; i++) {
tile[i] = gb->data[gb_i]; tile[i] = gb->data[gb_i];
gb_i++; gb_i++;
} }
if (opts->unique) { if (opts->unique) {
if (opts->mirror) {
index = get_mirrored_tile_index(tile, tiles, num_tiles,
tile_size, &flags);
} else {
index = get_tile_index(tile, tiles, num_tiles, index = get_tile_index(tile, tiles, num_tiles,
tile_size); tile_size);
}
if (index < 0) { if (index < 0) {
index = num_tiles; index = num_tiles;
tiles[num_tiles] = tile; tiles[num_tiles] = tile;
@@ -132,9 +226,15 @@ void create_tilemap(const struct Options *opts, struct GBImage *gb,
tiles[num_tiles] = tile; tiles[num_tiles] = tile;
num_tiles++; num_tiles++;
} }
if (*opts->tilemapfile) {
tilemap->data[tilemap->size] = index; tilemap->data[tilemap->size] = index;
tilemap->size++; tilemap->size++;
} }
if (*opts->attrmapfile) {
attrmap->data[attrmap->size] = flags;
attrmap->size++;
}
}
if (opts->unique) { if (opts->unique) {
free(gb->data); free(gb->data);
@@ -154,21 +254,61 @@ void create_tilemap(const struct Options *opts, struct GBImage *gb,
} }
void output_tilemap_file(const struct Options *opts, void output_tilemap_file(const struct Options *opts,
const struct Tilemap *tilemap) const struct Mapfile *tilemap)
{ {
FILE *f; FILE *f;
f = fopen(opts->mapfile, "wb"); f = fopen(opts->tilemapfile, "wb");
if (!f) if (!f)
err(1, "Opening tilemap file '%s' failed", opts->mapfile); err(1, "Opening tilemap file '%s' failed", opts->tilemapfile);
fwrite(tilemap->data, 1, tilemap->size, f); fwrite(tilemap->data, 1, tilemap->size, f);
fclose(f); fclose(f);
if (opts->mapout) if (opts->tilemapout)
free(opts->mapfile); free(opts->tilemapfile);
} }
void output_attrmap_file(const struct Options *opts,
const struct Mapfile *attrmap)
{
FILE *f;
f = fopen(opts->attrmapfile, "wb");
if (!f)
err(1, "Opening attrmap file '%s' failed", opts->attrmapfile);
fwrite(attrmap->data, 1, attrmap->size, f);
fclose(f);
if (opts->attrmapout)
free(opts->attrmapfile);
}
/*
* based on the Gaussian-like curve used by SameBoy since commit
* 65dd02cc52f531dbbd3a7e6014e99d5b24e71a4c (Oct 2017)
* with ties resolved by comparing the difference of the squares.
*/
static int reverse_curve[] = {
0, 0, 1, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7,
7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8,
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10,
10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13,
13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14,
14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17,
17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18,
18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21,
21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22,
22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24,
24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26,
26, 27, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 30, 30, 31,
};
void output_palette_file(const struct Options *opts, void output_palette_file(const struct Options *opts,
const struct RawIndexedImage *raw_image) const struct RawIndexedImage *raw_image)
{ {
@@ -181,10 +321,25 @@ void output_palette_file(const struct Options *opts,
err(1, "Opening palette file '%s' failed", opts->palfile); err(1, "Opening palette file '%s' failed", opts->palfile);
for (i = 0; i < raw_image->num_colors; i++) { for (i = 0; i < raw_image->num_colors; i++) {
color = int r = raw_image->palette[i].red;
raw_image->palette[i].blue >> 3 << 10 | int g = raw_image->palette[i].green;
raw_image->palette[i].green >> 3 << 5 | int b = raw_image->palette[i].blue;
raw_image->palette[i].red >> 3;
if (opts->colorcurve) {
g = (g * 4 - b) / 3;
if (g < 0)
g = 0;
r = reverse_curve[r];
g = reverse_curve[g];
b = reverse_curve[b];
} else {
r >>= 3;
g >>= 3;
b >>= 3;
}
color = b << 10 | g << 5 | r;
cur_bytes[0] = color & 0xFF; cur_bytes[0] = color & 0xFF;
cur_bytes[1] = color >> 8; cur_bytes[1] = color >> 8;
fwrite(cur_bytes, 2, 1, f); fwrite(cur_bytes, 2, 1, f);

View File

@@ -18,8 +18,8 @@
static void print_usage(void) static void print_usage(void)
{ {
printf( printf(
"usage: rgbgfx [-DFfhPTuVv] [-d #] [-o outfile] [-p palfile] [-t mapfile]\n" "usage: rgbgfx [-ADFfhmPTuVv] [-o outfile] [-a attrmap] [-d #] [-p palfile]\n"
" [-x #] infile\n"); " [-t tilemap] [-x #] infile\n");
exit(1); exit(1);
} }
@@ -30,21 +30,32 @@ int main(int argc, char *argv[])
struct ImageOptions png_options = {0}; struct ImageOptions png_options = {0};
struct RawIndexedImage *raw_image; struct RawIndexedImage *raw_image;
struct GBImage gb = {0}; struct GBImage gb = {0};
struct Tilemap tilemap = {0}; struct Mapfile tilemap = {0};
struct Mapfile attrmap = {0};
char *ext; char *ext;
const char *errmsg = "Warning: The PNG's %s setting is not the same as the setting defined on the command line."; const char *errmsg = "Warning: The PNG's %s setting is not the same as the setting defined on the command line.";
if (argc == 1) if (argc == 1)
print_usage(); print_usage();
opts.mapfile = ""; opts.tilemapfile = "";
opts.attrmapfile = "";
opts.palfile = ""; opts.palfile = "";
opts.outfile = ""; opts.outfile = "";
depth = 2; depth = 2;
while ((ch = getopt(argc, argv, "Dd:Ffho:Tt:uPp:Vvx:")) != -1) { while ((ch = getopt(argc, argv, "Aa:CDd:Ffhmo:Tt:uPp:Vvx:")) != -1) {
switch (ch) { switch (ch) {
case 'A':
opts.attrmapout = true;
break;
case 'a':
opts.attrmapfile = optarg;
break;
case 'C':
opts.colorcurve = true;
break;
case 'D': case 'D':
opts.debug = true; opts.debug = true;
break; break;
@@ -60,6 +71,10 @@ int main(int argc, char *argv[])
case 'h': case 'h':
opts.horizontal = true; opts.horizontal = true;
break; break;
case 'm':
opts.mirror = true;
opts.unique = true;
break;
case 'o': case 'o':
opts.outfile = optarg; opts.outfile = optarg;
break; break;
@@ -70,10 +85,10 @@ int main(int argc, char *argv[])
opts.palfile = optarg; opts.palfile = optarg;
break; break;
case 'T': case 'T':
opts.mapout = true; opts.tilemapout = true;
break; break;
case 't': case 't':
opts.mapfile = optarg; opts.tilemapfile = optarg;
break; break;
case 'u': case 'u':
opts.unique = true; opts.unique = true;
@@ -107,7 +122,8 @@ int main(int argc, char *argv[])
raw_image = input_png_file(&opts, &png_options); raw_image = input_png_file(&opts, &png_options);
png_options.mapfile = ""; png_options.tilemapfile = "";
png_options.attrmapfile = "";
png_options.palfile = ""; png_options.palfile = "";
if (png_options.horizontal != opts.horizontal) { if (png_options.horizontal != opts.horizontal) {
@@ -148,25 +164,45 @@ int main(int argc, char *argv[])
(raw_image->width / 8) * (raw_image->height / 8) - 1); (raw_image->width / 8) * (raw_image->height / 8) - 1);
} }
if (strcmp(png_options.mapfile, opts.mapfile) != 0) { if (strcmp(png_options.tilemapfile, opts.tilemapfile) != 0) {
if (opts.verbose) if (opts.verbose)
warnx(errmsg, "tilemap file"); warnx(errmsg, "tilemap file");
if (opts.hardfix) if (opts.hardfix)
png_options.mapfile = opts.mapfile; png_options.tilemapfile = opts.tilemapfile;
} }
if (!*opts.mapfile) if (!*opts.tilemapfile)
opts.mapfile = png_options.mapfile; opts.tilemapfile = png_options.tilemapfile;
if (png_options.mapout != opts.mapout) { if (png_options.tilemapout != opts.tilemapout) {
if (opts.verbose) if (opts.verbose)
warnx(errmsg, "tilemap file"); warnx(errmsg, "tilemap file");
if (opts.hardfix) if (opts.hardfix)
png_options.mapout = opts.mapout; png_options.tilemapout = opts.tilemapout;
} }
if (png_options.mapout) if (png_options.tilemapout)
opts.mapout = png_options.mapout; opts.tilemapout = png_options.tilemapout;
if (strcmp(png_options.attrmapfile, opts.attrmapfile) != 0) {
if (opts.verbose)
warnx(errmsg, "attrmap file");
if (opts.hardfix)
png_options.attrmapfile = opts.attrmapfile;
}
if (!*opts.attrmapfile)
opts.attrmapfile = png_options.attrmapfile;
if (png_options.attrmapout != opts.attrmapout) {
if (opts.verbose)
warnx(errmsg, "attrmap file");
if (opts.hardfix)
png_options.attrmapout = opts.attrmapout;
}
if (png_options.attrmapout)
opts.attrmapout = png_options.attrmapout;
if (strcmp(png_options.palfile, opts.palfile) != 0) { if (strcmp(png_options.palfile, opts.palfile) != 0) {
if (opts.verbose) if (opts.verbose)
@@ -189,19 +225,35 @@ int main(int argc, char *argv[])
if (png_options.palout) if (png_options.palout)
opts.palout = png_options.palout; opts.palout = png_options.palout;
if (!*opts.mapfile && opts.mapout) { if (!*opts.tilemapfile && opts.tilemapout) {
ext = strrchr(opts.infile, '.'); ext = strrchr(opts.infile, '.');
if (ext != NULL) { if (ext != NULL) {
size = ext - opts.infile + 9; size = ext - opts.infile + 9;
opts.mapfile = malloc(size); opts.tilemapfile = malloc(size);
strncpy(opts.mapfile, opts.infile, size); strncpy(opts.tilemapfile, opts.infile, size);
*strrchr(opts.mapfile, '.') = '\0'; *strrchr(opts.tilemapfile, '.') = '\0';
strcat(opts.mapfile, ".tilemap"); strcat(opts.tilemapfile, ".tilemap");
} else { } else {
opts.mapfile = malloc(strlen(opts.infile) + 9); opts.tilemapfile = malloc(strlen(opts.infile) + 9);
strcpy(opts.mapfile, opts.infile); strcpy(opts.tilemapfile, opts.infile);
strcat(opts.mapfile, ".tilemap"); strcat(opts.tilemapfile, ".tilemap");
}
}
if (!*opts.attrmapfile && opts.attrmapout) {
ext = strrchr(opts.infile, '.');
if (ext != NULL) {
size = ext - opts.infile + 9;
opts.attrmapfile = malloc(size);
strncpy(opts.attrmapfile, opts.infile, size);
*strrchr(opts.attrmapfile, '.') = '\0';
strcat(opts.attrmapfile, ".attrmap");
} else {
opts.attrmapfile = malloc(strlen(opts.infile) + 9);
strcpy(opts.attrmapfile, opts.infile);
strcat(opts.attrmapfile, ".attrmap");
} }
} }
@@ -226,17 +278,20 @@ int main(int argc, char *argv[])
gb.trim = opts.trim; gb.trim = opts.trim;
gb.horizontal = opts.horizontal; gb.horizontal = opts.horizontal;
if (*opts.outfile || *opts.mapfile) { if (*opts.outfile || *opts.tilemapfile || *opts.attrmapfile) {
raw_to_gb(raw_image, &gb); raw_to_gb(raw_image, &gb);
create_tilemap(&opts, &gb, &tilemap); create_mapfiles(&opts, &gb, &tilemap, &attrmap);
} }
if (*opts.outfile) if (*opts.outfile)
output_file(&opts, &gb); output_file(&opts, &gb);
if (*opts.mapfile) if (*opts.tilemapfile)
output_tilemap_file(&opts, &tilemap); output_tilemap_file(&opts, &tilemap);
if (*opts.attrmapfile)
output_attrmap_file(&opts, &attrmap);
if (*opts.palfile) if (*opts.palfile)
output_palette_file(&opts, raw_image); output_palette_file(&opts, raw_image);

View File

@@ -199,8 +199,8 @@ static struct RawIndexedImage *indexed_png_to_raw(struct PNGImage *img)
raw_image = create_raw_image(img->width, img->height, colors); raw_image = create_raw_image(img->width, img->height, colors);
/* /*
* Transparent palette entries are removed, and the palette is collapsed. * Transparent palette entries are removed, and the palette is
* Transparent pixels are then replaced with palette index 0. * collapsed. Transparent pixels are then replaced with palette index 0.
* This way, an indexed PNG can contain transparent pixels in *addition* * This way, an indexed PNG can contain transparent pixels in *addition*
* to 4 normal colors. * to 4 normal colors.
*/ */
@@ -649,10 +649,16 @@ static void get_text(const struct PNGImage *img,
png_options->trim = strtoul(text[i].text, NULL, 0); png_options->trim = strtoul(text[i].text, NULL, 0);
png_free_data(img->png, img->info, PNG_FREE_TEXT, i); png_free_data(img->png, img->info, PNG_FREE_TEXT, i);
} else if (strcmp(text[i].key, "t") == 0) { } else if (strcmp(text[i].key, "t") == 0) {
png_options->mapfile = text[i].text; png_options->tilemapfile = text[i].text;
png_free_data(img->png, img->info, PNG_FREE_TEXT, i); png_free_data(img->png, img->info, PNG_FREE_TEXT, i);
} else if (strcmp(text[i].key, "T") == 0 && !*text[i].text) { } else if (strcmp(text[i].key, "T") == 0 && !*text[i].text) {
png_options->mapout = true; png_options->tilemapout = true;
png_free_data(img->png, img->info, PNG_FREE_TEXT, i);
} else if (strcmp(text[i].key, "a") == 0) {
png_options->attrmapfile = text[i].text;
png_free_data(img->png, img->info, PNG_FREE_TEXT, i);
} else if (strcmp(text[i].key, "A") == 0 && !*text[i].text) {
png_options->attrmapout = true;
png_free_data(img->png, img->info, PNG_FREE_TEXT, i); png_free_data(img->png, img->info, PNG_FREE_TEXT, i);
} else if (strcmp(text[i].key, "p") == 0) { } else if (strcmp(text[i].key, "p") == 0) {
png_options->palfile = text[i].text; png_options->palfile = text[i].text;
@@ -699,18 +705,30 @@ static void set_text(const struct PNGImage *img,
text[0].compression = PNG_TEXT_COMPRESSION_NONE; text[0].compression = PNG_TEXT_COMPRESSION_NONE;
png_set_text(img->png, img->info, text, 1); png_set_text(img->png, img->info, text, 1);
} }
if (*png_options->mapfile) { if (*png_options->tilemapfile) {
text[0].key = "t"; text[0].key = "t";
text[0].text = ""; text[0].text = "";
text[0].compression = PNG_TEXT_COMPRESSION_NONE; text[0].compression = PNG_TEXT_COMPRESSION_NONE;
png_set_text(img->png, img->info, text, 1); png_set_text(img->png, img->info, text, 1);
} }
if (png_options->mapout) { if (png_options->tilemapout) {
text[0].key = "T"; text[0].key = "T";
text[0].text = ""; text[0].text = "";
text[0].compression = PNG_TEXT_COMPRESSION_NONE; text[0].compression = PNG_TEXT_COMPRESSION_NONE;
png_set_text(img->png, img->info, text, 1); png_set_text(img->png, img->info, text, 1);
} }
if (*png_options->attrmapfile) {
text[0].key = "a";
text[0].text = "";
text[0].compression = PNG_TEXT_COMPRESSION_NONE;
png_set_text(img->png, img->info, text, 1);
}
if (png_options->attrmapout) {
text[0].key = "A";
text[0].text = "";
text[0].compression = PNG_TEXT_COMPRESSION_NONE;
png_set_text(img->png, img->info, text, 1);
}
if (*png_options->palfile) { if (*png_options->palfile) {
text[0].key = "p"; text[0].key = "p";
text[0].text = ""; text[0].text = "";

View File

@@ -13,11 +13,12 @@
.Nd Game Boy graphics converter .Nd Game Boy graphics converter
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm rgbgfx .Nm rgbgfx
.Op Fl DfFhPTVv .Op Fl ADfFhmPTuVv
.Op Fl o Ar outfile .Op Fl o Ar outfile
.Op Fl a Ar attrmap
.Op Fl d Ar depth .Op Fl d Ar depth
.Op Fl p Ar palfile .Op Fl p Ar palfile
.Op Fl t Ar mapfile .Op Fl t Ar tilemap
.Op Fl x Ar tiles .Op Fl x Ar tiles
.Ar file .Ar file
.Sh DESCRIPTION .Sh DESCRIPTION
@@ -47,6 +48,21 @@ The input image may not contain more colors than the selected bit depth
allows. Transparent pixels are set to palette index 0. allows. Transparent pixels are set to palette index 0.
.Sh ARGUMENTS .Sh ARGUMENTS
.Bl -tag -width Ds .Bl -tag -width Ds
.It Fl a Ar attrmap
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
.Fl m
to keep track the mirror direction of mirrored duplicate tiles.
.It Fl A
Same as
.Fl a ,
but the attrmap file output name is made by taking the input filename, removing
the file extension, and appending
.Pa .attrmap .
.It Fl C
Use the color curve of the Game Boy Color when generating palettes.
.It Fl D .It Fl D
Debug features are enabled. Debug features are enabled.
.It Fl f .It Fl f
@@ -61,6 +77,12 @@ The bit depth of the output image (either 1 or 2).
By default, the bit depth is 2 (two bits per pixel). By default, the bit depth is 2 (two bits per pixel).
.It Fl h .It Fl h
Lay out tiles horizontally rather than vertically. Lay out tiles horizontally rather than vertically.
.It Fl m
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
.Fl u .
.It Fl o Ar outfile .It Fl o Ar outfile
The name of the output file. The name of the output file.
.It Fl p Ar palfile .It Fl p Ar palfile
@@ -74,17 +96,24 @@ Same as
but the palette file output name is made by taking the input PNG file's but the palette file output name is made by taking the input PNG file's
filename, removing the file extension, and appending filename, removing the file extension, and appending
.Pa .pal . .Pa .pal .
.It Fl t Ar mapfile .It Fl t Ar tilemap
If any tiles are the same, don't place the repeat tiles in the output file, and Generate a file of tile indices. For each tile in the input file, a byte is
make a tilemap file. written representing the index of the associated tile in the output file.
Useful in combination with
.Fl u
or
.Fl m
to keep track of duplicate tiles.
.It Fl T .It Fl T
Same as Same as
.Fl t , .Fl t ,
but the tilemap file output name is made by taking the input filename, but the tilemap file output name is made by taking the input filename, removing
removing the file extension, and appending the file extension, and appending
.Pa .tilemap . .Pa .tilemap .
.It Fl u .It Fl u
Truncate repeated tiles. Useful with tilemaps. 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.
.It Fl V .It Fl V
Print the version of the program and exit. Print the version of the program and exit.
.It Fl v .It Fl v
@@ -105,6 +134,14 @@ The following creates a planar 2bpp file with only unique tiles, and its tilemap
.Pp .Pp
.D1 $ rgbgfx -T -u -o out.2bpp in.png .D1 $ rgbgfx -T -u -o out.2bpp in.png
.Pp .Pp
The following creates a planar 2bpp file with only unique tiles (accounting for
tile mirroring) and its associated tilemap
.Pa out.tilemap
and attrmap
.Pa out.attrmap :
.Pp
.D1 $ rgbgfx -A -T -m -o out.2bpp in.png
.Pp
The following will do nothing: The following will do nothing:
.Pp .Pp
.D1 $ rgbgfx in.png .D1 $ rgbgfx in.png

2
src/link/.gitignore vendored
View File

@@ -1,2 +1,4 @@
parser.c parser.c
parser.h parser.h
lexer.c
lexer.h

View File

@@ -155,6 +155,11 @@ int32_t area_Avail(int32_t bank)
int32_t area_doAlloc(struct sFreeArea *pArea, int32_t org, int32_t size) int32_t area_doAlloc(struct sFreeArea *pArea, int32_t org, int32_t size)
{ {
if (size == 0) {
/* 0-byte SECTIONs don't take any room, they can go anywhere */
return org;
}
if ((org >= pArea->nOrg) if ((org >= pArea->nOrg)
&& ((org + size) <= (pArea->nOrg + pArea->nSize))) { && ((org + size) <= (pArea->nOrg + pArea->nSize))) {
@@ -695,24 +700,24 @@ void CreateSymbolTable(void)
if ((tSymbol->Type == SYM_EXPORT) && if ((tSymbol->Type == SYM_EXPORT) &&
((tSymbol->pSection == pSect) || ((tSymbol->pSection == pSect) ||
(tSymbol->pSection == NULL))) { (tSymbol->pSection == NULL))) {
if (tSymbol->pSection == NULL) if (tSymbol->pSection == NULL) {
sym_CreateSymbol( sym_CreateSymbol(tSymbol->pzName,
tSymbol->pzName,
tSymbol->nOffset, tSymbol->nOffset,
-1, -1,
tSymbol->pzObjFileName, tSymbol->pzObjFileName,
tSymbol->pzFileName, tSymbol->pzFileName,
tSymbol->nFileLine); tSymbol->nFileLine);
else } else {
sym_CreateSymbol( sym_CreateSymbol(tSymbol->pzName,
tSymbol->pzName, pSect->nOrg +
pSect->nOrg + tSymbol->nOffset, tSymbol->nOffset,
pSect->nBank, pSect->nBank,
tSymbol->pzObjFileName, tSymbol->pzObjFileName,
tSymbol->pzFileName, tSymbol->pzFileName,
tSymbol->nFileLine); tSymbol->nFileLine);
} }
} }
}
pSect = pSect->pNext; pSect = pSect->pNext;
} }
} }

View File

@@ -18,13 +18,18 @@
#include "link/mylink.h" #include "link/mylink.h"
#include "link/symbol.h" #include "link/symbol.h"
#define RPN_STACK_SIZE 256
static struct sSection *pCurrentSection; static struct sSection *pCurrentSection;
static int32_t rpnstack[256]; static int32_t rpnstack[RPN_STACK_SIZE];
static int32_t rpnp; static int32_t rpnp;
int32_t nPC; int32_t nPC;
static void rpnpush(int32_t i) static void rpnpush(int32_t i)
{ {
if (rpnp >= RPN_STACK_SIZE)
errx(1, "RPN stack overflow");
rpnstack[rpnp] = i; rpnstack[rpnp] = i;
rpnp++; rpnp++;
} }
@@ -35,13 +40,13 @@ static int32_t rpnpop(void)
return rpnstack[rpnp]; return rpnstack[rpnp];
} }
static int32_t getsymvalue(int32_t symid) static int32_t getsymvalue(struct sPatch *pPatch, int32_t symid)
{ {
const struct sSymbol *tSymbol = pCurrentSection->tSymbols[symid]; const struct sSymbol *tSymbol = pCurrentSection->tSymbols[symid];
switch (tSymbol->Type) { switch (tSymbol->Type) {
case SYM_IMPORT: case SYM_IMPORT:
return sym_GetValue(tSymbol->pzName); return sym_GetValue(pPatch, tSymbol->pzName);
case SYM_EXPORT: case SYM_EXPORT:
case SYM_LOCAL: case SYM_LOCAL:
@@ -75,14 +80,14 @@ static int32_t getrealbankfrominternalbank(int32_t n)
return n; return n;
} }
static int32_t getsymbank(int32_t symid) static int32_t getsymbank(struct sPatch *pPatch, int32_t symid)
{ {
int32_t nBank; int32_t nBank;
const struct sSymbol *tSymbol = pCurrentSection->tSymbols[symid]; const struct sSymbol *tSymbol = pCurrentSection->tSymbols[symid];
switch (tSymbol->Type) { switch (tSymbol->Type) {
case SYM_IMPORT: case SYM_IMPORT:
nBank = sym_GetBank(tSymbol->pzName); nBank = sym_GetBank(pPatch, tSymbol->pzName);
break; break;
case SYM_EXPORT: case SYM_EXPORT:
case SYM_LOCAL: case SYM_LOCAL:
@@ -209,8 +214,8 @@ int32_t calcrpn(struct sPatch *pPatch)
t |= (*rpn++) << 8; t |= (*rpn++) << 8;
t |= (*rpn++) << 16; t |= (*rpn++) << 16;
t |= (*rpn++) << 24; t |= (*rpn++) << 24;
rpnpush(getsymvalue(t)); rpnpush(getsymvalue(pPatch, t));
pPatch->oRelocPatch |= (getsymbank(t) != -1); pPatch->oRelocPatch |= (getsymbank(pPatch, t) != -1);
size -= 4; size -= 4;
break; break;
case RPN_BANK_SYM: case RPN_BANK_SYM:
@@ -219,7 +224,7 @@ int32_t calcrpn(struct sPatch *pPatch)
t |= (*rpn++) << 8; t |= (*rpn++) << 8;
t |= (*rpn++) << 16; t |= (*rpn++) << 16;
t |= (*rpn++) << 24; t |= (*rpn++) << 24;
rpnpush(getsymbank(t)); rpnpush(getsymbank(pPatch, t));
size -= 4; size -= 4;
break; break;
case RPN_BANK_SECT: case RPN_BANK_SECT:
@@ -229,7 +234,9 @@ int32_t calcrpn(struct sPatch *pPatch)
struct sSection *pSection = GetSectionByName(name); struct sSection *pSection = GetSectionByName(name);
if (pSection == NULL) { if (pSection == NULL) {
errx(1, "Requested BANK() of section \"%s\", which was not found.\n", errx(1,
"%s(%ld) : Requested BANK() of section \"%s\", which was not found.\n",
pPatch->pzFilename, pPatch->nLineNo,
name); name);
} }

View File

@@ -15,6 +15,7 @@
#include "link/main.h" #include "link/main.h"
#include "link/patch.h" #include "link/patch.h"
#include "link/mylink.h"
#include "types.h" #include "types.h"
@@ -53,7 +54,7 @@ void sym_Init(void)
tHash[i] = NULL; tHash[i] = NULL;
} }
int32_t sym_GetValue(char *tzName) int32_t sym_GetValue(struct sPatch *pPatch, char *tzName)
{ {
if (strcmp(tzName, "@") == 0) if (strcmp(tzName, "@") == 0)
return nPC; return nPC;
@@ -68,10 +69,13 @@ int32_t sym_GetValue(char *tzName)
return ((*ppSym)->nValue); return ((*ppSym)->nValue);
} }
errx(1, "Unknown symbol '%s'", tzName); errx(1,
"%s(%ld) : Unknown symbol '%s'",
pPatch->pzFilename, pPatch->nLineNo,
tzName);
} }
int32_t sym_GetBank(char *tzName) int32_t sym_GetBank(struct sPatch *pPatch, char *tzName)
{ {
struct ISymbol **ppSym; struct ISymbol **ppSym;
@@ -83,7 +87,10 @@ int32_t sym_GetBank(char *tzName)
return ((*ppSym)->nBank); return ((*ppSym)->nBank);
} }
errx(1, "Unknown symbol '%s'", tzName); errx(1,
"%s(%ld) : Unknown symbol '%s'",
pPatch->pzFilename, pPatch->nLineNo,
tzName);
} }
void sym_CreateSymbol(char *tzName, int32_t nValue, int32_t nBank, void sym_CreateSymbol(char *tzName, int32_t nValue, int32_t nBank,

View File

@@ -47,7 +47,7 @@ REPT NumberOfSymbols ; Number of symbols defined in this object file.
; 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 (unused). ; 1 = IMPORT this symbol from elsewhere
; 2 = EXPORT this symbol to other objects. ; 2 = EXPORT this symbol to other objects.
IF Type != 1 ; If symbol is defined in this object file. IF Type != 1 ; If symbol is defined in this object file.
@@ -164,8 +164,8 @@ special prefixes for integers and symbols.
.It Li $33 Ta Li < comparison .It Li $33 Ta Li < comparison
.It Li $34 Ta Li >= comparison .It Li $34 Ta Li >= comparison
.It Li $35 Ta Li <= comparison .It Li $35 Ta Li <= comparison
.It Li $40 Ta Li << comparison .It Li $40 Ta Li << operator
.It Li $41 Ta Li >> comparison .It Li $41 Ta Li >> operator
.It Li $50 Ta Li BANK(symbol), .It Li $50 Ta Li BANK(symbol),
a a
.Ar LONG .Ar LONG

View File

@@ -0,0 +1,20 @@
X = 42
PRINTT "{X}\n"
PRINTT "{x:X}\n"
PRINTT "{X:X}\n"
PRINTT "{d:X}\n"
PRINTT "{b:X}\n"
Y equ 1337
PRINTT "{b:Y}\n"
rsreset
R rb 0
PRINTT "{d:R}\n"
S equs "You can't format me!"
PRINTT "{X:S}\n"
SECTION "Test", ROM0
Label:
PRINTT "{x:Label}\n"

View File

@@ -0,0 +1,12 @@
ERROR: bracketed-symbols.asm(16):
Print types are only allowed for numbers
ERROR: bracketed-symbols.asm(20):
Expression must have a constant value
$2A
2a
2A
42
101010
10100111001
0
You can't format me!

View File

@@ -0,0 +1,11 @@
IF 0
"\
"
ELIF 1
WARN "Am I geting ahead of myself?"
ELSE
"\
"
ENDC
WARN "Hopefully not."

View File

@@ -0,0 +1,4 @@
warning: correct-line-number.asm(5):
Am I geting ahead of myself?
warning: correct-line-number.asm(11):
Hopefully not.

View File

@@ -1,4 +1,4 @@
SECTION "sec", ROM0
charmap "A", 1 charmap "A", 1
SECTION "sec", ROM0[0]
_A_ EQU "A" _A_ EQU "A"
db _A_ db _A_

View File

@@ -0,0 +1,2 @@
recurse EQUS "recurse"
recurse

View File

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

View File

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

View File

@@ -0,0 +1,2 @@
ERROR: garbage_char.asm(1):
Found garbage character: 0xFF

View File

@@ -0,0 +1 @@
INCLUDE "include-recursion.asm"

View File

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

View File

@@ -0,0 +1,45 @@
print: MACRO
printv \1
printt "\n"
ENDM
m1: MACRO
x\1
ENDM
S EQUS "y"
S2 EQUS "yy"
m2: MACRO
S\1
ENDM
m1 = 5
m2 = 6
m1 x = 7
m2 2 = 8
print x
print y
print xx
print yy
test_char: MACRO
VAR_DEF equs "sizeof_\1something = 0"
VAR_DEF
sizeof_\1something = 1
PURGE VAR_DEF
VAR_PRINT equs "printt \"sizeof_\1something equals {sizeof_\1something}\\n\""
VAR_PRINT
PURGE VAR_PRINT
ENDM
test_char _
test_char @
test_char #
test_char .
test_char :

View File

@@ -0,0 +1,10 @@
ERROR: label-macro-arg.asm(45) -> label-macro-arg.asm::test_char(31):
Macro 'something' not defined
$5
$6
$7
$8
sizeof__something equals $1
sizeof_@something equals $1
sizeof_#something equals $1
sizeof_.something equals $1

View File

@@ -1,3 +1,3 @@
ERROR: label-redefinition.asm(7): ERROR: label-redefinition.asm(7):
'Sym' already defined in m(6) 'Sym' already defined in label-redefinition.asm::m(6)
error: Assembly aborted in pass 1 (1 errors)! error: Assembly aborted (1 errors)!

View File

@@ -0,0 +1,7 @@
m: MACRO
ENDM
m2: MACRO
m \ ENDM
m2

View File

View File

@@ -0,0 +1,8 @@
m: MACRO
ENDM
REPT 1
m ENDR
REPT 1
m \ ENDR

View File

View File

@@ -0,0 +1,7 @@
; Test that \ followed by whitespace after a macro invocation at the end of the
; file doesn't cause a segfault.
bar: MACRO
ENDM
foo bar baz\

View File

@@ -1 +1,7 @@
foo @bar\ ; Test that \ after a macro invocation at the end of the file doesn't
; cause a segfault.
bar: MACRO
ENDM
foo bar baz\

View File

@@ -1,2 +0,0 @@
ERROR: line-continuation.asm(2) -> @(-1):
Macro '@' not defined

View File

@@ -0,0 +1,3 @@
SECTION "sec", ROM0
dw .test

View File

@@ -0,0 +1,2 @@
ERROR: local-ref-without-parent.asm(3):
Local label reference '.test' in main scope

View File

@@ -0,0 +1,2 @@
SECTION "Test", ROM0
.test:

View File

@@ -0,0 +1,2 @@
ERROR: local-without-parent.asm(2):
Local label in main scope

View File

@@ -1,3 +1,3 @@
ERROR: local-wrong-parent.asm(5): ERROR: local-wrong-parent.asm(5):
Not currently in the scope of 'WrongParent' Not currently in the scope of 'WrongParent'
error: Assembly aborted in pass 1 (1 errors)! error: Assembly aborted (1 errors)!

View File

@@ -0,0 +1,33 @@
SECTION "sec", ROM0
X0 EQUS "0"
m: MACRO
\1 EQUS STRCAT("{X\2}", "+0")
ENDM
n = 0
REPT $7E
n1 = n + 1
NSTR EQUS STRSUB("{n}", 2, STRLEN("{n}") - 1)
N1STR EQUS STRSUB("{n1}", 2, STRLEN("{n1}") - 1)
XN1 EQUS STRCAT("X", "{N1STR}")
m XN1, {NSTR}
PURGE NSTR, N1STR, XN1
n = n + 1
ENDR
; string of 127 zeros separated by plus signs
X EQUS "{X7E}"
db x+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+\
X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+\
X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+\
X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+\
X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+\
X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+\
X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+\
X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X
x db 0

View File

View File

@@ -1,2 +1,2 @@
ERROR: macro-@.asm(1) -> @(-1): ERROR: macro-@.asm(1):
Macro '@' not defined Macro '@' not defined

View File

@@ -0,0 +1,4 @@
recurse: MACRO
recurse
ENDM
recurse

View File

@@ -0,0 +1,2 @@
ERROR: macro-recursion.asm(4) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2) -> macro-recursion.asm::recurse(2):
Recursion limit (64) exceeded

View File

@@ -0,0 +1,104 @@
new_: MACRO
IF _NARG > 1
printt "newcharmap \1, \2\n"
newcharmap \1, \2
ELSE
printt "newcharmap \1\n"
newcharmap \1
ENDC
ENDM
set_: MACRO
printt "setcharmap \1\n"
setcharmap \1
ENDM
push_: MACRO
printt "pushc\n"
pushc
ENDM
pop_: MACRO
printt "popc\n"
popc
ENDM
print: MACRO
x = \1
printt "{x}\n"
ENDM
printt "main charmap\n"
charmap "ab", $0
print "ab"
new_ map1
print "ab"
new_ map2, main
print "ab"
set_ map1
print "ab"
new_ map3
charmap "ab", $1
print "ab"
new_ map4, map3
charmap "ab", $1
charmap "cd", $2
print "ab"
print "cd"
set_ map3
print "ab"
print "cd"
set_ main
SECTION "sec0", ROM0
print "ab"
printt "override main charmap\n"
charmap "ef", $3
print "ab"
print "ef"
set_ map1
push_
set_ map2
push_
set_ map3
print "ab"
print "cd"
print "ef"
pop_
print "ab"
pop_
print "ab"
new_ map1
set_ map5
pop_

View File

@@ -0,0 +1,44 @@
warning: multiple-charmaps.asm(75):
Using 'charmap' within a section when the current charmap is 'main' is deprecated
ERROR: multiple-charmaps.asm(100) -> multiple-charmaps.asm::new_(7):
Charmap 'map1' already exists
ERROR: multiple-charmaps.asm(102) -> multiple-charmaps.asm::set_(13):
Charmap 'map5' doesn't exist
ERROR: multiple-charmaps.asm(104) -> multiple-charmaps.asm::pop_(23):
No entries in the charmap stack
main charmap
$0
newcharmap map1
$6162
newcharmap map2, main
$0
setcharmap map1
$6162
newcharmap map3
$1
newcharmap map4, map3
$1
$2
setcharmap map3
$1
$6364
setcharmap main
$0
override main charmap
$6162
$3
setcharmap map1
pushc
setcharmap map2
pushc
setcharmap map3
$1
$6364
$6566
popc
$0
popc
$6162
newcharmap map1
setcharmap map5
popc

View File

@@ -0,0 +1,14 @@
testing: MACRO
db _NARG
shift
db _NARG
shift
db _NARG
shift
db _NARG
shift
db _NARG
ENDM
SECTION "Test output", ROM0[0]
testing 1, 2, 3

View File

Binary file not shown.

18
test/asm/nested-if.asm Normal file
View File

@@ -0,0 +1,18 @@
if 0
if(1)
endc
if 1
endc
if{x}
endc
endc
if 1
else
if(1)
endc
if 1
endc
if{x}
endc
endc

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

View File

@@ -1,2 +1,2 @@
ERROR: null-in-macro.asm(1): ERROR: null-in-macro.asm(2):
Unterminated MACRO definition. Found null character

View File

@@ -0,0 +1,7 @@
print: MACRO
printv \1
printt "\n"
ENDM
print 1 == 1 || 1 == 2
print (1 == 1) || (1 == 2)

View File

@@ -0,0 +1,2 @@
$1
$1

42
test/asm/overflow.asm Normal file
View File

@@ -0,0 +1,42 @@
SECTION "sec", ROM0
print_x: MACRO
printv x
printt "\n"
ENDM
x = 2147483647
x = x + 1
dl 2147483647+1
print_x
x = -2147483648
x = x - 1
dl -2147483648-1
print_x
x = -2147483648
x = x * -1
dl -2147483648 * -1
print_x
x = -2147483648
x = x / -1
dl -2147483648 / -1
print_x
x = -2147483648
x = x % -1
dl -2147483648 % -1
print_x
x = -1
x = x << 1
dl -1 << 1
print_x
x = 4294967295
x = 4294967296
x = `33333333
x = `333333333

18
test/asm/overflow.out Normal file
View File

@@ -0,0 +1,18 @@
warning: overflow.asm(24):
Division of min value by -1
warning: overflow.asm(25):
Division of min value by -1
warning: overflow.asm(34):
Left shift of negative value: -1
warning: overflow.asm(35):
Left shift of negative value: -1
warning: overflow.asm(39):
Integer constant '4294967296' is too large
warning: overflow.asm(42):
Graphics constant '`333333333' is too long
$80000000
$7FFFFFFF
$80000000
$80000000
$0
$FFFFFFFE

View File

@@ -0,0 +1 @@
POPS

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