Compare commits

...

185 Commits

Author SHA1 Message Date
Rangi
d05703c692 Release 0.5.0-rc2 2021-03-28 17:15:44 -04:00
Rangi
5dbfafcc55 Update man page copyrights to 2021 2021-03-28 16:37:15 -04:00
Rangi
a265b85d9d Report "1 error", not "1 errors", when assembly is aborted
This matches other such pluralized error messages
2021-03-28 15:55:32 -04:00
Rangi
28abf03c0a Output USED space, not FREE space, in the .map file
Fixes #808
2021-03-28 15:49:44 -04:00
Rangi
7e7f92f18c Assign section locations to all UNIONs/FRAGMENTs
Fixes the test case from #800

The `out.gb` output was corrected, since the two "test"
fragments have a different order in ROM than in SRAM.
It is effectively:

; ROM0[$0000], fragments ordered by size
    jr Label
    dw Label
    db 0

; SRAM[$a000], fragments ordered by .o order
    ds 1  ; db 0
    ds 2  ; jr Label
Label:    ; $a003
    ds 2  ; dw Label
2021-03-28 15:21:17 -04:00
Eldred Habert
7461170956 Add LOAD FRAGMENT pc test (#800)
Reproduces a reported problem, fix pending
2021-03-28 15:11:20 -04:00
Rangi
57d966d6e0 Merge pull request #804 from Rangi42/normal-backslash
Backslash in normal lexer mode must be a line continuation
2021-03-26 13:33:11 -04:00
Rangi
17752d7094 Backslash in normal lexer mode must be a line continuation
Macro args were already handled by `peek`, and character escapes
do not exist outside of string literals.

This only affects the error message printed when a non-whitespace
character comes after the backslash. Instead of "Illegal character
escape '%s'", it will print "Begun line continuation, but
encountered character '%s'".
2021-03-26 13:23:05 -04:00
ISSOtm
4216f0a9b0 Init top-level fstack node line number
It still gets written to the object file, generating Valgrind warnings
about using uninitialized memory. To silence those errors, and make
output more reproducible, init the line no to a dummy (0) value.
2021-03-25 11:26:58 +01:00
ISSOtm
e3fde986ad Remove hashmap collision warning
It was only used to check hashmap collision rate in the wild,
but it no longer has a purpose and produces spurious messages
2021-03-23 21:34:08 +01:00
Rangi
aa99ed056c Do not evaluate an untaken ELIF's condition
Fixes #764
2021-03-23 16:20:24 +01:00
ISSOtm
46d6652df1 Fix missing .sym/.map symbols in SECTION UNION/FRAGMENTs
Only the first "slice"'s symbols were considered, forgetting some symbols
2021-03-22 23:23:06 +01:00
ISSOtm
5406674cdd Install groff to build PDFs correctly in CI 2021-03-20 02:09:47 +01:00
ISSOtm
5d2e2e2182 Make docs update script also produce PDFs
See rgbds-www#12
2021-03-20 01:53:26 +01:00
ISSOtm
a929f36bc5 Replace UTF-8 hyphens with ASCII ones in man pages 2021-03-20 01:25:17 +01:00
ISSOtm
bdb84a901f Use sub-sections for the different symbol types
This will make them appear in the ToC, and generate HTML anchors for them
2021-03-19 11:04:22 +01:00
Rangi
093631ca0b Revise rgbasm(5) string symbol documentation
Clarify the differences between EQUS expansion and {interpolation}
2021-03-19 01:48:36 +01:00
Rangi
7e127a4e52 Don't expand string symbols in MACRO and FOR symbol names
Explicit {interpolation} can still achieve this, but
to match DEF, REDEF, and PURGE, these new directives that
define symbols do not expand string EQUS.
2021-03-19 01:48:36 +01:00
Rangi
b8093847dc New definition syntax with leading DEF keyword
This will enable fixing #457 later once the old
definition syntax is removed.
2021-03-19 01:48:36 +01:00
Rangi
8d1b56bcf5 rgblink identifies patches' PC sections after reading all sections
Fixes #794
2021-03-18 23:53:54 +01:00
ISSOtm
5fb7fcf461 Fix description of rgbgfx -h
The old description was backwards and mostly confusing.
2021-03-18 15:35:01 +01:00
Rangi
ee900ae7a3 Add a missing newline to a verbosePrint 2021-03-17 20:53:25 -04:00
ISSOtm
3ca58e13dc Fix verbose messages claiming non-existent errors
They were confusing when trying to debug other things
2021-03-14 18:52:16 +01:00
ISSOtm
aaa4e17454 Add verbose messages for early exit from -MG
Should help debugging Make invocations
2021-03-14 18:28:05 +01:00
daid
a81d383f75 Alignment mask was incorrectly checked for 1 instead of 0
This caused an `ALIGN[1]` to be ignored.
2021-03-11 13:24:59 +01:00
ISSOtm
60019cf476 Fix a bunch of Clang warnings
As reported by #789
Should avoid relying on 32-bit int (for implicit conversions)
and account for more extreme uses of RGBDS.
2021-03-10 10:56:57 +01:00
ISSOtm
5a6a44cbc1 Fix master documentation updater
Its path was not synced with a recent change
2021-03-10 01:15:51 +01:00
ISSOtm
b61a187b19 Add forgotten __RGBDS_RC__ symbol 2021-03-10 01:09:18 +01:00
ISSOtm
6382f13647 Release 0.5.0-rc1 2021-03-10 01:04:23 +01:00
ISSOtm
714d39c86f Make some INCBIN errors non-fatal 2021-03-10 01:02:45 +01:00
ISSOtm
f11241c2ae Add INCBIN tests 2021-03-10 01:02:45 +01:00
daid
cb47ac8b97 Change the start position check on INCBIN
Currently INCBIN gives an error if your start position == file size.
This means you cannot include an empty file.
It also means the text of the error message is incorrect
(as the start is not greater, but equal to the length of the file)
2021-03-10 01:02:45 +01:00
ISSOtm
028e7af7d1 Prepare release candidates
We'll use "-rcX" instead of "-pre" to allow multiple ones, jic
Additionally, they will be able to be detected using __RGBDS_RC__
Finally, adapt everything version-related to this new system
2021-03-10 00:06:32 +01:00
ISSOtm
e141da1d94 Compute fallback version string at compile time 2021-03-09 23:59:34 +01:00
ISSOtm
35367282e4 Add workflow to auto-prepare a release
Just push a tag beginning with "v" and a number to trigger it
2021-03-09 22:58:42 +01:00
ISSOtm
4c0fa6732e Fix release doc creation workflow 2021-03-09 22:58:42 +01:00
ISSOtm
e9d9a44687 Make release doc update also register docs 2021-03-09 22:58:42 +01:00
ISSOtm
611bd46e0b Ignore CRLF vs LF for syntax error test check 2021-03-09 22:58:32 +01:00
ISSOtm
e67254093e Delete version test if stale 2021-03-09 22:58:00 +01:00
ISSOtm
3ce3d2f662 Explicitly force tested projects to use tested bins 2021-03-09 22:56:59 +01:00
ISSOtm
3da9d81071 Ignore DLLs too
Since you may want to put them next to the EXEs here
2021-03-09 22:46:17 +01:00
Rangi
88646093ca Swap the Name and Type columns in Predeclared Symbols 2021-03-05 17:00:06 -05:00
Rangi
0a0427afbb windows-xbuild CI uses ubuntu-18.04 for its gcc 7.3.0 (#784)
GitHub's ubuntu-18.04 environment installs gcc 7.3.0.
The latest ubuntu-20.04 environment installs gcc 9.3.0.
Version 9.3.0 is incompatible with building libpng 1.6.37,
as of 2021-03-04.

Fixes #783
2021-03-05 11:27:12 -05:00
Rangi
88048cdcd7 Clarify the character encoding example in rgbasm.5 2021-03-05 10:08:43 -05:00
Rangi
c878ff8775 Report slack totals at the end of the .map file (#781)
Fixes #777
2021-03-05 08:53:27 -05:00
dannye
a4049a3f1b rgbasm(5): Clarify charmap behavior (#558) 2021-03-05 13:16:49 +01:00
Rangi
8a75cc5051 Test an indented anonymous label 2021-03-03 12:26:35 -05:00
Rangi
b674c5fcaa Comment refers to "built-in" symbols 2021-03-03 10:35:47 -05:00
daid
a6d3df7ac9 Put all local symbols in object/sym/map files (#774)
* Store all the local symbols in object files, not only the ones that need linker patches.
  This generates better map/sym files, and thus allows for better debugging as well.

* Add comments explaining the ->src and the (void)arg;
2021-03-03 10:59:51 +01:00
Rangi
365484ef18 Factor out handleCRLF()
This logic repeats ten times
2021-03-03 10:03:52 +01:00
Rangi
5e2bd35239 Factor out a 'plain_directive' parser rule similar to the reverted 'last_line'
Also expand on the comment explaining how the EOF-newline hack affects the parser
2021-03-02 20:46:17 -05:00
Rangi
6655e04ef0 Restore the "EOF-newline" lexer hack
This was removed in b3c0db218d
(along with two unrelated changes).

Removing this hack introduced issue #742, whereby INCLUDing
a file without a trailing newline can cause a syntax error.

A more proper fix would involve Bison's tracking locations,
but for now the EOF-newline hack fixes the issue while only
affecting some reported errors (expecting "newline"
instead of "end of file").

Fixes #742
2021-03-02 16:53:56 -08:00
ISSOtm
40c6b840f8 Add tests for certain directives at EOF without a newline 2021-03-02 16:53:56 -08:00
daid
5d6e0677d9 Fix error-related issues (#773)
* Mark `error` as a `format` function, to properly scan its format

* Fix the call to error() from parser.y:
  - Use '%s' to avoid passing an arbitrary format
  - Simplify yyerror overall

* Fix size parameter of %.*s format being an int... bonkers standard.

* Report the number of arguments required and provided on a STRFMT mismatch

* Add an assert to check for a very unlikely bug
2021-03-02 16:34:19 +01:00
Rangi
56071599e7 Allow trailing commas in bare lists
This applies to macro arguments, DB, DW, DL, DS,
PRINT, PRINTLN, EXPORT, PURGE, and OPT.

It also removes support for empty entries in DB/DW/DL.
(Deprecating it would require keeping parser support,
which is ambiguous with trailing commas.)

Fixes #753
2021-03-02 11:48:20 +01:00
Rangi
c637447d5d Make the "db/dw/dl directive without data in ROM" warning more specific
Also use uppercase for DB/DW/DL to be consistent
2021-03-02 11:48:20 +01:00
Rangi
ac2cefdd87 Refactor some math functions into a shared file for rgbasm and rgblink
Fixes #769

Fixes #770
2021-03-02 11:40:03 +01:00
Rangi
0774f5eb9d Rename math.c/mymath.h to fixpoint.c/.h
This also changes the functions' prefix
from "math_" to "fix_".
2021-03-02 11:40:03 +01:00
Rangi
76d8862900 Refactor part of getSection into createSection
getSection validates its parameters and calls
either mergeSections or createSection.
2021-02-28 16:12:03 -05:00
Rangi
1dafc1c762 Allow empty macro arguments, with a warning
Fixes #739
2021-02-28 10:38:49 -08:00
Rangi
63d15ac8c9 append_yylval_tzString should always evaluate its argument
Fixes #762
2021-02-28 10:38:49 -08:00
Rangi
1f579deaff Trim right whitespace from macro args before warning about length 2021-02-28 10:38:49 -08:00
Rangi
fad48326f8 Support /* block comments */ in macro arguments
Fixes #746
2021-02-28 10:38:49 -08:00
Rangi
e7d6ddf593 Fix linking tiny overlay files (#755)
* Fix compatibility of rgblink -O and -t

The -t "tiny mode" option makes ROM0 cover 0x8000 bytes,
not 0x4000. The -O "overlay" option fills areas uncovered
by sections with data from an overlay file. These needed
to cooperate so that the calculated uncovered overlay size
does not exceed the actual size of the ROM.

Fixes #754

* Print link test names like asm tests do

* Make the three test.sh scripts more similar
2021-02-24 23:04:51 -05:00
Rangi
953f79c0d9 Support 'MACRO mac' as well as 'mac: MACRO' for defining macros
The new syntax is used in documentation, but
the old syntax is not yet deprecated.
2021-02-25 04:42:35 +01:00
Rangi
3c5e1caa7c Disallow "." as a local label
Fixes #760
2021-02-25 04:40:42 +01:00
Rangi
d4028fff10 Prevent ELIF or ELSE after an ELSE
Fixes #749
2021-02-25 04:39:49 +01:00
Rangi
dd892d61d8 Correct rgbasm(5) about whitespace before labels
Also rephrase some more label-related documentation
2021-02-23 15:31:29 -05:00
Rangi
a09f2d4115 Test an indented macro label 2021-02-23 14:48:27 -05:00
Eldred Habert
dafef5a953 Remove column 1 restriction for labels with colons (#635)
Partial fix for #457
2021-02-23 14:42:24 -05:00
dannye
929e2a4490 rgbasm: Report conflicting file/line number for duplicate sections 2021-02-23 20:08:59 +01:00
dannye
cc1129093d Add duplicate-section test 2021-02-23 20:08:59 +01:00
ISSOtm
72911ada2d Prevent non-numeric symbol from overriding refs
Fixes #751
2021-02-22 13:00:25 +01:00
Rangi
037bc7abb3 Add an rgblink test case for an $8000-byte ROM0 section with -t 2021-02-21 16:30:02 -05:00
Rangi
5fd636ac4b Implement floored / and divisor-sign % operators (#745)
Fixes #703
2021-02-21 16:14:44 -05:00
Rangi
a6d844a9a5 Add more test cases for IF, REPT, and MACRO (#748) 2021-02-19 19:34:21 -05:00
ISSOtm
bee62076c6 Allow ENDC at EOF without a newline 2021-02-20 00:51:33 +01:00
ISSOtm
cd072d5e6a Add assertion check for potential UAF trigger
It actually currently triggers if an INCLUDE directive is given at EOF,
but #742 will fix that.
2021-02-19 16:53:30 +01:00
ISSOtm
6ef3ee1391 Give void* ptr to %p formatter
Silences a format type mismatch warning
2021-02-19 16:53:26 +01:00
ISSOtm
c29b616f93 Fix forgotten initialization of lexerState->isReferenced 2021-02-18 16:33:06 +01:00
Rangi
39c179aa32 Fix missing newline in a dbgPrint 2021-02-17 13:14:02 -05:00
Rangi
5c1ae4ce22 Shorter quine test cases 2021-02-17 11:51:21 -05:00
Rangi
efbfeca292 Avoid two peek(1) calls when lexing """multi-line strings""" 2021-02-17 10:36:36 -05:00
Rangi
7dd34f1572 Format INT32_MIN as '-2147483648', not '--2147483648' 2021-02-17 09:48:40 -05:00
Rangi
748e7dd4c7 Fix calculation of 2**30
In exponent(), 'base *= base;' should not run
when base is 65536, since it overflows an int32_t.

This also optimizes exponent() based on
gcc and clang -O3 test cases in godbolt.org.
2021-02-17 09:25:02 -05:00
Rangi
d049ffc0f0 Handle string literals within macro arguments (#685)
Fixes #683 and #691

The lexer's raw mode for reading macro args already attempted
to handle semicolons inside string literals, versus outside ones
which start comments. This change reuses the same function for
reading string literals in normal and raw modes, also handling:

- Commas in strings versus between macro args
- Character escapes
- {Interpolations} and \1-\9 args inside vs. outside strings
- Multi-line string literals

Macro args now allow escaping '\', '"', and '\\'.

A consistent model for expanding macro args and interpolations,
within macro args, string literals, and normal context:

- "{S}" should always equal the contents of S
- "\1" should always act like quoting the value of \1
2021-02-16 22:44:25 -05:00
Rangi
8c0275480c Allow ds to take multiple values to repeat for a count (#725)
Fixes #722
2021-02-16 22:01:23 -05:00
Rangi
76d6ef8695 Implement LOAD UNION and LOAD FRAGMENT
Fix #632
2021-02-17 03:42:06 +01:00
Rangi
c67a696a87 Use macros to convert between radians and fixed-point 65536ths 2021-02-16 18:07:05 -05:00
Rangi
ee20d9010e Make @ relative to the start of a ds even at link time
Fix #737
2021-02-16 22:47:07 +01:00
Rangi
2bc12447e2 Update rgbds(5) object file format documentation (#740) 2021-02-16 16:36:37 -05:00
Rangi
cb61da8842 -Wmacro-shift warns about shifting macro arguments too far (#741)
Fixes #735
2021-02-16 16:31:01 -05:00
ISSOtm
122ba6eba5 Update deprecated feature examples
Those have been removed, lol
2021-02-16 22:09:31 +01:00
Rangi
dddff0f450 Cannot start a new section that's already on the stack
This is only relevant for FRAGMENT or UNION sections, since
normal ones would fail with "Section already defined previously".

Fixes #730
2021-02-16 21:51:48 +01:00
Antonio Vivace
a919f922a1 Add FUNDING.yml to show the Sponsor button on the repository 2021-02-16 21:23:27 +01:00
Rangi
8f20620c16 Allow shifting macro arguments by a negative amount
Fixes #733
2021-02-14 16:21:00 +01:00
Rangi
96bce05be2 Newlines in multi-line strings update the line number
This affects error and warning messages, and dbgPrint
2021-02-14 16:16:52 +01:00
Rangi
fc2bf3d11d Prefer sizeof($$) to MAXSTRLEN + 1
This makes `strsubUTF8` similar to `strrpl` and `strfmt`
2021-02-14 16:16:52 +01:00
Rangi
8415ce3ed0 Correct the keywordDict size
The stale keywords XDEF and GLOBAL were removed,
so there can be fewer keyword nodes.
2021-02-14 16:16:52 +01:00
Rangi
ebb5aab6db Correct some comments
nPCOffset no longer exists, and the lexer only
returns T_NEWLINE for EOF in raw mode.
2021-02-14 16:16:52 +01:00
Rangi
464a3a4892 Separate extern getopt implementation from the unistd.h one
Fixes #710
2021-02-12 00:29:21 +01:00
ISSOtm
88e1cc7302 Make EOF token name consistent across Bison versions
The "end of file" name apparently only became a default recently
2021-02-11 13:04:43 +01:00
ISSOtm
b3c0db218d Remove "EOF-newline" lexer hack
In preparation for an upcoming change
Makes for nicer error messages, complaining about EOF instead of newlines
The hack had to be kept for the lexer raw mode to avoid a bug;
see the relevant code comment for more info.
2021-02-11 12:48:37 +01:00
ISSOtm
76446e6d00 Change behavior of merging FRAGMENTs to constrain each fragment individually
Additionally, remove the deprecated merging of non-fragment SECTIONs
2021-02-10 10:19:16 +01:00
ISSOtm
6623b1dc45 Fix CI on macOS
Apple supplies version 2.3 (from 2006!!), which doesn't support `%empty`.
2021-01-23 13:45:12 +01:00
ISSOtm
70bbb098d3 Remove stale keywords
They were removed from the grammar, but not the lexer
2021-01-23 00:05:56 +01:00
ISSOtm
1926065377 Enable Bison warnings
-Wall should be old enough.
Also use %empty instead of comments
2021-01-23 00:05:56 +01:00
Rangi
e3d355d976 Attempt to recover from syntax errors with bison
Fixes #595
2021-01-22 15:54:24 +01:00
ISSOtm
a679e02246 Get rid of asm.h and localasm.h
No need to make them global
2021-01-22 13:34:16 +01:00
ISSOtm
592e9b3725 Reorganize and comment better main() 2021-01-22 11:09:27 +01:00
ISSOtm
effc58241d Rework defining variables on command-line
Avoids allocating memory
Detects CLI errors even when failing to open file
Reports symbols as defined on command-line instead of input line 0 (!?)
2021-01-22 11:03:43 +01:00
ISSOtm
3697065afc Add asm/opt.c to CMakeLists
Fixes compiling with CMake
2021-01-22 11:01:33 +01:00
ISSOtm
fa0fa4d5ac Significantly overhaul OPT code
Simplify the mess that was option setting (2 redundant variables !?)
Move options to a separate file
Have "modules" own their options, and OPT only access them (less redundancy)
Simplify code, respect naming conventions better
2021-01-22 10:41:09 +01:00
ISSOtm
5acc48fa54 Error out when given several input files
Also rename tzMainFile
2021-01-22 09:44:41 +01:00
ISSOtm
1487eebe79 Remove time counter
Nobody uses that, and it requires running extra code for each line
2021-01-22 08:51:57 +01:00
ISSOtm
993c034039 Avoid using EXPAND_AND_STR with external defines
There is no guarantee that they are purely numeric, use the values instead
2021-01-22 08:51:35 +01:00
ISSOtm
09f16bda4a Use putc instead of fputc
Also rename some functions for consistency
2021-01-22 08:32:28 +01:00
ISSOtm
41d544a4eb Rewrite RGBFIX
- Make it work inside pipelines
- Add RGBFIX tests to the suite
- Be more flexible in accepted MBC names
- Add warnings for dangerous or nonsensical input params
- Improve man page
2021-01-20 21:22:55 +01:00
Rangi
f28b4abafc Fix a potential buffer overflow in strrpl
This caused an error using clang with -O3 -flto
2021-01-20 10:21:53 +01:00
ISSOtm
ca1c934629 Have CMake use specified compiler in CI, not system default
Otherwise it's just GCC on Linux and Clang on macOS...
2021-01-19 16:35:06 +01:00
Rangi
d5a00cf634 Comment cites the string hash algorithm (djb2)
"If you just want to have a good hash function,
and cannot wait, djb2 is one of the best string
hash functions I know."
2021-01-19 15:03:22 +01:00
Rangi
fb39c3a70e Consistently refer to "directives", not "pseudo-ops"
Some docs and warnings already referred to SECTION and
db/dw/dl "directives", but others used "pseudo-ops".
2021-01-19 15:03:22 +01:00
ISSOtm
15ec6efc28 Fix missing newline in charmap override warning 2021-01-17 20:13:51 +01:00
Rangi
93d83e17dc Don't override bison's internal 'yytnamerr' function
This is not used in all bison versions, which causes a
"defined but not used" warning for the 'rgbasm_yytnamerr'
replacement, treated as an error by 'make develop'.
2021-01-15 11:51:31 +01:00
Rangi
df16e64fc6 Handle MACRO and REPT/FOR bodies differently
Fixes #697
2021-01-15 02:16:37 +01:00
Rangi
a4ebb87858 Add Rangi to contributors 2021-01-14 18:36:10 +01:00
Rangi
5ef8e0a1f6 Use an IELR parser if available 2021-01-14 18:36:10 +01:00
Rangi
eb4952c188 Use more verbose syntax error messages
Fixes #385
2021-01-14 18:36:10 +01:00
ISSOtm
57b734a7df Reinstate RL into the _RS family
Removal of colon-less labels lifted the grammar ambiguity that
prevented `RL` from being usable as a variable declarator.
Thus, reinstate its functionality.
2021-01-11 01:38:03 +01:00
ISSOtm
5be1c0da62 Fix intra-section ALIGN not computing offset correctly 2021-01-09 23:29:08 +01:00
ISSOtm
b598911e96 Enable LTO in release builds
Fixes #693
2021-01-09 20:31:15 +01:00
Rangi
cab9cb06a3 Store IF depth relative to each fstack context
This disallows starting/ending an IF inside an
INCLUDEd file or a macro expansion
2021-01-09 20:12:14 +01:00
Rangi
62bea23c49 Implement BREAK to exit REPT and FOR loops
Fixes #684
2021-01-08 21:13:23 +01:00
Rangi
7ce5cf1595 Convert floating to fixed point by rounding, not truncation
Fixes #678
2021-01-04 21:23:43 +01:00
Rangi
7e3fc1db03 Fix Actions CI for MSVC
Fixes #616
2021-01-04 02:01:25 +01:00
Rangi
77279984a5 Implement STRRPL
Fixes #660

STRRPL(str, "", new) does nothing
(warn about it with -Wempty-strrpl)
2021-01-04 00:20:35 +01:00
Rangi
669a392fcd Revise the rgbasm(5) docs 2021-01-02 08:29:57 +01:00
ISSOtm
51ce0b038a Remove removed features from documentation 2021-01-02 02:47:13 +01:00
ISSOtm
bd244e6865 Remove deprecated features
Trimming off the fat!
- GLOBAL and XDEF keywords
- Colon-less global labels
- *-comments
2021-01-02 02:42:44 +01:00
Rangi
a70ecba06f Implement PRINT and PRINTLN (#672)
Fixes #669
Closes #368
Closes #624

Deprecate PRINTT, PRINTV, PRINTI, and PRINTF

Default STRFMT("%f") to 5 fractional digits like "{f:}"
Any use of string formatting will share this default
2021-01-02 02:37:32 +01:00
Rangi
9d2d5cfcfe Implement REDEF to allow redefining EQUS string equates
Fixes #677
2021-01-02 01:49:00 +01:00
ISSOtm
18f3c8ff9a Un-document deprecated _PI 2021-01-02 01:43:49 +01:00
Rangi
895ec5564d Update mathematical functions (#675)
Document the existing `ROUND`, `CEIL`, and `FLOOR` functions
Also update the trig function docs for searchability

Implement `POW` and `LOG`
Addresses part of #675

Implement ** for integer exponents
** has higher precedence than -, like Python, so -3**4 == -(3**4) == 81
2021-01-02 01:39:20 +01:00
Rangi
7bb6f71f0b Change FOREACH to FOR (#680) 2021-01-02 00:46:26 +01:00
Rangi
10e3f1a02b Deprecate built-in _PI
Fixes #670
2021-01-01 19:18:17 +01:00
Rangi
2a9d52871b Make dbgPrint in lexer.c report the correct colNo (#676)
Fixes #656
2021-01-01 18:44:47 +01:00
ISSOtm
c4fb5591ee Fix size of unterminated REPT/FOREACH blocks
Do not "uncapture" ENDR if it was not read in the first place
2020-12-30 15:52:24 +01:00
Rangi
c0ce1da4c3 Implement STRFMT and more printf-like format specifiers for string interpolation (#646)
Fixes #570
Fixes #178

Use errors for inapplicable format spec flags instead of -Wstring-format
2020-12-29 22:53:15 +01:00
ISSOtm
aa27e714d4 Make Bison version detection more portable
- POSIX sh actually does NOT support `+=`,
  but Bash does even in compatibility mode
- `mawk` does not fully support POSIX EREs (regexes),
  so work around its lack of brace support
Fixes building on Debian, apparently.
2020-12-29 22:31:15 +01:00
Rangi
6874f694e5 Implement FOREACH (#658)
This acts like `REPT` with a variable automatically
incremented across a range of values

Fixes #432
2020-12-29 21:30:42 +01:00
Rangi
3690546795 make checkpatch and make checkcodebase check the same files
Only check src and include (not test), and
exclude src/extern and include/extern.
2020-12-29 20:27:00 +01:00
ISSOtm
7bc42d468b Clean up temp test files even if interrupted
Avoids "tmp.*" piling up in /tmp
2020-12-26 14:38:04 +01:00
ISSOtm
097999cad3 Prevent tests from running if RGBDS hasn't been built
Prevents a *lot* of spurious errors due to files not generating
2020-12-26 14:26:50 +01:00
ISSOtm
f82edaa7ec Make gbdiff.bash handle CRLF sym files gracefully
Additionally, reduce the amount of lines piped through `cut`,
improving performance
2020-12-26 02:47:04 +01:00
ISSOtm
d8e8b796e7 Update tested projects to latest commits
Compatibility fixes, etc.
2020-12-26 02:02:38 +01:00
ISSOtm
900fd8cabc Improve gbdiff script
Use better constructs where possible
- <<< instead of echo|
- Parameter expansions instead of `cut`
- Etc.
Improves performance and reliability somewhat

Also, accept "d" between diff line info parts
2020-12-26 01:44:45 +01:00
ISSOtm
c20ac35074 Refactor readString
Much more readable, and avoids `peek(nz)`
2020-12-19 13:02:05 +01:00
Rangi
255b8bf9ba Implement """triple-quoted""" multi-line strings
Fixes #589
2020-12-19 12:34:32 +01:00
Rangi
ad6f17cd93 Support SOURCE_DATE_EPOCH for reproducible builds
See https://reproducible-builds.org/docs/source-date-epoch/

Fixes #286
2020-12-19 01:09:45 +01:00
Rangi
063a22ddf9 LOAD blocks cannot create a ROM section
Fixes #576
2020-12-19 00:58:54 +01:00
Rangi
1d9cc01ae1 Macro arguments within a string literal are read into the string, not expanded
Fixes #643
2020-12-15 21:28:15 +01:00
Rangi
f31deb5010 Fix STRUPR and STRLWR after 5aabb915ec
Fixes #647
2020-12-15 20:18:45 +01:00
Rangi
0956d300c4 Document \# 2020-12-14 10:48:03 +01:00
ISSOtm
4ef490f3cb Avoid interpreting Liquid in doc pages
They are auto-generated, so they wouldn't normally contain Liquid
(The post-processor may insert some, but it has control over the `raw` tags)
Some code introduced as examples contains `{{` due to nested brace expansions,
which was interpreted as (invalid) Liquid. This fixes such problems.
Additionally, Jekyll generates warnings about excerpts being part of a Liquid
block (the `{% raw %}`). This is fine, since doc pages don't use excerpts.
2020-12-14 10:35:30 +01:00
ISSOtm
8f2a894b88 Add anonymous labels
Fix #497
2020-12-14 10:14:40 +01:00
Rangi
0e40543757 Implement \# to expand to all unshifted macro arguments
Fixes #596
2020-12-14 00:12:36 +01:00
Rangi
ce58f6d6be Allow {symbol} interpolation outside of strings
Fixes #629

Closes #631
2020-12-13 23:53:16 +01:00
ISSOtm
5aabb915ec Allow STRCAT to take any number of args
Fixes bullet point 1 of #625
2020-12-12 23:46:32 +01:00
ISSOtm
0d9de01f9d Make charmap-converting a non-UTF8 string non-fatal 2020-12-12 14:16:50 +01:00
ISSOtm
f5b0eae9cd Remove custom action code when equivalent to default
Enables Bison to better reason about it, and should improve performance
2020-12-12 12:22:36 +01:00
Rangi
e6552064bf Specify rgbfix --mbc-type by name
This supports the names listed in Pan Docs:
https://gbdev.io/pandocs/#_0147-cartridge-type
Spaces may be replaced with underscores.

It also supports "ROM" for "ROM ONLY".
2020-12-12 01:53:42 +01:00
Rangi
861cb552c4 discardBlockComment sets lexerState->disableMacroArgs = true, like discardComment 2020-12-12 01:34:01 +01:00
ISSOtm
417cceb0de Document dw and dl with strings 2020-12-11 11:02:20 +01:00
Rangi
165bd8cb71 Allow 'dw' and 'dl' to apply to characters of strings
Fixes #568

The old behavior of `dw "string"` can be replicated with `dw ("string")`; likewise for dl
2020-12-11 11:02:20 +01:00
ISSOtm
e54e02dc77 Correct underscore-in-number documentation
No radix actually allows underscores at the beginning of a literal
2020-12-10 16:01:18 +01:00
ISSOtm
2bf3b08d76 Document underscores in numeric literals 2020-12-10 15:40:59 +01:00
ISSOtm
21b58b08b8 Fix not shifting CRLF at end of raw lines
Fixes line reporting errors on Windows
2020-12-10 15:37:50 +01:00
Rangi
af530859f0 Allow underscores in numeric literals
Fixes #539

Changes \@'s output to start with "_u", not "_", so it will be valid within labels but not numerics
2020-12-10 15:34:21 +01:00
Rangi
58739b0bf2 Implement STRRIN, like STRIN but searching from the right 2020-12-10 15:32:17 +01:00
ISSOtm
3e4c2fe712 Avoid error with old Bison versions
`api.token.raw` is only defined starting with Bison 3.5
Since it's not essential, define it on the command-line iff the Bison
version is sufficient.
2020-12-10 15:13:45 +01:00
ISSOtm
bdfce25db0 Avoid running version test when git describe fails
Can happen when not enough history has been fetched, notably in our CI
2020-12-10 13:43:22 +01:00
ISSOtm
2b6d9cd1e0 Avoid using yytoken_kind_t
Apparently it was added in a fairly recent Bison version...
2020-12-10 13:32:18 +01:00
ISSOtm
bf789dd7b3 Add automated test for version consistency
Automatically check that the version number constants
(__RGBDS_MAJOR__ etc.) match `rgbasm -V`
Should avoid the problem with 0.4.2's release...
2020-12-10 12:22:29 +01:00
ISSOtm
9b6f01047c Enable raw token types
Removes one layer of indirection for the parser, and helps remove all literals from the grammar

The latter preparing the next change
2020-12-09 21:22:05 +01:00
ISSOtm
3fe2fa43bb Switch to GNU Bison as a dependency
First step for #595
2020-12-09 20:30:31 +01:00
649 changed files with 8568 additions and 3370 deletions

3
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,3 @@
github: avivace
patreon: gbdev01
open_collective: gbdev

View File

@@ -7,6 +7,14 @@
sub(/b><\/td/, "th"); sub(/b><\/td/, "th");
} }
# The whole page is being generated, so it's not meant to contain any Liquid
BEGIN {
print "{% raw %}"
}
END {
print "{% endraw %}"
}
BEGIN { BEGIN {
in_synopsis = 0 in_synopsis = 0
} }

View File

@@ -5,20 +5,21 @@ usage() {
Usage: $0 [-h] [-r] <rgbds-www> <version> Usage: $0 [-h] [-r] <rgbds-www> <version>
Copy renders from RGBDS repository to rgbds-www documentation Copy renders from RGBDS repository to rgbds-www documentation
Execute from the root folder of the RGBDS repo, checked out at the desired tag Execute from the root folder of the RGBDS repo, checked out at the desired tag
<rgbds-www> : Path to the '_documentation' folder in the rgbds-www repository <rgbds-www> : Path to the rgbds-www repository
<version> : Version to be copied, such as 'v0.4.1' or 'master' <version> : Version to be copied, such as 'v0.4.1' or 'master'
-h Display this help message -h Display this help message
-r Update "latest stable" redirection pages (use for releases, not master) -r Update "latest stable" redirection pages and add a new entry to the index
(use for releases, not master)
EOF EOF
} }
update_redirects=0 is_release=0
bad_usage=0 bad_usage=0
while getopts ":hr" opt; do while getopts ":hr" opt; do
case $opt in case $opt in
r) r)
update_redirects=1 is_release=1
;; ;;
h) h)
usage usage
@@ -50,10 +51,10 @@ PAGES=(
[gbz80.7.html]=src/gbz80.7 [gbz80.7.html]=src/gbz80.7
) )
WWWPATH="/docs" WWWPATH="/docs"
mkdir -p "$1/$2" mkdir -p "$1/_documentation/$2"
# `mandoc` uses a different format for referring to man pages present in the **current** directory # `mandoc` uses a different format for referring to man pages present in the **current** directory.
# We want that format for RGBDS man pages, and the other one for the t=rest; # We want that format for RGBDS man pages, and the other one for the rest;
# we thus need to copy all pages to a temporary directory, and process them there. # we thus need to copy all pages to a temporary directory, and process them there.
# Copy all pages to current dir # Copy all pages to current dir
@@ -64,7 +65,7 @@ stem="${page%.html}"
manpage="${stem%.?}(${stem#*.})" manpage="${stem%.?}(${stem#*.})"
descr="$(awk -v 'FS=.Nd ' '/.Nd/ { print $2; }' "${PAGES[$page]}")" descr="$(awk -v 'FS=.Nd ' '/.Nd/ { print $2; }' "${PAGES[$page]}")"
cat - >"$1/$2/$page" <<EOF cat >"$1/_documentation/$2/$page" <<EOF
--- ---
layout: doc layout: doc
title: $manpage [$2] title: $manpage [$2]
@@ -75,9 +76,10 @@ EOF
if [ $stem = rgbasm.5 ]; then if [ $stem = rgbasm.5 ]; then
options+=,toc options+=,toc
fi fi
mandoc -Thtml -I os=Linux -O$options "${PAGES[$page]##*/}" | .github/actions/doc_postproc.awk >> "$1/$2/$page" mandoc -Thtml -I os=Linux -O$options "${PAGES[$page]##*/}" | .github/actions/doc_postproc.awk >> "$1/_documentation/$2/$page"
if [ $update_redirects -ne 0 ]; then groff -Tpdf -mdoc -wall "${PAGES[$page]##*/}" >"$1/_documentation/$2/$stem.pdf"
cat - >"$1/$page" <<EOF if [ $is_release -ne 0 ]; then
cat - >"$1/_documentation/$page" <<EOF
--- ---
redirect_to: $WWWPATH/$2/${page%.html} redirect_to: $WWWPATH/$2/${page%.html}
permalink: $WWWPATH/${page%.html}/ permalink: $WWWPATH/${page%.html}/
@@ -87,7 +89,8 @@ description: RGBDS latest stable — $descr
EOF EOF
fi fi
done done
cat - >"$1/$2/index.html" <<EOF
cat - >"$1/_documentation/$2/index.html" <<EOF
--- ---
layout: doc_index layout: doc_index
permalink: /docs/$2/ permalink: /docs/$2/
@@ -96,5 +99,15 @@ description: RGBDS $2 - Online manual
--- ---
EOF EOF
# If making a release, add a new entry right after `master`
if [ $is_release -ne 0 ]; then
awk '{ print }
/"name": "master"/ { print "\t\t{\"name\": \"'$2'\", \"text\": \"'$2'\" }," }
' "$1/_data/doc.json" >"$1/_data/doc.json.tmp"
mv "$1/_data/doc.json"{.tmp,}
fi
# Clean up # Clean up
rm "${PAGES[@]##*/}" rm "${PAGES[@]##*/}"

View File

@@ -4,11 +4,15 @@ case `echo $1 | cut -d '-' -f 1` in
sudo apt-get install -yq bison libpng-dev pkg-config sudo apt-get install -yq bison libpng-dev pkg-config
;; ;;
macos) macos)
brew install libpng pkg-config md5sha1sum brew install bison libpng pkg-config md5sha1sum
# For the version check below exclusively, re-do this before building
export PATH="/usr/local/opt/bison/bin:$PATH"
;; ;;
*) *)
echo "WARNING: Cannot install deps for OS '$1'" echo "WARNING: Cannot install deps for OS '$1'"
;; ;;
esac esac
yacc --version bison --version
make --version
cmake --version

View File

@@ -0,0 +1,96 @@
name: "Create release artifacts"
on:
push:
tags:
- v[0-9]*
jobs:
windows:
runs-on: windows-2019
steps:
- uses: actions/checkout@v2
- name: Get version from tag
shell: bash
run: | # Turn "refs/tags/vX.Y.Z" into "X.Y.Z"
VERSION="${{ github.ref }}"
echo "version=${VERSION##*/v}" >> $GITHUB_ENV
- name: Get zlib, libpng and bison
run: | # TODO: use an array
$wc = New-Object System.Net.WebClient
$wc.DownloadFile('https://www.zlib.net/zlib1211.zip', 'zlib.zip')
$hash = (Get-FileHash "zlib.zip" -Algorithm SHA256).Hash
if ($hash -ne 'd7510a8ee1918b7d0cad197a089c0a2cd4d6df05fee22389f67f115e738b178d') {
Write-Host "zlib SHA256 mismatch! ($hash)"
exit 1
}
$wc.DownloadFile('https://download.sourceforge.net/libpng/lpng1637.zip', 'libpng.zip')
$hash = (Get-FileHash "libpng.zip" -Algorithm SHA256).Hash
if ($hash -ne '3b4b1cbd0bae6822f749d39b1ccadd6297f05e2b85a83dd2ce6ecd7d09eabdf2') {
Write-Host "libpng SHA256 mismatch! ($hash)"
exit 1
}
$wc.DownloadFile('https://github.com/lexxmark/winflexbison/releases/download/v2.5.23/win_flex_bison-2.5.23.zip', 'winflexbison.zip')
$hash = (Get-FileHash "winflexbison.zip" -Algorithm SHA256).Hash
if ($hash -ne '6AA5C8EA662DA1550020A5804C28BE63FFAA53486DA9F6842E24C379EC422DFC') {
Write-Host "bison SHA256 mismatch! ($hash)"
}
Expand-Archive -DestinationPath . "zlib.zip"
Expand-Archive -DestinationPath . "libpng.zip"
Expand-Archive -DestinationPath install_dir "winflexbison.zip"
Move-Item zlib-1.2.11 zlib
Move-Item lpng1637 libpng
- name: Build 32-bit zlib
run: | # BUILD_SHARED_LIBS causes the output DLL to be correctly called `zlib1.dll`
cmake -S zlib -B zbuild32 -A Win32 -DCMAKE_INSTALL_PREFIX=install_dir -DBUILD_SHARED_LIBS=ON
cmake --build zbuild32 --config Release
cmake --install zbuild32
- name: Build 32-bit libpng
run: |
cmake -S libpng -B pngbuild32 -A Win32 -DCMAKE_INSTALL_PREFIX=install_dir -DPNG_SHARED=ON -DPNG_STATIC=ON -DPNG_TESTS=OFF
cmake --build pngbuild32 --config Release
cmake --install pngbuild32
- name: Build 32-bit Windows binaries
run: |
cmake -S . -B build32 -A Win32 -DCMAKE_INSTALL_PREFIX=install_dir -DCMAKE_BUILD_TYPE=Release
cmake --build build32 --config Release
cmake --install build32
- name: Package 32-bit binaries
run: |
Compress-Archive -LiteralPath @("install_dir/bin/rgbasm.exe", "install_dir/bin/rgblink.exe", "install_dir/bin/rgbfix.exe", "install_dir/bin/rgbgfx.exe", "install_dir/bin/zlib1.dll", "install_dir/bin/libpng16.dll") "rgbds-${{ env.version }}-win32.zip"
- name: Build 64-bit zlib
run: | # BUILD_SHARED_LIBS causes the output DLL to be correctly called `zlib1.dll`
cmake -S zlib -B zbuild64 -A x64 -DCMAKE_INSTALL_PREFIX=install_dir -DBUILD_SHARED_LIBS=ON
cmake --build zbuild64 --config Release
cmake --install zbuild64
- name: Build 64-bit libpng
run: |
cmake -S libpng -B pngbuild64 -A x64 -DCMAKE_INSTALL_PREFIX=install_dir -DPNG_SHARED=ON -DPNG_STATIC=ON -DPNG_TESTS=OFF
cmake --build pngbuild64 --config Release
cmake --install pngbuild64
- name: Build 64-bit Windows binaries
run: |
cmake -S . -B build64 -A x64 -DCMAKE_INSTALL_PREFIX=install_dir -DCMAKE_BUILD_TYPE=Release
cmake --build build64 --config Release
cmake --install build64
- name: Package 64-bit binaries
run: |
Compress-Archive -LiteralPath @("install_dir/bin/rgbasm.exe", "install_dir/bin/rgblink.exe", "install_dir/bin/rgbfix.exe", "install_dir/bin/rgbgfx.exe", "install_dir/bin/zlib1.dll", "install_dir/bin/libpng16.dll") "rgbds-${{ env.version }}-win64.zip"
- name: Package sources
run: |
make dist
- name: Release
uses: softprops/action-gh-release@v1
with:
body: |
Please ensure that the three assets below work properly.
Once that's done, replace this text with the changelog, un-draft the release, and update the `release` branch.
By the way, if you forgot to update `include/version.h`, RGBASM's version test is gonna fail in the tag's regression testing! (Use `git push --delete origin <tag>` to delete it)
draft: true # Don't publish the release quite yet...
prerelease: ${{ contains(github.ref, '-rc') }}
files: |
rgbds-${{ env.version }}-win32.zip
rgbds-${{ env.version }}-win64.zip
rgbds-${{ env.version }}.tar.gz
fail_on_unmatched_files: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -2,28 +2,26 @@ name: "Create release docs"
on: on:
release: release:
types: types:
- created - released # This avoids triggering on pre-releases
jobs: jobs:
build: build:
runs-on: ubuntu-18.04 runs-on: ubuntu-18.04
steps: steps:
- name: Checkout rgbds@master - name: Checkout rgbds@release
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
repository: gbdev/rgbds
ref: master
path: rgbds path: rgbds
- name: Checkout rgbds-www@master - name: Checkout rgbds-www@master
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
repository: gbdev/rgbds-www repository: ${{ github.repository_owner }}/rgbds-www
ref: master
path: rgbds-www path: rgbds-www
- name: Build and install mandoc # `-O toc` was added in 1.14.5, but the repos only have 1.14.4
- name: Build and install mandoc + install groff
run: | run: |
sudo apt-get -qq update sudo apt-get -qq update
sudo apt-get install -yq zlib1g-dev sudo apt-get install -yq groff zlib1g-dev
wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.5.tar.gz' wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.5.tar.gz'
tar xf mandoc-1.14.5.tar.gz tar xf mandoc-1.14.5.tar.gz
cd mandoc-1.14.5 cd mandoc-1.14.5
@@ -32,23 +30,23 @@ jobs:
sudo make install sudo make install
- name: Update pages - name: Update pages
working-directory: rgbds working-directory: rgbds
run: | run: | # The ref appears to be in the format "refs/tags/<version>", so strip that
./.github/actions/get-pages.sh ../rgbds-www/_documentation ${GITHUB_REF} ./.github/actions/get-pages.sh -r ../rgbds-www ${GITHUB_REF##*/}
- name: Push new pages - name: Push new pages
working-directory: rgbds-www working-directory: rgbds-www
run: | run: |
mkdir -p -m 700 ~/.ssh mkdir -p -m 700 ~/.ssh
echo "${{ secrets.SSH_KEY_SECRET }}" > ~/.ssh/id_ed25519 cat > ~/.ssh/id_ed25519 <<<"${{ secrets.SSH_KEY_SECRET }}"
chmod 0600 ~/.ssh/id_ed25519 chmod 0600 ~/.ssh/id_ed25519
eval $(ssh-agent -s) eval $(ssh-agent -s)
ssh-add ~/.ssh/id_ed25519 ssh-add ~/.ssh/id_ed25519
git config --global user.name "GitHub Action" git config --global user.name "GitHub Action"
git config --global user.email "community@gbdev.io" git config --global user.email "community@gbdev.io"
git add . git add -A
git commit -m "Create RGBDS ${GITHUB_REF} documentation" git commit -m "Create RGBDS ${GITHUB_REF##*/} documentation"
if git remote | grep -q origin; then if git remote | grep -q origin; then
git remote set-url origin git@github.com:gbdev/rgbds-www.git git remote set-url origin git@github.com:${{ github.repository_owner }}/rgbds-www.git
else else
git remote add origin git@github.com:gbdev/rgbds-www.git git remote add origin git@github.com:${{ github.repository_owner }}/rgbds-www.git
fi fi
git push origin master git push origin master

View File

@@ -27,14 +27,19 @@ jobs:
shell: bash shell: bash
run: | run: |
./.github/actions/install_deps.sh ${{ matrix.os }} ./.github/actions/install_deps.sh ${{ matrix.os }}
# The `export` lines are to allow working on macOS...
# Apple's base version is severely outdated, not even supporting -Wall,
# but it overrides Homebrew's version nonetheless...
- name: Build & install using Make - name: Build & install using Make
run: | run: |
export PATH="/usr/local/opt/bison/bin:$PATH"
make ${{ matrix.target }} -j Q= CC=${{ matrix.cc }} make ${{ matrix.target }} -j Q= CC=${{ matrix.cc }}
sudo make install -j Q= sudo make install -j Q=
if: matrix.buildsys == 'make' if: matrix.buildsys == 'make'
- name: Build & install using CMake - name: Build & install using CMake
run: | run: |
cmake -S . -B build -DCMAKE_VERBOSE_MAKEFILE=ON ${{ matrix.cmakevars }} export PATH="/usr/local/opt/bison/bin:$PATH"
cmake -S . -B build -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=${{ matrix.cc }} ${{ matrix.cmakevars }}
cmake --build build cmake --build build
cp build/src/rgb{asm,link,fix,gfx} . cp build/src/rgb{asm,link,fix,gfx} .
sudo cmake --install build sudo cmake --install build
@@ -128,7 +133,7 @@ jobs:
strategy: strategy:
matrix: matrix:
bits: [32, 64] bits: [32, 64]
os: [ubuntu-latest] os: [ubuntu-18.04]
include: include:
- bits: 32 - bits: 32
arch: i686 arch: i686

View File

@@ -31,10 +31,10 @@ jobs:
repository: gbdev/rgbds-www repository: gbdev/rgbds-www
ref: master ref: master
path: rgbds-www path: rgbds-www
- name: Build and install mandoc - name: Build and install mandoc + install groff
run: | run: |
sudo apt-get -qq update sudo apt-get -qq update
sudo apt-get install -yq zlib1g-dev sudo apt-get install -yq groff zlib1g-dev
wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.5.tar.gz' wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.5.tar.gz'
tar xf mandoc-1.14.5.tar.gz tar xf mandoc-1.14.5.tar.gz
cd mandoc-1.14.5 cd mandoc-1.14.5
@@ -44,7 +44,7 @@ jobs:
- name: Update pages - name: Update pages
working-directory: rgbds working-directory: rgbds
run: | run: |
./.github/actions/get-pages.sh ../rgbds-www/_documentation master ./.github/actions/get-pages.sh ../rgbds-www master
- name: Push new pages - name: Push new pages
working-directory: rgbds-www working-directory: rgbds-www
run: | run: |

1
.gitignore vendored
View File

@@ -5,6 +5,7 @@ rgbgfx
rgbshim.sh rgbshim.sh
*.o *.o
*.exe *.exe
*.dll
.checkpatch-camelcase.* .checkpatch-camelcase.*
CMakeCache.txt CMakeCache.txt
CMakeFiles CMakeFiles

View File

@@ -6,8 +6,8 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
# #
cmake_minimum_required(VERSION 3.0) # 3.9 required for LTO checks
cmake_policy(VERSION 3.0) cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(rgbds project(rgbds
LANGUAGES C) LANGUAGES C)
@@ -23,15 +23,15 @@ if(srcdir STREQUAL bindir)
message(FATAL_ERROR "Terminating configuration") message(FATAL_ERROR "Terminating configuration")
endif() endif()
include_directories("${PROJECT_SOURCE_DIR}/include") option(SANITIZERS "Build with sanitizers enabled" OFF) # Ignored on MSVC
option(MORE_WARNINGS "Turn on more warnings" OFF) # Ignored on MSVC
option(SANITIZERS "Build with sanitizers enabled" OFF)
option(MORE_WARNINGS "Turn on more warnings" OFF)
option(TRACE_PARSER "Trace parser execution" OFF) option(TRACE_PARSER "Trace parser execution" OFF)
option(TRACE_LEXER "Trace lexer execution" OFF) option(TRACE_LEXER "Trace lexer execution" OFF)
if(MSVC) if(MSVC)
add_compile_options(/W1 /MP) # MSVC's standard library triggers warning C5105,
# "macro expansion producing 'defined' has undefined behavior"
add_compile_options(/std:c11 /W1 /MP /wd5105)
add_definitions(/D_CRT_SECURE_NO_WARNINGS) add_definitions(/D_CRT_SECURE_NO_WARNINGS)
else() else()
add_compile_options(-Wall -pedantic) add_compile_options(-Wall -pedantic)
@@ -65,6 +65,8 @@ execute_process(COMMAND git describe --tags --dirty --always
ERROR_QUIET) ERROR_QUIET)
string(STRIP "${GIT_REV}" GIT_REV) string(STRIP "${GIT_REV}" GIT_REV)
include_directories("${PROJECT_SOURCE_DIR}/include")
add_definitions(-DBUILD_VERSION_STRING="${GIT_REV}") add_definitions(-DBUILD_VERSION_STRING="${GIT_REV}")
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
@@ -72,6 +74,20 @@ set(CMAKE_C_STANDARD_REQUIRED True)
add_subdirectory(src) add_subdirectory(src)
# By default, build in Release mode; Debug mode must be explicitly requested
# (You may want to augment it with the options above)
if(CMAKE_BUILD_TYPE STREQUAL "Release")
message(CHECK_START "Checking if LTO is supported")
include(CheckIPOSupported)
check_ipo_supported(RESULT enable_lto)
if(enable_lto)
message(CHECK_PASS "yes")
set_property(TARGET rgbasm rgblink rgbfix rgbgfx PROPERTY INTERPROCEDURAL_OPTIMIZATION ON)
else()
message(CHECK_FAIL "no")
endif()
endif()
if(TRACE_PARSER) if(TRACE_PARSER)
target_compile_definitions(rgbasm PRIVATE -DYYDEBUG) target_compile_definitions(rgbasm PRIVATE -DYYDEBUG)
endif() endif()

View File

@@ -40,6 +40,8 @@ Other contributors
- Quint Guvernator <quint@guvernator.net> - Quint Guvernator <quint@guvernator.net>
- Rangi <http://github.com/Rangi42>
- Sanqui <gsanky@gmail.com> - Sanqui <gsanky@gmail.com>
- YamaArashi <shadow962@live.com> - YamaArashi <shadow962@live.com>

View File

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

View File

@@ -33,7 +33,7 @@ VERSION_STRING := `git describe --tags --dirty --always 2>/dev/null`
WARNFLAGS := -Wall WARNFLAGS := -Wall
# Overridable CFLAGS # Overridable CFLAGS
CFLAGS ?= -O3 -DNDEBUG CFLAGS ?= -O3 -flto -DNDEBUG
# Non-overridable CFLAGS # Non-overridable CFLAGS
REALCFLAGS := ${CFLAGS} ${WARNFLAGS} -std=gnu11 -D_POSIX_C_SOURCE=200809L \ REALCFLAGS := ${CFLAGS} ${WARNFLAGS} -std=gnu11 -D_POSIX_C_SOURCE=200809L \
-Iinclude -Iinclude
@@ -43,9 +43,9 @@ LDFLAGS ?=
REALLDFLAGS := ${LDFLAGS} ${WARNFLAGS} \ REALLDFLAGS := ${LDFLAGS} ${WARNFLAGS} \
-DBUILD_VERSION_STRING=\"${VERSION_STRING}\" -DBUILD_VERSION_STRING=\"${VERSION_STRING}\"
YFLAGS ?= YFLAGS ?= -Wall
YACC := yacc BISON := bison
RM := rm -rf RM := rm -rf
# Rules to build the RGBDS binaries # Rules to build the RGBDS binaries
@@ -54,12 +54,14 @@ all: rgbasm rgblink rgbfix rgbgfx
rgbasm_obj := \ rgbasm_obj := \
src/asm/charmap.o \ src/asm/charmap.o \
src/asm/fixpoint.o \
src/asm/format.o \
src/asm/fstack.o \ src/asm/fstack.o \
src/asm/lexer.o \ src/asm/lexer.o \
src/asm/macro.o \ src/asm/macro.o \
src/asm/main.o \ src/asm/main.o \
src/asm/math.o \
src/asm/parser.o \ src/asm/parser.o \
src/asm/opt.o \
src/asm/output.o \ src/asm/output.o \
src/asm/rpn.o \ src/asm/rpn.o \
src/asm/section.o \ src/asm/section.o \
@@ -70,7 +72,8 @@ rgbasm_obj := \
src/extern/getopt.o \ src/extern/getopt.o \
src/extern/utf8decoder.o \ src/extern/utf8decoder.o \
src/hashmap.o \ src/hashmap.o \
src/linkdefs.o src/linkdefs.o \
src/opmath.o
src/asm/lexer.o src/asm/main.o: src/asm/parser.h src/asm/lexer.o src/asm/main.o: src/asm/parser.h
@@ -86,7 +89,8 @@ rgblink_obj := \
src/extern/err.o \ src/extern/err.o \
src/extern/getopt.o \ src/extern/getopt.o \
src/hashmap.o \ src/hashmap.o \
src/linkdefs.o src/linkdefs.o \
src/opmath.o
rgbfix_obj := \ rgbfix_obj := \
src/fix/main.o \ src/fix/main.o \
@@ -114,15 +118,27 @@ rgbgfx: ${rgbgfx_obj}
# Rules to process files # Rules to process files
# We want the yacc invocations to pass through our rules, not default ones # We want the Bison invocation to pass through our rules, not default ones
.y.o: .y.o:
# yacc-generated C files have an accompanying header # Bison-generated C files have an accompanying header
.c.h: src/asm/parser.h: src/asm/parser.c
$Qtouch $@ $Qtouch $@
.y.c: src/asm/parser.c: src/asm/parser.y
$Q${YACC} -d ${YFLAGS} -o $@ $< $QDEFS=; \
add_flag(){ \
if src/check_bison_ver.sh $$1 $$2; then \
DEFS="-D$$3 $$DEFS"; \
fi \
}; \
add_flag 3 5 api.token.raw=true; \
add_flag 3 6 parse.error=detailed; \
add_flag 3 0 parse.error=verbose; \
add_flag 3 0 parse.lac=full; \
add_flag 3 0 lr.type=ielr; \
echo "DEFS=$$DEFS"; \
${BISON} $$DEFS -d ${YFLAGS} -o $@ $<
.c.o: .c.o:
$Q${CC} ${REALCFLAGS} ${PNGCFLAGS} -c -o $@ $< $Q${CC} ${REALCFLAGS} ${PNGCFLAGS} -c -o $@ $<
@@ -178,6 +194,7 @@ checkpatch:
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" \
-- src include '!src/extern' '!include/extern' \
| ${CHECKPATCH} - || true; \ | ${CHECKPATCH} - || true; \
done done
@@ -189,7 +206,7 @@ develop:
$Qenv $(MAKE) -j WARNFLAGS="-Werror -Wall -Wextra -Wpedantic -Wno-type-limits \ $Qenv $(MAKE) -j WARNFLAGS="-Werror -Wall -Wextra -Wpedantic -Wno-type-limits \
-Wno-sign-compare -Wvla -Wformat -Wformat-security -Wformat-overflow=2 \ -Wno-sign-compare -Wvla -Wformat -Wformat-security -Wformat-overflow=2 \
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused \ -Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused \
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5 \ -Wuninitialized -Wunknown-pragmas -Wstrict-overflow=4 \
-Wstringop-overflow=4 -Walloc-zero -Wduplicated-cond \ -Wstringop-overflow=4 -Walloc-zero -Wduplicated-cond \
-Wfloat-equal -Wshadow -Wcast-qual -Wcast-align -Wlogical-op \ -Wfloat-equal -Wshadow -Wcast-qual -Wcast-align -Wlogical-op \
-Wnested-externs -Wno-aggressive-loop-optimizations -Winline \ -Wnested-externs -Wno-aggressive-loop-optimizations -Winline \
@@ -206,11 +223,11 @@ develop:
# install instructions instead. # install instructions instead.
mingw32: mingw32:
$Qmake CC=i686-w64-mingw32-gcc YACC=bison \ $Qmake CC=i686-w64-mingw32-gcc BISON=bison \
PKG_CONFIG=i686-w64-mingw32-pkg-config -j PKG_CONFIG=i686-w64-mingw32-pkg-config -j
mingw64: mingw64:
$Qmake CC=x86_64-w64-mingw32-gcc YACC=bison \ $Qmake CC=x86_64-w64-mingw32-gcc BISON=bison \
PKG_CONFIG=x86_64-w64-mingw32-pkg-config -j PKG_CONFIG=x86_64-w64-mingw32-pkg-config -j
wine-shim: wine-shim:

View File

@@ -31,31 +31,33 @@ diff <(xxd $1) <(xxd $2) | while read -r LINE; do
# Separator between files switches states # Separator between files switches states
echo $LINE echo $LINE
STATE=3 STATE=3
elif echo $LINE | grep -Eq '^[0-9]+(,[0-9]+)?c[0-9]+(,[0-9]+)?'; then elif grep -Eq '^[0-9]+(,[0-9]+)?[cd][0-9]+(,[0-9]+)?' <<< "$LINE"; then
# Line info resets the whole thing # Line info resets the whole thing
STATE=1 STATE=1
elif [ $STATE -eq 1 -o $STATE -eq 3 ]; then elif [ $STATE -eq 1 -o $STATE -eq 3 ]; then
# Compute the GB address from the ROM offset # Compute the GB address from the ROM offset
OFS=$(echo $LINE | cut -d ' ' -f 2 | tr -d ':') OFS=$(cut -d ' ' -f 2 <<< "$LINE" | tr -d ':')
BANK=$((0x$OFS / 0x4000)) BANK=$((0x$OFS / 0x4000))
ADDR=$((0x$OFS % 0x4000 + ($BANK != 0) * 0x4000)) ADDR=$((0x$OFS % 0x4000 + ($BANK != 0) * 0x4000))
# Try finding the preceding symbol closest to the diff # Try finding the preceding symbol closest to the diff
if [ $STATE -eq 1 ]; then if [ $STATE -eq 1 ]; then
STATE=2 STATE=2
SYMFILE=$(echo $1 | cut -d '.' -f 1).sym SYMFILE=${1%.*}.sym
else else
STATE=4 STATE=4
SYMFILE=$(echo $2 | cut -d '.' -f 1).sym SYMFILE=${2%.*}.sym
fi fi
EXTRA=$(if [ -f "$SYMFILE" ]; then EXTRA=$(if [ -f "$SYMFILE" ]; then
# Read the sym file for such a symbol # Read the sym file for such a symbol
# Ignore comment lines, only pick matching bank # Ignore comment lines, only pick matching bank
grep -Fv ';' "$SYMFILE" | # (The bank regex ignores comments already, make `cut` and `tr` process less lines)
grep -Ei $(printf "^%02x:" $BANK) | grep -Ei $(printf "^%02x:" $BANK) "$SYMFILE" |
cut -d ';' -f 1 |
tr -d "\r" |
while read -r SYMADDR SYM; do while read -r SYMADDR SYM; do
SYMADDR=$((0x$(echo $SYMADDR | cut -d ':' -f 2))) SYMADDR=$((0x${SYMADDR#*:}))
if [ $SYMADDR -le $ADDR ]; then if [ $SYMADDR -le $ADDR ]; then
printf " (%s+%#x)\n" $SYM $(($ADDR - $SYMADDR)) printf " (%s+%#x)\n" "$SYM" $(($ADDR - $SYMADDR))
fi fi
# TODO: assumes sorted sym files # TODO: assumes sorted sym files
done | tail -n 1 done | tail -n 1
@@ -63,9 +65,9 @@ diff <(xxd $1) <(xxd $2) | while read -r LINE; do
printf "%02x:%04x %s\n" $BANK $ADDR $EXTRA printf "%02x:%04x %s\n" $BANK $ADDR $EXTRA
fi fi
if [ $STATE -eq 2 -o $STATE -eq 4 ]; then if [ $STATE -eq 2 -o $STATE -eq 4 ]; then
OFS=$(echo $LINE | cut -d ' ' -f 2 | tr -d ':') OFS=$(cut -d ' ' -f 2 <<< "$LINE" | tr -d ':')
BANK=$((0x$OFS / 0x4000)) BANK=$((0x$OFS / 0x4000))
ADDR=$((0x$OFS % 0x4000 + ($BANK != 0) * 0x4000)) ADDR=$((0x$OFS % 0x4000 + ($BANK != 0) * 0x4000))
printf "%s %02x:%04x: %s\n" "$(echo $LINE | cut -d ' ' -f 1)" $BANK $ADDR "$(echo $LINE | cut -d ' ' -f 3-)" printf "%s %02x:%04x: %s\n" "${LINE:0:1}" $BANK $ADDR "${LINE#*: }"
fi fi
done done

View File

@@ -1,31 +0,0 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
/*
* Contains some assembler-wide defines and externs
*/
#ifndef RGBDS_ASM_ASM_H
#define RGBDS_ASM_ASM_H
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "asm/localasm.h"
#include "asm/symbol.h"
#define MAXMACROARGS 99999
#define MAXINCPATHS 128
extern uint32_t nTotalLines;
extern uint32_t nIFDepth;
extern struct Section *pCurrentSection;
#endif /* RGBDS_ASM_ASM_H */

31
include/asm/fixpoint.h Normal file
View File

@@ -0,0 +1,31 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2021, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#ifndef RGBDS_ASM_FIXPOINT_H
#define RGBDS_ASM_FIXPOINT_H
#include <stdint.h>
int32_t fix_Callback_PI(void);
void fix_Print(int32_t i);
int32_t fix_Sin(int32_t i);
int32_t fix_Cos(int32_t i);
int32_t fix_Tan(int32_t i);
int32_t fix_ASin(int32_t i);
int32_t fix_ACos(int32_t i);
int32_t fix_ATan(int32_t i);
int32_t fix_ATan2(int32_t i, int32_t j);
int32_t fix_Mul(int32_t i, int32_t j);
int32_t fix_Div(int32_t i, int32_t j);
int32_t fix_Pow(int32_t i, int32_t j);
int32_t fix_Log(int32_t i, int32_t j);
int32_t fix_Round(int32_t i);
int32_t fix_Ceil(int32_t i);
int32_t fix_Floor(int32_t i);
#endif /* RGBDS_ASM_FIXPOINT_H */

63
include/asm/format.h Normal file
View File

@@ -0,0 +1,63 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 2020, RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#ifndef RGBDS_FORMAT_SPEC_H
#define RGBDS_FORMAT_SPEC_H
#include <stdint.h>
#include <stdbool.h>
enum FormatState {
FORMAT_SIGN, // expects '+' or ' ' (optional)
FORMAT_PREFIX, // expects '#' (optional)
FORMAT_ALIGN, // expects '-' (optional)
FORMAT_WIDTH, // expects '0'-'9', max 255 (optional) (leading '0' indicates pad)
FORMAT_FRAC, // got '.', expects '0'-'9', max 255 (optional)
FORMAT_DONE, // got [duXxbofs] (required)
FORMAT_INVALID, // got unexpected character
};
struct FormatSpec {
enum FormatState state;
int sign;
bool prefix;
bool alignLeft;
bool padZero;
uint8_t width;
bool hasFrac;
uint8_t fracWidth;
int type;
bool valid;
};
struct StrFmtArg {
union {
uint32_t number;
char *string;
};
bool isNumeric;
};
#define INITIAL_STRFMT_ARG_SIZE 4
struct StrFmtArgList {
char *format;
size_t nbArgs;
size_t capacity;
struct StrFmtArg *args;
};
struct FormatSpec fmt_NewSpec(void);
bool fmt_IsEmpty(struct FormatSpec const *fmt);
bool fmt_IsValid(struct FormatSpec const *fmt);
bool fmt_IsFinished(struct FormatSpec const *fmt);
void fmt_UseCharacter(struct FormatSpec *fmt, int c);
void fmt_FinishCharacters(struct FormatSpec *fmt);
void fmt_PrintString(char *buf, size_t bufLen, struct FormatSpec const *fmt, char const *value);
void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uint32_t value);
#endif /* RGBDS_FORMAT_SPEC_H */

View File

@@ -13,10 +13,10 @@
#ifndef RGBDS_ASM_FSTACK_H #ifndef RGBDS_ASM_FSTACK_H
#define RGBDS_ASM_FSTACK_H #define RGBDS_ASM_FSTACK_H
#include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include "asm/asm.h"
#include "asm/lexer.h" #include "asm/lexer.h"
#include "types.h" #include "types.h"
@@ -73,6 +73,10 @@ bool yywrap(void);
void fstk_RunInclude(char const *path); void fstk_RunInclude(char const *path);
void fstk_RunMacro(char const *macroName, struct MacroArgs *args); void fstk_RunMacro(char const *macroName, struct MacroArgs *args);
void fstk_RunRept(uint32_t count, int32_t nReptLineNo, char *body, size_t size); void fstk_RunRept(uint32_t count, int32_t nReptLineNo, char *body, size_t size);
void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
int32_t reptLineNo, char *body, size_t size);
void fstk_StopRept(void);
bool fstk_Break(void);
void fstk_Init(char const *mainPath, size_t maxRecursionDepth); void fstk_Init(char const *mainPath, size_t maxRecursionDepth);

View File

@@ -9,6 +9,8 @@
#ifndef RGBDS_ASM_LEXER_H #ifndef RGBDS_ASM_LEXER_H
#define RGBDS_ASM_LEXER_H #define RGBDS_ASM_LEXER_H
#include <stdbool.h>
#define MAXSTRLEN 255 #define MAXSTRLEN 255
struct LexerState; struct LexerState;
@@ -30,17 +32,21 @@ static inline void lexer_SetStateAtEOL(struct LexerState *state)
lexerStateEOL = state; lexerStateEOL = state;
} }
extern char const *binDigits; extern char binDigits[2];
extern char const *gfxDigits; extern char gfxDigits[4];
static inline void lexer_SetBinDigits(char const *digits) static inline void lexer_SetBinDigits(char const digits[2])
{ {
binDigits = digits; binDigits[0] = digits[0];
binDigits[1] = digits[1];
} }
static inline void lexer_SetGfxDigits(char const *digits) static inline void lexer_SetGfxDigits(char const digits[4])
{ {
gfxDigits = digits; gfxDigits[0] = digits[0];
gfxDigits[1] = digits[1];
gfxDigits[2] = digits[2];
gfxDigits[3] = digits[3];
} }
/* /*
@@ -56,18 +62,40 @@ enum LexerMode {
LEXER_NORMAL, LEXER_NORMAL,
LEXER_RAW, LEXER_RAW,
LEXER_SKIP_TO_ELIF, LEXER_SKIP_TO_ELIF,
LEXER_SKIP_TO_ENDC LEXER_SKIP_TO_ENDC,
LEXER_SKIP_TO_ENDR
}; };
void lexer_SetMode(enum LexerMode mode); void lexer_SetMode(enum LexerMode mode);
void lexer_ToggleStringExpansion(bool enable); void lexer_ToggleStringExpansion(bool enable);
uint32_t lexer_GetIFDepth(void);
void lexer_IncIFDepth(void);
void lexer_DecIFDepth(void);
bool lexer_RanIFBlock(void);
bool lexer_ReachedELSEBlock(void);
void lexer_RunIFBlock(void);
void lexer_ReachELSEBlock(void);
struct CaptureBody {
uint32_t lineNo;
char *body;
size_t size;
};
char const *lexer_GetFileName(void); char const *lexer_GetFileName(void);
uint32_t lexer_GetLineNo(void); uint32_t lexer_GetLineNo(void);
uint32_t lexer_GetColNo(void); uint32_t lexer_GetColNo(void);
void lexer_DumpStringExpansions(void); void lexer_DumpStringExpansions(void);
int yylex(void); int yylex(void);
void lexer_CaptureRept(char **capture, size_t *size); void lexer_CaptureRept(struct CaptureBody *capture);
void lexer_CaptureMacroBody(char **capture, size_t *size); void lexer_CaptureMacroBody(struct CaptureBody *capture);
#define INITIAL_DS_ARG_SIZE 2
struct DsArgList {
size_t nbArgs;
size_t capacity;
struct Expression *args;
};
#endif /* RGBDS_ASM_LEXER_H */ #endif /* RGBDS_ASM_LEXER_H */

View File

@@ -1,131 +0,0 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#ifndef RGBDS_ASM_LOCALASM_H
#define RGBDS_ASM_LOCALASM_H
/*
* GB Z80 instruction groups
*
* n3 = 3-bit
* n = 8-bit
* nn = 16-bit
*
* ADC A,n : 0xCE
* ADC A,r : 0x88|r
* ADD A,n : 0xC6
* ADD A,r : 0x80|r
* ADD HL,ss : 0x09|(ss<<4)
* ADD SP,n : 0xE8
* AND A,n : 0xE6
* AND A,r : 0xA0|r
* BIT n3,r : 0xCB 0x40|(n3<<3)|r
* CALL cc,nn : 0xC4|(cc<<3)
* CALL nn : 0xCD
* CCF : 0x3F
* CP A,n : 0xFE
* CP A,r : 0xB8|r
* CPL : 0x2F
* DAA : 0x27
* DEC r : 0x05|(r<<3)
* DEC ss : 0x0B|(ss<<4)
* DI : 0xF3
* EI : 0xFB
* HALT : 0x76
* INC r : 0x04|(r<<3)
* INC ss : 0x03|(ss<<4)
* JP HL : 0xE9
* JP cc,nn : 0xC2|(cc<<3)
* JP nn : 0xC3|(cc<<3)
* JR n : 0x18
* JR cc,n : 0x20|(cc<<3)
* LD (nn),SP : 0x08
* LD ($FF00+C),A : 0xE2
* LD ($FF00+n),A : 0xE0
* LD (nn),A : 0xEA
* LD (rr),A : 0x02|(rr<<4) // HL+ and HL- included
* LD A,($FF00+C) : 0xF2
* LD A,($FF00+n) : 0xF0
* LD A,(nn) : 0xFA
* LD A,(rr) : 0x0A|(rr<<4) // HL+ and HL- included
* LD HL,SP+n : 0xF8
* LD SP,HL : 0xF9
* LD r,n : 0x06|(r<<3)
* LD r,r' : 0x40|(r<<3)|r' // NOTE: LD (HL),(HL) not allowed
* LD ss,nn : 0x01|(ss<<4)
* NOP : 0x00
* OR A,n : 0xF6
* OR A,r : 0xB0|r
* POP tt : 0xC1|(tt<<4)
* PUSH tt : 0xC5|(tt<<4)
* RES n3,r : 0xCB 0x80|(n3<<3)|r
* RET : 0xC9
* RET cc : 0xC0|(cc<<3)
* RETI : 0xD9
* RL r : 0xCB 0x10|r
* RLA : 0x17
* RLC r : 0xCB 0x00|r
* RLCA : 0x07
* RR r : 0xCB 0x18|r
* RRA : 0x1F
* RRC r : 0xCB 0x08|r
* RRCA : 0x0F
* RST n : 0xC7|n
* SBC A,n : 0xDE
* SBC A,r : 0x98|r
* SCF : 0x37
* SET n3,r : 0xCB 0xC0|(n8<<3)|r
* SLA r : 0xCB 0x20|r
* SRA r : 0xCB 0x28|r
* SRL r : 0xCB 0x38|r
* STOP : 0x10 0x00
* SUB A,n : 0xD6
* SUB A,r : 0x90|r
* SWAP r : 0xCB 0x30|r
* XOR A,n : 0xEE
* XOR A,r : 0xA8|r
*/
/* "r" defs */
enum {
REG_B = 0,
REG_C,
REG_D,
REG_E,
REG_H,
REG_L,
REG_HL_IND,
REG_A
};
/* "rr" defs */
enum {
REG_BC_IND = 0,
REG_DE_IND,
REG_HL_INDINC,
REG_HL_INDDEC,
};
/* "ss" defs (SP) and "tt" defs (AF) */
enum {
REG_BC = 0,
REG_DE = 1,
REG_HL = 2,
REG_SP = 3,
REG_AF = 3
};
/* "cc" defs */
enum {
CC_NZ = 0,
CC_Z,
CC_NC,
CC_C
};
#endif /* RGBDS_ASM_LOCALASM_H */

View File

@@ -10,6 +10,7 @@
#define RGBDS_MACRO_H #define RGBDS_MACRO_H
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include "asm/warning.h" #include "asm/warning.h"
@@ -24,6 +25,7 @@ void macro_AppendArg(struct MacroArgs **args, char *s);
void macro_UseNewArgs(struct MacroArgs *args); void macro_UseNewArgs(struct MacroArgs *args);
void macro_FreeArgs(struct MacroArgs *args); void macro_FreeArgs(struct MacroArgs *args);
char const *macro_GetArg(uint32_t i); char const *macro_GetArg(uint32_t i);
char *macro_GetAllArgs(void);
uint32_t macro_GetUniqueID(void); uint32_t macro_GetUniqueID(void);
char const *macro_GetUniqueIDStr(void); char const *macro_GetUniqueIDStr(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-2021, Carsten Sorensen and RGBDS contributors.
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@@ -15,19 +15,6 @@
#include "helpers.h" #include "helpers.h"
struct sOptions {
char binary[2];
char gbgfx[4];
int32_t fillchar;
};
extern char *tzNewMacro;
extern uint32_t ulNewMacroSize;
extern int32_t nGBGfxID;
extern int32_t nBinaryID;
extern struct sOptions DefaultOptions;
extern struct sOptions CurrentOptions;
extern bool haltnop; extern bool haltnop;
extern bool optimizeloads; extern bool optimizeloads;
extern bool verbose; extern bool verbose;
@@ -39,13 +26,6 @@ extern bool oGeneratedMissingIncludes;
extern bool oFailedOnMissingInclude; extern bool oFailedOnMissingInclude;
extern bool oGeneratePhonyDeps; extern bool oGeneratePhonyDeps;
void opt_Push(void);
void opt_Pop(void);
void opt_Parse(char *s);
void upperstring(char *s);
void lowerstring(char *s);
/* TODO: are these really needed? */ /* TODO: are these really needed? */
#define YY_FATAL_ERROR fatalerror #define YY_FATAL_ERROR fatalerror

View File

@@ -1,29 +0,0 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#ifndef RGBDS_ASM_MATH_H
#define RGBDS_ASM_MATH_H
#include <stdint.h>
void math_DefinePI(void);
void math_Print(int32_t i);
int32_t math_Sin(int32_t i);
int32_t math_Cos(int32_t i);
int32_t math_Tan(int32_t i);
int32_t math_ASin(int32_t i);
int32_t math_ACos(int32_t i);
int32_t math_ATan(int32_t i);
int32_t math_ATan2(int32_t i, int32_t j);
int32_t math_Mul(int32_t i, int32_t j);
int32_t math_Div(int32_t i, int32_t j);
int32_t math_Round(int32_t i);
int32_t math_Ceil(int32_t i);
int32_t math_Floor(int32_t i);
#endif /* RGBDS_ASM_MATH_H */

22
include/asm/opt.h Normal file
View File

@@ -0,0 +1,22 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 2021, Eldred Habert and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#ifndef RGBDS_OPT_H
#define RGBDS_OPT_H
void opt_B(char chars[2]);
void opt_G(char chars[4]);
void opt_P(uint8_t fill);
void opt_Parse(char const *option);
void opt_Push(void);
void opt_Pop(void);
#endif

View File

@@ -14,6 +14,7 @@
#include "linkdefs.h" #include "linkdefs.h"
struct Expression; struct Expression;
struct FileStackNode;
extern char *tzObjectname; extern char *tzObjectname;
extern struct Section *pSectionList, *pCurrentSection; extern struct Section *pSectionList, *pCurrentSection;
@@ -21,8 +22,7 @@ extern struct Section *pSectionList, *pCurrentSection;
void out_RegisterNode(struct FileStackNode *node); void out_RegisterNode(struct FileStackNode *node);
void out_ReplaceNode(struct FileStackNode *node); void out_ReplaceNode(struct FileStackNode *node);
void out_SetFileName(char *s); void out_SetFileName(char *s);
void out_CreatePatch(uint32_t type, struct Expression const *expr, void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs, uint32_t pcShift);
uint32_t ofs);
bool out_CreateAssert(enum AssertionType type, struct Expression const *expr, bool out_CreateAssert(enum AssertionType type, struct Expression const *expr,
char const *message, uint32_t ofs); char const *message, uint32_t ofs);
void out_WriteObject(void); void out_WriteObject(void);

View File

@@ -27,9 +27,6 @@ struct Expression {
uint32_t nRPNPatchSize; // Size the expression will take in the obj file uint32_t nRPNPatchSize; // Size the expression will take in the obj file
}; };
/* FIXME: Should be defined in `parser.h`, but impossible with POSIX Yacc */
extern int32_t nPCOffset;
/* /*
* Determines if an expression is known at assembly time * Determines if an expression is known at assembly time
*/ */

View File

@@ -14,12 +14,16 @@
#include "linkdefs.h" #include "linkdefs.h"
extern uint8_t fillByte;
struct Expression; struct Expression;
struct Section { struct Section {
char *name; char *name;
enum SectionType type; enum SectionType type;
enum SectionModifier modifier; enum SectionModifier modifier;
struct FileStackNode *src; /* Where the section was defined */
uint32_t fileLine; /* Line where the section was defined */
uint32_t size; uint32_t size;
uint32_t org; uint32_t org;
uint32_t bank; uint32_t bank;
@@ -41,7 +45,8 @@ void out_NewSection(char const *name, uint32_t secttype, uint32_t org,
struct SectionSpec const *attributes, struct SectionSpec const *attributes,
enum SectionModifier mod); enum SectionModifier mod);
void out_SetLoadSection(char const *name, uint32_t secttype, uint32_t org, void out_SetLoadSection(char const *name, uint32_t secttype, uint32_t org,
struct SectionSpec const *attributes); struct SectionSpec const *attributes,
enum SectionModifier mod);
void out_EndLoadSection(void); void out_EndLoadSection(void);
struct Section *sect_GetSymbolSection(void); struct Section *sect_GetSymbolSection(void);
@@ -56,13 +61,15 @@ void sect_CheckUnionClosed(void);
void out_AbsByte(uint8_t b); void out_AbsByte(uint8_t b);
void out_AbsByteGroup(uint8_t const *s, int32_t length); void out_AbsByteGroup(uint8_t const *s, int32_t length);
void out_AbsWordGroup(uint8_t const *s, int32_t length);
void out_AbsLongGroup(uint8_t const *s, int32_t length);
void out_Skip(int32_t skip, bool ds); void out_Skip(int32_t skip, bool ds);
void out_String(char const *s); void out_String(char const *s);
void out_RelByte(struct Expression *expr); void out_RelByte(struct Expression *expr, uint32_t pcShift);
void out_RelBytes(struct Expression *expr, uint32_t n); void out_RelBytes(uint32_t n, struct Expression *exprs, size_t size);
void out_RelWord(struct Expression *expr); void out_RelWord(struct Expression *expr, uint32_t pcShift);
void out_RelLong(struct Expression *expr); void out_RelLong(struct Expression *expr, uint32_t pcShift);
void out_PCRelByte(struct Expression *expr); void out_PCRelByte(struct Expression *expr, uint32_t pcShift);
void out_BinaryFile(char const *s, int32_t startPos); void out_BinaryFile(char const *s, int32_t startPos);
void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length); void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length);

View File

@@ -12,9 +12,11 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <time.h>
#include "asm/section.h" #include "asm/section.h"
#include "platform.h" // MIN_NB_ELMS
#include "types.h" #include "types.h"
#define HASHSIZE (1 << 16) #define HASHSIZE (1 << 16)
@@ -73,7 +75,7 @@ static inline bool sym_IsConstant(struct Symbol const *sym)
if (sym->type == SYM_LABEL) { if (sym->type == SYM_LABEL) {
struct Section const *sect = sym_GetSection(sym); struct Section const *sect = sym_GetSection(sym);
return sect && sect->org != -1; return sect && sect->org != (uint32_t)-1;
} }
return sym->type == SYM_EQU || sym->type == SYM_SET; return sym->type == SYM_EQU || sym->type == SYM_SET;
} }
@@ -115,6 +117,8 @@ int32_t sym_GetValue(struct Symbol const *sym);
void sym_SetExportAll(bool set); void sym_SetExportAll(bool set);
struct Symbol *sym_AddLocalLabel(char const *symName); struct Symbol *sym_AddLocalLabel(char const *symName);
struct Symbol *sym_AddLabel(char const *symName); struct Symbol *sym_AddLabel(char const *symName);
struct Symbol *sym_AddAnonLabel(void);
void sym_WriteAnonLabelName(char buf[MIN_NB_ELMS(MAXSYMLEN + 1)], uint32_t ofs, bool neg);
void sym_Export(char const *symName); void sym_Export(char const *symName);
struct Symbol *sym_AddEqu(char const *symName, int32_t value); struct Symbol *sym_AddEqu(char const *symName, int32_t value);
struct Symbol *sym_AddSet(char const *symName, int32_t value); struct Symbol *sym_AddSet(char const *symName, int32_t value);
@@ -137,8 +141,9 @@ struct Symbol const *sym_GetPC(void);
struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size); struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size);
struct Symbol *sym_Ref(char const *symName); struct Symbol *sym_Ref(char const *symName);
struct Symbol *sym_AddString(char const *symName, char const *value); struct Symbol *sym_AddString(char const *symName, char const *value);
struct Symbol *sym_RedefString(char const *symName, char const *value);
void sym_Purge(char const *symName); void sym_Purge(char const *symName);
void sym_Init(void); void sym_Init(time_t now);
/* Functions to save and restore the current symbol scope. */ /* Functions to save and restore the current symbol scope. */
char const *sym_GetCurrentSymbolScope(void); char const *sym_GetCurrentSymbolScope(void);

View File

@@ -13,6 +13,9 @@
uint32_t calchash(const char *s); uint32_t calchash(const char *s);
char const *print(int c); char const *print(int c);
/*
* @return The number of bytes read, or 0 if invalid data was found
*/
size_t readUTF8Char(uint8_t *dest, char const *src); size_t readUTF8Char(uint8_t *dest, char const *src);
#endif /* RGBDS_UTIL_H */ #endif /* RGBDS_UTIL_H */

View File

@@ -18,11 +18,13 @@ enum WarningID {
WARNING_BUILTIN_ARG, /* Invalid args to builtins */ WARNING_BUILTIN_ARG, /* Invalid args to builtins */
WARNING_CHARMAP_REDEF, /* Charmap entry re-definition */ WARNING_CHARMAP_REDEF, /* Charmap entry re-definition */
WARNING_DIV, /* Division undefined behavior */ WARNING_DIV, /* Division undefined behavior */
WARNING_EMPTY_DATA_DIRECTIVE, /* `db`, `dw` or `dl` with no directive in ROM */ WARNING_EMPTY_DATA_DIRECTIVE, /* `db`, `dw` or `dl` directive without data in ROM */
WARNING_EMPTY_ENTRY, /* Empty entry in `db`, `dw` or `dl` */ WARNING_EMPTY_MACRO_ARG, /* Empty macro argument */
WARNING_EMPTY_STRRPL, /* Empty second argument in `STRRPL` */
WARNING_LARGE_CONSTANT, /* Constants too large */ WARNING_LARGE_CONSTANT, /* Constants too large */
WARNING_LONG_STR, /* String too long for internal buffers */ WARNING_LONG_STR, /* String too long for internal buffers */
WARNING_NESTED_COMMENT, /* Comment-start delimeter in a block comment */ WARNING_MACRO_SHIFT, /* Shift past available arguments in macro */
WARNING_NESTED_COMMENT, /* Comment-start delimiter in a block comment */
WARNING_OBSOLETE, /* Obsolete things */ WARNING_OBSOLETE, /* Obsolete things */
WARNING_SHIFT, /* Shifting undefined behavior */ WARNING_SHIFT, /* Shifting undefined behavior */
WARNING_SHIFT_AMOUNT, /* Strange shift amount */ WARNING_SHIFT_AMOUNT, /* Strange shift amount */
@@ -46,7 +48,7 @@ void processWarningFlag(char const *flag);
* Used to warn the user about problems that don't prevent the generation of * Used to warn the user about problems that don't prevent the generation of
* valid code. * valid code.
*/ */
void warning(enum WarningID id, const char *fmt, ...); void warning(enum WarningID id, const char *fmt, ...) format_(printf, 2, 3);
/* /*
* Used for errors that compromise the whole assembly process by affecting the * Used for errors that compromise the whole assembly process by affecting the
@@ -55,7 +57,7 @@ void warning(enum WarningID id, const char *fmt, ...);
* It is also used when the assembler goes into an invalid state (for example, * It is also used when the assembler goes into an invalid state (for example,
* when it fails to allocate memory). * when it fails to allocate memory).
*/ */
_Noreturn void fatalerror(const char *fmt, ...); _Noreturn void fatalerror(const char *fmt, ...) format_(printf, 1, 2);
/* /*
* Used for errors that make it impossible to assemble correctly, but don't * Used for errors that make it impossible to assemble correctly, but don't
@@ -63,6 +65,6 @@ _Noreturn void fatalerror(const char *fmt, ...);
* get a list of all errors at the end, making it easier to fix all of them at * get a list of all errors at the end, making it easier to fix all of them at
* once. * once.
*/ */
void error(const char *fmt, ...); void error(const char *fmt, ...) format_(printf, 1, 2);
#endif #endif

View File

@@ -26,8 +26,8 @@
#ifndef RGBDS_EXTERN_GETOPT_H #ifndef RGBDS_EXTERN_GETOPT_H
#define RGBDS_EXTERN_GETOPT_H #define RGBDS_EXTERN_GETOPT_H
extern char *optarg; extern char *musl_optarg;
extern int optind, opterr, optopt, optreset; extern int musl_optind, musl_opterr, musl_optopt, musl_optreset;
struct option { struct option {
const char *name; const char *name;

View File

@@ -32,6 +32,58 @@
static inline _Noreturn unreachable_(void) {} static inline _Noreturn unreachable_(void) {}
#endif #endif
// Use builtins whenever possible, and shim them otherwise
#ifdef __GNUC__ // GCC or compatible
#define ctz __builtin_ctz
#define clz __builtin_clz
#elif defined(_MSC_VER)
#include <assert.h>
#include <intrin.h>
#pragma intrinsic(_BitScanReverse, _BitScanForward)
static inline int ctz(unsigned int x)
{
unsigned long cnt;
assert(x != 0);
_BitScanForward(&cnt, x);
return cnt;
}
static inline int clz(unsigned int x)
{
unsigned long cnt;
assert(x != 0);
_BitScanReverse(&cnt, x);
return 31 - cnt;
}
#else
// FIXME: these are rarely used, and need testing...
#include <limits.h>
static inline int ctz(unsigned int x)
{
int cnt = 0;
while (!(x & 1)) {
x >>= 1;
cnt++;
}
return cnt;
}
static inline int clz(unsigned int x)
{
int cnt = 0;
while (x <= UINT_MAX / 2) {
x <<= 1;
cnt++;
}
return cnt;
}
#endif
// Macros for stringification // Macros for stringification
#define STR(x) #x #define STR(x) #x
#define EXPAND_AND_STR(x) STR(x) #define EXPAND_AND_STR(x) STR(x)

View File

@@ -30,11 +30,11 @@ struct AttachedSymbol {
struct Patch { struct Patch {
struct FileStackNode const *src; struct FileStackNode const *src;
uint32_t lineNo; uint32_t lineNo;
int32_t offset; uint32_t offset;
uint32_t pcSectionID; uint32_t pcSectionID;
uint32_t pcOffset; uint32_t pcOffset;
enum PatchType type; enum PatchType type;
int32_t rpnSize; uint32_t rpnSize;
uint8_t *rpnExpression; uint8_t *rpnExpression;
struct Section const *pcSection; struct Section const *pcSection;

View File

@@ -14,7 +14,7 @@
#define RGBDS_OBJECT_VERSION_STRING "RGB%1u" #define RGBDS_OBJECT_VERSION_STRING "RGB%1u"
#define RGBDS_OBJECT_VERSION_NUMBER 9U #define RGBDS_OBJECT_VERSION_NUMBER 9U
#define RGBDS_OBJECT_REV 6U #define RGBDS_OBJECT_REV 7U
enum AssertionType { enum AssertionType {
ASSERT_WARN, ASSERT_WARN,
@@ -29,6 +29,7 @@ enum RPNCommand {
RPN_DIV = 0x03, RPN_DIV = 0x03,
RPN_MOD = 0x04, RPN_MOD = 0x04,
RPN_UNSUB = 0x05, RPN_UNSUB = 0x05,
RPN_EXP = 0x06,
RPN_OR = 0x10, RPN_OR = 0x10,
RPN_AND = 0x11, RPN_AND = 0x11,

20
include/opmath.h Normal file
View File

@@ -0,0 +1,20 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2021, RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#ifndef RGBDS_OP_MATH_H
#define RGBDS_OP_MATH_H
#include <stdint.h>
int32_t op_divide(int32_t dividend, int32_t divisor);
int32_t op_modulo(int32_t dividend, int32_t divisor);
int32_t op_exponent(int32_t base, uint32_t power);
int32_t op_shift_left(int32_t value, int32_t amount);
int32_t op_shift_right(int32_t value, int32_t amount);
#endif /* RGBDS_OP_MATH_H */

View File

@@ -34,9 +34,40 @@
/* MSVC doesn't use POSIX types or defines for `read` */ /* MSVC doesn't use POSIX types or defines for `read` */
#ifdef _MSC_VER #ifdef _MSC_VER
# include <io.h>
# define STDIN_FILENO 0 # define STDIN_FILENO 0
# define STDOUT_FILENO 1
# define STDERR_FILENO 2
# define ssize_t int # define ssize_t int
# define SSIZE_MAX INT_MAX # define SSIZE_MAX INT_MAX
#else
# include <fcntl.h>
# include <unistd.h>
#endif
/* MSVC doesn't support `[static N]` for array arguments from C99 */
#ifdef _MSC_VER
# define MIN_NB_ELMS(N)
#else
# define MIN_NB_ELMS(N) static (N)
#endif
// MSVC uses a different name for O_RDWR, and needs an additional _O_BINARY flag
#ifdef _MSC_VER
# include <fcntl.h>
# define O_RDWR _O_RDWR
# define S_ISREG(field) ((field) & _S_IFREG)
# define O_BINARY _O_BINARY
#elif !defined(O_BINARY) // Cross-compilers define O_BINARY
# define O_BINARY 0 // POSIX says we shouldn't care!
#endif // _MSC_VER
// Windows has stdin and stdout open as text by default, which we may not want
#if defined(_MSC_VER) || defined(__MINGW32__)
# include <io.h>
# define setmode(fd, mode) _setmode(fd, mode)
#else
# define setmode(fd, mode) ((void)0)
#endif #endif
#endif /* RGBDS_PLATFORM_H */ #endif /* RGBDS_PLATFORM_H */

View File

@@ -1,7 +1,7 @@
/* /*
* This file is part of RGBDS. * This file is part of RGBDS.
* *
* Copyright (c) 2017-2018, Antonio Nino Diaz and RGBDS contributors. * Copyright (c) 2017-2021, Antonio Nino Diaz and RGBDS contributors.
* *
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
@@ -9,9 +9,10 @@
#ifndef EXTERN_VERSION_H #ifndef EXTERN_VERSION_H
#define EXTERN_VERSION_H #define EXTERN_VERSION_H
#define PACKAGE_VERSION_MAJOR (0) #define PACKAGE_VERSION_MAJOR 0
#define PACKAGE_VERSION_MINOR (4) #define PACKAGE_VERSION_MINOR 5
#define PACKAGE_VERSION_PATCH (2) #define PACKAGE_VERSION_PATCH 0
#define PACKAGE_VERSION_RC 2
const char *get_package_version_string(void); const char *get_package_version_string(void);

View File

@@ -12,29 +12,45 @@ set(common_src
"version.c" "version.c"
) )
find_package(BISON REQUIRED)
find_package(PkgConfig) find_package(PkgConfig)
if(MSVC OR NOT PKG_CONFIG_FOUND)
if(NOT PKG_CONFIG_FOUND)
# fallback to find_package # fallback to find_package
find_package(PNG REQUIRED) find_package(PNG REQUIRED)
else() else()
pkg_check_modules(LIBPNG REQUIRED libpng) pkg_check_modules(LIBPNG REQUIRED libpng)
endif() endif()
find_package(BISON REQUIRED)
set(BISON_FLAGS "-Wall")
# Set sompe optimization flags on versions that support them
if(BISON_VERSION VERSION_GREATER_EQUAL "3.5")
set(BISON_FLAGS "${BISON_FLAGS} -Dapi.token.raw=true")
endif()
if(BISON_VERSION VERSION_GREATER_EQUAL "3.6")
set(BISON_FLAGS "${BISON_FLAGS} -Dparse.error=detailed")
elseif(BISON_VERSION VERSION_GREATER_EQUAL "3.0")
set(BISON_FLAGS "${BISON_FLAGS} -Dparse.error=verbose")
endif()
if(BISON_VERSION VERSION_GREATER_EQUAL "3.0")
set(BISON_FLAGS "${BISON_FLAGS} -Dparse.lac=full")
set(BISON_FLAGS "${BISON_FLAGS} -Dlr.type=ielr")
endif()
BISON_TARGET(PARSER "asm/parser.y" BISON_TARGET(PARSER "asm/parser.y"
"${PROJECT_SOURCE_DIR}/src/asm/parser.c" "${PROJECT_SOURCE_DIR}/src/asm/parser.c"
COMPILE_FLAGS "${BISON_FLAGS}"
DEFINES_FILE "${PROJECT_SOURCE_DIR}/src/asm/parser.h" DEFINES_FILE "${PROJECT_SOURCE_DIR}/src/asm/parser.h"
) )
set(rgbasm_src set(rgbasm_src
"${BISON_PARSER_OUTPUT_SOURCE}" "${BISON_PARSER_OUTPUT_SOURCE}"
"asm/charmap.c" "asm/charmap.c"
"asm/fixpoint.c"
"asm/format.c"
"asm/fstack.c" "asm/fstack.c"
"asm/lexer.c" "asm/lexer.c"
"asm/macro.c" "asm/macro.c"
"asm/main.c" "asm/main.c"
"asm/math.c" "asm/opt.c"
"asm/output.c" "asm/output.c"
"asm/rpn.c" "asm/rpn.c"
"asm/section.c" "asm/section.c"
@@ -44,6 +60,7 @@ set(rgbasm_src
"extern/utf8decoder.c" "extern/utf8decoder.c"
"hashmap.c" "hashmap.c"
"linkdefs.c" "linkdefs.c"
"opmath.c"
) )
set(rgbfix_src set(rgbfix_src
@@ -67,6 +84,7 @@ set(rgblink_src
"link/symbol.c" "link/symbol.c"
"hashmap.c" "hashmap.c"
"linkdefs.c" "linkdefs.c"
"opmath.c"
) )
foreach(PROG "asm" "fix" "gfx" "link") foreach(PROG "asm" "fix" "gfx" "link")

View File

@@ -13,7 +13,6 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "asm/asm.h"
#include "asm/charmap.h" #include "asm/charmap.h"
#include "asm/main.h" #include "asm/main.h"
#include "asm/output.h" #include "asm/output.h"
@@ -183,7 +182,7 @@ void charmap_Add(char *mapping, uint8_t value)
} }
if (node->isTerminal) if (node->isTerminal)
warning(WARNING_CHARMAP_REDEF, "Overriding charmap mapping"); warning(WARNING_CHARMAP_REDEF, "Overriding charmap mapping\n");
node->isTerminal = true; node->isTerminal = true;
node->value = value; node->value = value;
@@ -229,6 +228,10 @@ size_t charmap_Convert(char const *input, uint8_t *output)
} else if (*input) { /* No match found */ } else if (*input) { /* No match found */
size_t codepointLen = readUTF8Char(output, input); size_t codepointLen = readUTF8Char(output, input);
if (codepointLen == 0) {
error("Input string is not valid UTF-8!\n");
break;
}
input += codepointLen; /* OK because UTF-8 has no NUL in multi-byte chars */ input += codepointLen; /* OK because UTF-8 has no NUL in multi-byte chars */
output += codepointLen; output += codepointLen;
outputLen += codepointLen; outputLen += codepointLen;

170
src/asm/fixpoint.c Normal file
View File

@@ -0,0 +1,170 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2021, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
/*
* Fixed-point math routines
*/
#include <inttypes.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include "asm/fixpoint.h"
#include "asm/symbol.h"
#include "asm/warning.h"
#define fix2double(i) ((double)((i) / 65536.0))
#define double2fix(d) ((int32_t)round((d) * 65536.0))
// pi radians == 32768 fixed-point "degrees"
#define fdeg2rad(f) ((f) * (M_PI / 32768.0))
#define rad2fdeg(r) ((r) * (32768.0 / M_PI))
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
/*
* Return the _PI symbol value
*/
int32_t fix_Callback_PI(void)
{
warning(WARNING_OBSOLETE, "`_PI` is deprecated; use 3.14159\n");
return double2fix(M_PI);
}
/*
* Print a fixed point value
*/
void fix_Print(int32_t i)
{
uint32_t u = i;
const char *sign = "";
if (i < 0) {
u = -u;
sign = "-";
}
printf("%s%" PRIu32 ".%05" PRIu32, sign, u >> 16,
((uint32_t)(fix2double(u) * 100000 + 0.5)) % 100000);
}
/*
* Calculate sine
*/
int32_t fix_Sin(int32_t i)
{
return double2fix(sin(fdeg2rad(fix2double(i))));
}
/*
* Calculate cosine
*/
int32_t fix_Cos(int32_t i)
{
return double2fix(cos(fdeg2rad(fix2double(i))));
}
/*
* Calculate tangent
*/
int32_t fix_Tan(int32_t i)
{
return double2fix(tan(fdeg2rad(fix2double(i))));
}
/*
* Calculate arcsine
*/
int32_t fix_ASin(int32_t i)
{
return double2fix(rad2fdeg(asin(fix2double(i))));
}
/*
* Calculate arccosine
*/
int32_t fix_ACos(int32_t i)
{
return double2fix(rad2fdeg(acos(fix2double(i))));
}
/*
* Calculate arctangent
*/
int32_t fix_ATan(int32_t i)
{
return double2fix(rad2fdeg(atan(fix2double(i))));
}
/*
* Calculate atan2
*/
int32_t fix_ATan2(int32_t i, int32_t j)
{
return double2fix(rad2fdeg(atan2(fix2double(i), fix2double(j))));
}
/*
* Multiplication
*/
int32_t fix_Mul(int32_t i, int32_t j)
{
return double2fix(fix2double(i) * fix2double(j));
}
/*
* Division
*/
int32_t fix_Div(int32_t i, int32_t j)
{
return double2fix(fix2double(i) / fix2double(j));
}
/*
* Power
*/
int32_t fix_Pow(int32_t i, int32_t j)
{
return double2fix(pow(fix2double(i), fix2double(j)));
}
/*
* Logarithm
*/
int32_t fix_Log(int32_t i, int32_t j)
{
return double2fix(log(fix2double(i)) / log(fix2double(j)));
}
/*
* Round
*/
int32_t fix_Round(int32_t i)
{
return double2fix(round(fix2double(i)));
}
/*
* Ceil
*/
int32_t fix_Ceil(int32_t i)
{
return double2fix(ceil(fix2double(i)));
}
/*
* Floor
*/
int32_t fix_Floor(int32_t i)
{
return double2fix(floor(fix2double(i)));
}

299
src/asm/format.c Normal file
View File

@@ -0,0 +1,299 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 2020, RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#include <inttypes.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "asm/format.h"
#include "asm/warning.h"
struct FormatSpec fmt_NewSpec(void)
{
struct FormatSpec fmt = {0};
return fmt;
}
bool fmt_IsEmpty(struct FormatSpec const *fmt)
{
return !fmt->state;
}
bool fmt_IsValid(struct FormatSpec const *fmt)
{
return fmt->valid || fmt->state == FORMAT_DONE;
}
bool fmt_IsFinished(struct FormatSpec const *fmt)
{
return fmt->state >= FORMAT_DONE;
}
void fmt_UseCharacter(struct FormatSpec *fmt, int c)
{
if (fmt->state == FORMAT_INVALID)
return;
switch (c) {
/* sign */
case ' ':
case '+':
if (fmt->state > FORMAT_SIGN)
goto invalid;
fmt->state = FORMAT_PREFIX;
fmt->sign = c;
break;
/* prefix */
case '#':
if (fmt->state > FORMAT_PREFIX)
goto invalid;
fmt->state = FORMAT_ALIGN;
fmt->prefix = true;
break;
/* align */
case '-':
if (fmt->state > FORMAT_ALIGN)
goto invalid;
fmt->state = FORMAT_WIDTH;
fmt->alignLeft = true;
break;
/* pad and width */
case '0':
if (fmt->state < FORMAT_WIDTH)
fmt->padZero = true;
/* fallthrough */
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (fmt->state < FORMAT_WIDTH) {
fmt->state = FORMAT_WIDTH;
fmt->width = c - '0';
} else if (fmt->state == FORMAT_WIDTH) {
fmt->width = fmt->width * 10 + (c - '0');
} else if (fmt->state == FORMAT_FRAC) {
fmt->fracWidth = fmt->fracWidth * 10 + (c - '0');
} else {
goto invalid;
}
break;
case '.':
if (fmt->state > FORMAT_WIDTH)
goto invalid;
fmt->state = FORMAT_FRAC;
fmt->hasFrac = true;
break;
/* type */
case 'd':
case 'u':
case 'X':
case 'x':
case 'b':
case 'o':
case 'f':
case 's':
if (fmt->state >= FORMAT_DONE)
goto invalid;
fmt->state = FORMAT_DONE;
fmt->valid = true;
fmt->type = c;
break;
default:
invalid:
fmt->state = FORMAT_INVALID;
fmt->valid = false;
}
}
void fmt_FinishCharacters(struct FormatSpec *fmt)
{
if (!fmt_IsValid(fmt))
fmt->state = FORMAT_INVALID;
}
void fmt_PrintString(char *buf, size_t bufLen, struct FormatSpec const *fmt, char const *value)
{
if (fmt->sign)
error("Formatting string with sign flag '%c'\n", fmt->sign);
if (fmt->prefix)
error("Formatting string with prefix flag '#'\n");
if (fmt->padZero)
error("Formatting string with padding flag '0'\n");
if (fmt->hasFrac)
error("Formatting string with fractional width\n");
if (fmt->type != 's')
error("Formatting string as type '%c'\n", fmt->type);
size_t len = strlen(value);
size_t totalLen = fmt->width > len ? fmt->width : len;
if (totalLen + 1 > bufLen) /* bufLen includes terminator */
error("Formatted string value too long\n");
size_t padLen = fmt->width > len ? fmt->width - len : 0;
if (fmt->alignLeft) {
strncpy(buf, value, len < bufLen ? len : bufLen);
for (size_t i = 0; i < totalLen && len + i < bufLen; i++)
buf[len + i] = ' ';
} else {
for (size_t i = 0; i < padLen && i < bufLen; i++)
buf[i] = ' ';
if (bufLen > padLen)
strncpy(buf + padLen, value, bufLen - padLen - 1);
}
buf[totalLen] = '\0';
}
void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uint32_t value)
{
if (fmt->type != 'X' && fmt->type != 'x' && fmt->type != 'b' && fmt->type != 'o'
&& fmt->prefix)
error("Formatting type '%c' with prefix flag '#'\n", fmt->type);
if (fmt->type != 'f' && fmt->hasFrac)
error("Formatting type '%c' with fractional width\n", fmt->type);
if (fmt->type == 's')
error("Formatting number as type 's'\n");
char sign = fmt->sign; /* 0 or ' ' or '+' */
if (fmt->type == 'd' || fmt->type == 'f') {
int32_t v = value;
if (v < 0 && v != INT32_MIN) {
sign = '-';
value = -v;
}
}
char prefix = !fmt->prefix ? 0
: fmt->type == 'X' ? '$'
: fmt->type == 'x' ? '$'
: fmt->type == 'b' ? '%'
: fmt->type == 'o' ? '&'
: 0;
char valueBuf[262]; /* Max 5 digits + decimal + 255 fraction digits + terminator */
if (fmt->type == 'b') {
/* Special case for binary */
char *ptr = valueBuf;
do {
*ptr++ = (value & 1) + '0';
value >>= 1;
} while (value);
*ptr = '\0';
/* Reverse the digits */
size_t valueLen = ptr - valueBuf;
for (size_t i = 0, j = valueLen - 1; i < j; i++, j--) {
char c = valueBuf[i];
valueBuf[i] = valueBuf[j];
valueBuf[j] = c;
}
} else if (fmt->type == 'f') {
/* Special case for fixed-point */
/* Default fractional width (C's is 6 for "%f"; here 5 is enough) */
uint8_t fracWidth = fmt->hasFrac ? fmt->fracWidth : 5;
if (fracWidth) {
char spec[16]; /* Max "%" + 5-char PRIu32 + ".%0255.f" + terminator */
snprintf(spec, sizeof(spec), "%%" PRIu32 ".%%0%d.f", fracWidth);
snprintf(valueBuf, sizeof(valueBuf), spec, value >> 16,
(value % 65536) / 65536.0 * pow(10, fracWidth) + 0.5);
} else {
snprintf(valueBuf, sizeof(valueBuf), "%" PRIu32, value >> 16);
}
} else {
char const *spec = fmt->type == 'd' ? "%" PRId32
: fmt->type == 'u' ? "%" PRIu32
: fmt->type == 'X' ? "%" PRIX32
: fmt->type == 'x' ? "%" PRIx32
: fmt->type == 'o' ? "%" PRIo32
: "%" PRId32;
snprintf(valueBuf, sizeof(valueBuf), spec, value);
}
size_t len = strlen(valueBuf);
size_t numLen = len;
if (sign)
numLen++;
if (prefix)
numLen++;
size_t totalLen = fmt->width > numLen ? fmt->width : numLen;
if (totalLen + 1 > bufLen) /* bufLen includes terminator */
error("Formatted numeric value too long\n");
size_t padLen = fmt->width > numLen ? fmt->width - numLen : 0;
if (fmt->alignLeft) {
size_t pos = 0;
if (sign && pos < bufLen)
buf[pos++] = sign;
if (prefix && pos < bufLen)
buf[pos++] = prefix;
strcpy(buf + pos, valueBuf);
pos += len;
for (size_t i = 0; i < totalLen && pos + i < bufLen; i++)
buf[pos + i] = ' ';
} else {
size_t pos = 0;
if (fmt->padZero) {
/* sign, then prefix, then zero padding */
if (sign && pos < bufLen)
buf[pos++] = sign;
if (prefix && pos < bufLen)
buf[pos++] = prefix;
for (size_t i = 0; i < padLen && pos < bufLen; i++)
buf[pos++] = '0';
} else {
/* space padding, then sign, then prefix */
for (size_t i = 0; i < padLen && pos < bufLen; i++)
buf[pos++] = ' ';
if (sign && pos < bufLen)
buf[pos++] = sign;
if (prefix && pos < bufLen)
buf[pos++] = prefix;
}
if (bufLen > pos)
strcpy(buf + pos, valueBuf);
}
buf[totalLen] = '\0';
}

View File

@@ -21,8 +21,10 @@
#include "asm/warning.h" #include "asm/warning.h"
#include "platform.h" /* S_ISDIR (stat macro) */ #include "platform.h" /* S_ISDIR (stat macro) */
#define MAXINCPATHS 128
#ifdef LEXER_DEBUG #ifdef LEXER_DEBUG
#define dbgPrint(...) fprintf(stderr, "[lexer] " __VA_ARGS__) #define dbgPrint(...) fprintf(stderr, "[fstack] " __VA_ARGS__)
#else #else
#define dbgPrint(...) #define dbgPrint(...)
#endif #endif
@@ -34,6 +36,9 @@ struct Context {
uint32_t uniqueID; uint32_t uniqueID;
struct MacroArgs *macroArgs; /* Macro args are *saved* here */ struct MacroArgs *macroArgs; /* Macro args are *saved* here */
uint32_t nbReptIters; uint32_t nbReptIters;
int32_t forValue;
int32_t forStep;
char *forName;
}; };
static struct Context *contextStack; static struct Context *contextStack;
@@ -44,7 +49,7 @@ size_t nMaxRecursionDepth;
static unsigned int nbIncPaths = 0; static unsigned int nbIncPaths = 0;
static char const *includePaths[MAXINCPATHS]; static char const *includePaths[MAXINCPATHS];
char const *dumpNodeAndParents(struct FileStackNode const *node) static const char *dumpNodeAndParents(struct FileStackNode const *node)
{ {
char const *name; char const *name;
@@ -85,6 +90,9 @@ void fstk_DumpCurrent(void)
struct FileStackNode *fstk_GetFileStack(void) struct FileStackNode *fstk_GetFileStack(void)
{ {
if (!contextStack)
return NULL;
struct FileStackNode *node = contextStack->fileInfo; struct FileStackNode *node = contextStack->fileInfo;
/* Mark node and all of its parents as referenced if not already so they don't get freed */ /* Mark node and all of its parents as referenced if not already so they don't get freed */
@@ -167,8 +175,14 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
char const *incPath = i ? includePaths[i - 1] : ""; char const *incPath = i ? includePaths[i - 1] : "";
int len = snprintf(*fullPath, *size, "%s%s", incPath, path); int len = snprintf(*fullPath, *size, "%s%s", incPath, path);
if (len < 0) {
error("snprintf error during include path search: %s\n",
strerror(errno));
break;
}
/* Oh how I wish `asnprintf` was standard... */ /* Oh how I wish `asnprintf` was standard... */
if (len >= *size) { /* `len` doesn't include the terminator, `size` does */ if ((size_t)len >= *size) { /* `len` doesn't include the terminator, `size` does */
*size = len + 1; *size = len + 1;
*fullPath = realloc(*fullPath, *size); *fullPath = realloc(*fullPath, *size);
if (!*fullPath) { if (!*fullPath) {
@@ -179,10 +193,7 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
len = sprintf(*fullPath, "%s%s", incPath, path); len = sprintf(*fullPath, "%s%s", incPath, path);
} }
if (len < 0) { if (isPathValid(*fullPath)) {
error("snprintf error during include path search: %s\n",
strerror(errno));
} else if (isPathValid(*fullPath)) {
printDep(*fullPath); printDep(*fullPath);
return true; return true;
} }
@@ -197,6 +208,12 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
bool yywrap(void) bool yywrap(void)
{ {
uint32_t nIFDepth = lexer_GetIFDepth();
if (nIFDepth != 0)
fatalerror("Ended block with %" PRIu32 " unterminated IF construct%s\n",
nIFDepth, nIFDepth == 1 ? "" : "s");
if (contextStack->fileInfo->type == NODE_REPT) { /* The context is a REPT block, which may loop */ if (contextStack->fileInfo->type == NODE_REPT) { /* The context is a REPT block, which may loop */
struct FileStackReptNode *fileInfo = (struct FileStackReptNode *)contextStack->fileInfo; struct FileStackReptNode *fileInfo = (struct FileStackReptNode *)contextStack->fileInfo;
@@ -216,6 +233,17 @@ bool yywrap(void)
contextStack->fileInfo = (struct FileStackNode *)fileInfo; contextStack->fileInfo = (struct FileStackNode *)fileInfo;
} }
/* If this is a FOR, update the symbol value */
if (contextStack->forName && fileInfo->iters[0] <= contextStack->nbReptIters) {
contextStack->forValue += contextStack->forStep;
struct Symbol *sym = sym_AddSet(contextStack->forName,
contextStack->forValue);
/* This error message will refer to the current iteration */
if (sym->type != SYM_SET)
fatalerror("Failed to update FOR symbol value\n");
}
/* Advance to the next iteration */
fileInfo->iters[0]++; fileInfo->iters[0]++;
/* If this wasn't the last iteration, wrap instead of popping */ /* If this wasn't the last iteration, wrap instead of popping */
if (fileInfo->iters[0] <= contextStack->nbReptIters) { if (fileInfo->iters[0] <= contextStack->nbReptIters) {
@@ -231,17 +259,20 @@ bool yywrap(void)
struct Context *context = contextStack; struct Context *context = contextStack;
contextStack = contextStack->parent; contextStack = contextStack->parent;
assert(contextDepth != 0); // This is never supposed to underflow
contextDepth--; contextDepth--;
lexer_DeleteState(context->lexerState); lexer_DeleteState(context->lexerState);
/* Restore args if a macro (not REPT) saved them */ /* Restore args if a macro (not REPT) saved them */
if (context->fileInfo->type == NODE_MACRO) { if (context->fileInfo->type == NODE_MACRO) {
dbgPrint("Restoring macro args %p\n", contextStack->macroArgs); dbgPrint("Restoring macro args %p\n", (void *)contextStack->macroArgs);
macro_UseNewArgs(contextStack->macroArgs); macro_UseNewArgs(contextStack->macroArgs);
} }
/* Free the file stack node */ /* Free the file stack node */
if (!context->fileInfo->referenced) if (!context->fileInfo->referenced)
free(context->fileInfo); free(context->fileInfo);
/* Free the FOR symbol name */
free(context->forName);
/* Free the entry and make its parent the current entry */ /* Free the entry and make its parent the current entry */
free(context); free(context);
@@ -253,6 +284,7 @@ bool yywrap(void)
/* /*
* Make sure not to switch the lexer state before calling this, so the saved line no is correct * Make sure not to switch the lexer state before calling this, so the saved line no is correct
* BE CAREFUL!! This modifies the file stack directly, you should have set up the file info first * BE CAREFUL!! This modifies the file stack directly, you should have set up the file info first
* Callers should set contextStack->lexerState after this so it is not NULL
*/ */
static void newContext(struct FileStackNode *fileInfo) static void newContext(struct FileStackNode *fileInfo)
{ {
@@ -267,13 +299,13 @@ static void newContext(struct FileStackNode *fileInfo)
fileInfo->referenced = false; fileInfo->referenced = false;
fileInfo->lineNo = lexer_GetLineNo(); fileInfo->lineNo = lexer_GetLineNo();
context->fileInfo = fileInfo; context->fileInfo = fileInfo;
context->forName = NULL;
/* /*
* Link new entry to its parent so it's reachable later * Link new entry to its parent so it's reachable later
* ERRORS SHOULD NOT OCCUR AFTER THIS!! * ERRORS SHOULD NOT OCCUR AFTER THIS!!
*/ */
context->parent = contextStack; context->parent = contextStack;
contextStack = context; contextStack = context;
} }
void fstk_RunInclude(char const *path) void fstk_RunInclude(char const *path)
@@ -285,10 +317,14 @@ void fstk_RunInclude(char const *path)
if (!fstk_FindFile(path, &fullPath, &size)) { if (!fstk_FindFile(path, &fullPath, &size)) {
free(fullPath); free(fullPath);
if (oGeneratedMissingIncludes) if (oGeneratedMissingIncludes) {
if (verbose)
printf("Aborting (-MG) on INCLUDE file '%s' (%s)\n",
path, strerror(errno));
oFailedOnMissingInclude = true; oFailedOnMissingInclude = true;
else } else {
error("Unable to open included file '%s': %s\n", path, strerror(errno)); error("Unable to open included file '%s': %s\n", path, strerror(errno));
}
return; return;
} }
dbgPrint("Full path: \"%s\"\n", fullPath); dbgPrint("Full path: \"%s\"\n", fullPath);
@@ -376,9 +412,8 @@ void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
memcpy(dest, macro->name, macroNameLen + 1); memcpy(dest, macro->name, macroNameLen + 1);
newContext((struct FileStackNode *)fileInfo); newContext((struct FileStackNode *)fileInfo);
/* Line minus 1 because buffer begins with a newline */
contextStack->lexerState = lexer_OpenFileView(macro->macro, macro->macroSize, contextStack->lexerState = lexer_OpenFileView(macro->macro, macro->macroSize,
macro->fileLine - 1); macro->fileLine);
if (!contextStack->lexerState) if (!contextStack->lexerState)
fatalerror("Failed to set up lexer for macro invocation\n"); fatalerror("Failed to set up lexer for macro invocation\n");
lexer_SetStateAtEOL(contextStack->lexerState); lexer_SetStateAtEOL(contextStack->lexerState);
@@ -386,12 +421,8 @@ void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
macro_UseNewArgs(args); macro_UseNewArgs(args);
} }
void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size) static bool newReptContext(int32_t reptLineNo, char *body, size_t size)
{ {
dbgPrint("Running REPT(%" PRIu32 ")\n", count);
if (count == 0)
return;
uint32_t reptDepth = contextStack->fileInfo->type == NODE_REPT uint32_t reptDepth = contextStack->fileInfo->type == NODE_REPT
? ((struct FileStackReptNode *)contextStack->fileInfo)->reptDepth ? ((struct FileStackReptNode *)contextStack->fileInfo)->reptDepth
: 0; : 0;
@@ -400,7 +431,7 @@ void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
if (!fileInfo) { if (!fileInfo) {
error("Failed to alloc file info for REPT: %s\n", strerror(errno)); error("Failed to alloc file info for REPT: %s\n", strerror(errno));
return; return false;
} }
fileInfo->node.type = NODE_REPT; fileInfo->node.type = NODE_REPT;
fileInfo->reptDepth = reptDepth + 1; fileInfo->reptDepth = reptDepth + 1;
@@ -417,11 +448,75 @@ void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
contextStack->lexerState = lexer_OpenFileView(body, size, reptLineNo); contextStack->lexerState = lexer_OpenFileView(body, size, reptLineNo);
if (!contextStack->lexerState) if (!contextStack->lexerState)
fatalerror("Failed to set up lexer for rept block\n"); fatalerror("Failed to set up lexer for REPT block\n");
lexer_SetStateAtEOL(contextStack->lexerState); lexer_SetStateAtEOL(contextStack->lexerState);
contextStack->uniqueID = macro_UseNewUniqueID(); contextStack->uniqueID = macro_UseNewUniqueID();
contextStack->nbReptIters = count; return true;
}
void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
{
dbgPrint("Running REPT(%" PRIu32 ")\n", count);
if (count == 0)
return;
if (!newReptContext(reptLineNo, body, size))
return;
contextStack->nbReptIters = count;
contextStack->forName = NULL;
}
void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
int32_t reptLineNo, char *body, size_t size)
{
dbgPrint("Running FOR(\"%s\", %" PRId32 ", %" PRId32 ", %" PRId32 ")\n",
symName, start, stop, step);
struct Symbol *sym = sym_AddSet(symName, start);
if (sym->type != SYM_SET)
return;
uint32_t count = 0;
if (step > 0 && start < stop)
count = (stop - start - 1) / step + 1;
else if (step < 0 && stop < start)
count = (start - stop - 1) / -step + 1;
else if (step == 0)
error("FOR cannot have a step value of 0\n");
if (count == 0)
return;
if (!newReptContext(reptLineNo, body, size))
return;
contextStack->nbReptIters = count;
contextStack->forValue = start;
contextStack->forStep = step;
contextStack->forName = strdup(symName);
if (!contextStack->forName)
fatalerror("Not enough memory for FOR symbol name: %s\n", strerror(errno));
}
void fstk_StopRept(void)
{
/* Prevent more iterations */
contextStack->nbReptIters = 0;
}
bool fstk_Break(void)
{
dbgPrint("Breaking out of REPT/FOR\n");
if (contextStack->fileInfo->type != NODE_REPT) {
error("BREAK can only be used inside a REPT/FOR block\n");
return false;
}
fstk_StopRept();
return true;
} }
void fstk_Init(char const *mainPath, size_t maxRecursionDepth) void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
@@ -444,6 +539,7 @@ void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
context->fileInfo = (struct FileStackNode *)fileInfo; context->fileInfo = (struct FileStackNode *)fileInfo;
/* lineNo and reptIter are unused on the top-level context */ /* lineNo and reptIter are unused on the top-level context */
context->fileInfo->parent = NULL; context->fileInfo->parent = NULL;
context->fileInfo->lineNo = 0; // This still gets written to the object file, so init it
context->fileInfo->referenced = false; context->fileInfo->referenced = false;
context->fileInfo->type = NODE_FILE; context->fileInfo->type = NODE_FILE;
memcpy(fileInfo->name, fileName, len + 1); memcpy(fileInfo->name, fileName, len + 1);
@@ -453,6 +549,9 @@ void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
context->uniqueID = 0; context->uniqueID = 0;
macro_SetUniqueID(0); macro_SetUniqueID(0);
context->nbReptIters = 0; context->nbReptIters = 0;
context->forValue = 0;
context->forStep = 0;
context->forName = NULL;
/* Now that it's set up properly, register the context */ /* Now that it's set up properly, register the context */
contextStack = context; contextStack = context;

File diff suppressed because it is too large Load Diff

View File

@@ -2,14 +2,16 @@
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "asm/asm.h"
#include "asm/macro.h" #include "asm/macro.h"
#include "asm/warning.h" #include "asm/warning.h"
#define MAXMACROARGS 99999
/* /*
* Your average macro invocation does not go past the tens, but some go further * Your average macro invocation does not go past the tens, but some go further
* This ensures that sane and slightly insane invocations suffer no penalties, * This ensures that sane and slightly insane invocations suffer no penalties,
@@ -36,7 +38,7 @@ static uint32_t maxUniqueID = 0;
* guarantees the size of the buffer will be correct. I was unable to find a * guarantees the size of the buffer will be correct. I was unable to find a
* better solution, but if you have one, please feel free! * better solution, but if you have one, please feel free!
*/ */
static char uniqueIDBuf[] = "_" EXPAND_AND_STR(UINT32_MAX); static char uniqueIDBuf[] = "_u4294967295"; // UINT32_MAX
static char *uniqueIDPtr = NULL; static char *uniqueIDPtr = NULL;
struct MacroArgs *macro_GetCurrentArgs(void) struct MacroArgs *macro_GetCurrentArgs(void)
@@ -60,14 +62,15 @@ struct MacroArgs *macro_NewArgs(void)
void macro_AppendArg(struct MacroArgs **argPtr, char *s) void macro_AppendArg(struct MacroArgs **argPtr, char *s)
{ {
#define macArgs (*argPtr) #define macArgs (*argPtr)
if (s[0] == '\0')
warning(WARNING_EMPTY_MACRO_ARG, "Empty macro argument\n");
if (macArgs->nbArgs == MAXMACROARGS) if (macArgs->nbArgs == MAXMACROARGS)
error("A maximum of " EXPAND_AND_STR(MAXMACROARGS) error("A maximum of " EXPAND_AND_STR(MAXMACROARGS) " arguments is allowed\n");
" arguments is allowed\n");
if (macArgs->nbArgs >= macArgs->capacity) { if (macArgs->nbArgs >= macArgs->capacity) {
macArgs->capacity *= 2; macArgs->capacity *= 2;
/* Check that overflow didn't roll us back */ /* Check that overflow didn't roll us back */
if (macArgs->capacity <= macArgs->nbArgs) if (macArgs->capacity <= macArgs->nbArgs)
fatalerror("Failed to add new macro argument: possible capacity overflow\n"); fatalerror("Failed to add new macro argument: capacity overflow\n");
macArgs = realloc(macArgs, SIZEOF_ARGS(macArgs->capacity)); macArgs = realloc(macArgs, SIZEOF_ARGS(macArgs->capacity));
if (!macArgs) if (!macArgs)
fatalerror("Error adding new macro argument: %s\n", strerror(errno)); fatalerror("Error adding new macro argument: %s\n", strerror(errno));
@@ -98,6 +101,40 @@ char const *macro_GetArg(uint32_t i)
: macroArgs->args[realIndex]; : macroArgs->args[realIndex];
} }
char *macro_GetAllArgs(void)
{
if (!macroArgs)
return NULL;
if (macroArgs->shift >= macroArgs->nbArgs)
return "";
size_t len = 0;
for (uint32_t i = macroArgs->shift; i < macroArgs->nbArgs; i++)
len += strlen(macroArgs->args[i]) + 1; /* 1 for comma */
char *str = malloc(len + 1); /* 1 for '\0' */
char *ptr = str;
if (!str)
fatalerror("Failed to allocate memory for expanding '\\#': %s\n", strerror(errno));
for (uint32_t i = macroArgs->shift; i < macroArgs->nbArgs; i++) {
size_t n = strlen(macroArgs->args[i]);
memcpy(ptr, macroArgs->args[i], n);
ptr += n;
/* Commas go between args and after a last empty arg */
if (i < macroArgs->nbArgs - 1 || n == 0)
*ptr++ = ','; /* no space after comma */
}
*ptr = '\0';
return str;
}
uint32_t macro_GetUniqueID(void) uint32_t macro_GetUniqueID(void)
{ {
return uniqueID; return uniqueID;
@@ -117,7 +154,8 @@ void macro_SetUniqueID(uint32_t id)
if (uniqueID > maxUniqueID) if (uniqueID > maxUniqueID)
maxUniqueID = uniqueID; maxUniqueID = uniqueID;
/* The buffer is guaranteed to be the correct size */ /* The buffer is guaranteed to be the correct size */
sprintf(uniqueIDBuf, "_%" PRIu32, id); /* This is a valid label fragment, but not a valid numeric */
sprintf(uniqueIDBuf, "_u%" PRIu32, id);
uniqueIDPtr = uniqueIDBuf; uniqueIDPtr = uniqueIDBuf;
} }
} }
@@ -132,12 +170,17 @@ void macro_ShiftCurrentArgs(int32_t count)
{ {
if (!macroArgs) { if (!macroArgs) {
error("Cannot shift macro arguments outside of a macro\n"); error("Cannot shift macro arguments outside of a macro\n");
} else if (count < 0) { } else if (count > 0 && ((uint32_t)count > macroArgs->nbArgs
error("Cannot shift arguments by negative amount %" PRId32 "\n", count); || macroArgs->shift > macroArgs->nbArgs - count)) {
} else if (macroArgs->shift < macroArgs->nbArgs) { warning(WARNING_MACRO_SHIFT,
macroArgs->shift += count; "Cannot shift macro arguments past their end\n");
if (macroArgs->shift > macroArgs->nbArgs)
macroArgs->shift = macroArgs->nbArgs; macroArgs->shift = macroArgs->nbArgs;
} else if (count < 0 && macroArgs->shift < (uint32_t)-count) {
warning(WARNING_MACRO_SHIFT,
"Cannot shift macro arguments past their beginning\n");
macroArgs->shift = 0;
} else {
macroArgs->shift += count;
} }
} }

View File

@@ -19,9 +19,11 @@
#include <time.h> #include <time.h>
#include "asm/charmap.h" #include "asm/charmap.h"
#include "asm/format.h"
#include "asm/fstack.h" #include "asm/fstack.h"
#include "asm/lexer.h" #include "asm/lexer.h"
#include "asm/main.h" #include "asm/main.h"
#include "asm/opt.h"
#include "asm/output.h" #include "asm/output.h"
#include "asm/rpn.h" #include "asm/rpn.h"
#include "asm/symbol.h" #include "asm/symbol.h"
@@ -38,15 +40,6 @@
// Unfortunately, macOS still ships 2.3, which is from 2008... // Unfortunately, macOS still ships 2.3, which is from 2008...
int yyparse(void); int yyparse(void);
size_t cldefines_index;
size_t cldefines_numindices;
size_t cldefines_bufsize;
const size_t cldefine_entrysize = 2 * sizeof(void *);
char **cldefines;
clock_t nStartClock, nEndClock;
uint32_t nTotalLines, nIFDepth;
#if defined(YYDEBUG) && YYDEBUG #if defined(YYDEBUG) && YYDEBUG
extern int yydebug; extern int yydebug;
#endif #endif
@@ -57,162 +50,11 @@ bool oFailedOnMissingInclude;
bool oGeneratePhonyDeps; bool oGeneratePhonyDeps;
char *tzTargetFileName; char *tzTargetFileName;
/*
* Option stack
*/
struct sOptions DefaultOptions;
struct sOptions CurrentOptions;
bool haltnop; bool haltnop;
bool optimizeloads; bool optimizeloads;
bool verbose; bool verbose;
bool warnings; /* True to enable warnings, false to disable them. */ bool warnings; /* True to enable warnings, false to disable them. */
struct sOptionStackEntry {
struct sOptions Options;
struct sOptionStackEntry *next;
};
struct sOptionStackEntry *pOptionStack;
void opt_SetCurrentOptions(struct sOptions *opt)
{
CurrentOptions = *opt;
lexer_SetGfxDigits(CurrentOptions.gbgfx);
lexer_SetBinDigits(CurrentOptions.binary);
}
void opt_Parse(char *s)
{
struct sOptions newopt;
newopt = CurrentOptions;
switch (s[0]) {
case 'g':
if (strlen(&s[1]) == 4) {
newopt.gbgfx[0] = s[1];
newopt.gbgfx[1] = s[2];
newopt.gbgfx[2] = s[3];
newopt.gbgfx[3] = s[4];
} else {
error("Must specify exactly 4 characters for option 'g'\n");
}
break;
case 'b':
if (strlen(&s[1]) == 2) {
newopt.binary[0] = s[1];
newopt.binary[1] = s[2];
} else {
error("Must specify exactly 2 characters for option 'b'\n");
}
break;
case 'z':
warning(WARNING_OBSOLETE, "Option 'z' is a deprecated alias for 'p'\n");
/* fallthrough */
case 'p':
if (strlen(&s[1]) <= 2) {
int result;
unsigned int fillchar;
result = sscanf(&s[1], "%x", &fillchar);
if (result != EOF && result != 1)
error("Invalid argument for option 'z'\n");
else
newopt.fillchar = fillchar;
} else {
error("Invalid argument for option 'z'\n");
}
break;
default:
error("Unknown option\n");
break;
}
opt_SetCurrentOptions(&newopt);
}
void opt_Push(void)
{
struct sOptionStackEntry *pOpt;
pOpt = malloc(sizeof(struct sOptionStackEntry));
if (pOpt == NULL)
fatalerror("No memory for option stack\n");
pOpt->Options = CurrentOptions;
pOpt->next = pOptionStack;
pOptionStack = pOpt;
}
void opt_Pop(void)
{
if (pOptionStack == NULL)
fatalerror("No entries in the option stack\n");
struct sOptionStackEntry *pOpt;
pOpt = pOptionStack;
opt_SetCurrentOptions(&(pOpt->Options));
pOptionStack = pOpt->next;
free(pOpt);
}
void opt_AddDefine(char *s)
{
char *value, *equals;
if (cldefines_index >= cldefines_numindices) {
/* Check for overflows */
if ((cldefines_numindices * 2) < cldefines_numindices)
fatalerror("No memory for command line defines\n");
if ((cldefines_bufsize * 2) < cldefines_bufsize)
fatalerror("No memory for command line defines\n");
cldefines_numindices *= 2;
cldefines_bufsize *= 2;
cldefines = realloc(cldefines, cldefines_bufsize);
if (!cldefines)
fatalerror("No memory for command line defines\n");
}
equals = strchr(s, '=');
if (equals) {
*equals = '\0';
value = equals + 1;
} else {
value = "1";
}
cldefines[cldefines_index++] = s;
cldefines[cldefines_index++] = value;
}
static void opt_ParseDefines(void)
{
uint32_t i;
for (i = 0; i < cldefines_index; i += 2)
sym_AddString(cldefines[i], cldefines[i + 1]);
}
void upperstring(char *s)
{
while (*s) {
*s = toupper(*s);
s++;
}
}
void lowerstring(char *s)
{
while (*s) {
*s = tolower(*s);
s++;
}
}
/* Escapes Make-special chars from a string */ /* Escapes Make-special chars from a string */
static char *make_escape(const char *str) static char *make_escape(const char *str)
{ {
@@ -234,7 +76,7 @@ static char *make_escape(const char *str)
} }
/* Short options */ /* Short options */
static char const *optstring = "b:D:Eg:hi:LM:o:p:r:VvW:w"; static const char *optstring = "b:D:Eg:hi:LM:o:p:r:VvW:w";
/* Variables for the long-only options */ /* Variables for the long-only options */
static int depType; /* Variants of `-M` */ static int depType; /* Variants of `-M` */
@@ -276,7 +118,7 @@ static void print_usage(void)
fputs( fputs(
"Usage: rgbasm [-EhLVvw] [-b chars] [-D name[=value]] [-g chars] [-i path]\n" "Usage: rgbasm [-EhLVvw] [-b chars] [-D name[=value]] [-g chars] [-i path]\n"
" [-M depend_file] [-MG] [-MP] [-MT target_file] [-MQ target_file]\n" " [-M depend_file] [-MG] [-MP] [-MT target_file] [-MQ target_file]\n"
" [-o out_file] [-p pad_value] [-r depth] [-W warning] <file> ...\n" " [-o out_file] [-p pad_value] [-r depth] [-W warning] <file>\n"
"Useful options:\n" "Useful options:\n"
" -E, --export-all export all labels\n" " -E, --export-all export all labels\n"
" -M, --dependfile <path> set the output dependency file\n" " -M, --dependfile <path> set the output dependency file\n"
@@ -295,120 +137,130 @@ int main(int argc, char *argv[])
int ch; int ch;
char *ep; char *ep;
struct sOptions newopt; time_t now = time(NULL);
char const *sourceDateEpoch = getenv("SOURCE_DATE_EPOCH");
char *tzMainfile; /*
* Support SOURCE_DATE_EPOCH for reproducible builds
* https://reproducible-builds.org/docs/source-date-epoch/
*/
if (sourceDateEpoch)
now = (time_t)strtoul(sourceDateEpoch, NULL, 0);
dependfile = NULL; dependfile = NULL;
/* Initial number of allocated elements in array */
cldefines_numindices = 32;
cldefines_bufsize = cldefines_numindices * cldefine_entrysize;
cldefines = malloc(cldefines_bufsize);
if (!cldefines)
fatalerror("No memory for command line defines\n");
#if defined(YYDEBUG) && YYDEBUG #if defined(YYDEBUG) && YYDEBUG
yydebug = 1; yydebug = 1;
#endif #endif
// Perform some init for below
sym_Init(now);
// Set defaults
oGeneratePhonyDeps = false; oGeneratePhonyDeps = false;
oGeneratedMissingIncludes = false; oGeneratedMissingIncludes = false;
oFailedOnMissingInclude = false; oFailedOnMissingInclude = false;
tzTargetFileName = NULL; tzTargetFileName = NULL;
uint32_t maxRecursionDepth = 64;
size_t nTargetFileNameLen = 0;
DefaultOptions.gbgfx[0] = '0'; opt_B("01");
DefaultOptions.gbgfx[1] = '1'; opt_G("0123");
DefaultOptions.gbgfx[2] = '2'; opt_P(0);
DefaultOptions.gbgfx[3] = '3';
DefaultOptions.binary[0] = '0';
DefaultOptions.binary[1] = '1';
DefaultOptions.fillchar = 0;
optimizeloads = true; optimizeloads = true;
haltnop = true; haltnop = true;
verbose = false; verbose = false;
warnings = true; warnings = true;
bool exportall = false; sym_SetExportAll(false);
uint32_t maxRecursionDepth = 64;
size_t nTargetFileNameLen = 0;
opt_SetCurrentOptions(&DefaultOptions); while ((ch = musl_getopt_long_only(argc, argv, optstring, longopts, NULL)) != -1) {
newopt = CurrentOptions;
while ((ch = musl_getopt_long_only(argc, argv, optstring, longopts,
NULL)) != -1) {
switch (ch) { switch (ch) {
case 'b': case 'b':
if (strlen(optarg) == 2) { if (strlen(musl_optarg) == 2)
newopt.binary[0] = optarg[1]; opt_B(&musl_optarg[1]);
newopt.binary[1] = optarg[2]; else
} else {
errx(1, "Must specify exactly 2 characters for option 'b'"); errx(1, "Must specify exactly 2 characters for option 'b'");
}
break; break;
char *equals;
case 'D': case 'D':
opt_AddDefine(optarg); equals = strchr(musl_optarg, '=');
break; if (equals) {
case 'E': *equals = '\0';
exportall = true; sym_AddString(musl_optarg, equals + 1);
break;
case 'g':
if (strlen(optarg) == 4) {
newopt.gbgfx[0] = optarg[1];
newopt.gbgfx[1] = optarg[2];
newopt.gbgfx[2] = optarg[3];
newopt.gbgfx[3] = optarg[4];
} else { } else {
errx(1, "Must specify exactly 4 characters for option 'g'"); sym_AddString(musl_optarg, "1");
} }
break; break;
case 'E':
sym_SetExportAll(true);
break;
case 'g':
if (strlen(musl_optarg) == 4)
opt_G(&musl_optarg[1]);
else
errx(1, "Must specify exactly 4 characters for option 'g'");
break;
case 'h': case 'h':
haltnop = false; haltnop = false;
break; break;
case 'i': case 'i':
fstk_AddIncludePath(optarg); fstk_AddIncludePath(musl_optarg);
break; break;
case 'L': case 'L':
optimizeloads = false; optimizeloads = false;
break; break;
case 'M': case 'M':
if (!strcmp("-", optarg)) if (!strcmp("-", musl_optarg))
dependfile = stdout; dependfile = stdout;
else else
dependfile = fopen(optarg, "w"); dependfile = fopen(musl_optarg, "w");
if (dependfile == NULL) if (dependfile == NULL)
err(1, "Could not open dependfile %s", err(1, "Could not open dependfile %s", musl_optarg);
optarg);
break; break;
case 'o':
out_SetFileName(optarg);
break;
case 'p':
newopt.fillchar = strtoul(optarg, &ep, 0);
if (optarg[0] == '\0' || *ep != '\0') case 'o':
out_SetFileName(musl_optarg);
break;
unsigned long fill;
case 'p':
fill = strtoul(musl_optarg, &ep, 0);
if (musl_optarg[0] == '\0' || *ep != '\0')
errx(1, "Invalid argument for option 'p'"); errx(1, "Invalid argument for option 'p'");
if (newopt.fillchar < 0 || newopt.fillchar > 0xFF) if (fill < 0 || fill > 0xFF)
errx(1, "Argument for option 'p' must be between 0 and 0xFF"); errx(1, "Argument for option 'p' must be between 0 and 0xFF");
opt_P(fill);
break; break;
case 'r':
maxRecursionDepth = strtoul(optarg, &ep, 0);
if (optarg[0] == '\0' || *ep != '\0') case 'r':
maxRecursionDepth = strtoul(musl_optarg, &ep, 0);
if (musl_optarg[0] == '\0' || *ep != '\0')
errx(1, "Invalid argument for option 'r'"); errx(1, "Invalid argument for option 'r'");
break; 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);
case 'v': case 'v':
verbose = true; verbose = true;
break; break;
case 'W': case 'W':
processWarningFlag(optarg); processWarningFlag(musl_optarg);
break; break;
case 'w': case 'w':
warnings = false; warnings = false;
break; break;
@@ -419,28 +271,27 @@ int main(int argc, char *argv[])
case 'G': case 'G':
oGeneratedMissingIncludes = true; oGeneratedMissingIncludes = true;
break; break;
case 'P': case 'P':
oGeneratePhonyDeps = true; oGeneratePhonyDeps = true;
break; break;
case 'Q': case 'Q':
case 'T': case 'T':
if (optind == argc) if (musl_optind == argc)
errx(1, "-M%c takes a target file name argument", errx(1, "-M%c takes a target file name argument", depType);
depType); ep = musl_optarg;
ep = optarg;
if (depType == 'Q') if (depType == 'Q')
ep = make_escape(ep); ep = make_escape(ep);
nTargetFileNameLen += strlen(ep) + 1; nTargetFileNameLen += strlen(ep) + 1;
if (!tzTargetFileName) { if (!tzTargetFileName) {
/* On first alloc, make an empty str */ /* On first alloc, make an empty str */
tzTargetFileName = tzTargetFileName = malloc(nTargetFileNameLen + 1);
malloc(nTargetFileNameLen + 1);
if (tzTargetFileName) if (tzTargetFileName)
*tzTargetFileName = '\0'; *tzTargetFileName = '\0';
} else { } else {
tzTargetFileName = tzTargetFileName = realloc(tzTargetFileName,
realloc(tzTargetFileName,
nTargetFileNameLen + 1); nTargetFileNameLen + 1);
} }
if (tzTargetFileName == NULL) if (tzTargetFileName == NULL)
@@ -448,8 +299,8 @@ int main(int argc, char *argv[])
strcat(tzTargetFileName, ep); strcat(tzTargetFileName, ep);
if (depType == 'Q') if (depType == 'Q')
free(ep); free(ep);
char *ptr = tzTargetFileName + char *ptr = tzTargetFileName + strlen(tzTargetFileName);
strlen(tzTargetFileName);
*ptr++ = ' '; *ptr++ = ' ';
*ptr = '\0'; *ptr = '\0';
break; break;
@@ -462,76 +313,50 @@ int main(int argc, char *argv[])
/* NOTREACHED */ /* NOTREACHED */
} }
} }
argc -= optind;
argv += optind;
if (tzTargetFileName == NULL) if (tzTargetFileName == NULL)
tzTargetFileName = tzObjectname; tzTargetFileName = tzObjectname;
opt_SetCurrentOptions(&newopt); if (argc == musl_optind) {
fputs("FATAL: No input files\n", stderr);
DefaultOptions = CurrentOptions; print_usage();
} else if (argc != musl_optind + 1) {
if (argc == 0) { fputs("FATAL: More than one input file given\n", stderr);
fputs("FATAL: no input files\n", stderr);
print_usage(); print_usage();
} }
tzMainfile = argv[argc - 1]; char const *mainFileName = argv[musl_optind];
if (verbose) if (verbose)
printf("Assembling %s\n", tzMainfile); printf("Assembling %s\n", mainFileName);
if (dependfile) { if (dependfile) {
if (!tzTargetFileName) if (!tzTargetFileName)
errx(1, "Dependency files can only be created if a target file is specified with either -o, -MQ or -MT.\n"); errx(1, "Dependency files can only be created if a target file is specified with either -o, -MQ or -MT\n");
fprintf(dependfile, "%s: %s\n", tzTargetFileName, tzMainfile); fprintf(dependfile, "%s: %s\n", tzTargetFileName, mainFileName);
} }
/* Init file stack; important to do first, since it provides the file name, line, etc */
lexer_Init();
fstk_Init(tzMainfile, maxRecursionDepth);
nStartClock = clock();
nTotalLines = 0;
nIFDepth = 0;
sym_Init();
sym_SetExportAll(exportall);
opt_ParseDefines();
charmap_New("main", NULL); charmap_New("main", NULL);
opt_SetCurrentOptions(&DefaultOptions); // Init lexer and file stack, prodiving file info
lexer_Init();
fstk_Init(mainFileName, maxRecursionDepth);
// Perform parse (yyparse is auto-generated from `parser.y`)
if (yyparse() != 0 && nbErrors == 0)
nbErrors = 1;
if (yyparse() != 0 || nbErrors != 0)
errx(1, "Assembly aborted (%u errors)!", nbErrors);
if (dependfile) if (dependfile)
fclose(dependfile); fclose(dependfile);
if (nIFDepth != 0)
errx(1, "Unterminated IF construct (%" PRIu32 " levels)!",
nIFDepth);
sect_CheckUnionClosed(); sect_CheckUnionClosed();
double timespent; if (nbErrors != 0)
errx(1, "Assembly aborted (%u error%s)!", nbErrors,
nEndClock = clock(); nbErrors == 1 ? "" : "s");
timespent = ((double)(nEndClock - nStartClock))
/ (double)CLOCKS_PER_SEC;
if (verbose) {
printf("Success! %" PRIu32 " lines in %d.%02d seconds ",
nTotalLines, (int)timespent,
((int)(timespent * 100.0)) % 100);
if (timespent < FLT_MIN_EXP)
printf("(INFINITY lines/minute)\n");
else
printf("(%d lines/minute)\n",
(int)(60 / timespent * nTotalLines));
}
// If parse aborted due to missing an include, and `-MG` was given, exit normally
if (oFailedOnMissingInclude) if (oFailedOnMissingInclude)
return 0; return 0;

View File

@@ -1,147 +0,0 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
/*
* Fixedpoint math routines
*/
#include <inttypes.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include "asm/mymath.h"
#include "asm/symbol.h"
#define fx2double(i) ((double)((i) / 65536.0))
#define double2fx(d) ((int32_t)((d) * 65536.0))
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
/*
* Define the _PI symbol
*/
void math_DefinePI(void)
{
sym_AddEqu("_PI", double2fx(M_PI));
}
/*
* Print a fixed point value
*/
void math_Print(int32_t i)
{
uint32_t u = i;
const char *sign = "";
if (i < 0) {
u = -u;
sign = "-";
}
printf("%s%" PRIu32 ".%05" PRIu32, sign, u >> 16,
((uint32_t)(fx2double(u) * 100000 + 0.5)) % 100000);
}
/*
* Calculate sine
*/
int32_t math_Sin(int32_t i)
{
return double2fx(sin(fx2double(i) * 2 * M_PI / 65536));
}
/*
* Calculate cosine
*/
int32_t math_Cos(int32_t i)
{
return double2fx(cos(fx2double(i) * 2 * M_PI / 65536));
}
/*
* Calculate tangent
*/
int32_t math_Tan(int32_t i)
{
return double2fx(tan(fx2double(i) * 2 * M_PI / 65536));
}
/*
* Calculate arcsine
*/
int32_t math_ASin(int32_t i)
{
return double2fx(asin(fx2double(i)) / 2 / M_PI * 65536);
}
/*
* Calculate arccosine
*/
int32_t math_ACos(int32_t i)
{
return double2fx(acos(fx2double(i)) / 2 / M_PI * 65536);
}
/*
* Calculate arctangent
*/
int32_t math_ATan(int32_t i)
{
return double2fx(atan(fx2double(i)) / 2 / M_PI * 65536);
}
/*
* Calculate atan2
*/
int32_t math_ATan2(int32_t i, int32_t j)
{
return double2fx(atan2(fx2double(i), fx2double(j)) / 2 / M_PI * 65536);
}
/*
* Multiplication
*/
int32_t math_Mul(int32_t i, int32_t j)
{
return double2fx(fx2double(i) * fx2double(j));
}
/*
* Division
*/
int32_t math_Div(int32_t i, int32_t j)
{
return double2fx(fx2double(i) / fx2double(j));
}
/*
* Round
*/
int32_t math_Round(int32_t i)
{
return double2fx(round(fx2double(i)));
}
/*
* Ceil
*/
int32_t math_Ceil(int32_t i)
{
return double2fx(ceil(fx2double(i)));
}
/*
* Floor
*/
int32_t math_Floor(int32_t i)
{
return double2fx(floor(fx2double(i)));
}

111
src/asm/opt.c Normal file
View File

@@ -0,0 +1,111 @@
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "asm/lexer.h"
#include "asm/section.h"
#include "asm/warning.h"
struct OptStackEntry {
char binary[2];
char gbgfx[4];
int32_t fillByte;
struct OptStackEntry *next;
};
static struct OptStackEntry *stack = NULL;
void opt_B(char chars[2])
{
lexer_SetBinDigits(chars);
}
void opt_G(char chars[4])
{
lexer_SetGfxDigits(chars);
}
void opt_P(uint8_t fill)
{
fillByte = fill;
}
void opt_Parse(char *s)
{
switch (s[0]) {
case 'b':
if (strlen(&s[1]) == 2)
opt_B(&s[1]);
else
error("Must specify exactly 2 characters for option 'b'\n");
break;
case 'g':
if (strlen(&s[1]) == 4)
opt_G(&s[1]);
else
error("Must specify exactly 4 characters for option 'g'\n");
break;
case 'p':
if (strlen(&s[1]) <= 2) {
int result;
unsigned int fillchar;
result = sscanf(&s[1], "%x", &fillchar);
if (result != EOF && result != 1)
error("Invalid argument for option 'p'\n");
else
opt_P(fillchar);
} else {
error("Invalid argument for option 'p'\n");
}
break;
default:
error("Unknown option '%c'\n", s[0]);
break;
}
}
void opt_Push(void)
{
struct OptStackEntry *entry = malloc(sizeof(*entry));
if (entry == NULL)
fatalerror("Failed to alloc option stack entry: %s\n", strerror(errno));
// Both of these pulled from lexer.h
entry->binary[0] = binDigits[0];
entry->binary[1] = binDigits[1];
entry->gbgfx[0] = gfxDigits[0];
entry->gbgfx[1] = gfxDigits[1];
entry->gbgfx[2] = gfxDigits[2];
entry->gbgfx[3] = gfxDigits[3];
entry->fillByte = fillByte; // Pulled from section.h
entry->next = stack;
stack = entry;
}
void opt_Pop(void)
{
if (stack == NULL) {
error("No entries in the option stack\n");
return;
}
struct OptStackEntry *entry = stack;
opt_B(entry->binary);
opt_G(entry->gbgfx);
opt_P(entry->fillByte);
stack = entry->next;
free(entry);
}

View File

@@ -18,7 +18,6 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "asm/asm.h"
#include "asm/charmap.h" #include "asm/charmap.h"
#include "asm/fstack.h" #include "asm/fstack.h"
#include "asm/main.h" #include "asm/main.h"
@@ -69,7 +68,7 @@ static struct FileStackNode *fileStackNodes = NULL;
/* /*
* Count the number of sections used in this object * Count the number of sections used in this object
*/ */
static uint32_t countsections(void) static uint32_t countSections(void)
{ {
uint32_t count = 0; uint32_t count = 0;
@@ -82,7 +81,7 @@ static uint32_t countsections(void)
/* /*
* Count the number of patches used in this object * Count the number of patches used in this object
*/ */
static uint32_t countpatches(struct Section const *sect) static uint32_t countPatches(struct Section const *sect)
{ {
uint32_t r = 0; uint32_t r = 0;
@@ -96,7 +95,7 @@ static uint32_t countpatches(struct Section const *sect)
/** /**
* Count the number of assertions used in this object * Count the number of assertions used in this object
*/ */
static uint32_t countasserts(void) static uint32_t countAsserts(void)
{ {
struct Assertion *assert = assertions; struct Assertion *assert = assertions;
uint32_t count = 0; uint32_t count = 0;
@@ -111,22 +110,22 @@ static uint32_t countasserts(void)
/* /*
* Write a long to a file (little-endian) * Write a long to a file (little-endian)
*/ */
static void fputlong(uint32_t i, FILE *f) static void putlong(uint32_t i, FILE *f)
{ {
fputc(i, f); putc(i, f);
fputc(i >> 8, f); putc(i >> 8, f);
fputc(i >> 16, f); putc(i >> 16, f);
fputc(i >> 24, f); putc(i >> 24, f);
} }
/* /*
* Write a NULL-terminated string to a file * Write a NULL-terminated string to a file
*/ */
static void fputstring(char const *s, FILE *f) static void putstring(char const *s, FILE *f)
{ {
while (*s) while (*s)
fputc(*s++, f); putc(*s++, f);
fputc(0, f); putc(0, f);
} }
static uint32_t getNbFileStackNodes(void) static uint32_t getNbFileStackNodes(void)
@@ -137,9 +136,9 @@ static uint32_t getNbFileStackNodes(void)
void out_RegisterNode(struct FileStackNode *node) void out_RegisterNode(struct FileStackNode *node)
{ {
/* If node is not already registered, register it (and parents), and give it a unique ID */ /* If node is not already registered, register it (and parents), and give it a unique ID */
while (node->ID == -1) { while (node->ID == (uint32_t)-1) {
node->ID = getNbFileStackNodes(); node->ID = getNbFileStackNodes();
if (node->ID == -1) if (node->ID == (uint32_t)-1)
fatalerror("Reached too many file stack nodes; try splitting the file up\n"); fatalerror("Reached too many file stack nodes; try splitting the file up\n");
node->next = fileStackNodes; node->next = fileStackNodes;
fileStackNodes = node; fileStackNodes = node;
@@ -204,14 +203,13 @@ static uint32_t getSectIDIfAny(struct Section const *sect)
static void writepatch(struct Patch const *patch, FILE *f) static void writepatch(struct Patch const *patch, FILE *f)
{ {
assert(patch->src->ID != -1); assert(patch->src->ID != -1);
putlong(patch->src->ID, f);
fputlong(patch->src->ID, f); putlong(patch->lineNo, f);
fputlong(patch->lineNo, f); putlong(patch->nOffset, f);
fputlong(patch->nOffset, f); putlong(getSectIDIfAny(patch->pcSection), f);
fputlong(getSectIDIfAny(patch->pcSection), f); putlong(patch->pcOffset, f);
fputlong(patch->pcOffset, f); putc(patch->type, f);
fputc(patch->type, f); putlong(patch->nRPNSize, f);
fputlong(patch->nRPNSize, f);
fwrite(patch->pRPN, 1, patch->nRPNSize, f); fwrite(patch->pRPN, 1, patch->nRPNSize, f);
} }
@@ -220,23 +218,23 @@ static void writepatch(struct Patch const *patch, FILE *f)
*/ */
static void writesection(struct Section const *sect, FILE *f) static void writesection(struct Section const *sect, FILE *f)
{ {
fputstring(sect->name, f); putstring(sect->name, f);
fputlong(sect->size, f); putlong(sect->size, f);
bool isUnion = sect->modifier == SECTION_UNION; bool isUnion = sect->modifier == SECTION_UNION;
bool isFragment = sect->modifier == SECTION_FRAGMENT; bool isFragment = sect->modifier == SECTION_FRAGMENT;
fputc(sect->type | isUnion << 7 | isFragment << 6, f); putc(sect->type | isUnion << 7 | isFragment << 6, f);
fputlong(sect->org, f); putlong(sect->org, f);
fputlong(sect->bank, f); putlong(sect->bank, f);
fputc(sect->align, f); putc(sect->align, f);
fputlong(sect->alignOfs, f); putlong(sect->alignOfs, f);
if (sect_HasData(sect->type)) { if (sect_HasData(sect->type)) {
fwrite(sect->data, 1, sect->size, f); fwrite(sect->data, 1, sect->size, f);
fputlong(countpatches(sect), f); putlong(countPatches(sect), f);
for (struct Patch const *patch = sect->patches; patch != NULL; for (struct Patch const *patch = sect->patches; patch != NULL;
patch = patch->next) patch = patch->next)
@@ -249,17 +247,17 @@ static void writesection(struct Section const *sect, FILE *f)
*/ */
static void writesymbol(struct Symbol const *sym, FILE *f) static void writesymbol(struct Symbol const *sym, FILE *f)
{ {
fputstring(sym->name, f); putstring(sym->name, f);
if (!sym_IsDefined(sym)) { if (!sym_IsDefined(sym)) {
fputc(SYMTYPE_IMPORT, f); putc(SYMTYPE_IMPORT, f);
} else { } else {
assert(sym->src->ID != -1); assert(sym->src->ID != -1);
fputc(sym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, f); putc(sym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, f);
fputlong(sym->src->ID, f); putlong(sym->src->ID, f);
fputlong(sym->fileLine, f); putlong(sym->fileLine, f);
fputlong(getSectIDIfAny(sym_GetSection(sym)), f); putlong(getSectIDIfAny(sym_GetSection(sym)), f);
fputlong(sym->value, f); putlong(sym->value, f);
} }
} }
@@ -268,7 +266,7 @@ static void registerSymbol(struct Symbol *sym)
*objectSymbolsTail = sym; *objectSymbolsTail = sym;
objectSymbolsTail = &sym->next; objectSymbolsTail = &sym->next;
out_RegisterNode(sym->src); out_RegisterNode(sym->src);
if (nbSymbols == -1) if (nbSymbols == (uint32_t)-1)
fatalerror("Registered too many symbols (%" PRIu32 fatalerror("Registered too many symbols (%" PRIu32
"); try splitting up your files\n", (uint32_t)-1); "); try splitting up your files\n", (uint32_t)-1);
sym->ID = nbSymbols++; sym->ID = nbSymbols++;
@@ -280,7 +278,7 @@ static void registerSymbol(struct Symbol *sym)
*/ */
static uint32_t getSymbolID(struct Symbol *sym) static uint32_t getSymbolID(struct Symbol *sym)
{ {
if (sym->ID == -1 && !sym_IsPC(sym)) if (sym->ID == (uint32_t)-1 && !sym_IsPC(sym))
registerSymbol(sym); registerSymbol(sym);
return sym->ID; return sym->ID;
} }
@@ -411,10 +409,15 @@ static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, ui
/* /*
* Create a new patch (includes the rpn expr) * Create a new patch (includes the rpn expr)
*/ */
void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs) void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs, uint32_t pcShift)
{ {
struct Patch *patch = allocpatch(type, expr, ofs); struct Patch *patch = allocpatch(type, expr, ofs);
// If the patch had a quantity of bytes output before it,
// PC is not at the patch's location, but at the location
// before those bytes.
patch->pcOffset -= pcShift;
patch->next = pCurrentSection->patches; patch->next = pCurrentSection->patches;
pCurrentSection->patches = patch; pCurrentSection->patches = patch;
} }
@@ -446,30 +449,32 @@ bool out_CreateAssert(enum AssertionType type, struct Expression const *expr,
static void writeassert(struct Assertion *assert, FILE *f) static void writeassert(struct Assertion *assert, FILE *f)
{ {
writepatch(assert->patch, f); writepatch(assert->patch, f);
fputstring(assert->message, f); putstring(assert->message, f);
} }
static void writeFileStackNode(struct FileStackNode const *node, FILE *f) static void writeFileStackNode(struct FileStackNode const *node, FILE *f)
{ {
fputlong(node->parent ? node->parent->ID : -1, f); putlong(node->parent ? node->parent->ID : -1, f);
fputlong(node->lineNo, f); putlong(node->lineNo, f);
fputc(node->type, f); putc(node->type, f);
if (node->type != NODE_REPT) { if (node->type != NODE_REPT) {
fputstring(((struct FileStackNamedNode const *)node)->name, f); putstring(((struct FileStackNamedNode const *)node)->name, f);
} else { } else {
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node; struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
fputlong(reptNode->reptDepth, f); putlong(reptNode->reptDepth, f);
/* Iters are stored by decreasing depth, so reverse the order for output */ /* Iters are stored by decreasing depth, so reverse the order for output */
for (uint32_t i = reptNode->reptDepth; i--; ) for (uint32_t i = reptNode->reptDepth; i--; )
fputlong(reptNode->iters[i], f); putlong(reptNode->iters[i], f);
} }
} }
static void registerExportedSymbol(struct Symbol *symbol, void *arg) static void registerUnregisteredSymbol(struct Symbol *symbol, void *arg)
{ {
(void)arg; (void)arg; // sym_ForEach requires a void* parameter, but we are not using it.
if (sym_IsExported(symbol) && symbol->ID == -1) {
// Check for symbol->src, to skip any built-in symbol from rgbasm
if (symbol->src && symbol->ID == (uint32_t)-1) {
registerSymbol(symbol); registerSymbol(symbol);
} }
} }
@@ -488,16 +493,16 @@ void out_WriteObject(void)
if (!f) if (!f)
err(1, "Couldn't write file '%s'", tzObjectname); err(1, "Couldn't write file '%s'", tzObjectname);
/* Also write exported symbols that weren't written above */ /* Also write symbols that weren't written above */
sym_ForEach(registerExportedSymbol, NULL); sym_ForEach(registerUnregisteredSymbol, NULL);
fprintf(f, RGBDS_OBJECT_VERSION_STRING, RGBDS_OBJECT_VERSION_NUMBER); fprintf(f, RGBDS_OBJECT_VERSION_STRING, RGBDS_OBJECT_VERSION_NUMBER);
fputlong(RGBDS_OBJECT_REV, f); putlong(RGBDS_OBJECT_REV, f);
fputlong(nbSymbols, f); putlong(nbSymbols, f);
fputlong(countsections(), f); putlong(countSections(), f);
fputlong(getNbFileStackNodes(), f); putlong(getNbFileStackNodes(), f);
for (struct FileStackNode const *node = fileStackNodes; node; node = node->next) { for (struct FileStackNode const *node = fileStackNodes; node; node = node->next) {
writeFileStackNode(node, f); writeFileStackNode(node, f);
if (node->next && node->next->ID != node->ID - 1) if (node->next && node->next->ID != node->ID - 1)
@@ -512,7 +517,7 @@ void out_WriteObject(void)
for (struct Section *sect = pSectionList; sect; sect = sect->next) for (struct Section *sect = pSectionList; sect; sect = sect->next)
writesection(sect, f); writesection(sect, f);
fputlong(countasserts(), f); putlong(countAsserts(), f);
for (struct Assertion *assert = assertions; assert; for (struct Assertion *assert = assertions; assert;
assert = assert->next) assert = assert->next)
writeassert(assert, f); writeassert(assert, f);

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,11 @@
.\" .\"
.\" This file is part of RGBDS. .\" This file is part of RGBDS.
.\" .\"
.\" Copyright (c) 2010-2019, Anthony J. Bentley and RGBDS contributors. .\" Copyright (c) 2010-2021, Anthony J. Bentley and RGBDS contributors.
.\" .\"
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd July 8, 2019 .Dd March 28, 2021
.Dt RGBASM 1 .Dt RGBASM 1
.Os .Os
.Sh NAME .Sh NAME
@@ -179,9 +179,9 @@ Enables literally every warning.
.Pp .Pp
The following warnings are actual warning flags; with each description, the corresponding warning flag is included. The following warnings are actual warning flags; with each description, the corresponding warning flag is included.
Note that each of these flag also has a negation (for example, Note that each of these flag also has a negation (for example,
.Fl Wempty-entry .Fl Wcharmap-redef
enables the warning that enables the warning that
.Fl Wno-empty-entry .Fl Wno-charmap-redef
disables). disables).
Only the non-default flag is listed here. Only the non-default flag is listed here.
Ignoring the Ignoring the
@@ -209,12 +209,16 @@ This warning is enabled by
.Fl Wall . .Fl Wall .
.It Fl Wdiv .It Fl Wdiv
Warn when dividing the smallest negative integer by -1, which yields itself due to integer overflow. Warn when dividing the smallest negative integer by -1, which yields itself due to integer overflow.
.It Fl Wempty-entry .It Fl Wempty-macro-arg
Warn when an empty entry is encountered in a Warn when a macro argument is empty.
.Ic db , dw , dl
list.
This warning is enabled by This warning is enabled by
.Fl Wextra . .Fl Wextra .
.It Fl Wempty-strrpl
Warn when
.Fn STRRPL
is called with an empty string as its second argument (the substring to replace).
This warning is enabled by
.Fl Wall .
.It Fl Wlarge-constant .It Fl Wlarge-constant
Warn when a constant too large to fit in a signed 32-bit integer is encountered. Warn when a constant too large to fit in a signed 32-bit integer is encountered.
This warning is enabled by This warning is enabled by
@@ -223,12 +227,16 @@ This warning is enabled by
Warn when a string too long to fit in internal buffers is encountered. Warn when a string too long to fit in internal buffers is encountered.
This warning is enabled by This warning is enabled by
.Fl Wall . .Fl Wall .
.It Fl Wmacro-shift
Warn when shifting macro arguments past their limits.
This warning is enabled by
.Fl Wextra .
.It Fl Wno-obsolete .It Fl Wno-obsolete
Warn when obsolete constructs such as the Warn when obsolete constructs such as the
.Ic jp [hl] .Ic _PI
instruction or constant or
.Ic HOME .Ic PRINTT
section type are encountered. directive are encountered.
.It Fl Wshift .It Fl Wshift
Warn when shifting right a negative value. Warn when shifting right a negative value.
Use a division by 2^N instead. Use a division by 2^N instead.

File diff suppressed because it is too large Load Diff

View File

@@ -15,15 +15,18 @@
#include <inttypes.h> #include <inttypes.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include "asm/asm.h"
#include "asm/main.h" #include "asm/main.h"
#include "asm/output.h"
#include "asm/rpn.h" #include "asm/rpn.h"
#include "asm/section.h" #include "asm/section.h"
#include "asm/symbol.h" #include "asm/symbol.h"
#include "asm/warning.h" #include "asm/warning.h"
#include "opmath.h"
/* Makes an expression "not known", also setting its error message */ /* Makes an expression "not known", also setting its error message */
#define makeUnknown(expr_, ...) do { \ #define makeUnknown(expr_, ...) do { \
struct Expression *_expr = expr_; \ struct Expression *_expr = expr_; \
@@ -124,16 +127,6 @@ void rpn_Symbol(struct Expression *expr, char const *tzSym)
uint8_t *ptr = reserveSpace(expr, nameLen + 1); uint8_t *ptr = reserveSpace(expr, nameLen + 1);
*ptr++ = RPN_SYM; *ptr++ = RPN_SYM;
memcpy(ptr, sym->name, nameLen); memcpy(ptr, sym->name, nameLen);
/* RGBLINK assumes PC is at the byte being computed... */
if (sym_IsPC(sym) && nPCOffset) {
struct Expression pc = *expr, offset;
rpn_Number(&offset, nPCOffset);
rpn_BinaryOp(RPN_SUB, expr, &pc, &offset);
if (!rpn_isKnown(expr))
expr->isSymbol = true;
}
} else { } else {
rpn_Number(expr, sym_GetConstantValue(tzSym)); rpn_Number(expr, sym_GetConstantValue(tzSym));
} }
@@ -146,7 +139,7 @@ void rpn_BankSelf(struct Expression *expr)
if (!pCurrentSection) { if (!pCurrentSection) {
error("PC has no bank outside a section\n"); error("PC has no bank outside a section\n");
expr->nVal = 1; expr->nVal = 1;
} else if (pCurrentSection->bank == -1) { } else if (pCurrentSection->bank == (uint32_t)-1) {
makeUnknown(expr, "Current section's bank is not known"); makeUnknown(expr, "Current section's bank is not known");
expr->nRPNPatchSize++; expr->nRPNPatchSize++;
*reserveSpace(expr, 1) = RPN_BANK_SELF; *reserveSpace(expr, 1) = RPN_BANK_SELF;
@@ -172,7 +165,7 @@ void rpn_BankSymbol(struct Expression *expr, char const *tzSym)
sym = sym_Ref(tzSym); sym = sym_Ref(tzSym);
assert(sym); // If the symbol didn't exist, it should have been created assert(sym); // If the symbol didn't exist, it should have been created
if (sym_GetSection(sym) && sym_GetSection(sym)->bank != -1) { if (sym_GetSection(sym) && sym_GetSection(sym)->bank != (uint32_t)-1) {
/* Symbol's section is known and bank is fixed */ /* Symbol's section is known and bank is fixed */
expr->nVal = sym_GetSection(sym)->bank; expr->nVal = sym_GetSection(sym)->bank;
} else { } else {
@@ -193,7 +186,7 @@ void rpn_BankSection(struct Expression *expr, char const *tzSectionName)
struct Section *pSection = out_FindSectionByName(tzSectionName); struct Section *pSection = out_FindSectionByName(tzSectionName);
if (pSection && pSection->bank != -1) { if (pSection && pSection->bank != (uint32_t)-1) {
expr->nVal = pSection->bank; expr->nVal = pSection->bank;
} else { } else {
makeUnknown(expr, "Section \"%s\"'s bank is not known", makeUnknown(expr, "Section \"%s\"'s bank is not known",
@@ -253,45 +246,6 @@ void rpn_LOGNOT(struct Expression *expr, const struct Expression *src)
} }
} }
static int32_t shift(int32_t shiftee, int32_t amount)
{
if (amount >= 0) {
// Left shift
if (amount >= 32) {
warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %"
PRId32 "\n", amount);
return 0;
} else {
/*
* Use unsigned to force a bitwise shift
* Casting back is OK because the types implement two's
* complement behavior
*/
return (uint32_t)shiftee << amount;
}
} else {
// Right shift
amount = -amount;
if (amount >= 32) {
warning(WARNING_SHIFT_AMOUNT,
"Shifting right by large amount %" PRId32 "\n", amount);
return shiftee < 0 ? -1 : 0;
} else if (shiftee >= 0) {
return shiftee >> amount;
} else {
/*
* The C standard leaves shifting right negative values
* undefined, so use a left shift manually sign-extended
*/
return (uint32_t)shiftee >> amount
| -(UINT32_C(1) << (32 - amount));
}
}
}
struct Symbol const *rpn_SymbolOf(struct Expression const *expr) struct Symbol const *rpn_SymbolOf(struct Expression const *expr)
{ {
if (!rpn_isSymbol(expr)) if (!rpn_isSymbol(expr))
@@ -377,11 +331,17 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
"Shifting left by negative amount %" PRId32 "\n", "Shifting left by negative amount %" PRId32 "\n",
src2->nVal); src2->nVal);
expr->nVal = shift(src1->nVal, src2->nVal); if (src2->nVal >= 32)
warning(WARNING_SHIFT_AMOUNT,
"Shifting left by large amount %" PRId32 "\n",
src2->nVal);
expr->nVal = op_shift_left(src1->nVal, src2->nVal);
break; break;
case RPN_SHR: case RPN_SHR:
if (src1->nVal < 0) if (src1->nVal < 0)
warning(WARNING_SHIFT, "Shifting negative value %" PRId32 "\n", warning(WARNING_SHIFT, "Shifting right negative value %"
PRId32 "\n",
src1->nVal); src1->nVal);
if (src2->nVal < 0) if (src2->nVal < 0)
@@ -389,7 +349,12 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
"Shifting right by negative amount %" PRId32 "\n", "Shifting right by negative amount %" PRId32 "\n",
src2->nVal); src2->nVal);
expr->nVal = shift(src1->nVal, -src2->nVal); if (src2->nVal >= 32)
warning(WARNING_SHIFT_AMOUNT,
"Shifting right by large amount %" PRId32 "\n",
src2->nVal);
expr->nVal = op_shift_right(src1->nVal, src2->nVal);
break; break;
case RPN_MUL: case RPN_MUL:
expr->nVal = uleft * uright; expr->nVal = uleft * uright;
@@ -403,17 +368,26 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
PRId32 "\n", INT32_MIN, INT32_MIN); PRId32 "\n", INT32_MIN, INT32_MIN);
expr->nVal = INT32_MIN; expr->nVal = INT32_MIN;
} else { } else {
expr->nVal = src1->nVal / src2->nVal; expr->nVal = op_divide(src1->nVal, src2->nVal);
} }
break; break;
case RPN_MOD: case RPN_MOD:
if (src2->nVal == 0) if (src2->nVal == 0)
fatalerror("Division by zero\n"); fatalerror("Modulo by zero\n");
if (src1->nVal == INT32_MIN && src2->nVal == -1) if (src1->nVal == INT32_MIN && src2->nVal == -1)
expr->nVal = 0; expr->nVal = 0;
else else
expr->nVal = src1->nVal % src2->nVal; expr->nVal = op_modulo(src1->nVal, src2->nVal);
break;
case RPN_EXP:
if (src2->nVal < 0)
fatalerror("Exponentiation by negative power\n");
if (src1->nVal == INT32_MIN && src2->nVal == -1)
expr->nVal = 0;
else
expr->nVal = op_exponent(src1->nVal, src2->nVal);
break; break;
case RPN_UNSUB: case RPN_UNSUB:

View File

@@ -1,4 +1,5 @@
#include <assert.h>
#include <errno.h> #include <errno.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdbool.h> #include <stdbool.h>
@@ -11,11 +12,14 @@
#include "asm/output.h" #include "asm/output.h"
#include "asm/rpn.h" #include "asm/rpn.h"
#include "asm/section.h" #include "asm/section.h"
#include "asm/symbol.h"
#include "asm/warning.h" #include "asm/warning.h"
#include "extern/err.h" #include "extern/err.h"
#include "platform.h" // strdup #include "platform.h" // strdup
uint8_t fillByte;
struct SectionStackEntry { struct SectionStackEntry {
struct Section *section; struct Section *section;
char const *scope; /* Section's symbol scope */ char const *scope; /* Section's symbol scope */
@@ -26,7 +30,7 @@ struct SectionStackEntry {
struct SectionStackEntry *sectionStack; struct SectionStackEntry *sectionStack;
uint32_t curOffset; /* Offset into the current section (see sect_GetSymbolOffset) */ uint32_t curOffset; /* Offset into the current section (see sect_GetSymbolOffset) */
static struct Section *currentLoadSection = NULL; static struct Section *currentLoadSection = NULL;
uint32_t loadOffset; /* The offset of the LOAD section within its parent */ int32_t loadOffset; /* Offset into the LOAD section's parent (see sect_GetOutputOffset) */
struct UnionStackEntry { struct UnionStackEntry {
uint32_t start; uint32_t start;
@@ -83,185 +87,172 @@ static inline void reserveSpace(uint32_t delta_size)
struct Section *out_FindSectionByName(const char *name) struct Section *out_FindSectionByName(const char *name)
{ {
struct Section *sect = pSectionList; for (struct Section *sect = pSectionList; sect; sect = sect->next) {
while (sect) {
if (strcmp(name, sect->name) == 0) if (strcmp(name, sect->name) == 0)
return sect; return sect;
sect = sect->next;
} }
return NULL; return NULL;
} }
/* #define mask(align) ((1U << (align)) - 1)
* Find a section by name and type. If it doesn't exist, create it
*/
static struct Section *getSection(char const *name, enum SectionType type,
uint32_t org, struct SectionSpec const *attrs,
enum SectionModifier mod)
{
#define mask(align) ((1 << (align)) - 1)
uint32_t bank = attrs->bank;
uint8_t alignment = attrs->alignment;
uint16_t alignOffset = attrs->alignOfs;
if (bank != -1) {
if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM
&& type != SECTTYPE_SRAM && type != SECTTYPE_WRAMX)
error("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections\n");
else if (bank < bankranges[type][0]
|| bank > bankranges[type][1])
error("%s bank value $%" PRIx32 " out of range ($%" PRIx32 " to $%"
PRIx32 ")\n", typeNames[type], bank,
bankranges[type][0], bankranges[type][1]);
}
if (alignOffset >= 1 << alignment) {
error("Alignment offset must not be greater than alignment (%" PRIu16 " < %u)\n",
alignOffset, 1U << alignment);
alignOffset = 0;
}
if (alignment != 0) {
/* It doesn't make sense to have both alignment and org set */
uint32_t mask = mask(alignment);
if (org != -1) {
if ((org - alignOffset) & mask)
error("Section \"%s\"'s fixed address doesn't match its alignment\n",
name);
alignment = 0; /* Ignore it if it's satisfied */
} else if (startaddr[type] & mask) {
error("Section \"%s\"'s alignment cannot be attained in %s\n",
name, typeNames[type]);
}
}
if (org != -1) {
if (org < startaddr[type] || org > endaddr(type))
error("Section \"%s\"'s fixed address %#" PRIx32
" is outside of range [%#" PRIx16 "; %#" PRIx16 "]\n",
name, org, startaddr[type], endaddr(type));
}
if (nbbanks(type) == 1)
bank = bankranges[type][0];
struct Section *sect = out_FindSectionByName(name);
if (sect) {
unsigned int nbSectErrors = 0;
#define fail(...) \ #define fail(...) \
do { \ do { \
error(__VA_ARGS__); \ error(__VA_ARGS__); \
nbSectErrors++; \ nbSectErrors++; \
} while (0) } while (0)
if (type != sect->type) static unsigned int mergeSectUnion(struct Section *sect, enum SectionType type, uint32_t org,
fail("Section \"%s\" already exists but with type %s\n", uint8_t alignment, uint16_t alignOffset)
sect->name, typeNames[sect->type]); {
assert(alignment < 16); // Should be ensured by the caller
unsigned int nbSectErrors = 0;
if (sect->modifier != mod)
fail("Section \"%s\" already declared as %s section\n",
sect->name, sectionModNames[sect->modifier]);
/* /*
* Normal sections need to have exactly identical constraints; * Unionized sections only need "compatible" constraints, and they end up with the strictest
* but unionized sections only need "compatible" constraints, * combination of both.
* and they end up with the strictest combination of both
*/
if (mod == SECTION_UNION) {
/*
* WARNING: see comment about assumption in
* `EndLoadSection` if modifying the following check!
*/ */
if (sect_HasData(type)) if (sect_HasData(type))
fail("Cannot declare ROM sections as UNION\n"); fail("Cannot declare ROM sections as UNION\n");
if (org != -1) {
if (org != (uint32_t)-1) {
/* If both are fixed, they must be the same */ /* If both are fixed, they must be the same */
if (sect->org != -1 && sect->org != org) if (sect->org != (uint32_t)-1 && sect->org != org)
fail("Section \"%s\" already declared as fixed at different address $%" fail("Section already declared as fixed at different address $%04"
PRIx32 "\n", PRIx32 "\n", sect->org);
sect->name, sect->org); else if (sect->align != 0 && (mask(sect->align) & (org - sect->alignOfs)))
else if (sect->align != 0 fail("Section already declared as aligned to %u bytes (offset %"
&& (mask(sect->align) PRIu16 ")\n", 1U << sect->align, sect->alignOfs);
& (org - sect->alignOfs)))
fail("Section \"%s\" already declared as aligned to %u bytes (offset %"
PRIu16 ")\n", sect->name, 1U << sect->align, sect->alignOfs);
else else
/* Otherwise, just override */ /* Otherwise, just override */
sect->org = org; sect->org = org;
} else if (alignment != 0) { } else if (alignment != 0) {
/* Make sure any fixed address is compatible */ /* Make sure any fixed address given is compatible */
if (sect->org != -1) { if (sect->org != (uint32_t)-1) {
if ((sect->org - alignOffset) if ((sect->org - alignOffset) & mask(alignment))
& mask(alignment)) fail("Section already declared as fixed at incompatible address $%04"
fail("Section \"%s\" already declared as fixed at incompatible address $%" PRIx32 "\n", sect->org);
PRIx32 "\n", sect->name, sect->org);
/* Check if alignment offsets are compatible */ /* Check if alignment offsets are compatible */
} else if ((alignOffset & mask(sect->align)) } else if ((alignOffset & mask(sect->align))
!= (sect->alignOfs != (sect->alignOfs & mask(alignment))) {
& mask(alignment))) { fail("Section already declared with incompatible %" PRIu8
fail("Section \"%s\" already declared with incompatible %" "-byte alignment (offset %" PRIu16 ")\n",
PRIu8 "-byte alignment (offset %" PRIu16 ")\n", sect->align, sect->alignOfs);
sect->name, sect->align, sect->alignOfs);
} else if (alignment > sect->align) { } else if (alignment > sect->align) {
/* // If the section is not fixed, its alignment is the largest of both
* If the section is not fixed,
* its alignment is the largest of both
*/
sect->align = alignment; sect->align = alignment;
sect->alignOfs = alignOffset; sect->alignOfs = alignOffset;
} }
} }
return nbSectErrors;
}
static unsigned int mergeFragments(struct Section *sect, enum SectionType type, uint32_t org,
uint8_t alignment, uint16_t alignOffset)
{
(void)type;
assert(alignment < 16); // Should be ensured by the caller
unsigned int nbSectErrors = 0;
/*
* Fragments only need "compatible" constraints, and they end up with the strictest
* combination of both.
* The merging is however performed at the *end* of the original section!
*/
if (org != (uint32_t)-1) {
uint16_t curOrg = org - sect->size;
/* If both are fixed, they must be the same */
if (sect->org != (uint32_t)-1 && sect->org != curOrg)
fail("Section already declared as fixed at incompatible address $%04"
PRIx32 " (cur addr = %04" PRIx32 ")\n",
sect->org, sect->org + sect->size);
else if (sect->align != 0 && (mask(sect->align) & (curOrg - sect->alignOfs)))
fail("Section already declared as aligned to %u bytes (offset %"
PRIu16 ")\n", 1U << sect->align, sect->alignOfs);
else
/* Otherwise, just override */
sect->org = curOrg;
} else if (alignment != 0) {
int32_t curOfs = (alignOffset - sect->size) % (1U << alignment);
if (curOfs < 0)
curOfs += 1U << alignment;
/* Make sure any fixed address given is compatible */
if (sect->org != (uint32_t)-1) {
if ((sect->org - curOfs) & mask(alignment))
fail("Section already declared as fixed at incompatible address $%04"
PRIx32 "\n", sect->org);
/* Check if alignment offsets are compatible */
} else if ((curOfs & mask(sect->align)) != (sect->alignOfs & mask(alignment))) {
fail("Section already declared with incompatible %" PRIu8
"-byte alignment (offset %" PRIu16 ")\n",
sect->align, sect->alignOfs);
} else if (alignment > sect->align) {
// If the section is not fixed, its alignment is the largest of both
sect->align = alignment;
sect->alignOfs = curOfs;
}
}
return nbSectErrors;
}
static void mergeSections(struct Section *sect, enum SectionType type, uint32_t org, uint32_t bank,
uint8_t alignment, uint16_t alignOffset, enum SectionModifier mod)
{
unsigned int nbSectErrors = 0;
if (type != sect->type)
fail("Section already exists but with type %s\n", typeNames[sect->type]);
if (sect->modifier != mod) {
fail("Section already declared as %s section\n", sectionModNames[sect->modifier]);
} else {
switch (mod) {
case SECTION_UNION:
case SECTION_FRAGMENT:
nbSectErrors += (mod == SECTION_UNION ? mergeSectUnion : mergeFragments)
(sect, type, org, alignment, alignOffset);
// Common checks
/* If the section's bank is unspecified, override it */ /* If the section's bank is unspecified, override it */
if (sect->bank == -1) if (sect->bank == (uint32_t)-1)
sect->bank = bank; sect->bank = bank;
/* If both specify a bank, it must be the same one */ /* If both specify a bank, it must be the same one */
else if (bank != -1 && sect->bank != bank) else if (bank != (uint32_t)-1 && sect->bank != bank)
fail("Section \"%s\" already declared with different bank %" fail("Section already declared with different bank %" PRIu32 "\n",
PRIu32 "\n", sect->name, sect->bank); sect->bank);
} else { /* Section fragments are handled identically in RGBASM */ break;
/* However, concaternating non-fragments will be made an error */
if (sect->modifier != SECTION_FRAGMENT || mod != SECTION_FRAGMENT)
warning(WARNING_OBSOLETE,
"Concatenation of non-fragment sections is deprecated\n");
if (org != sect->org) { case SECTION_NORMAL:
if (sect->org == -1) fail("Section already defined previously at ");
fail("Section \"%s\" already declared as floating\n", fstk_Dump(sect->src, sect->fileLine);
sect->name); putc('\n', stderr);
else break;
fail("Section \"%s\" already declared as fixed at $%"
PRIx32 "\n", sect->name, sect->org);
}
if (bank != sect->bank) {
if (sect->bank == -1)
fail("Section \"%s\" already declared as floating bank\n",
sect->name);
else
fail("Section \"%s\" already declared as fixed at bank %"
PRIu32 "\n", sect->name, sect->bank);
}
if (alignment != sect->align) {
if (sect->align == 0)
fail("Section \"%s\" already declared as unaligned\n",
sect->name);
else
fail("Section \"%s\" already declared as aligned to %u bytes\n",
sect->name, 1U << sect->align);
} }
} }
if (nbSectErrors) if (nbSectErrors)
fatalerror("Cannot create section \"%s\" (%u errors)\n", fatalerror("Cannot create section \"%s\" (%u error%s)\n",
sect->name, nbSectErrors); sect->name, nbSectErrors, nbSectErrors == 1 ? "" : "s");
#undef fail }
return sect;
} #undef fail
/*
* Create a new section, not yet in the list.
*/
static struct Section *createSection(char const *name, enum SectionType type,
uint32_t org, uint32_t bank, uint8_t alignment,
uint16_t alignOffset, enum SectionModifier mod)
{
struct Section *sect = malloc(sizeof(*sect));
sect = malloc(sizeof(*sect));
if (sect == NULL) if (sect == NULL)
fatalerror("Not enough memory for section: %s\n", strerror(errno)); fatalerror("Not enough memory for section: %s\n", strerror(errno));
@@ -271,34 +262,105 @@ static struct Section *getSection(char const *name, enum SectionType type,
sect->type = type; sect->type = type;
sect->modifier = mod; sect->modifier = mod;
sect->src = fstk_GetFileStack();
sect->fileLine = lexer_GetLineNo();
sect->size = 0; sect->size = 0;
sect->org = org; sect->org = org;
sect->bank = bank; sect->bank = bank;
sect->align = alignment; sect->align = alignment;
sect->alignOfs = alignOffset; sect->alignOfs = alignOffset;
sect->next = pSectionList; sect->next = NULL;
sect->patches = NULL; sect->patches = NULL;
/* It is only needed to allocate memory for ROM sections. */ /* It is only needed to allocate memory for ROM sections. */
if (sect_HasData(type)) { if (sect_HasData(type)) {
uint32_t sectsize; sect->data = malloc(maxsize[type]);
sectsize = maxsize[type];
sect->data = malloc(sectsize);
if (sect->data == NULL) if (sect->data == NULL)
fatalerror("Not enough memory for section: %s\n", strerror(errno)); fatalerror("Not enough memory for section: %s\n", strerror(errno));
} else { } else {
sect->data = NULL; sect->data = NULL;
} }
/* return sect;
* Add the new section to the list }
* at the beginning because order doesn't matter
/*
* Find a section by name and type. If it doesn't exist, create it.
*/ */
static struct Section *getSection(char const *name, enum SectionType type, uint32_t org,
struct SectionSpec const *attrs, enum SectionModifier mod)
{
uint32_t bank = attrs->bank;
uint8_t alignment = attrs->alignment;
uint16_t alignOffset = attrs->alignOfs;
// First, validate parameters, and normalize them if applicable
if (bank != (uint32_t)-1) {
if (type != SECTTYPE_ROMX && type != SECTTYPE_VRAM
&& type != SECTTYPE_SRAM && type != SECTTYPE_WRAMX)
error("BANK only allowed for ROMX, WRAMX, SRAM, or VRAM sections\n");
else if (bank < bankranges[type][0]
|| bank > bankranges[type][1])
error("%s bank value $%04" PRIx32 " out of range ($%04" PRIx32 " to $%04"
PRIx32 ")\n", typeNames[type], bank,
bankranges[type][0], bankranges[type][1]);
} else if (nbbanks(type) == 1) {
// If the section type only has a single bank, implicitly force it
bank = bankranges[type][0];
}
if (alignOffset >= 1 << alignment) {
error("Alignment offset (%" PRIu16 ") must be smaller than alignment size (%u)\n",
alignOffset, 1U << alignment);
alignOffset = 0;
}
if (org != (uint32_t)-1) {
if (org < startaddr[type] || org > endaddr(type))
error("Section \"%s\"'s fixed address %#" PRIx32
" is outside of range [%#" PRIx16 "; %#" PRIx16 "]\n",
name, org, startaddr[type], endaddr(type));
}
if (alignment != 0) {
if (alignment > 16) {
error("Alignment must be between 0 and 16, not %u\n", alignment);
alignment = 16;
}
/* It doesn't make sense to have both alignment and org set */
uint32_t mask = mask(alignment);
if (org != (uint32_t)-1) {
if ((org - alignOffset) & mask)
error("Section \"%s\"'s fixed address doesn't match its alignment\n",
name);
alignment = 0; /* Ignore it if it's satisfied */
} else if (startaddr[type] & mask) {
error("Section \"%s\"'s alignment cannot be attained in %s\n",
name, typeNames[type]);
} else if (alignment == 16) {
// Treat an alignment of 16 as being fixed at address 0
alignment = 0;
org = 0;
// The address is known to be valid, since the alignment is
}
}
// Check if another section exists with the same name; merge if yes, otherwise create one
struct Section *sect = out_FindSectionByName(name);
if (sect) {
mergeSections(sect, type, org, bank, alignment, alignOffset, mod);
} else {
sect = createSection(name, type, org, bank, alignment, alignOffset, mod);
// Add the new section to the list (order doesn't matter)
sect->next = pSectionList;
pSectionList = sect; pSectionList = sect;
}
return sect; return sect;
#undef mask
} }
/* /*
@@ -321,6 +383,11 @@ void out_NewSection(char const *name, uint32_t type, uint32_t org,
if (currentLoadSection) if (currentLoadSection)
fatalerror("Cannot change the section within a `LOAD` block\n"); fatalerror("Cannot change the section within a `LOAD` block\n");
for (struct SectionStackEntry *stack = sectionStack; stack; stack = stack->next) {
if (stack->section && !strcmp(name, stack->section->name))
fatalerror("Section '%s' is already on the stack\n", name);
}
struct Section *sect = getSection(name, type, org, attribs, mod); struct Section *sect = getSection(name, type, org, attribs, mod);
changeSection(); changeSection();
@@ -332,18 +399,22 @@ void out_NewSection(char const *name, uint32_t type, uint32_t org,
* Set the current section by name and type * Set the current section by name and type
*/ */
void out_SetLoadSection(char const *name, uint32_t type, uint32_t org, void out_SetLoadSection(char const *name, uint32_t type, uint32_t org,
struct SectionSpec const *attribs) struct SectionSpec const *attribs,
enum SectionModifier mod)
{ {
checkcodesection(); checkcodesection();
if (currentLoadSection) if (currentLoadSection)
fatalerror("`LOAD` blocks cannot be nested\n"); fatalerror("`LOAD` blocks cannot be nested\n");
struct Section *sect = getSection(name, type, org, attribs, false); if (sect_HasData(type))
error("`LOAD` blocks cannot create a ROM section\n");
struct Section *sect = getSection(name, type, org, attribs, mod);
loadOffset = curOffset;
curOffset = 0; /* curOffset -= loadOffset; */
changeSection(); changeSection();
loadOffset = curOffset - (mod == SECTION_UNION ? 0 : sect->size);
curOffset -= loadOffset;
currentLoadSection = sect; currentLoadSection = sect;
} }
@@ -351,11 +422,11 @@ void out_EndLoadSection(void)
{ {
if (!currentLoadSection) if (!currentLoadSection)
error("Found `ENDL` outside of a `LOAD` block\n"); error("Found `ENDL` outside of a `LOAD` block\n");
currentLoadSection = NULL;
changeSection(); changeSection();
curOffset += loadOffset; curOffset += loadOffset;
loadOffset = 0; loadOffset = 0;
currentLoadSection = NULL;
} }
struct Section *sect_GetSymbolSection(void) struct Section *sect_GetSymbolSection(void)
@@ -380,24 +451,24 @@ void sect_AlignPC(uint8_t alignment, uint16_t offset)
{ {
checksection(); checksection();
struct Section *sect = sect_GetSymbolSection(); struct Section *sect = sect_GetSymbolSection();
uint16_t alignSize = 1 << alignment; // Size of an aligned "block"
if (sect->org != -1) { if (sect->org != (uint32_t)-1) {
if ((sym_GetPCValue() - offset) % (1 << alignment)) if ((sym_GetPCValue() - offset) % alignSize)
error("Section's fixed address fails required alignment (PC = $%04" error("Section's fixed address fails required alignment (PC = $%04" PRIx32
PRIx32 ")\n", sym_GetPCValue()); ")\n", sym_GetPCValue());
} else if (sect->align != 0) { } else if (sect->align != 0) {
if ((((sect->alignOfs + curOffset) % (1 << sect->align)) if ((((sect->alignOfs + curOffset) % (1 << sect->align)) - offset) % alignSize) {
- offset) % (1 << alignment)) {
error("Section's alignment fails required alignment (offset from section start = $%04" error("Section's alignment fails required alignment (offset from section start = $%04"
PRIx32 ")\n", curOffset); PRIx32 ")\n", curOffset);
} else if (alignment > sect->align) { } else if (alignment > sect->align) {
sect->align = alignment; sect->align = alignment;
sect->alignOfs = sect->alignOfs = (offset - curOffset) % alignSize;
(offset - curOffset) % (1 << alignment);
} }
} else { } else {
sect->align = alignment; sect->align = alignment;
sect->alignOfs = offset; // We need `(sect->alignOfs + curOffset) % alignSize == offset
sect->alignOfs = (offset - curOffset) % alignSize;
} }
} }
@@ -430,10 +501,10 @@ static inline void writelong(uint32_t b)
writebyte(b >> 24); writebyte(b >> 24);
} }
static inline void createPatch(enum PatchType type, static inline void createPatch(enum PatchType type, struct Expression const *expr,
struct Expression const *expr) uint32_t pcShift)
{ {
out_CreatePatch(type, expr, sect_GetOutputOffset()); out_CreatePatch(type, expr, sect_GetOutputOffset(), pcShift);
} }
void sect_StartUnion(void) void sect_StartUnion(void)
@@ -483,7 +554,7 @@ void sect_EndUnion(void)
void sect_CheckUnionClosed(void) void sect_CheckUnionClosed(void)
{ {
if (unionStack) if (unionStack)
fatalerror("Unterminated UNION construct!\n"); error("Unterminated UNION construct!\n");
} }
/* /*
@@ -506,6 +577,24 @@ void out_AbsByteGroup(uint8_t const *s, int32_t length)
writebyte(*s++); writebyte(*s++);
} }
void out_AbsWordGroup(uint8_t const *s, int32_t length)
{
checkcodesection();
reserveSpace(length * 2);
while (length--)
writeword(*s++);
}
void out_AbsLongGroup(uint8_t const *s, int32_t length)
{
checkcodesection();
reserveSpace(length * 4);
while (length--)
writelong(*s++);
}
/* /*
* Skip this many bytes * Skip this many bytes
*/ */
@@ -515,14 +604,15 @@ void out_Skip(int32_t skip, bool ds)
reserveSpace(skip); reserveSpace(skip);
if (!ds && sect_HasData(pCurrentSection->type)) if (!ds && sect_HasData(pCurrentSection->type))
warning(WARNING_EMPTY_DATA_DIRECTIVE, "db/dw/dl directive without data in ROM\n"); warning(WARNING_EMPTY_DATA_DIRECTIVE, "%s directive without data in ROM\n",
(skip == 4) ? "DL" : (skip == 2) ? "DW" : "DB");
if (!sect_HasData(pCurrentSection->type)) { if (!sect_HasData(pCurrentSection->type)) {
growSection(skip); growSection(skip);
} else { } else {
checkcodesection(); checkcodesection();
while (skip--) while (skip--)
writebyte(CurrentOptions.fillchar); writebyte(fillByte);
} }
} }
@@ -542,13 +632,13 @@ void out_String(char const *s)
* Output a relocatable byte. Checking will be done to see if it * Output a relocatable byte. Checking will be done to see if it
* is an absolute value in disguise. * is an absolute value in disguise.
*/ */
void out_RelByte(struct Expression *expr) void out_RelByte(struct Expression *expr, uint32_t pcShift)
{ {
checkcodesection(); checkcodesection();
reserveSpace(1); reserveSpace(1);
if (!rpn_isKnown(expr)) { if (!rpn_isKnown(expr)) {
createPatch(PATCHTYPE_BYTE, expr); createPatch(PATCHTYPE_BYTE, expr, pcShift);
writebyte(0); writebyte(0);
} else { } else {
writebyte(expr->nVal); writebyte(expr->nVal);
@@ -560,33 +650,37 @@ void out_RelByte(struct Expression *expr)
* Output several copies of a relocatable byte. Checking will be done to see if * Output several copies of a relocatable byte. Checking will be done to see if
* it is an absolute value in disguise. * it is an absolute value in disguise.
*/ */
void out_RelBytes(struct Expression *expr, uint32_t n) void out_RelBytes(uint32_t n, struct Expression *exprs, size_t size)
{ {
checkcodesection(); checkcodesection();
reserveSpace(n); reserveSpace(n);
while (n--) { for (uint32_t i = 0; i < n; i++) {
struct Expression *expr = &exprs[i % size];
if (!rpn_isKnown(expr)) { if (!rpn_isKnown(expr)) {
createPatch(PATCHTYPE_BYTE, expr); createPatch(PATCHTYPE_BYTE, expr, i);
writebyte(0); writebyte(0);
} else { } else {
writebyte(expr->nVal); writebyte(expr->nVal);
} }
} }
rpn_Free(expr);
for (size_t i = 0; i < size; i++)
rpn_Free(&exprs[i]);
} }
/* /*
* Output a relocatable word. Checking will be done to see if * Output a relocatable word. Checking will be done to see if
* it's an absolute value in disguise. * it's an absolute value in disguise.
*/ */
void out_RelWord(struct Expression *expr) void out_RelWord(struct Expression *expr, uint32_t pcShift)
{ {
checkcodesection(); checkcodesection();
reserveSpace(2); reserveSpace(2);
if (!rpn_isKnown(expr)) { if (!rpn_isKnown(expr)) {
createPatch(PATCHTYPE_WORD, expr); createPatch(PATCHTYPE_WORD, expr, pcShift);
writeword(0); writeword(0);
} else { } else {
writeword(expr->nVal); writeword(expr->nVal);
@@ -598,13 +692,13 @@ void out_RelWord(struct Expression *expr)
* Output a relocatable longword. Checking will be done to see if * Output a relocatable longword. Checking will be done to see if
* is an absolute value in disguise. * is an absolute value in disguise.
*/ */
void out_RelLong(struct Expression *expr) void out_RelLong(struct Expression *expr, uint32_t pcShift)
{ {
checkcodesection(); checkcodesection();
reserveSpace(2); reserveSpace(2);
if (!rpn_isKnown(expr)) { if (!rpn_isKnown(expr)) {
createPatch(PATCHTYPE_LONG, expr); createPatch(PATCHTYPE_LONG, expr, pcShift);
writelong(0); writelong(0);
} else { } else {
writelong(expr->nVal); writelong(expr->nVal);
@@ -616,14 +710,14 @@ void out_RelLong(struct Expression *expr)
* Output a PC-relative relocatable byte. Checking will be done to see if it * Output a PC-relative relocatable byte. Checking will be done to see if it
* is an absolute value in disguise. * is an absolute value in disguise.
*/ */
void out_PCRelByte(struct Expression *expr) void out_PCRelByte(struct Expression *expr, uint32_t pcShift)
{ {
checkcodesection(); checkcodesection();
reserveSpace(1); reserveSpace(1);
struct Symbol const *pc = sym_GetPC(); struct Symbol const *pc = sym_GetPC();
if (!rpn_IsDiffConstant(expr, pc)) { if (!rpn_IsDiffConstant(expr, pc)) {
createPatch(PATCHTYPE_JR, expr); createPatch(PATCHTYPE_JR, expr, pcShift);
writebyte(0); writebyte(0);
} else { } else {
struct Symbol const *sym = rpn_SymbolOf(expr); struct Symbol const *sym = rpn_SymbolOf(expr);
@@ -667,10 +761,13 @@ void out_BinaryFile(char const *s, int32_t startPos)
if (!f) { if (!f) {
if (oGeneratedMissingIncludes) { if (oGeneratedMissingIncludes) {
if (verbose)
printf("Aborting (-MG) on INCBIN file '%s' (%s)\n", s, strerror(errno));
oFailedOnMissingInclude = true; oFailedOnMissingInclude = true;
return; return;
} }
fatalerror("Error opening INCBIN file '%s': %s\n", s, strerror(errno)); error("Error opening INCBIN file '%s': %s\n", s, strerror(errno));
return;
} }
int32_t fsize = -1; int32_t fsize = -1;
@@ -680,7 +777,7 @@ void out_BinaryFile(char const *s, int32_t startPos)
if (fseek(f, 0, SEEK_END) != -1) { if (fseek(f, 0, SEEK_END) != -1) {
fsize = ftell(f); fsize = ftell(f);
if (startPos >= fsize) { if (startPos > fsize) {
error("Specified start position is greater than length of file\n"); error("Specified start position is greater than length of file\n");
fclose(f); fclose(f);
return; return;
@@ -733,10 +830,13 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
if (!f) { if (!f) {
free(fullPath); free(fullPath);
if (oGeneratedMissingIncludes) { if (oGeneratedMissingIncludes) {
if (verbose)
printf("Aborting (-MG) on INCBIN file '%s' (%s)\n", s, strerror(errno));
oFailedOnMissingInclude = true; oFailedOnMissingInclude = true;
return; return;
} }
fatalerror("Error opening INCBIN file '%s': %s\n", s, strerror(errno)); error("Error opening INCBIN file '%s': %s\n", s, strerror(errno));
return;
} }
checkcodesection(); checkcodesection();
@@ -747,13 +847,16 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
if (fseek(f, 0, SEEK_END) != -1) { if (fseek(f, 0, SEEK_END) != -1) {
fsize = ftell(f); fsize = ftell(f);
if (start_pos >= fsize) { if (start_pos > fsize) {
error("Specified start position is greater than length of file\n"); error("Specified start position is greater than length of file\n");
return; return;
} }
if ((start_pos + length) > fsize) if ((start_pos + length) > fsize) {
fatalerror("Specified range in INCBIN is out of bounds\n"); error("Specified range in INCBIN is out of bounds (%" PRIu32 " + %" PRIu32
" > %" PRIu32 ")\n", start_pos, length, fsize);
return;
}
fseek(f, start_pos, SEEK_SET); fseek(f, start_pos, SEEK_SET);
} else { } else {

View File

@@ -13,16 +13,16 @@
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <inttypes.h> #include <inttypes.h>
#include <limits.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include "asm/asm.h" #include "asm/fixpoint.h"
#include "asm/fstack.h" #include "asm/fstack.h"
#include "asm/macro.h" #include "asm/macro.h"
#include "asm/main.h" #include "asm/main.h"
#include "asm/mymath.h"
#include "asm/output.h" #include "asm/output.h"
#include "asm/section.h" #include "asm/section.h"
#include "asm/symbol.h" #include "asm/symbol.h"
@@ -153,10 +153,12 @@ int32_t sym_GetValue(struct Symbol const *sym)
static void dumpFilename(struct Symbol const *sym) static void dumpFilename(struct Symbol const *sym)
{ {
if (!sym->src) if (sym->src)
fputs("<builtin>", stderr);
else
fstk_Dump(sym->src, sym->fileLine); fstk_Dump(sym->src, sym->fileLine);
else if (sym->fileLine == 0)
fputs("<command-line>", stderr);
else
fputs("<builtin>", stderr);
} }
/* /*
@@ -165,7 +167,7 @@ static void dumpFilename(struct Symbol const *sym)
static void setSymbolFilename(struct Symbol *sym) static void setSymbolFilename(struct Symbol *sym)
{ {
sym->src = fstk_GetFileStack(); sym->src = fstk_GetFileStack();
sym->fileLine = lexer_GetLineNo(); sym->fileLine = sym->src ? lexer_GetLineNo() : 0; // This is (NULL, 1) for built-ins
} }
/* /*
@@ -177,7 +179,7 @@ static void updateSymbolFilename(struct Symbol *sym)
setSymbolFilename(sym); setSymbolFilename(sym);
/* If the old node was referenced, ensure the new one is */ /* If the old node was referenced, ensure the new one is */
if (oldSrc->referenced && oldSrc->ID != -1) if (oldSrc && oldSrc->referenced && oldSrc->ID != (uint32_t)-1)
out_RegisterNode(sym->src); out_RegisterNode(sym->src);
/* TODO: unref the old node, and use `out_ReplaceNode` instead if deleting it */ /* TODO: unref the old node, and use `out_ReplaceNode` instead if deleting it */
} }
@@ -222,6 +224,19 @@ static void fullSymbolName(char *output, size_t outputSize,
fatalerror("Symbol name is too long: '%s%s'\n", scopeName, localName); fatalerror("Symbol name is too long: '%s%s'\n", scopeName, localName);
} }
static void assignStringSymbol(struct Symbol *sym, char const *value)
{
char *string = strdup(value);
if (string == NULL)
fatalerror("No memory for string equate: %s\n", strerror(errno));
sym->type = SYM_EQUS;
/* TODO: use other fields */
sym->macro = string;
sym->macroSize = strlen(string);
}
struct Symbol *sym_FindExactSymbol(char const *name) struct Symbol *sym_FindExactSymbol(char const *name)
{ {
return hash_GetElement(symbols, name); return hash_GetElement(symbols, name);
@@ -262,7 +277,7 @@ struct Symbol const *sym_GetPC(void)
static inline bool isReferenced(struct Symbol const *sym) static inline bool isReferenced(struct Symbol const *sym)
{ {
return sym->ID != -1; return sym->ID != (uint32_t)-1;
} }
/* /*
@@ -283,6 +298,11 @@ void sym_Purge(char const *symName)
if (symbol->name == labelScope) if (symbol->name == labelScope)
labelScope = NULL; labelScope = NULL;
/*
* FIXME: this leaks symbol->macro for SYM_EQUS and SYM_MACRO, but this can't
* free(symbol->macro) because the expansion may be purging itself.
*/
hash_RemoveElement(symbols, symbol->name); hash_RemoveElement(symbols, symbol->name);
/* TODO: ideally, also unref the file stack nodes */ /* TODO: ideally, also unref the file stack nodes */
free(symbol); free(symbol);
@@ -295,7 +315,7 @@ uint32_t sym_GetPCValue(void)
if (!sect) if (!sect)
error("PC has no value outside a section\n"); error("PC has no value outside a section\n");
else if (sect->org == -1) else if (sect->org == (uint32_t)-1)
error("Expected constant PC but section is not fixed\n"); error("Expected constant PC but section is not fixed\n");
else else
return CallbackPC(); return CallbackPC();
@@ -346,8 +366,10 @@ void sym_SetCurrentSymbolScope(char const *newScope)
* Create a symbol that will be non-relocatable and ensure that it * Create a symbol that will be non-relocatable and ensure that it
* hasn't already been defined or referenced in a context that would * hasn't already been defined or referenced in a context that would
* require that it be relocatable * require that it be relocatable
* @param symbolName The name of the symbol to create
* @param numeric If false, the symbol may not have been referenced earlier
*/ */
static struct Symbol *createNonrelocSymbol(char const *symbolName) static struct Symbol *createNonrelocSymbol(char const *symbolName, bool numeric)
{ {
struct Symbol *symbol = sym_FindExactSymbol(symbolName); struct Symbol *symbol = sym_FindExactSymbol(symbolName);
@@ -357,6 +379,12 @@ static struct Symbol *createNonrelocSymbol(char const *symbolName)
error("'%s' already defined at ", symbolName); error("'%s' already defined at ", symbolName);
dumpFilename(symbol); dumpFilename(symbol);
putc('\n', stderr); putc('\n', stderr);
} else if (!numeric) {
// The symbol has already been referenced, but it's not allowed
error("'%s' already referenced at ", symbolName);
dumpFilename(symbol);
putc('\n', stderr);
return NULL; // Don't allow overriding the symbol, that'd be bad!
} }
return symbol; return symbol;
@@ -367,7 +395,10 @@ static struct Symbol *createNonrelocSymbol(char const *symbolName)
*/ */
struct Symbol *sym_AddEqu(char const *symName, int32_t value) struct Symbol *sym_AddEqu(char const *symName, int32_t value)
{ {
struct Symbol *sym = createNonrelocSymbol(symName); struct Symbol *sym = createNonrelocSymbol(symName, true);
if (!sym)
return NULL;
sym->type = SYM_EQU; sym->type = SYM_EQU;
sym->value = value; sym->value = value;
@@ -389,18 +420,33 @@ struct Symbol *sym_AddEqu(char const *symName, int32_t value)
*/ */
struct Symbol *sym_AddString(char const *symName, char const *value) struct Symbol *sym_AddString(char const *symName, char const *value)
{ {
struct Symbol *sym = createNonrelocSymbol(symName); struct Symbol *sym = createNonrelocSymbol(symName, false);
size_t len = strlen(value);
char *string = malloc(len + 1);
if (string == NULL) if (!sym)
fatalerror("No memory for string equate: %s\n", strerror(errno)); return NULL;
strcpy(string, value);
sym->type = SYM_EQUS; assignStringSymbol(sym, value);
/* TODO: use other fields */ return sym;
sym->macroSize = len; }
sym->macro = string;
struct Symbol *sym_RedefString(char const *symName, char const *value)
{
struct Symbol *sym = sym_FindExactSymbol(symName);
if (!sym) {
sym = createsymbol(symName);
} else if (sym->type != SYM_EQUS) {
error("'%s' already defined as non-EQUS at ", symName);
dumpFilename(sym);
putc('\n', stderr);
}
/*
* FIXME: this leaks the previous sym->macro value, but this can't
* free(sym->macro) because the expansion may be redefining itself.
*/
assignStringSymbol(sym, value);
return sym; return sym;
} }
@@ -492,8 +538,10 @@ struct Symbol *sym_AddLocalLabel(char const *name)
* Check that `labelScope[i]` ended the check, guaranteeing that `name` is at least * Check that `labelScope[i]` ended the check, guaranteeing that `name` is at least
* as long, and then that this was the entirety of the `Parent` part of `name`. * as long, and then that this was the entirety of the `Parent` part of `name`.
*/ */
if (labelScope[i] != '\0' || name[i] != '.') if (labelScope[i] != '\0' || name[i] != '.') {
error("Not currently in the scope of '%.*s'\n", parentLen, name); assert(parentLen <= INT_MAX);
error("Not currently in the scope of '%.*s'\n", (int)parentLen, name);
}
if (strchr(&name[parentLen + 1], '.')) /* There will at least be a terminator */ if (strchr(&name[parentLen + 1], '.')) /* There will at least be a terminator */
fatalerror("'%s' is a nonsensical reference to a nested local label\n", fatalerror("'%s' is a nonsensical reference to a nested local label\n",
name); name);
@@ -515,11 +563,60 @@ struct Symbol *sym_AddLabel(char const *name)
return sym; return sym;
} }
static uint32_t anonLabelID;
/*
* Add an anonymous label
*/
struct Symbol *sym_AddAnonLabel(void)
{
if (anonLabelID == UINT32_MAX) {
error("Only %" PRIu32 " anonymous labels can be created!", anonLabelID);
return NULL;
}
char name[MAXSYMLEN + 1];
sym_WriteAnonLabelName(name, 0, true); // The direction is important!!
anonLabelID++;
return addLabel(name);
}
/*
* Write an anonymous label's name to a buffer
*/
void sym_WriteAnonLabelName(char buf[MIN_NB_ELMS(MAXSYMLEN + 1)], uint32_t ofs, bool neg)
{
uint32_t id = 0;
if (neg) {
if (ofs > anonLabelID)
error("Reference to anonymous label %" PRIu32 " before, when only %" PRIu32
" ha%s been created so far\n",
ofs, anonLabelID, anonLabelID == 1 ? "s" : "ve");
else
id = anonLabelID - ofs;
} else {
ofs--; // We're referencing symbols that haven't been created yet...
if (ofs > UINT32_MAX - anonLabelID)
error("Reference to anonymous label %" PRIu32 " after, when only %" PRIu32
" may still be created\n", ofs + 1, UINT32_MAX - anonLabelID);
else
id = anonLabelID + ofs;
}
sprintf(buf, "!%u", id);
}
/* /*
* Export a symbol * Export a symbol
*/ */
void sym_Export(char const *symName) void sym_Export(char const *symName)
{ {
if (symName[0] == '!') {
error("Anonymous labels cannot be exported\n");
return;
}
struct Symbol *sym = sym_FindScopedSymbol(symName); struct Symbol *sym = sym_FindScopedSymbol(symName);
/* If the symbol doesn't exist, create a ref that can be purged */ /* If the symbol doesn't exist, create a ref that can be purged */
@@ -533,7 +630,10 @@ void sym_Export(char const *symName)
*/ */
struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size) struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size)
{ {
struct Symbol *sym = createNonrelocSymbol(symName); struct Symbol *sym = createNonrelocSymbol(symName, false);
if (!sym)
return NULL;
sym->type = SYM_MACRO; sym->type = SYM_MACRO;
sym->macroSize = size; sym->macroSize = size;
@@ -599,14 +699,14 @@ static inline struct Symbol *createBuiltinSymbol(char const *name)
sym->isBuiltin = true; sym->isBuiltin = true;
sym->hasCallback = true; sym->hasCallback = true;
sym->src = NULL; sym->src = NULL;
sym->fileLine = 0; sym->fileLine = 1; // This is 0 for CLI-defined symbols
return sym; return sym;
} }
/* /*
* Initialize the symboltable * Initialize the symboltable
*/ */
void sym_Init(void) void sym_Init(time_t now)
{ {
PCSymbol = createBuiltinSymbol("@"); PCSymbol = createBuiltinSymbol("@");
struct Symbol *_NARGSymbol = createBuiltinSymbol("_NARG"); struct Symbol *_NARGSymbol = createBuiltinSymbol("_NARG");
@@ -627,8 +727,9 @@ void sym_Init(void)
sym_AddEqu("__RGBDS_MAJOR__", PACKAGE_VERSION_MAJOR)->isBuiltin = true; sym_AddEqu("__RGBDS_MAJOR__", PACKAGE_VERSION_MAJOR)->isBuiltin = true;
sym_AddEqu("__RGBDS_MINOR__", PACKAGE_VERSION_MINOR)->isBuiltin = true; sym_AddEqu("__RGBDS_MINOR__", PACKAGE_VERSION_MINOR)->isBuiltin = true;
sym_AddEqu("__RGBDS_PATCH__", PACKAGE_VERSION_PATCH)->isBuiltin = true; sym_AddEqu("__RGBDS_PATCH__", PACKAGE_VERSION_PATCH)->isBuiltin = true;
#ifdef PACKAGE_VERSION_RC
time_t now = time(NULL); sym_AddEqu("__RGBDS_RC__", PACKAGE_VERSION_RC)->isBuiltin = true;
#endif
if (now == (time_t)-1) { if (now == (time_t)-1) {
warn("Couldn't determine current time"); warn("Couldn't determine current time");
@@ -671,6 +772,14 @@ void sym_Init(void)
#undef addString #undef addString
labelScope = NULL; labelScope = NULL;
anonLabelID = 0;
math_DefinePI(); /* _PI is deprecated */
struct Symbol *_PISymbol = createBuiltinSymbol("_PI");
_PISymbol->type = SYM_EQU;
_PISymbol->src = NULL;
_PISymbol->fileLine = 0;
_PISymbol->hasCallback = true;
_PISymbol->numCallback = fix_Callback_PI;
} }

View File

@@ -16,7 +16,9 @@
#include "extern/utf8decoder.h" #include "extern/utf8decoder.h"
/* /*
* Calculate the hash value for a string * Calculate the hash value for a string.
* Uses the djb2 algorithm (xor version).
* http://www.cse.yorku.ca/~oz/hash.html
*/ */
uint32_t calchash(const char *s) uint32_t calchash(const char *s)
{ {
@@ -55,7 +57,7 @@ char const *print(int c)
default: /* Print as hex */ default: /* Print as hex */
buf[1] = 'x'; buf[1] = 'x';
sprintf(&buf[2], "%02hhx", c); sprintf(&buf[2], "%02hhx", (uint8_t)c);
return buf; return buf;
} }
buf[2] = '\0'; buf[2] = '\0';
@@ -70,7 +72,7 @@ size_t readUTF8Char(uint8_t *dest, char const *src)
for (;;) { for (;;) {
if (decode(&state, &codep, src[i]) == 1) if (decode(&state, &codep, src[i]) == 1)
fatalerror("invalid UTF-8 character\n"); return 0;
dest[i] = src[i]; dest[i] = src[i];
i++; i++;

View File

@@ -10,6 +10,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include "asm/fstack.h" #include "asm/fstack.h"
@@ -33,9 +34,11 @@ static enum WarningState const defaultWarnings[NB_WARNINGS] = {
[WARNING_CHARMAP_REDEF] = WARNING_DISABLED, [WARNING_CHARMAP_REDEF] = WARNING_DISABLED,
[WARNING_DIV] = WARNING_DISABLED, [WARNING_DIV] = WARNING_DISABLED,
[WARNING_EMPTY_DATA_DIRECTIVE] = WARNING_DISABLED, [WARNING_EMPTY_DATA_DIRECTIVE] = WARNING_DISABLED,
[WARNING_EMPTY_ENTRY] = WARNING_DISABLED, [WARNING_EMPTY_MACRO_ARG] = WARNING_DISABLED,
[WARNING_EMPTY_STRRPL] = WARNING_DISABLED,
[WARNING_LARGE_CONSTANT] = WARNING_DISABLED, [WARNING_LARGE_CONSTANT] = WARNING_DISABLED,
[WARNING_LONG_STR] = WARNING_DISABLED, [WARNING_LONG_STR] = WARNING_DISABLED,
[WARNING_MACRO_SHIFT] = WARNING_DISABLED,
[WARNING_NESTED_COMMENT] = WARNING_ENABLED, [WARNING_NESTED_COMMENT] = WARNING_ENABLED,
[WARNING_OBSOLETE] = WARNING_ENABLED, [WARNING_OBSOLETE] = WARNING_ENABLED,
[WARNING_SHIFT] = WARNING_DISABLED, [WARNING_SHIFT] = WARNING_DISABLED,
@@ -73,9 +76,11 @@ static char const *warningFlags[NB_WARNINGS_ALL] = {
"charmap-redef", "charmap-redef",
"div", "div",
"empty-data-directive", "empty-data-directive",
"empty-entry", "empty-macro-arg",
"empty-strrpl",
"large-constant", "large-constant",
"long-string", "long-string",
"macro-shift",
"nested-comment", "nested-comment",
"obsolete", "obsolete",
"shift", "shift",
@@ -98,6 +103,7 @@ static uint8_t const _wallCommands[] = {
WARNING_BUILTIN_ARG, WARNING_BUILTIN_ARG,
WARNING_CHARMAP_REDEF, WARNING_CHARMAP_REDEF,
WARNING_EMPTY_DATA_DIRECTIVE, WARNING_EMPTY_DATA_DIRECTIVE,
WARNING_EMPTY_STRRPL,
WARNING_LARGE_CONSTANT, WARNING_LARGE_CONSTANT,
WARNING_LONG_STR, WARNING_LONG_STR,
META_WARNING_DONE META_WARNING_DONE
@@ -105,7 +111,8 @@ static uint8_t const _wallCommands[] = {
/* Warnings that are less likely to indicate an error */ /* Warnings that are less likely to indicate an error */
static uint8_t const _wextraCommands[] = { static uint8_t const _wextraCommands[] = {
WARNING_EMPTY_ENTRY, WARNING_EMPTY_MACRO_ARG,
WARNING_MACRO_SHIFT,
WARNING_NESTED_COMMENT, WARNING_NESTED_COMMENT,
META_WARNING_DONE META_WARNING_DONE
}; };
@@ -115,9 +122,11 @@ static uint8_t const _weverythingCommands[] = {
WARNING_BUILTIN_ARG, WARNING_BUILTIN_ARG,
WARNING_DIV, WARNING_DIV,
WARNING_EMPTY_DATA_DIRECTIVE, WARNING_EMPTY_DATA_DIRECTIVE,
WARNING_EMPTY_ENTRY, WARNING_EMPTY_MACRO_ARG,
WARNING_EMPTY_STRRPL,
WARNING_LARGE_CONSTANT, WARNING_LARGE_CONSTANT,
WARNING_LONG_STR, WARNING_LONG_STR,
WARNING_MACRO_SHIFT,
WARNING_NESTED_COMMENT, WARNING_NESTED_COMMENT,
WARNING_OBSOLETE, WARNING_OBSOLETE,
WARNING_SHIFT, WARNING_SHIFT,

7
src/check_bison_ver.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/sh
bison -V | awk -v major="$1" -v minor="$2" '
/^bison.*[0-9]+(\.[0-9]+)(\.[0-9]+)?$/ {
match($0, /[0-9]+(\.[0-9]+)(\.[0-9]+)?$/);
split(substr($0, RSTART), ver, ".");
if (ver[1] == major && ver[2] >= minor) { exit 0 } else { exit 1 }
}'

201
src/extern/getopt.c vendored
View File

@@ -26,21 +26,16 @@
#include <stddef.h> #include <stddef.h>
#include <stdlib.h> #include <stdlib.h>
#include <limits.h> #include <limits.h>
#ifndef _MSC_VER
# include <unistd.h>
#endif
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <wchar.h> #include <wchar.h>
#include "extern/getopt.h" #include "extern/getopt.h"
#ifdef _MSC_VER char *musl_optarg;
char *optarg; int musl_optind = 1, musl_opterr = 1, musl_optopt;
int optind=1, opterr=1, optopt; int musl_optreset = 0;
#endif static int musl_optpos;
int optreset=0;
static int optpos;
static void musl_getopt_msg(const char *a, const char *b, const char *c, size_t l) static void musl_getopt_msg(const char *a, const char *b, const char *c, size_t l)
{ {
@@ -52,7 +47,6 @@ static void musl_getopt_msg(const char *a, const char *b, const char *c, size_t
putc('\n', f); putc('\n', f);
} }
#ifdef _MSC_VER
static int getopt(int argc, char *argv[], const char *optstring) static int getopt(int argc, char *argv[], const char *optstring)
{ {
int i; int i;
@@ -60,40 +54,42 @@ static int getopt(int argc, char *argv[], const char *optstring)
int k, l; int k, l;
char *optchar; char *optchar;
if (!optind || optreset) { if (!musl_optind || musl_optreset) {
optreset = 0; musl_optreset = 0;
optpos = 0; musl_optpos = 0;
optind = 1; musl_optind = 1;
} }
if (optind >= argc || !argv[optind]) if (musl_optind >= argc || !argv[musl_optind])
return -1; return -1;
if (argv[optind][0] != '-') { if (argv[musl_optind][0] != '-') {
if (optstring[0] == '-') { if (optstring[0] == '-') {
optarg = argv[optind++]; musl_optarg = argv[musl_optind++];
return 1; return 1;
} }
return -1; return -1;
} }
if (!argv[optind][1]) if (!argv[musl_optind][1])
return -1; return -1;
if (argv[optind][1] == '-' && !argv[optind][2]) if (argv[musl_optind][1] == '-' && !argv[musl_optind][2])
return optind++, -1; return musl_optind++, -1;
if (!optpos) optpos++; if (!musl_optpos)
if ((k = mbtowc(&c, argv[optind]+optpos, MB_LEN_MAX)) < 0) { musl_optpos++;
k = mbtowc(&c, argv[musl_optind] + musl_optpos, MB_LEN_MAX);
if (k < 0) {
k = 1; k = 1;
c = 0xfffd; /* replacement char */ c = 0xfffd; /* replacement char */
} }
optchar = argv[optind]+optpos; optchar = argv[musl_optind] + musl_optpos;
optpos += k; musl_optpos += k;
if (!argv[optind][optpos]) { if (!argv[musl_optind][musl_optpos]) {
optind++; musl_optind++;
optpos = 0; musl_optpos = 0;
} }
if (optstring[0] == '-' || optstring[0] == '+') if (optstring[0] == '-' || optstring[0] == '+')
@@ -103,39 +99,43 @@ static int getopt(int argc, char *argv[], const char *optstring)
d = 0; d = 0;
do { do {
l = mbtowc(&d, optstring+i, MB_LEN_MAX); l = mbtowc(&d, optstring+i, MB_LEN_MAX);
if (l>0) i+=l; else i++; if (l > 0)
i += l;
else
i++;
} while (l && d != c); } while (l && d != c);
if (d != c || c == ':') { if (d != c || c == ':') {
optopt = c; musl_optopt = c;
if (optstring[0] != ':' && opterr) if (optstring[0] != ':' && musl_opterr)
musl_getopt_msg(argv[0], ": unrecognized option: ", optchar, k); musl_getopt_msg(argv[0], ": unrecognized option: ", optchar, k);
return '?'; return '?';
} }
if (optstring[i] == ':') { if (optstring[i] == ':') {
optarg = 0; musl_optarg = 0;
if (optstring[i+1] != ':' || optpos) { if (optstring[i + 1] != ':' || musl_optpos) {
optarg = argv[optind++] + optpos; musl_optarg = argv[musl_optind++] + musl_optpos;
optpos = 0; musl_optpos = 0;
} }
if (optind > argc) { if (musl_optind > argc) {
optopt = c; musl_optopt = c;
if (optstring[0] == ':') return ':'; if (optstring[0] == ':')
if (opterr) musl_getopt_msg(argv[0], return ':';
": option requires an argument: ", if (musl_opterr)
musl_getopt_msg(argv[0], ": option requires an argument: ",
optchar, k); optchar, k);
return '?'; return '?';
} }
} }
return c; return c;
} }
#endif /* _MSC_VER */
static void permute(char **argv, int dest, int src) static void permute(char **argv, int dest, int src)
{ {
char *tmp = argv[src]; char *tmp = argv[src];
int i; int i;
for (i=src; i>dest; i--)
for (i = src; i > dest; i--)
argv[i] = argv[i-1]; argv[i] = argv[i-1];
argv[dest] = tmp; argv[dest] = tmp;
} }
@@ -145,49 +145,61 @@ static int musl_getopt_long_core(int argc, char **argv, const char *optstring, c
static int musl_getopt_long(int argc, char **argv, const char *optstring, const struct option *longopts, int *idx, int longonly) static int musl_getopt_long(int argc, char **argv, const char *optstring, const struct option *longopts, int *idx, int longonly)
{ {
int ret, skipped, resumed; int ret, skipped, resumed;
if (!optind || optreset) {
optreset = 0; if (!musl_optind || musl_optreset) {
optpos = 0; musl_optreset = 0;
optind = 1; musl_optpos = 0;
musl_optind = 1;
} }
if (optind >= argc || !argv[optind]) return -1;
skipped = optind; if (musl_optind >= argc || !argv[musl_optind])
return -1;
skipped = musl_optind;
if (optstring[0] != '+' && optstring[0] != '-') { if (optstring[0] != '+' && optstring[0] != '-') {
int i; int i;
for (i=optind; ; i++) { for (i = musl_optind; ; i++) {
if (i >= argc || !argv[i]) return -1; if (i >= argc || !argv[i])
if (argv[i][0] == '-' && argv[i][1]) break; return -1;
if (argv[i][0] == '-' && argv[i][1])
break;
} }
optind = i; musl_optind = i;
} }
resumed = optind; resumed = musl_optind;
ret = musl_getopt_long_core(argc, argv, optstring, longopts, idx, longonly); ret = musl_getopt_long_core(argc, argv, optstring, longopts, idx, longonly);
if (resumed > skipped) { if (resumed > skipped) {
int i, cnt = optind-resumed; int i, cnt = musl_optind - resumed;
for (i=0; i<cnt; i++)
permute(argv, skipped, optind-1); for (i = 0; i < cnt; i++)
optind = skipped + cnt; permute(argv, skipped, musl_optind - 1);
musl_optind = skipped + cnt;
} }
return ret; return ret;
} }
static int musl_getopt_long_core(int argc, char **argv, const char *optstring, const struct option *longopts, int *idx, int longonly) static int musl_getopt_long_core(int argc, char **argv, const char *optstring, const struct option *longopts, int *idx, int longonly)
{ {
optarg = 0; musl_optarg = 0;
if (longopts && argv[optind][0] == '-' && if (longopts && argv[musl_optind][0] == '-' &&
((longonly && argv[optind][1] && argv[optind][1] != '-') || ((longonly && argv[musl_optind][1] && argv[musl_optind][1] != '-') ||
(argv[optind][1] == '-' && argv[optind][2]))) (argv[musl_optind][1] == '-' && argv[musl_optind][2]))) {
{ int colon = optstring[optstring[0] == '+' || optstring[0] == '-'] == ':';
int colon = optstring[optstring[0]=='+'||optstring[0]=='-']==':';
int i, cnt, match = 0; int i, cnt, match = 0;
char *arg = 0, *opt, *start = argv[optind]+1; char *arg = 0, *opt, *start = argv[musl_optind] + 1;
for (cnt=i=0; longopts[i].name; i++) {
for (cnt = i = 0; longopts[i].name; i++) {
const char *name = longopts[i].name; const char *name = longopts[i].name;
opt = start; opt = start;
if (*opt == '-') opt++; if (*opt == '-')
while (*opt && *opt != '=' && *opt == *name) opt++;
name++, opt++; while (*opt && *opt != '=' && *opt == *name) {
if (*opt && *opt != '=') continue; name++;
opt++;
}
if (*opt && *opt != '=')
continue;
arg = opt; arg = opt;
match = i; match = i;
if (!*name) { if (!*name) {
@@ -196,25 +208,28 @@ static int musl_getopt_long_core(int argc, char **argv, const char *optstring, c
} }
cnt++; cnt++;
} }
if (cnt==1 && longonly && arg-start == mblen(start, MB_LEN_MAX)) { if (cnt == 1 && longonly && arg - start == mblen(start, MB_LEN_MAX)) {
int l = arg-start; int l = arg - start;
for (i=0; optstring[i]; i++) {
int j; for (i = 0; optstring[i]; i++) {
for (j=0; j<l && start[j]==optstring[i+j]; j++); int j = 0;
if (j==l) {
while (j < l && start[j] == optstring[i + j])
j++;
if (j == l) {
cnt++; cnt++;
break; break;
} }
} }
} }
if (cnt==1) { if (cnt == 1) {
i = match; i = match;
opt = arg; opt = arg;
optind++; musl_optind++;
if (*opt == '=') { if (*opt == '=') {
if (!longopts[i].has_arg) { if (!longopts[i].has_arg) {
optopt = longopts[i].val; musl_optopt = longopts[i].val;
if (colon || !opterr) if (colon || !musl_opterr)
return '?'; return '?';
musl_getopt_msg(argv[0], musl_getopt_msg(argv[0],
": option does not take an argument: ", ": option does not take an argument: ",
@@ -222,36 +237,40 @@ static int musl_getopt_long_core(int argc, char **argv, const char *optstring, c
strlen(longopts[i].name)); strlen(longopts[i].name));
return '?'; return '?';
} }
optarg = opt+1; musl_optarg = opt + 1;
} else if (longopts[i].has_arg == required_argument) { } else if (longopts[i].has_arg == required_argument) {
if (!(optarg = argv[optind])) { musl_optarg = argv[musl_optind];
optopt = longopts[i].val; if (!musl_optarg) {
if (colon) return ':'; musl_optopt = longopts[i].val;
if (!opterr) return '?'; if (colon)
return ':';
if (!musl_opterr)
return '?';
musl_getopt_msg(argv[0], musl_getopt_msg(argv[0],
": option requires an argument: ", ": option requires an argument: ",
longopts[i].name, longopts[i].name,
strlen(longopts[i].name)); strlen(longopts[i].name));
return '?'; return '?';
} }
optind++; musl_optind++;
} }
if (idx) *idx = i; if (idx)
*idx = i;
if (longopts[i].flag) { if (longopts[i].flag) {
*longopts[i].flag = longopts[i].val; *longopts[i].flag = longopts[i].val;
return 0; return 0;
} }
return longopts[i].val; return longopts[i].val;
} }
if (argv[optind][1] == '-') { if (argv[musl_optind][1] == '-') {
optopt = 0; musl_optopt = 0;
if (!colon && opterr) if (!colon && musl_opterr)
musl_getopt_msg(argv[0], cnt ? musl_getopt_msg(argv[0], cnt ?
": option is ambiguous: " : ": option is ambiguous: " :
": unrecognized option: ", ": unrecognized option: ",
argv[optind]+2, argv[musl_optind] + 2,
strlen(argv[optind]+2)); strlen(argv[musl_optind] + 2));
optind++; musl_optind++;
return '?'; return '?';
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,11 @@
.\" .\"
.\" This file is part of RGBDS. .\" This file is part of RGBDS.
.\" .\"
.\" Copyright (c) 2010-2017, Anthony J. Bentley and RGBDS contributors. .\" Copyright (c) 2010-2021, Anthony J. Bentley and RGBDS contributors.
.\" .\"
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd December 5, 2019 .Dd March 28, 2021
.Dt RGBFIX 1 .Dt RGBFIX 1
.Os .Os
.Sh NAME .Sh NAME
@@ -24,39 +24,48 @@
.Op Fl p Ar pad_value .Op Fl p Ar pad_value
.Op Fl r Ar ram_size .Op Fl r Ar ram_size
.Op Fl t Ar title_str .Op Fl t Ar title_str
.Ar file .Op Ar
.Sh DESCRIPTION .Sh DESCRIPTION
The The
.Nm .Nm
program changes headers of Game Boy ROM images. program changes headers of Game Boy ROM images, typically generated by
.Xr rgblink 1 ,
though it will work with
.Em any
Game Boy ROM.
It also performs other correctness operations, such as padding. It also performs other correctness operations, such as padding.
.Nm
only changes the fields for which it has values specified.
Developers are advised to fill those fields with 0x00 bytes in their source code before running
.Nm ,
and to have already populated whichever fields they don't specify using
.Nm .
.Pp .Pp
Note that options can be abbreviated as long as the abbreviation is unambiguous: Note that options can be abbreviated as long as the abbreviation is unambiguous:
.Fl Fl verb .Fl Fl color-o
is is
.Fl Fl verbose , .Fl Fl color-only ,
but but
.Fl Fl ver .Fl Fl color
is invalid because it could also be is invalid because it could also be
.Fl Fl version . .Fl Fl color-compatible .
The arguments are as follows: Options later in the command line override those set earlier.
Accepted options are as follows:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Fl C , Fl Fl color-only .It Fl C , Fl Fl color-only
Set the Game Boy Color\(enonly flag: Set the Game Boy Color\(enonly flag
.Ad 0x143 .Pq Ad 0x143
= 0xC0. to 0xC0.
If both this and the This overrides
.Fl c .Fl c
flag are set, this takes precedence. if it was set prior.
.It Fl c , Fl Fl color-compatible .It Fl c , Fl Fl color-compatible
Set the Game Boy Color\(encompatible flag: Set the Game Boy Color\(encompatible flag:
.Ad 0x143 .Pq Ad 0x143
= 0x80. to 0x80.
If both this and the This overrides
.Fl C .Fl c
flag are set, if it was set prior.
.Fl C
takes precedence.
.It Fl f Ar fix_spec , Fl Fl fix-spec Ar fix_spec .It Fl f Ar fix_spec , Fl Fl fix-spec Ar fix_spec
Fix certain header values that the Game Boy checks for correctness. Fix certain header values that the Game Boy checks for correctness.
Alternatively, intentionally trash these values by writing their binary inverse instead. Alternatively, intentionally trash these values by writing their binary inverse instead.
@@ -83,54 +92,63 @@ Trash the global checksum.
.It Fl i Ar game_id , Fl Fl game-id Ar game_id .It Fl i Ar game_id , Fl Fl game-id Ar game_id
Set the game ID string Set the game ID string
.Pq Ad 0x13F Ns \(en Ns Ad 0x142 .Pq Ad 0x13F Ns \(en Ns Ad 0x142
to a given string of exactly 4 characters. to a given string.
If both this and the title are set, the game ID will overwrite the overlapping portion of the title. If it's longer than 4 chars, it will be truncated, and a warning emitted.
.It Fl j , Fl Fl non-japanese .It Fl j , Fl Fl non-japanese
Set the non-Japanese region flag: Set the non-Japanese region flag
.Ad 0x14A .Pq Ad 0x14A
= 1. to 0x01.
.It Fl k Ar licensee_str , Fl Fl new-licensee Ar licensee_str .It Fl k Ar licensee_str , Fl Fl new-licensee Ar licensee_str
Set the new licensee string Set the new licensee string
.Pq Ad 0x144 Ns \(en Ns Ad 0x145 .Pq Ad 0x144 Ns \(en Ns Ad 0x145
to a given string, truncated to at most two characters. to a given string.
If it's longer than 2 chars, it will be truncated, and a warning emitted.
.It Fl l Ar licensee_id , Fl Fl old-licensee Ar licensee_id .It Fl l Ar licensee_id , Fl Fl old-licensee Ar licensee_id
Set the old licensee code, Set the old licensee code
.Ad 0x14B , .Pq Ad 0x14B
to a given value from 0 to 0xFF. to a given value from 0 to 0xFF.
This value is deprecated and should be set to 0x33 in all new software. This value is deprecated and should be set to 0x33 in all new software.
.It Fl m Ar mbc_type , Fl Fl mbc-type Ar mbc_type .It Fl m Ar mbc_type , Fl Fl mbc-type Ar mbc_type
Set the MBC type, Set the MBC type
.Ad 0x147 , .Pq Ad 0x147
to a given value from 0 to 0xFF. to a given value from 0 to 0xFF.
This value may also be an MBC name from the Pan Docs.
.It Fl n Ar rom_version , Fl Fl rom-version Ar rom_version .It Fl n Ar rom_version , Fl Fl rom-version Ar rom_version
Set the ROM version, Set the ROM version
.Ad 0x14C , .Pq Ad 0x14C
to a given value from 0 to 0xFF. to a given value from 0 to 0xFF.
.It Fl p Ar pad_value , Fl Fl pad-value Ar pad_value .It Fl p Ar pad_value , Fl Fl pad-value Ar pad_value
Pad the image to a valid size with a given pad value from 0 to 0xFF. Pad the ROM image to a valid size with a given pad value from 0 to 255 (0xFF).
.Nm .Nm
will automatically pick a size from 32 KiB, 64 KiB, 128 KiB, ..., 8192 KiB. will automatically pick a size from 32 KiB, 64 KiB, 128 KiB, ..., 8192 KiB.
The cartridge size byte The cartridge size byte
.Pq Ad 0x148 .Pq Ad 0x148
will be changed to reflect this new size. will be changed to reflect this new size.
The recommended padding value is 0xFF, to speed up writing the ROM to flash chips, and to avoid "nop slides" into VRAM.
.It Fl r Ar ram_size , Fl Fl ram-size Ar ram_size .It Fl r Ar ram_size , Fl Fl ram-size Ar ram_size
Set the RAM size, Set the RAM size
.Ad 0x149 , .Pq Ad 0x149
to a given value from 0 to 0xFF. to a given value from 0 to 0xFF.
.It Fl s , Fl Fl sgb-compatible .It Fl s , Fl Fl sgb-compatible
Set the SGB flag: Set the SGB flag
.Ad 0x146 .Pq Ad 0x146
= 3. This flag will be ignored by the SGB unless the old licensee code is 0x33! to 0x03.
This flag will be ignored by the SGB unless the old licensee code is 0x33!
If this is given as well as
.Fl l ,
but is not set to 0x33, a warning will be printed.
.It Fl t Ar title , Fl Fl title Ar title .It Fl t Ar title , Fl Fl title Ar title
Set the title string Set the title string
.Pq Ad 0x134 Ns \(en Ns Ad 0x143 .Pq Ad 0x134 Ns \(en Ns Ad 0x143
to a given string, truncated to at most 16 characters. to a given string.
It is recommended to use 15 characters instead, to avoid clashing with the CGB flag If the title is longer than the max length, it will be truncated, and a warning emitted.
.Po Fl c The max length is 11 characters if the game ID
.Pq Fl i
is specified, 15 characters if the CGB flag
.Fl ( c
or or
.Fl C .Fl C )
.Pc . is specified but the game ID is not, and 16 characters otherwise.
If both this and the game ID are set, the game ID will overwrite the overlapping portion of the title.
.It Fl V , Fl Fl version .It Fl V , Fl Fl version
Print the version of the program and exit. Print the version of the program and exit.
.It Fl v , Fl Fl validate .It Fl v , Fl Fl validate
@@ -138,7 +156,7 @@ Equivalent to
.Fl f Cm lhg . .Fl f Cm lhg .
.El .El
.Sh EXAMPLES .Sh EXAMPLES
Most values in the ROM header are only cosmetic. Most values in the ROM header do not matter to the actual console, and most are seldom useful anyway.
The bare minimum requirements for a workable program are the header checksum, the Nintendo logo, and (if needed) the CGB/SGB flags. The bare minimum requirements for a workable program are the header checksum, the Nintendo logo, and (if needed) the CGB/SGB flags.
It is a good idea to pad the image to a valid size as well It is a good idea to pad the image to a valid size as well
.Pq Do valid Dc meaning a power of 2, times 32 KiB . .Pq Do valid Dc meaning a power of 2, times 32 KiB .
@@ -151,14 +169,13 @@ a valid size:
The following will make a SGB-enabled, color-enabled game with a title of The following will make a SGB-enabled, color-enabled game with a title of
.Dq foobar , .Dq foobar ,
and pad it to a valid size. and pad it to a valid size.
.Po .Pq The Game Boy itself does not use the title, but some emulators or ROM managers do.
The Game Boy itself does not use the title, but some emulators or ROM managers do.
.Pc
.Pp .Pp
.D1 $ rgbfix -vcs -l 0x33 -p 255 -t foobar baz.gb .D1 $ rgbfix -vcs -l 0x33 -p 255 -t foobar baz.gb
.Pp .Pp
The following will duplicate the header (sans global checksum) of the game The following will duplicate the header of the game
.Dq Survival Kids : .Dq Survival Kids ,
sans global checksum:
.Pp .Pp
.D1 $ rgbfix -cjsv -k A4 -l 0x33 -m 0x1B -p 0xFF -r 3 -t SURVIVALKIDAVKE \ .D1 $ rgbfix -cjsv -k A4 -l 0x33 -m 0x1B -p 0xFF -r 3 -t SURVIVALKIDAVKE \
SurvivalKids.gbc SurvivalKids.gbc

View File

@@ -1,11 +1,11 @@
.\" .\"
.\" This file is part of RGBDS. .\" This file is part of RGBDS.
.\" .\"
.\" Copyright (c) 2017-2018, Antonio Nino Diaz and RGBDS contributors. .\" Copyright (c) 2017-2021, Antonio Nino Diaz and RGBDS contributors.
.\" .\"
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd February 23, 2018 .Dd March 28, 2021
.Dt GBZ80 7 .Dt GBZ80 7
.Os .Os
.Sh NAME .Sh NAME

View File

@@ -96,7 +96,7 @@ int main(int argc, char *argv[])
opts.attrmapout = true; opts.attrmapout = true;
break; break;
case 'a': case 'a':
opts.attrmapfile = optarg; opts.attrmapfile = musl_optarg;
break; break;
case 'C': case 'C':
opts.colorcurve = true; opts.colorcurve = true;
@@ -105,7 +105,7 @@ int main(int argc, char *argv[])
opts.debug = true; opts.debug = true;
break; break;
case 'd': case 'd':
depth = strtoul(optarg, NULL, 0); depth = strtoul(musl_optarg, NULL, 0);
break; break;
case 'F': case 'F':
opts.hardfix = true; opts.hardfix = true;
@@ -121,19 +121,19 @@ int main(int argc, char *argv[])
opts.unique = true; opts.unique = true;
break; break;
case 'o': case 'o':
opts.outfile = optarg; opts.outfile = musl_optarg;
break; break;
case 'P': case 'P':
opts.palout = true; opts.palout = true;
break; break;
case 'p': case 'p':
opts.palfile = optarg; opts.palfile = musl_optarg;
break; break;
case 'T': case 'T':
opts.tilemapout = true; opts.tilemapout = true;
break; break;
case 't': case 't':
opts.tilemapfile = optarg; opts.tilemapfile = musl_optarg;
break; break;
case 'u': case 'u':
opts.unique = true; opts.unique = true;
@@ -145,15 +145,15 @@ int main(int argc, char *argv[])
opts.verbose = true; opts.verbose = true;
break; break;
case 'x': case 'x':
opts.trim = strtoul(optarg, NULL, 0); opts.trim = strtoul(musl_optarg, NULL, 0);
break; break;
default: default:
print_usage(); print_usage();
/* NOTREACHED */ /* NOTREACHED */
} }
} }
argc -= optind; argc -= musl_optind;
argv += optind; argv += musl_optind;
if (argc == 0) { if (argc == 0) {
fputs("FATAL: no input files\n", stderr); fputs("FATAL: no input files\n", stderr);

View File

@@ -1,11 +1,11 @@
.\" .\"
.\" This file is part of RGBDS. .\" This file is part of RGBDS.
.\" .\"
.\" Copyright (c) 2013-2018, stag019 and RGBDS contributors. .\" Copyright (c) 2013-2021, stag019 and RGBDS contributors.
.\" .\"
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd December 5, 2019 .Dd March 28, 2021
.Dt RGBGFX 1 .Dt RGBGFX 1
.Os .Os
.Sh NAME .Sh NAME
@@ -78,7 +78,8 @@ Same as
.Fl f , .Fl f ,
but additionally, the supplied command line parameters are saved within the PNG and will be loaded and automatically used next time. but additionally, the supplied command line parameters are saved within the PNG and will be loaded and automatically used next time.
.It Fl h , Fl Fl horizontal .It Fl h , Fl Fl horizontal
Lay out tiles horizontally rather than vertically. Lay out tiles in column-major order (column by column), instead of the default row-major order (line by line).
Especially useful for "8x16" OBJ mode, if the input image is 16 pixels tall.
.It Fl m , Fl Fl mirror-tiles .It Fl m , Fl Fl mirror-tiles
Truncate tiles by checking for tiles that are mirrored versions of others and omitting these from the output file. 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. Useful with tilemaps and attrmaps together to keep track of the duplicated tiles and the dimension mirrored.

View File

@@ -112,6 +112,13 @@ static inline void assignSection(struct Section *section,
section->org = location->address; section->org = location->address;
section->bank = location->bank; section->bank = location->bank;
// Propagate the assigned location to all UNIONs/FRAGMENTs
// so `jr` patches in them will have the correct offset
for (struct Section *next = section->nextu; next != NULL; next = next->nextu) {
next->org = section->org;
next->bank = section->bank;
}
nbSectionsToAssign--; nbSectionsToAssign--;
out_AddSection(section); out_AddSection(section);

View File

@@ -217,23 +217,23 @@ int main(int argc, char *argv[])
isWRA0Mode = true; isWRA0Mode = true;
break; break;
case 'l': case 'l':
linkerScriptName = optarg; linkerScriptName = musl_optarg;
break; break;
case 'm': case 'm':
mapFileName = optarg; mapFileName = musl_optarg;
break; break;
case 'n': case 'n':
symFileName = optarg; symFileName = musl_optarg;
break; break;
case 'O': case 'O':
overlayFileName = optarg; overlayFileName = musl_optarg;
break; break;
case 'o': case 'o':
outputFileName = optarg; outputFileName = musl_optarg;
break; break;
case 'p': case 'p':
value = strtoul(optarg, &endptr, 0); value = strtoul(musl_optarg, &endptr, 0);
if (optarg[0] == '\0' || *endptr != '\0') { if (musl_optarg[0] == '\0' || *endptr != '\0') {
error(NULL, 0, "Invalid argument for option 'p'"); error(NULL, 0, "Invalid argument for option 'p'");
value = 0xFF; value = 0xFF;
} }
@@ -245,7 +245,7 @@ int main(int argc, char *argv[])
break; break;
case 's': case 's':
/* FIXME: nobody knows what this does, figure it out */ /* FIXME: nobody knows what this does, figure it out */
(void)optarg; (void)musl_optarg;
warning(NULL, 0, "Nobody has any idea what `-s` does"); warning(NULL, 0, "Nobody has any idea what `-s` does");
break; break;
case 't': case 't':
@@ -271,7 +271,7 @@ int main(int argc, char *argv[])
} }
} }
int curArgIndex = optind; int curArgIndex = musl_optind;
/* If no input files were specified, the user must have screwed up */ /* If no input files were specified, the user must have screwed up */
if (curArgIndex == argc) { if (curArgIndex == argc) {

View File

@@ -188,7 +188,7 @@ static void readFileStackNode(FILE *file, struct FileStackNode fileNodes[], uint
tryReadlong(parentID, file, tryReadlong(parentID, file,
"%s: Cannot read node #%" PRIu32 "'s parent ID: %s", fileName, i); "%s: Cannot read node #%" PRIu32 "'s parent ID: %s", fileName, i);
fileNodes[i].parent = parentID == -1 ? NULL : &fileNodes[parentID]; fileNodes[i].parent = parentID == (uint32_t)-1 ? NULL : &fileNodes[parentID];
tryReadlong(fileNodes[i].lineNo, file, tryReadlong(fileNodes[i].lineNo, file,
"%s: Cannot read node #%" PRIu32 "'s line number: %s", fileName, i); "%s: Cannot read node #%" PRIu32 "'s line number: %s", fileName, i);
tryGetc(fileNodes[i].type, file, "%s: Cannot read node #%" PRIu32 "'s type: %s", tryGetc(fileNodes[i].type, file, "%s: Cannot read node #%" PRIu32 "'s type: %s",
@@ -260,11 +260,11 @@ static void readSymbol(FILE *file, struct Symbol *symbol,
* @param fileName The filename to report in errors * @param fileName The filename to report in errors
* @param i The number of the patch to report in errors * @param i The number of the patch to report in errors
*/ */
static void readPatch(FILE *file, struct Patch *patch, char const *fileName, static void readPatch(FILE *file, struct Patch *patch, char const *fileName, char const *sectName,
char const *sectName, uint32_t i, uint32_t i, struct FileStackNode fileNodes[])
struct Section *fileSections[], struct FileStackNode fileNodes[])
{ {
uint32_t nodeID; uint32_t nodeID;
uint8_t type;
tryReadlong(nodeID, file, tryReadlong(nodeID, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s node ID: %s", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s node ID: %s",
@@ -279,14 +279,13 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
tryReadlong(patch->pcSectionID, file, tryReadlong(patch->pcSectionID, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
fileName, sectName, i); fileName, sectName, i);
patch->pcSection = patch->pcSectionID == -1 ? NULL
: fileSections[patch->pcSectionID];
tryReadlong(patch->pcOffset, file, tryReadlong(patch->pcOffset, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
fileName, sectName, i); fileName, sectName, i);
tryGetc(patch->type, file, tryGetc(type, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s type: %s", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s type: %s",
fileName, sectName, i); fileName, sectName, i);
patch->type = type;
tryReadlong(patch->rpnSize, file, tryReadlong(patch->rpnSize, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s RPN size: %s", "%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s RPN size: %s",
fileName, sectName, i); fileName, sectName, i);
@@ -304,6 +303,16 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
feof(file) ? "Unexpected end of file" : strerror(errno)); feof(file) ? "Unexpected end of file" : strerror(errno));
} }
/**
* Sets a patch's pcSection from its pcSectionID.
* @param patch The struct to fix
*/
static void linkPatchToPCSect(struct Patch *patch, struct Section *fileSections[])
{
patch->pcSection = patch->pcSectionID == (uint32_t)-1 ? NULL
: fileSections[patch->pcSectionID];
}
/** /**
* Reads a section from a file. * Reads a section from a file.
* @param file The file to read from * @param file The file to read from
@@ -311,7 +320,7 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
* @param fileName The filename to report in errors * @param fileName The filename to report in errors
*/ */
static void readSection(FILE *file, struct Section *section, char const *fileName, static void readSection(FILE *file, struct Section *section, char const *fileName,
struct Section *fileSections[], struct FileStackNode fileNodes[]) struct FileStackNode fileNodes[])
{ {
int32_t tmp; int32_t tmp;
uint8_t byte; uint8_t byte;
@@ -349,6 +358,8 @@ static void readSection(FILE *file, struct Section *section, char const *fileNam
section->bank = tmp; section->bank = tmp;
tryGetc(byte, file, "%s: Cannot read \"%s\"'s alignment: %s", tryGetc(byte, file, "%s: Cannot read \"%s\"'s alignment: %s",
fileName, section->name); fileName, section->name);
if (byte > 16)
byte = 16;
section->isAlignFixed = byte != 0; section->isAlignFixed = byte != 0;
section->alignMask = (1 << byte) - 1; section->alignMask = (1 << byte) - 1;
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s alignment offset: %s", tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s alignment offset: %s",
@@ -386,12 +397,9 @@ static void readSection(FILE *file, struct Section *section, char const *fileNam
malloc(sizeof(*patches) * section->nbPatches + 1); malloc(sizeof(*patches) * section->nbPatches + 1);
if (!patches) if (!patches)
err(1, "%s: Unable to read \"%s\"'s patches", fileName, err(1, "%s: Unable to read \"%s\"'s patches", fileName, section->name);
section->name); for (uint32_t i = 0; i < section->nbPatches; i++)
for (uint32_t i = 0; i < section->nbPatches; i++) { readPatch(file, &patches[i], fileName, section->name, i, fileNodes);
readPatch(file, &patches[i], fileName, section->name,
i, fileSections, fileNodes);
}
section->patches = patches; section->patches = patches;
} }
} }
@@ -433,13 +441,13 @@ static void linkSymToSect(struct Symbol const *symbol, struct Section *section)
*/ */
static void readAssertion(FILE *file, struct Assertion *assert, static void readAssertion(FILE *file, struct Assertion *assert,
char const *fileName, uint32_t i, char const *fileName, uint32_t i,
struct Section *fileSections[], struct FileStackNode fileNodes[]) struct FileStackNode fileNodes[])
{ {
char assertName[sizeof("Assertion #" EXPAND_AND_STR(UINT32_MAX))]; char assertName[sizeof("Assertion #4294967295")]; // UINT32_MAX
snprintf(assertName, sizeof(assertName), "Assertion #%" PRIu32, i); snprintf(assertName, sizeof(assertName), "Assertion #%" PRIu32, i);
readPatch(file, &assert->patch, fileName, assertName, 0, fileSections, fileNodes); readPatch(file, &assert->patch, fileName, assertName, 0, fileNodes);
tryReadstr(assert->message, file, "%s: Cannot read assertion's message: %s", tryReadstr(assert->message, file, "%s: Cannot read assertion's message: %s",
fileName); fileName);
} }
@@ -547,7 +555,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
err(1, "%s: Couldn't create new section", fileName); err(1, "%s: Couldn't create new section", fileName);
fileSections[i]->nextu = NULL; fileSections[i]->nextu = NULL;
readSection(file, fileSections[i], fileName, fileSections, nodes[fileID].nodes); readSection(file, fileSections[i], fileName, nodes[fileID].nodes);
fileSections[i]->fileSymbols = fileSymbols; fileSections[i]->fileSymbols = fileSymbols;
if (nbSymPerSect[i]) { if (nbSymPerSect[i]) {
fileSections[i]->symbols = malloc(nbSymPerSect[i] fileSections[i]->symbols = malloc(nbSymPerSect[i]
@@ -565,7 +573,15 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
free(nbSymPerSect); free(nbSymPerSect);
/* Give symbols pointers to their sections */ /* Give patches' PC section pointers to their sections */
for (uint32_t i = 0; i < nbSections; i++) {
if (sect_HasData(fileSections[i]->type)) {
for (uint32_t j = 0; j < fileSections[i]->nbPatches; j++)
linkPatchToPCSect(&fileSections[i]->patches[j], fileSections);
}
}
/* Give symbols' section pointers to their sections */
for (uint32_t i = 0; i < nbSymbols; i++) { for (uint32_t i = 0; i < nbSymbols; i++) {
int32_t sectionID = fileSymbols[i]->sectionID; int32_t sectionID = fileSymbols[i]->sectionID;
@@ -597,7 +613,8 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
if (!assertion) if (!assertion)
err(1, "%s: Couldn't create new assertion", fileName); err(1, "%s: Couldn't create new assertion", fileName);
readAssertion(file, assertion, fileName, i, fileSections, nodes[fileID].nodes); readAssertion(file, assertion, fileName, i, nodes[fileID].nodes);
linkPatchToPCSect(&assertion->patch, fileSections);
assertion->fileSymbols = fileSymbols; assertion->fileSymbols = fileSymbols;
assertion->next = assertions; assertion->next = assertions;
assertions = assertion; assertions = assertion;
@@ -633,7 +650,7 @@ static void freeSection(struct Section *section, void *arg)
free(section->name); free(section->name);
if (sect_HasData(section->type)) { if (sect_HasData(section->type)) {
free(section->data); free(section->data);
for (int32_t i = 0; i < section->nbPatches; i++) for (uint32_t i = 0; i < section->nbPatches; i++)
free(section->patches[i].rpnExpression); free(section->patches[i].rpnExpression);
free(section->patches); free(section->patches);
} }

View File

@@ -17,7 +17,13 @@
#include "extern/err.h" #include "extern/err.h"
FILE * outputFile; #include "linkdefs.h"
#include "platform.h" // MIN_NB_ELMS
#define BANK_SIZE 0x4000
FILE *outputFile;
FILE *overlayFile; FILE *overlayFile;
FILE *symFile; FILE *symFile;
FILE *mapFile; FILE *mapFile;
@@ -35,6 +41,18 @@ static struct {
} *banks; } *banks;
} sections[SECTTYPE_INVALID]; } sections[SECTTYPE_INVALID];
/* Defines the order in which types are output to the sym and map files */
static enum SectionType typeMap[SECTTYPE_INVALID] = {
SECTTYPE_ROM0,
SECTTYPE_ROMX,
SECTTYPE_VRAM,
SECTTYPE_SRAM,
SECTTYPE_WRAM0,
SECTTYPE_WRAMX,
SECTTYPE_OAM,
SECTTYPE_HRAM
};
void out_AddSection(struct Section const *section) void out_AddSection(struct Section const *section)
{ {
static uint32_t maxNbBanks[] = { static uint32_t maxNbBanks[] = {
@@ -60,8 +78,7 @@ void out_AddSection(struct Section const *section)
sections[section->type].banks = sections[section->type].banks =
realloc(sections[section->type].banks, realloc(sections[section->type].banks,
sizeof(*sections[0].banks) * minNbBanks); sizeof(*sections[0].banks) * minNbBanks);
for (uint32_t i = sections[section->type].nbBanks; for (uint32_t i = sections[section->type].nbBanks; i < minNbBanks; i++) {
i < minNbBanks; i++) {
sections[section->type].banks[i].sections = NULL; sections[section->type].banks[i].sections = NULL;
sections[section->type].banks[i].zeroLenSections = NULL; sections[section->type].banks[i].zeroLenSections = NULL;
} }
@@ -103,43 +120,63 @@ struct Section const *out_OverlappingSection(struct Section const *section)
/** /**
* Performs sanity checks on the overlay file. * Performs sanity checks on the overlay file.
* @return The number of ROM banks in the overlay file
*/ */
static void checkOverlay(void) static uint32_t checkOverlaySize(void)
{ {
if (!overlayFile) if (!overlayFile)
return; return 0;
if (fseek(overlayFile, 0, SEEK_END) != 0) { if (fseek(overlayFile, 0, SEEK_END) != 0) {
warnx("Overlay file is not seekable, cannot check if properly formed"); warnx("Overlay file is not seekable, cannot check if properly formed");
return; return 0;
} }
long overlaySize = ftell(overlayFile); long overlaySize = ftell(overlayFile);
if (overlaySize % 0x4000)
errx(1, "Overlay file must have a size multiple of 0x4000");
/* Reset back to beginning */ /* Reset back to beginning */
fseek(overlayFile, 0, SEEK_SET); fseek(overlayFile, 0, SEEK_SET);
uint32_t nbOverlayBanks = overlaySize / 0x4000 - 1; if (overlaySize % BANK_SIZE)
errx(1, "Overlay file must have a size multiple of 0x4000");
if (nbOverlayBanks < 1) uint32_t nbOverlayBanks = overlaySize / BANK_SIZE;
if (is32kMode && nbOverlayBanks != 2)
errx(1, "Overlay must be exactly 0x8000 bytes large");
if (nbOverlayBanks < 2)
errx(1, "Overlay must be at least 0x8000 bytes large"); errx(1, "Overlay must be at least 0x8000 bytes large");
if (nbOverlayBanks > sections[SECTTYPE_ROMX].nbBanks) { return nbOverlayBanks;
}
/**
* Expand sections[SECTTYPE_ROMX].banks to cover all the overlay banks.
* This ensures that writeROM will output each bank, even if some are not
* covered by any sections.
* @param nbOverlayBanks The number of banks in the overlay file
*/
static void coverOverlayBanks(uint32_t nbOverlayBanks)
{
/* 2 if is32kMode, 1 otherwise */
uint32_t nbRom0Banks = maxsize[SECTTYPE_ROM0] / BANK_SIZE;
/* Discount ROM0 banks to avoid outputting too much */
uint32_t nbUncoveredBanks = nbOverlayBanks - nbRom0Banks > sections[SECTTYPE_ROMX].nbBanks
? nbOverlayBanks - nbRom0Banks
: 0;
if (nbUncoveredBanks > sections[SECTTYPE_ROMX].nbBanks) {
sections[SECTTYPE_ROMX].banks = sections[SECTTYPE_ROMX].banks =
realloc(sections[SECTTYPE_ROMX].banks, realloc(sections[SECTTYPE_ROMX].banks,
sizeof(*sections[SECTTYPE_ROMX].banks) * sizeof(*sections[SECTTYPE_ROMX].banks) * nbUncoveredBanks);
nbOverlayBanks);
if (!sections[SECTTYPE_ROMX].banks) if (!sections[SECTTYPE_ROMX].banks)
err(1, "Failed to realloc banks for overlay"); err(1, "Failed to realloc banks for overlay");
for (uint32_t i = sections[SECTTYPE_ROMX].nbBanks; for (uint32_t i = sections[SECTTYPE_ROMX].nbBanks; i < nbUncoveredBanks; i++) {
i < nbOverlayBanks; i++) {
sections[SECTTYPE_ROMX].banks[i].sections = NULL; sections[SECTTYPE_ROMX].banks[i].sections = NULL;
sections[SECTTYPE_ROMX].banks[i].zeroLenSections = NULL; sections[SECTTYPE_ROMX].banks[i].zeroLenSections = NULL;
} }
sections[SECTTYPE_ROMX].nbBanks = nbOverlayBanks; sections[SECTTYPE_ROMX].nbBanks = nbUncoveredBanks;
} }
} }
@@ -195,16 +232,19 @@ static void writeROM(void)
outputFile = openFile(outputFileName, "wb"); outputFile = openFile(outputFileName, "wb");
overlayFile = openFile(overlayFileName, "rb"); overlayFile = openFile(overlayFileName, "rb");
checkOverlay(); uint32_t nbOverlayBanks = checkOverlaySize();
if (nbOverlayBanks > 0)
coverOverlayBanks(nbOverlayBanks);
if (outputFile) { if (outputFile) {
if (sections[SECTTYPE_ROM0].nbBanks > 0) if (sections[SECTTYPE_ROM0].nbBanks > 0)
writeBank(sections[SECTTYPE_ROM0].banks[0].sections, writeBank(sections[SECTTYPE_ROM0].banks[0].sections,
0x0000, 0x4000); startaddr[SECTTYPE_ROM0], maxsize[SECTTYPE_ROM0]);
for (uint32_t i = 0 ; i < sections[SECTTYPE_ROMX].nbBanks; i++) for (uint32_t i = 0 ; i < sections[SECTTYPE_ROMX].nbBanks; i++)
writeBank(sections[SECTTYPE_ROMX].banks[i].sections, writeBank(sections[SECTTYPE_ROMX].banks[i].sections,
0x4000, 0x4000); startaddr[SECTTYPE_ROMX], maxsize[SECTTYPE_ROMX]);
} }
closeFile(outputFile); closeFile(outputFile);
@@ -296,12 +336,13 @@ static void writeSymBank(struct SortedSections const *bankSections)
/** /**
* Write a bank's contents to the map file * Write a bank's contents to the map file
* @param bankSections The bank's sections * @param bankSections The bank's sections
* @return The bank's used space
*/ */
static void writeMapBank(struct SortedSections const *sectList, static uint16_t writeMapBank(struct SortedSections const *sectList,
enum SectionType type, uint32_t bank) enum SectionType type, uint32_t bank)
{ {
if (!mapFile) if (!mapFile)
return; return 0;
struct SortedSection const *section = sectList->sections; struct SortedSection const *section = sectList->sections;
struct SortedSection const *zeroLenSection = sectList->zeroLenSections; struct SortedSection const *zeroLenSection = sectList->zeroLenSections;
@@ -309,14 +350,14 @@ static void writeMapBank(struct SortedSections const *sectList,
fprintf(mapFile, "%s bank #%" PRIu32 ":\n", typeNames[type], fprintf(mapFile, "%s bank #%" PRIu32 ":\n", typeNames[type],
bank + bankranges[type][0]); bank + bankranges[type][0]);
uint16_t slack = maxsize[type]; uint16_t used = 0;
while (section || zeroLenSection) { while (section || zeroLenSection) {
struct SortedSection const **pickedSection = struct SortedSection const **pickedSection =
nextSection(&section, &zeroLenSection); nextSection(&section, &zeroLenSection);
struct Section const *sect = (*pickedSection)->section; struct Section const *sect = (*pickedSection)->section;
slack -= sect->size; used += sect->size;
if (sect->size != 0) if (sect->size != 0)
fprintf(mapFile, " SECTION: $%04" PRIx16 "-$%04" PRIx16 " ($%04" PRIx16 " byte%s) [\"%s\"]\n", fprintf(mapFile, " SECTION: $%04" PRIx16 "-$%04" PRIx16 " ($%04" PRIx16 " byte%s) [\"%s\"]\n",
@@ -327,19 +368,57 @@ static void writeMapBank(struct SortedSections const *sectList,
fprintf(mapFile, " SECTION: $%04" PRIx16 " (0 bytes) [\"%s\"]\n", fprintf(mapFile, " SECTION: $%04" PRIx16 " (0 bytes) [\"%s\"]\n",
sect->org, sect->name); sect->org, sect->name);
uint16_t org = sect->org;
while (sect) {
for (size_t i = 0; i < sect->nbSymbols; i++) for (size_t i = 0; i < sect->nbSymbols; i++)
fprintf(mapFile, " $%04" PRIx32 " = %s\n", fprintf(mapFile, " $%04" PRIx32 " = %s\n",
sect->symbols[i]->offset + sect->org, sect->symbols[i]->offset + org,
sect->symbols[i]->name); sect->symbols[i]->name);
sect = sect->nextu; // Also print symbols in the following "pieces"
}
*pickedSection = (*pickedSection)->next; *pickedSection = (*pickedSection)->next;
} }
if (slack == maxsize[type])
if (used == 0) {
fputs(" EMPTY\n\n", mapFile); fputs(" EMPTY\n\n", mapFile);
else } else {
uint16_t slack = maxsize[type] - used;
fprintf(mapFile, " SLACK: $%04" PRIx16 " byte%s\n\n", slack, fprintf(mapFile, " SLACK: $%04" PRIx16 " byte%s\n\n", slack,
slack == 1 ? "" : "s"); slack == 1 ? "" : "s");
}
return used;
}
/**
* Write the total used space by section type to the map file
* @param usedMap The total used space by section type
*/
static void writeMapUsed(uint32_t usedMap[MIN_NB_ELMS(SECTTYPE_INVALID)])
{
if (!mapFile)
return;
fputs("USED:\n", mapFile);
for (uint8_t i = 0; i < SECTTYPE_INVALID; i++) {
enum SectionType type = typeMap[i];
// Do not output used space for VRAM or OAM
if (type == SECTTYPE_VRAM || type == SECTTYPE_OAM)
continue;
if (sections[type].nbBanks > 0) {
fprintf(mapFile, " %s: $%04" PRIx32 " byte%s in %" PRIu32 " bank%s\n",
typeNames[type], usedMap[type], usedMap[type] == 1 ? "" : "s",
sections[type].nbBanks, sections[type].nbBanks == 1 ? "" : "s");
}
}
} }
/** /**
@@ -350,16 +429,7 @@ static void writeSymAndMap(void)
if (!symFileName && !mapFileName) if (!symFileName && !mapFileName)
return; return;
enum SectionType typeMap[SECTTYPE_INVALID] = { uint32_t usedMap[SECTTYPE_INVALID] = {0};
SECTTYPE_ROM0,
SECTTYPE_ROMX,
SECTTYPE_VRAM,
SECTTYPE_SRAM,
SECTTYPE_WRAM0,
SECTTYPE_WRAMX,
SECTTYPE_OAM,
SECTTYPE_HRAM
};
symFile = openFile(symFileName, "w"); symFile = openFile(symFileName, "w");
mapFile = openFile(mapFileName, "w"); mapFile = openFile(mapFileName, "w");
@@ -370,16 +440,16 @@ static void writeSymAndMap(void)
for (uint8_t i = 0; i < SECTTYPE_INVALID; i++) { for (uint8_t i = 0; i < SECTTYPE_INVALID; i++) {
enum SectionType type = typeMap[i]; enum SectionType type = typeMap[i];
if (sections[type].nbBanks > 0) { for (uint32_t bank = 0; bank < sections[type].nbBanks; bank++) {
for (uint32_t bank = 0; bank < sections[type].nbBanks; struct SortedSections const *sect = &sections[type].banks[bank];
bank++) {
writeSymBank(&sections[type].banks[bank]); writeSymBank(sect);
writeMapBank(&sections[type].banks[bank], usedMap[type] += writeMapBank(sect, type, bank);
type, bank);
}
} }
} }
writeMapUsed(usedMap);
closeFile(symFile); closeFile(symFile);
closeFile(mapFile); closeFile(mapFile);
} }

View File

@@ -18,50 +18,10 @@
#include "link/symbol.h" #include "link/symbol.h"
#include "linkdefs.h" #include "linkdefs.h"
#include "opmath.h"
#include "extern/err.h" #include "extern/err.h"
static int32_t asl(int32_t value, int32_t shiftamt); // Forward decl for below
static int32_t asr(int32_t value, int32_t shiftamt)
{
uint32_t uvalue = value;
// Get the easy cases out of the way
if (shiftamt == 0)
return value;
if (value == 0 || shiftamt <= -32)
return 0;
if (shiftamt > 31)
return (value < 0) ? -1 : 0;
if (shiftamt < 0)
return asl(value, -shiftamt);
if (value > 0)
return uvalue >> shiftamt;
{
// Calculate an OR mask for sign extension
// 1->0x80000000, 2->0xC0000000, ..., 31->0xFFFFFFFE
uint32_t shiftamt_high_bits = -((uint32_t)1 << (32 - shiftamt));
return (uvalue >> shiftamt) | shiftamt_high_bits;
}
}
static int32_t asl(int32_t value, int32_t shiftamt)
{
// Repeat the easy cases here to avoid INT_MIN funny business
if (shiftamt == 0)
return value;
if (value == 0 || shiftamt >= 32)
return 0;
if (shiftamt < -31)
return (value < 0) ? -1 : 0;
if (shiftamt < 0)
return asr(value, -shiftamt);
return (uint32_t)value << shiftamt;
}
/* /*
* This is an "empty"-type stack. Apart from the actual values, we also remember * This is an "empty"-type stack. Apart from the actual values, we also remember
* whether the value is a placeholder inserted for error recovery. This allows * whether the value is a placeholder inserted for error recovery. This allows
@@ -219,7 +179,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
popRPN(); popRPN();
value = INT32_MAX; value = INT32_MAX;
} else { } else {
value = popRPN() / value; value = op_divide(popRPN(), value);
} }
break; break;
case RPN_MOD: case RPN_MOD:
@@ -231,12 +191,24 @@ static int32_t computeRPNExpr(struct Patch const *patch,
popRPN(); popRPN();
value = 0; value = 0;
} else { } else {
value = popRPN() % value; value = op_modulo(popRPN(), value);
} }
break; break;
case RPN_UNSUB: case RPN_UNSUB:
value = -popRPN(); value = -popRPN();
break; break;
case RPN_EXP:
value = popRPN();
if (value < 0) {
if (!isError)
error(patch->src, patch->lineNo, "Exponent by negative");
isError = true;
popRPN();
value = 0;
} else {
value = op_exponent(popRPN(), value);
}
break;
case RPN_OR: case RPN_OR:
value = popRPN() | popRPN(); value = popRPN() | popRPN();
@@ -288,11 +260,11 @@ static int32_t computeRPNExpr(struct Patch const *patch,
case RPN_SHL: case RPN_SHL:
value = popRPN(); value = popRPN();
value = asl(popRPN(), value); value = op_shift_left(popRPN(), value);
break; break;
case RPN_SHR: case RPN_SHR:
value = popRPN(); value = popRPN();
value = asr(popRPN(), value); value = op_shift_right(popRPN(), value);
break; break;
case RPN_BANK_SYM: case RPN_BANK_SYM:
@@ -428,7 +400,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
void patch_CheckAssertions(struct Assertion *assert) void patch_CheckAssertions(struct Assertion *assert)
{ {
verbosePrint("Checking assertions..."); verbosePrint("Checking assertions...\n");
initRPNStack(); initRPNStack();
while (assert) { while (assert) {
@@ -490,9 +462,10 @@ static void applyFilePatches(struct Section *section, struct Section *dataSectio
/* `jr` is quite unlike the others... */ /* `jr` is quite unlike the others... */
if (patch->type == PATCHTYPE_JR) { if (patch->type == PATCHTYPE_JR) {
/* Target is relative to the byte *after* the operand */ // Offset is relative to the byte *after* the operand
// PC as operand to `jr` is lower than reference PC by 2
uint16_t address = patch->pcSection->org uint16_t address = patch->pcSection->org
+ patch->pcOffset + 1; + patch->pcOffset + 2;
int16_t jumpOffset = value - address; int16_t jumpOffset = value - address;
if (!isError && (jumpOffset < -128 || jumpOffset > 127)) if (!isError && (jumpOffset < -128 || jumpOffset > 127))

View File

@@ -1,11 +1,11 @@
.\" .\"
.\" This file is part of RGBDS. .\" This file is part of RGBDS.
.\" .\"
.\" Copyright (c) 2010-2019, Anthony J. Bentley and RGBDS contributors. .\" Copyright (c) 2010-2021, Anthony J. Bentley and RGBDS contributors.
.\" .\"
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd November 26, 2019 .Dd March 28, 2021
.Dt RGBLINK 1 .Dt RGBLINK 1
.Os .Os
.Sh NAME .Sh NAME

View File

@@ -1,11 +1,11 @@
.\" .\"
.\" This file is part of RGBDS. .\" This file is part of RGBDS.
.\" .\"
.\" Copyright (c) 2017-2018, Antonio Nino Diaz and RGBDS contributors. .\" Copyright (c) 2017-2021, Antonio Nino Diaz and RGBDS contributors.
.\" .\"
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd November 26, 2019 .Dd March 28, 2021
.Dt RGBLINK 5 .Dt RGBLINK 5
.Os .Os
.Sh NAME .Sh NAME

View File

@@ -39,62 +39,123 @@ void sect_ForEach(void (*callback)(struct Section *, void *), void *arg)
hash_ForEach(sections, forEach, &callbackArg); hash_ForEach(sections, forEach, &callbackArg);
} }
static void mergeSections(struct Section *target, struct Section *other, enum SectionModifier mod) static void checkSectUnionCompat(struct Section *target, struct Section *other)
{ {
if (target->type != other->type)
errx(1, "Section \"%s\" is defined with conflicting types %s and %s",
other->name,
typeNames[target->type], typeNames[other->type]);
if (other->isAddressFixed) { if (other->isAddressFixed) {
if (target->isAddressFixed) { if (target->isAddressFixed) {
if (target->org != other->org) if (target->org != other->org)
errx(1, "Section \"%s\" is defined with conflicting addresses $%" PRIx16 " and $%" PRIx16, errx(1, "Section \"%s\" is defined with conflicting addresses $%04"
PRIx16 " and $%04" PRIx16,
other->name, target->org, other->org); other->name, target->org, other->org);
} else if (target->isAlignFixed) { } else if (target->isAlignFixed) {
if ((other->org - target->alignOfs) & target->alignMask) if ((other->org - target->alignOfs) & target->alignMask)
errx(1, "Section \"%s\" is defined with conflicting %" PRIu16 "-byte alignment (offset %" PRIu16 ") and address $%" PRIx16, errx(1, "Section \"%s\" is defined with conflicting %" PRIu16
"-byte alignment (offset %" PRIu16 ") and address $%04" PRIx16,
other->name, target->alignMask + 1, other->name, target->alignMask + 1,
target->alignOfs, other->org); target->alignOfs, other->org);
} }
target->isAddressFixed = true; target->isAddressFixed = true;
target->org = other->org; target->org = other->org;
} else if (other->isAlignFixed) { } else if (other->isAlignFixed) {
if (target->isAddressFixed) { if (target->isAddressFixed) {
if ((target->org - other->alignOfs) & other->alignMask) if ((target->org - other->alignOfs) & other->alignMask)
errx(1, "Section \"%s\" is defined with conflicting address $%" PRIx16 " and %" PRIu16 "-byte alignment (offset %" PRIu16 ")", errx(1, "Section \"%s\" is defined with conflicting address $%04"
PRIx16 " and %" PRIu16 "-byte alignment (offset %" PRIu16 ")",
other->name, target->org, other->name, target->org,
other->alignMask + 1, other->alignOfs); other->alignMask + 1, other->alignOfs);
} else if (target->isAlignFixed } else if (target->isAlignFixed
&& (other->alignMask & target->alignOfs) && (other->alignMask & target->alignOfs)
!= (target->alignMask & other->alignOfs)) { != (target->alignMask & other->alignOfs)) {
errx(1, "Section \"%s\" is defined with conflicting %" PRIu16 "-byte alignment (offset %" PRIu16 ") and %" PRIu16 "-byte alignment (offset %" PRIu16 ")", errx(1, "Section \"%s\" is defined with conflicting %" PRIu16
other->name, target->alignMask + 1, "-byte alignment (offset %" PRIu16 ") and %" PRIu16
target->alignOfs, other->alignMask + 1, "-byte alignment (offset %" PRIu16 ")",
other->alignOfs); other->name, target->alignMask + 1, target->alignOfs,
} else if (!target->isAlignFixed other->alignMask + 1, other->alignOfs);
|| (other->alignMask > target->alignMask)) { } else if (!target->isAlignFixed || (other->alignMask > target->alignMask)) {
target->isAlignFixed = true; target->isAlignFixed = true;
target->alignMask = other->alignMask; target->alignMask = other->alignMask;
} }
} }
}
static void checkFragmentCompat(struct Section *target, struct Section *other)
{
if (other->isAddressFixed) {
uint16_t org = other->org - target->size;
if (target->isAddressFixed) {
if (target->org != org)
errx(1, "Section \"%s\" is defined with conflicting addresses $%04"
PRIx16 " and $%04" PRIx16,
other->name, target->org, other->org);
} else if (target->isAlignFixed) {
if ((org - target->alignOfs) & target->alignMask)
errx(1, "Section \"%s\" is defined with conflicting %" PRIu16
"-byte alignment (offset %" PRIu16 ") and address $%04" PRIx16,
other->name, target->alignMask + 1,
target->alignOfs, other->org);
}
target->isAddressFixed = true;
target->org = org;
} else if (other->isAlignFixed) {
int32_t ofs = (other->alignOfs - target->size) % (other->alignMask + 1);
if (ofs < 0)
ofs += other->alignMask + 1;
if (target->isAddressFixed) {
if ((target->org - ofs) & other->alignMask)
errx(1, "Section \"%s\" is defined with conflicting address $%04"
PRIx16 " and %" PRIu16 "-byte alignment (offset %" PRIu16 ")",
other->name, target->org,
other->alignMask + 1, other->alignOfs);
} else if (target->isAlignFixed
&& (other->alignMask & target->alignOfs) != (target->alignMask & ofs)) {
errx(1, "Section \"%s\" is defined with conflicting %" PRIu16
"-byte alignment (offset %" PRIu16 ") and %" PRIu16
"-byte alignment (offset %" PRIu16 ")",
other->name, target->alignMask + 1, target->alignOfs,
other->alignMask + 1, other->alignOfs);
} else if (!target->isAlignFixed || (other->alignMask > target->alignMask)) {
target->isAlignFixed = true;
target->alignMask = other->alignMask;
target->alignOfs = ofs;
}
}
}
static void mergeSections(struct Section *target, struct Section *other, enum SectionModifier mod)
{
// Common checks
if (target->type != other->type)
errx(1, "Section \"%s\" is defined with conflicting types %s and %s",
other->name, typeNames[target->type], typeNames[other->type]);
if (other->isBankFixed) { if (other->isBankFixed) {
if (!target->isBankFixed) { if (!target->isBankFixed) {
target->isBankFixed = true; target->isBankFixed = true;
target->bank = other->bank; target->bank = other->bank;
} else if (target->bank != other->bank) { } else if (target->bank != other->bank) {
errx(1, "Section \"%s\" is defined with conflicting banks %" PRIu32 " and %" PRIu32, errx(1, "Section \"%s\" is defined with conflicting banks %" PRIu32 " and %"
other->name, target->bank, other->bank); PRIu32, other->name, target->bank, other->bank);
} }
} }
switch (mod) { switch (mod) {
case SECTION_UNION: case SECTION_UNION:
checkSectUnionCompat(target, other);
if (other->size > target->size) if (other->size > target->size)
target->size = other->size; target->size = other->size;
break; break;
case SECTION_FRAGMENT: case SECTION_FRAGMENT:
checkFragmentCompat(target, other);
target->size += other->size; target->size += other->size;
other->offset = target->size - other->size; other->offset = target->size - other->size;
if (sect_HasData(target->type)) { if (sect_HasData(target->type)) {
@@ -133,11 +194,7 @@ void sect_AddSection(struct Section *section)
section->name, typeNames[section->type]); section->name, typeNames[section->type]);
} else { } else {
/* If not, add it */ /* If not, add it */
bool collided = hash_AddElement(sections, section->name, hash_AddElement(sections, section->name, section);
section);
if (beVerbose && collided)
warnx("Section hashmap collision occurred!");
} }
} }
@@ -167,41 +224,37 @@ static void doSanityChecks(struct Section *section, void *ptr)
fail("Section \"%s\" has an invalid type.", section->name); fail("Section \"%s\" has an invalid type.", section->name);
if (is32kMode && section->type == SECTTYPE_ROMX) { if (is32kMode && section->type == SECTTYPE_ROMX) {
if (section->isBankFixed && section->bank != 1) if (section->isBankFixed && section->bank != 1)
fail("%s: ROMX sections must be in bank 1 with option -t.", fail("%s: ROMX sections must be in bank 1 (if any) with option -t",
section->name); section->name);
else else
section->type = SECTTYPE_ROM0; section->type = SECTTYPE_ROM0;
} }
if (isWRA0Mode && section->type == SECTTYPE_WRAMX) { if (isWRA0Mode && section->type == SECTTYPE_WRAMX) {
if (section->isBankFixed && section->bank != 1) if (section->isBankFixed && section->bank != 1)
fail("%s: WRAMX sections must be in bank 1 with options -w or -d.", fail("%s: WRAMX sections must be in bank 1 with options -w or -d",
section->name); section->name);
else else
section->type = SECTTYPE_WRAMX; section->type = SECTTYPE_WRAMX;
} }
if (isDmgMode && section->type == SECTTYPE_VRAM && section->bank == 1) if (isDmgMode && section->type == SECTTYPE_VRAM && section->bank == 1)
fail("%s: VRAM bank 1 can't be used with option -d.", fail("%s: VRAM bank 1 can't be used with option -d",
section->name); section->name);
/* /*
* Check if alignment is reasonable, this is important to avoid UB * Check if alignment is reasonable, this is important to avoid UB
* An alignment of zero is equivalent to no alignment, basically * An alignment of zero is equivalent to no alignment, basically
*/ */
if (section->isAlignFixed && section->alignMask == 1) if (section->isAlignFixed && section->alignMask == 0)
section->isAlignFixed = false; section->isAlignFixed = false;
/* Too large an alignment may not be satisfiable */ /* Too large an alignment may not be satisfiable */
if (section->isAlignFixed if (section->isAlignFixed && (section->alignMask & startaddr[section->type]))
&& (section->alignMask & startaddr[section->type])) fail("%s: %s sections cannot be aligned to $%04" PRIx16 " bytes",
fail("%s: %s sections cannot be aligned to $%" PRIx16 " bytes", section->name, typeNames[section->type], section->alignMask + 1);
section->name, typeNames[section->type],
section->alignMask + 1);
uint32_t minbank = bankranges[section->type][0], uint32_t minbank = bankranges[section->type][0], maxbank = bankranges[section->type][1];
maxbank = bankranges[section->type][1];
if (section->isBankFixed && section->bank < minbank if (section->isBankFixed && section->bank < minbank && section->bank > maxbank)
&& section->bank > maxbank)
fail(minbank == maxbank fail(minbank == maxbank
? "Cannot place section \"%s\" in bank %" PRIu32 ", it must be %" PRIu32 ? "Cannot place section \"%s\" in bank %" PRIu32 ", it must be %" PRIu32
: "Cannot place section \"%s\" in bank %" PRIu32 ", it must be between %" PRIu32 " and %" PRIu32, : "Cannot place section \"%s\" in bank %" PRIu32 ", it must be between %" PRIu32 " and %" PRIu32,
@@ -219,35 +272,26 @@ static void doSanityChecks(struct Section *section, void *ptr)
section->isBankFixed = true; section->isBankFixed = true;
} }
if (section->isAlignFixed) {
enum SectionType type = section->type;
/* It doesn't make sense to have both org and alignment set */
if (section->isAddressFixed) { if (section->isAddressFixed) {
if (section->org & section->alignMask) /* It doesn't make sense to have both org and alignment set */
if (section->isAlignFixed) {
if ((section->org & section->alignMask) != section->alignOfs)
fail("Section \"%s\"'s fixed address doesn't match its alignment", fail("Section \"%s\"'s fixed address doesn't match its alignment",
section->name); section->name);
section->isAlignFixed = false; section->isAlignFixed = false;
} else if ((endaddr(type) & section->alignMask)
== startaddr[type]) {
section->org = startaddr[type];
section->isAlignFixed = false;
section->isAddressFixed = true;
}
} }
if (section->isAddressFixed) {
/* Ensure the target address is valid */ /* Ensure the target address is valid */
if (section->org < startaddr[section->type] if (section->org < startaddr[section->type]
|| section->org > endaddr(section->type)) || section->org > endaddr(section->type))
fail("Section \"%s\"'s fixed address %#" PRIx16 " is outside of range [%#" PRIx16 "; %#" PRIx16 "]", fail("Section \"%s\"'s fixed address %#" PRIx16 " is outside of range [%#"
section->name, section->org, PRIx16 "; %#" PRIx16 "]", section->name, section->org,
startaddr[section->type], endaddr(section->type)); startaddr[section->type], endaddr(section->type));
if (section->org + section->size > endaddr(section->type) + 1) if (section->org + section->size > endaddr(section->type) + 1)
fail("Section \"%s\"'s end address %#" PRIx16 " is greater than last address %#" PRIx16, fail("Section \"%s\"'s end address %#" PRIx16
section->name, section->org + section->size, " is greater than last address %#" PRIx16, section->name,
endaddr(section->type) + 1); section->org + section->size, endaddr(section->type) + 1);
} }
#undef fail #undef fail

View File

@@ -13,11 +13,11 @@ uint16_t startaddr[] = {
}; };
uint16_t maxsize[] = { uint16_t maxsize[] = {
[SECTTYPE_ROM0] = 0x8000, [SECTTYPE_ROM0] = 0x8000, // patched to 0x4000 if !is32kMode
[SECTTYPE_ROMX] = 0x4000, [SECTTYPE_ROMX] = 0x4000,
[SECTTYPE_VRAM] = 0x2000, [SECTTYPE_VRAM] = 0x2000,
[SECTTYPE_SRAM] = 0x2000, [SECTTYPE_SRAM] = 0x2000,
[SECTTYPE_WRAM0] = 0x2000, [SECTTYPE_WRAM0] = 0x2000, // patched to 0x1000 if !isWRA0Mode
[SECTTYPE_WRAMX] = 0x1000, [SECTTYPE_WRAMX] = 0x1000,
[SECTTYPE_OAM] = 0x00A0, [SECTTYPE_OAM] = 0x00A0,
[SECTTYPE_HRAM] = 0x007F [SECTTYPE_HRAM] = 0x007F

88
src/opmath.c Normal file
View File

@@ -0,0 +1,88 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 1997-2021, RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
/*
* Mathematical operators that don't reuse C's behavior
*/
#include <stdint.h>
#include "opmath.h"
int32_t op_divide(int32_t dividend, int32_t divisor)
{
// Adjust division to floor toward negative infinity,
// not truncate toward zero
return dividend / divisor - ((dividend % divisor < 0) != (divisor < 0));
}
int32_t op_modulo(int32_t dividend, int32_t divisor)
{
int32_t remainder = dividend % divisor;
// Adjust modulo to have the sign of the divisor,
// not the sign of the dividend
return remainder + divisor * ((remainder < 0) != (divisor < 0));
}
int32_t op_exponent(int32_t base, uint32_t power)
{
int32_t result = 1;
for (;;) {
if (power % 2)
result *= base;
power /= 2;
if (!power)
break;
base *= base;
}
return result;
}
int32_t op_shift_left(int32_t value, int32_t amount)
{
// Get the easy cases out of the way
if (amount == 0)
return value;
if (value == 0 || amount >= 32)
return 0;
if (amount < -31)
return (value < 0) ? -1 : 0;
if (amount < 0)
return op_shift_right(value, -amount);
// Use unsigned to force a bitwise shift
// Casting back is OK because the types implement two's complement behavior
return (uint32_t)value << amount;
}
int32_t op_shift_right(int32_t value, int32_t amount)
{
// Repeat the easy cases here to avoid INT_MIN funny business
if (amount == 0)
return value;
if (value == 0 || amount <= -32)
return 0;
if (amount > 31)
return (value < 0) ? -1 : 0;
if (amount < 0)
return op_shift_left(value, -amount);
if (value > 0)
return (uint32_t)value >> amount;
// Calculate an OR mask for sign extension
// 1->0x80000000, 2->0xC0000000, ..., 31->0xFFFFFFFE
uint32_t amount_high_bits = -(UINT32_C(1) << (32 - amount));
// The C standard leaves shifting right negative values
// undefined, so use a left shift manually sign-extended
return ((uint32_t)value >> amount) | amount_high_bits;
}

View File

@@ -1,11 +1,11 @@
.\" .\"
.\" This file is part of RGBDS. .\" This file is part of RGBDS.
.\" .\"
.\" Copyright (c) 2017-2020, Antonio Nino Diaz and RGBDS contributors. .\" Copyright (c) 2017-2021, Antonio Nino Diaz and RGBDS contributors.
.\" .\"
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd January 26, 2018 .Dd March 28, 2021
.Dt RGBDS 5 .Dt RGBDS 5
.Os .Os
.Sh NAME .Sh NAME
@@ -22,11 +22,11 @@ This toolchain is in development and new features may require adding more inform
The following types are used: The following types are used:
.Pp .Pp
.Ar LONG .Ar LONG
is a 32bit integer stored in littleendian format. is a 32-bit integer stored in little-endian format.
.Ar BYTE .Ar BYTE
is an 8bit integer. is an 8-bit integer.
.Ar STRING .Ar STRING
is a 0terminated string of is a 0-terminated string of
.Ar BYTE . .Ar BYTE .
.Bd -literal .Bd -literal
; Header ; Header
@@ -41,7 +41,7 @@ LONG NumberOfSections ; The number of sections used in this file.
LONG NumberOfNodes ; The number of nodes contained in this file. LONG NumberOfNodes ; The number of nodes contained in this file.
REPT NumberOfNodes ; IMPORTANT NOTE: the nodes are actually written in REPT NumberOfNodes ; IMPORTANT NOTE: the nodes are actually written in
; **reverse** order, meaningthe node with ID 0 is ; **reverse** order, meaning the node with ID 0 is
; the last one in the file! ; the last one in the file!
LONG ParentID ; ID of the parent node, -1 means this is the root. LONG ParentID ; ID of the parent node, -1 means this is the root.
@@ -225,6 +225,7 @@ with some bytes being special prefixes for integers and symbols.
.It Li $03 Ta Li / operator .It Li $03 Ta Li / operator
.It Li $04 Ta Li % operator .It Li $04 Ta Li % operator
.It Li $05 Ta Li unary - .It Li $05 Ta Li unary -
.It Li $06 Ta Li ** operator
.It Li $10 Ta Li | operator .It Li $10 Ta Li | operator
.It Li $11 Ta Li & operator .It Li $11 Ta Li & operator
.It Li $12 Ta Li ^ operator .It Li $12 Ta Li ^ operator

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-2021, Anthony J. Bentley and RGBDS contributors.
.\" .\"
.\" SPDX-License-Identifier: MIT .\" SPDX-License-Identifier: MIT
.\" .\"
.Dd March 7, 2018 .Dd March 28, 2021
.Dt RGBDS 7 .Dt RGBDS 7
.Os .Os
.Sh NAME .Sh NAME

View File

@@ -9,17 +9,24 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "helpers.h"
#include "version.h" #include "version.h"
const char *get_package_version_string(void) const char *get_package_version_string(void)
{ {
static char s[50]; // The following conditional should be simplified by the compiler.
/* The following conditional should be simplified by the compiler. */
if (strlen(BUILD_VERSION_STRING) == 0) { if (strlen(BUILD_VERSION_STRING) == 0) {
snprintf(s, sizeof(s), "v%d.%d.%d", PACKAGE_VERSION_MAJOR, // Fallback if version string can't be obtained from Git
PACKAGE_VERSION_MINOR, PACKAGE_VERSION_PATCH); #ifndef PACKAGE_VERSION_RC
return s; return "v" EXPAND_AND_STR(PACKAGE_VERSION_MAJOR)
"." EXPAND_AND_STR(PACKAGE_VERSION_MINOR)
"." EXPAND_AND_STR(PACKAGE_VERSION_PATCH);
#else
return "v" EXPAND_AND_STR(PACKAGE_VERSION_MAJOR)
"." EXPAND_AND_STR(PACKAGE_VERSION_MINOR)
"." EXPAND_AND_STR(PACKAGE_VERSION_PATCH)
"-rc" EXPAND_AND_STR(PACKAGE_VERSION_RC);
#endif
} else { } else {
return BUILD_VERSION_STRING; return BUILD_VERSION_STRING;
} }

4
test/asm/.gitignore vendored
View File

@@ -1 +1,3 @@
quote\"file.* /quote\"file.*
/version.asm
/version.out

8
test/asm/align-16.asm Normal file
View File

@@ -0,0 +1,8 @@
SECTION "Byte", ROM0
db 2
SECTION "ROM0", ROM0, ALIGN[16]
db 1

View File

@@ -0,0 +1 @@


View File

@@ -0,0 +1,2 @@
SECTION "Tesst", ROM0, ALIGN[1,2]

View File

@@ -0,0 +1,3 @@
ERROR: align-large-ofs.asm(2):
Alignment offset (2) must be smaller than alignment size (2)
error: Assembly aborted (1 error)!

View File

2
test/asm/align-large.asm Normal file
View File

@@ -0,0 +1,2 @@
SECTION "You lost the game", ROM0[17]

0
test/asm/align-large.err Normal file
View File

0
test/asm/align-large.out Normal file
View File

View File

@@ -0,0 +1,18 @@
: ; Outside of section
SECTION "Anonymous label errors test", ROM0
db :-- ; Reference goes too far back
; Uncomment this if you're a badass with a *lot* of RAM
; REPT 2147483647
; :
; ENDR
; REPT 2147483647
; :
; ENDR
; db :+ ; OK
; db :++ ; Reference goes too far
:: ; Syntax error, can't export this

View File

@@ -0,0 +1,7 @@
ERROR: anon-label-bad.asm(2):
Label "!0" created outside of a SECTION
ERROR: anon-label-bad.asm(6):
Reference to anonymous label 2 before, when only 1 has been created so far
ERROR: anon-label-bad.asm(18):
syntax error, unexpected :
error: Assembly aborted (3 errors)!

View File

View File

@@ -0,0 +1,7 @@
ERROR: anon-label-bad.asm(2):
Label "!0" created outside of a SECTION
ERROR: anon-label-bad.asm(6):
Reference to anonymous label 2 before, when only 1 has been created so far
ERROR: anon-label-bad.asm(18):
syntax error
error: Assembly aborted (3 errors)!

12
test/asm/anon-label.asm Normal file
View File

@@ -0,0 +1,12 @@
SECTION "Anonymous label test", ROM0[0]
ld hl, :++
: ld a, [hli]
ldh [c], a
dec c
jr nz, :-
ret
:
dw $7FFF, $1061, $03E0, $58A5

0
test/asm/anon-label.err Normal file
View File

0
test/asm/anon-label.out Normal file
View File

BIN
test/asm/anon-label.out.bin Normal file

Binary file not shown.

View File

@@ -1,24 +1,23 @@
print_all: MACRO print_all: MACRO
REPT _NARG REPT _NARG
PRINTT " \1" PRINT " \1"
SHIFT SHIFT
ENDR ENDR
PRINTT "\n" PRINTLN
ENDM ENDM
print_some: MACRO print_some: MACRO
PRINTT "\1" PRINT "\1"
SHIFT 5 SHIFT 5
PRINTT "\2\6\9" PRINT "\2\6\9"
SHIFT 17 SHIFT 17
SHIFT SHIFT
PRINTT "\3\9" PRINT "\3\9"
ENDM ENDM
bad: MACRO bad: MACRO
shift _NARG - 1 shift _NARG - 1
PRINTT \1 PRINTLN \1
PRINTT "\n"
ENDM ENDM
bad_rept: MACRO bad_rept: MACRO
@@ -27,8 +26,7 @@ bad_rept: MACRO
shift shift
ENDR ENDR
ENDR ENDR
PRINTT \1 PRINTLN \1
PRINTT "\n"
ENDM ENDM
print_all This test, probably, passes\,, but who knows, ? print_all This test, probably, passes\,, but who knows, ?

View File

@@ -1,3 +1,3 @@
ERROR: assert-nosect-bank.asm(1): ERROR: assert-nosect-bank.asm(1):
PC has no bank outside a section PC has no bank outside a section
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: assert@-no-sect.asm(1): ERROR: assert@-no-sect.asm(1):
PC has no value outside a section PC has no value outside a section
error: Assembly aborted (1 errors)! error: Assembly aborted (1 error)!

View File

@@ -5,8 +5,7 @@ def_sect: macro
SECTION "\1", \2, BANK[\3] SECTION "\1", \2, BANK[\3]
ENDC ENDC
PRINTV BANK("\1") PRINTLN BANK("\1")
PRINTT "\n"
endm endm
def_sect ROM0_ok, ROM0 def_sect ROM0_ok, ROM0

View File

@@ -1,9 +1,9 @@
ERROR: bank.asm(14) -> bank.asm::def_sect(8): ERROR: bank.asm(13) -> bank.asm::def_sect(8):
Expected constant expression: Section "ROMX_bad"'s bank is not known Expected constant expression: Section "ROMX_bad"'s bank is not known
ERROR: bank.asm(16) -> bank.asm::def_sect(8): ERROR: bank.asm(15) -> bank.asm::def_sect(8):
Expected constant expression: Section "VRAM_bad"'s bank is not known Expected constant expression: Section "VRAM_bad"'s bank is not known
ERROR: bank.asm(18) -> bank.asm::def_sect(8): ERROR: bank.asm(17) -> bank.asm::def_sect(8):
Expected constant expression: Section "SRAM_bad"'s bank is not known Expected constant expression: Section "SRAM_bad"'s bank is not known
ERROR: bank.asm(21) -> bank.asm::def_sect(8): ERROR: bank.asm(20) -> bank.asm::def_sect(8):
Expected constant expression: Section "WRAMX_bad"'s bank is not known Expected constant expression: Section "WRAMX_bad"'s bank is not known
error: Assembly aborted (4 errors)! error: Assembly aborted (4 errors)!

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