Compare commits

...

80 Commits

Author SHA1 Message Date
ISSOtm
432c769d60 Release v0.5.0 2021-04-17 22:53:00 +02:00
Rangi
9923fa3eee Fix expansions that start from the end of another expansion (#839)
Do not free an expansion until its offset is *past* its size.
This means potentially freeing a nested stack of expansions
all at once.

Fixes #696
2021-04-17 13:14:40 -04:00
Rangi
750e93be3d Further simplify formatting code
- Remove redundant length checks before `memcpy`
- Coerce `sign` and `prefix` to boolean for `numLen`
2021-04-17 01:11:11 -04:00
Rangi
ee5da4468d Fix interpolation/STRFMT overflow issues (#838)
Widths and fractional widths greater than 255 would overflow a
uint8_t and wrap around to smaller values.

Total formatted lengths greater than the avilable buffer size
would overflow it and potentially corrupt memory.

Fixes #830
Closes #831
2021-04-17 00:52:55 -04:00
Rangi
503c3b5364 Revert "Fix interpolation/STRFMT overflow issues"
This reverts commit 992be3fd9b.
2021-04-16 22:19:37 -04:00
Rangi
992be3fd9b Fix interpolation/STRFMT overflow issues
Widths and fractional widths greater than 255 would overflow a
uint8_t and wrap around to smaller values.

Total formatted lengths greater than the avilable buffer size
would overflow it and potentially corrupt memory.

Fixes #830
Closes #831
2021-04-16 22:00:17 -04:00
Rangi
c755fa3469 readIdentifier does not process characters that get truncated
Previously a '.' could be past the truncation limit but still
cause the identifier to be marked as local, violating an
assertion in `sym_AddLocalLabel`.

Fixes #832
2021-04-16 21:15:01 -04:00
Rangi
e78a1d5bfd readInterpolation is limited by nMaxRecursionDepth
Fixes #837
2021-04-16 16:10:46 -04:00
Rangi
d2f6def2eb Remove unused function hash_ReplaceElement 2021-04-16 12:36:45 -04:00
Jakub Kądziołka
1ffd7cb5bb Make tests work on NixOS
Some distributions, such as NixOS and Guix, only have the /bin/sh and
/usr/bin/env binaries in standard locations.
2021-04-16 16:38:04 +02:00
Jakub Kądziołka
215e26b478 charmap: Store hashmap nodes in charmap stack
This helps update all the pointers during reallocation.
2021-04-16 16:00:26 +02:00
Jakub Kądziołka
8885f7bcf6 hash_AddElement: return the new node 2021-04-16 16:00:26 +02:00
Jakub Kądziołka
5334fc334e Don't report hashmap collisions
This doesn't seem to be very useful, and keeping this "feature" is
difficult.
2021-04-16 16:00:26 +02:00
Jakub Kądziołka
f97663aa37 hashmap: add hash_GetNode 2021-04-16 16:00:26 +02:00
Jakub Kądziołka
08bdbd1949 Add an .editorconfig
This automatically sets up the formatting settings in a number of
editors, regardless of the user's preferences.
2021-04-16 15:56:57 +02:00
Rangi
5c852c7651 Store the nested expansions starting from the deepest one (#829)
This shortens the lexer by 100 lines and simplifies
access to expansion contents, since it usually needs the
deepest one, not the top-level one.

Fixes #813
2021-04-16 09:54:13 -04:00
Rangi
6be3584467 LexerState's 'size' and 'offset' for mmapped files are unsigned
These were using signed 'off_t' because that is the type of
'st_size' from 'stat()', but neither one can be negative.
2021-04-16 10:23:37 +02:00
Rangi
8c90d9d2d7 Get rid of skip in struct Expansion
This was only used to skip the two macro arg characters,
but shiftChar() can skip them before the expansion.
2021-04-16 10:23:37 +02:00
Rangi
f69e666b00 expansionOfs cannot be negative
lexerState->expansionOfs is always either set to 0, or updated by
adding a positive quantity:

    if (distance > lexerState->expansions->distance) {
        lexerState->expansionOfs += distance - lexerState->expansions->distance;
        ...
    }

so it will always be positive or zero.
2021-04-16 10:23:37 +02:00
Rangi
eba06404f0 peek(0) => peek()
This does not completely refactor `peek` as #708 suggested,
to make it shift and cache a character itself. However it
does simplify the lexer code.
2021-04-16 10:23:37 +02:00
Rangi
9558ccea1b shiftChars(1) => shiftChar()
Only two sites were for distances greater than 1:
a `shiftChars(2)`, trivial to just do two `shiftChar()`s;
and `shiftChars(size)` in `reportGarbageChar`, which
can be a `for` loop, and should be fixed anyway to
"avoid having to peek further than 0".
2021-04-16 10:23:37 +02:00
Rangi
260d372acd Lex $ff00+c without needing large peek lookahead
This also allows arbitrary amounts of whitespace in `$ff00 + c`,
instead of needing to fit in the 42-byte LEXER_BUF_SIZE
2021-04-16 10:23:37 +02:00
Rangi
4e1b0ce793 Rephrase CONTRIBUTING.rst
Fix #729
2021-04-14 21:19:54 -04:00
Rangi
363ee9578c Document the release process in RELEASE.rst
Fix #718
2021-04-14 21:13:37 -04:00
Rangi
8fa5a4255e Mention alternative mnemonics in gbz80(7)
Fixes #819
2021-04-14 16:59:31 -04:00
Rangi
b3312886fb Use a lookupExpansion, but not as an X macro
Instead of defining `LOOKUP_PRE_NEST` and `LOOKUP_POST_NEST`,
pass a variable name and a block to `lookupExpansion`; it
will assign successive looked-up expansions to the variable
and use them in the block.

The technique of using `__VA_ARGS__` to allow commas within a
block passed to a macro is not original, and should be stable.
2021-04-13 17:58:46 +02:00
Rangi
7fc8a65d0a Refactor the lexer to not use the lookupExpansion X macro
This macro was only used twice, in `beginExpansion` and
`lexer_DumpStringExpansions`, with `getExpansionAtDistance`
already containing an inlined and slightly modified version
of `lookupExpansion` (retaining the `LOOKUP_PRE_NEST` and
`LOOKUP_POST_NEST` macros, but with both of them doing nothing).

Not using an X macro here makes the actual control flow in both
places more obvious, and I think the repeated code is acceptable
for the same reasons as the similar-but-distinct implementations
of `readString`, `appendStringLiteral`, `yylex_NORMAL`, and
`yylex_RAW`.
2021-04-13 17:58:46 +02:00
Rangi
c278a361da Remove the unused calchash djb2 hash function
Note that hashmap.c uses its own FNV-1a hash function
2021-04-13 17:41:12 +02:00
Rangi
a2f52867ad Rename print to printChar
This clarifies its usage, for printing a single character
in error messages.
2021-04-13 17:41:12 +02:00
Rangi
ab79e6bede Change how print(c) formats reported characters
Printable ASCII becomes single-quoted, using backslash
escapes if necessary. Unprintable characters use 0xNN
formatting, without quotes.
2021-04-13 17:41:12 +02:00
Rangi
850c78aaf4 Report garbage chars as their bytes; don't try decoding them as UTF-8
This decoding required high lookahead, and was not even
consistently useful (the `garbage_char` test case was not
valid UTF-8 and so did not benefit from `reportGarbageChar`).

This limits UTF-8 handling to the `STRLEN` and `STRSUB`
built-in functions, and to charmap conversion.
2021-04-13 17:41:12 +02:00
Rangi
c08cf783c8 Remove 'inline' from functions not in headers 2021-04-13 10:27:08 -04:00
Rangi
25a8518fbf Remove unused function removeLeadingZeros 2021-04-13 10:12:40 -04:00
Rangi
49174f4486 Define the UTC time components as EQU, not EQUS
Fixes #827
2021-04-13 10:11:21 -04:00
Rangi
81327b0d99 Merge branch 'master' of https://github.com/gbdev/rgbds 2021-04-13 09:31:35 -04:00
Rangi
c0859e64f7 fstk_FindFile checks for sprintf failure 2021-04-13 09:31:05 -04:00
Rangi
3e0b7d428f Fix an unclosed file and unfreed memory in out_BinaryFileSlice
Use 'goto cleanup' in both out_BinaryFileSlice and out_BinaryFile
2021-04-13 09:19:59 -04:00
ISSOtm
ba3428314b Add contact info for potential contributors
I'm going on a break, but I'll stay available if anyone wants to contribute.
2021-04-13 00:27:49 +02:00
Rangi
bcb78f5d18 Define __RGBDS_VERSION__ as the output of rgbasm --version (sans "rgbasm")
Fixes #824
2021-04-09 19:42:48 +02:00
ISSOtm
de7d1facf3 Add assertion that an expansion's total len doesn't overflow
Typically not needed because the recursion depth limit should prevent it,
but it might help debug weird lexer issues.
2021-04-03 18:31:30 +02:00
Rangi
310d34c655 Comment on REG_SP and REG_AF both being 3
No instruction needs to distinguish them both
2021-03-31 18:41:42 -04:00
Rangi
39c38f9838 Add a test case for unattainable SECTION UNION alignment
This test cases used to fail an assertion in `make develop`
2021-03-31 16:09:52 -04:00
Rangi
576b063519 Fix unattainable alignments to address 0
Fixes #818
2021-03-31 16:06:15 -04:00
Rangi
596e17ee61 Factor out a common strlen into beginExpansion
This avoids the possibility of `size` not matching `str`
2021-03-31 14:41:38 -04:00
Rangi
363b3d0134 Flush stdout after PRINTLN
This allows debug PRINTLN statements to run even if
subsequent rgbasm directives cause a crash.
2021-03-31 11:22:41 -04:00
Rangi
c7ed9a275e Do not expand empty strings
Fixes #813
2021-03-31 10:21:04 -04:00
Rangi
49aac2961d Warn about backwards FOR loops with -Wbackwards-for (-Wall)
Fixes #816
2021-03-31 10:00:21 -04:00
Rangi
3741bd4617 Speed up the div-mod test case
Test various explicit cases, not nested `for` loops
over thousands of cases
2021-03-31 09:37:23 -04:00
Jakub Kądziołka
937c9888a4 Robustly disallow overwriting builtin symbols (#817) 2021-03-31 09:03:57 -04:00
Rangi
61a9bfd33c Some tests use the new macro <name> syntax
This happens to make quine.asm shorter
2021-03-31 00:00:18 -04:00
Jakub Kądziołka
d08bcc455d Handle errors when opening source file
Before this commit, opening a file for which the user didn't have
permission resulted in a "Bad file descriptor" error.
2021-03-30 23:35:50 +02:00
Rangi
aaa92659ea Require a plus sign in ld hl, sp + <e8>
Fixes #810
2021-03-30 13:05:21 -04:00
Rangi
be877134e5 Remove support for ld bc/de/hl/sp for ld hl, bc/de/hl/sp
Fixes #811
2021-03-30 13:01:49 -04:00
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
152 changed files with 1635 additions and 1064 deletions

9
.editorconfig Normal file
View File

@@ -0,0 +1,9 @@
[*]
root = true
indent_style = tab
indent_size = tab
tab_width = 8
charset = utf-8
insert_final_newline = true
trim_trailing_whitespace = true
end_of_line = lf

View File

@@ -53,8 +53,8 @@ PAGES=(
WWWPATH="/docs"
mkdir -p "$1/_documentation/$2"
# `mandoc` uses a different format for referring to man pages present in the **current** directory
# We want that format for RGBDS man pages, and the other one for the t=rest;
# `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 rest;
# we thus need to copy all pages to a temporary directory, and process them there.
# Copy all pages to current dir
@@ -77,6 +77,7 @@ EOF
options+=,toc
fi
mandoc -Thtml -I os=Linux -O$options "${PAGES[$page]##*/}" | .github/actions/doc_postproc.awk >> "$1/_documentation/$2/$page"
groff -Tpdf -mdoc -wall "${PAGES[$page]##*/}" >"$1/_documentation/$2/$stem.pdf"
if [ $is_release -ne 0 ]; then
cat - >"$1/_documentation/$page" <<EOF
---
@@ -88,6 +89,7 @@ description: RGBDS latest stable — $descr
EOF
fi
done
cat - >"$1/_documentation/$2/index.html" <<EOF
---
layout: doc_index
@@ -97,6 +99,7 @@ description: RGBDS $2 - Online manual
---
EOF
# If making a release, add a new entry right after `master`
if [ $is_release -ne 0 ]; then
awk '{ print }
@@ -105,5 +108,6 @@ if [ $is_release -ne 0 ]; then
mv "$1/_data/doc.json"{.tmp,}
fi
# Clean up
rm "${PAGES[@]##*/}"

View File

@@ -18,10 +18,10 @@ jobs:
repository: ${{ github.repository_owner }}/rgbds-www
path: rgbds-www
# `-O toc` was added in 1.14.5, but the repos only have 1.14.4
- name: Build and install mandoc
- name: Build and install mandoc + install groff
run: |
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'
tar xf mandoc-1.14.5.tar.gz
cd mandoc-1.14.5

View File

@@ -31,10 +31,10 @@ jobs:
repository: gbdev/rgbds-www
ref: master
path: rgbds-www
- name: Build and install mandoc
- name: Build and install mandoc + install groff
run: |
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'
tar xf mandoc-1.14.5.tar.gz
cd mandoc-1.14.5
@@ -44,7 +44,7 @@ jobs:
- name: Update pages
working-directory: rgbds
run: |
./.github/actions/get-pages.sh ../rgbds-www/_documentation master
./.github/actions/get-pages.sh ../rgbds-www master
- name: Push new pages
working-directory: rgbds-www
run: |

View File

@@ -1,10 +1,9 @@
Contributing
============
RGBDS was created in the late 90's and has received contributions from several
RGBDS was created in the late '90s and has received contributions from several
developers since then. It wouldn't have been possible to get to this point
without their work and, for that reason, it is always open to the contributions
of other people.
without their work, and it is always open to the contributions of other people.
Reporting Bugs
--------------

View File

@@ -18,6 +18,8 @@ The documentation of this toolchain can be viewed online
`here <https://rgbds.gbdev.io/docs/>`__, it is generated from the man pages
found in this repository.
If you want to contribute or maintain RGBDS, and have questions regarding the code, its organisation, etc. you can find me `on GBDev <https://gbdev.io/chat>`__ or via mail at ``rgbds at eldred dot fr``.
1. Installing RGBDS
-------------------

65
RELEASE.rst Normal file
View File

@@ -0,0 +1,65 @@
Releasing
=========
This describes for the maintainers of RGBDS how to publish a new release on
GitHub.
1. Update, commit, and push `include/version.h <include/version.h>`__ with
values for ``PACKAGE_VERSION_MAJOR``, ``PACKAGE_VERSION_MINOR``,
``PACKAGE_VERSION_PATCH``, and ``PACKAGE_VERSION_RC``. Only define
``PACKAGE_VERSION_RC`` if you are publishing a release candidate! You can
use ``git commit -m "Release <version>"`` and ``git push origin master``.
2. Create a Git tag formatted as ``v<MAJOR>.<MINOR>.<PATCH>``, or
``v<MAJOR>.<MINOR>.<PATCH>-rc<RC>`` for a release candidate. ``MAJOR``,
``MINOR``, ``PATCH``, and ``RC`` should match their values from
`include/version.h <include/version.h>`__. You can use ``git tag <tag>``.
3. Push the tag to GitHub. You can use ``git push origin <tag>``.
GitHub Actions will run the `create-release-artifacts.yaml
<.github/workflows/create-release-artifacts.yaml>`__ workflow to detect the
tag starting with "``v[0-9]``" and automatically do the following:
1. Build 32-bit and 64-bit RGBDS binaries for Windows with ``cmake``.
2. Package the binaries into zip files.
3. Package the source code into a tar.gz file with ``make dist``.
4. Create a draft GitHub release for the tag, attaching the three
packaged files. It will be a prerelease if the tag contains "``-rc``".
If an error occurred in the above steps, delete the tag and restart the
procedure. You can use ``git push --delete origin <tag>`` and
``git tag --delete <tag>``.
4. GitHub Actions will run the `create-release-docs.yml
<.github/workflows/create-release-docs.yml>`__ workflow to add the release
documentation to `rgbds-www <https://github.com/gbdev/rgbds-www>`__.
For a release candidate, which creates a prerelease, you will have to
take these steps yourself.
1. Clone `rgbds-www <https://github.com/gbdev/rgbds-www>`__. You can use
``git clone https://github.com/gbdev/rgbds-www.git``.
2. Make sure that you have installed ``groff`` and ``mandoc``. You will
need ``mandoc`` 1.14.5 or later to support ``-O toc``.
3. Run ``.github/actions/get-pages.sh -r <path/to/rgbds-www> <tag>``. This
will render the RGBDS documentation as HTML and PDF and copy it to
``rgbds-www``.
If you do not have ``groff`` installed, you can change
``groff -Tpdf -mdoc -wall`` to ``mandoc -Tpdf -I os=Linux`` in
`.github/actions/get-pages.sh <.github/actions/get-pages.sh>`__ and it
will suffice.
4. Commit and push the documentation. You can use ``git commit -m
"Create RGBDS <tag> documentation"`` and ``git push origin master``
(within the ``rgbds-www`` directory, not RGBDS).
5. Write a changelog in the GitHub draft release.
6. Click the "Publish release" button to publish it!

View File

@@ -28,9 +28,9 @@ struct FormatSpec {
bool prefix;
bool alignLeft;
bool padZero;
uint8_t width;
size_t width;
bool hasFrac;
uint8_t fracWidth;
size_t fracWidth;
int type;
bool valid;
};

View File

@@ -75,7 +75,7 @@ static inline bool sym_IsConstant(struct Symbol const *sym)
if (sym->type == SYM_LABEL) {
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;
}

View File

@@ -11,8 +11,8 @@
#include <stdint.h>
uint32_t calchash(const char *s);
char const *print(int c);
char const *printChar(int c);
/*
* @return The number of bytes read, or 0 if invalid data was found
*/

View File

@@ -15,6 +15,7 @@ extern unsigned int nbErrors;
enum WarningID {
WARNING_ASSERT, /* Assertions */
WARNING_BACKWARDS_FOR, /* `for` loop with backwards range */
WARNING_BUILTIN_ARG, /* Invalid args to builtins */
WARNING_CHARMAP_REDEF, /* Charmap entry re-definition */
WARNING_DIV, /* Division undefined behavior */

View File

@@ -29,19 +29,9 @@ typedef struct HashMapEntry *HashMap[HASHMAP_NB_BUCKETS];
* @param map The HashMap to add the element to
* @param key The key with which the element will be stored and retrieved
* @param element The element to add
* @return True if a collision occurred (for statistics)
* @return A pointer to the pointer to the element.
*/
bool hash_AddElement(HashMap map, char const *key, void *element);
/**
* Replaces an element with an already-present key in a hashmap.
* @warning Inserting a NULL will make `hash_GetElement`'s return ambiguous!
* @param map The HashMap to replace the element in
* @param key The key with which the element will be stored and retrieved
* @param element The element to replace
* @return True if the element was found and replaced
*/
bool hash_ReplaceElement(HashMap const map, char const *key, void *element);
void **hash_AddElement(HashMap map, char const *key, void *element);
/**
* Removes an element from a hashmap.
@@ -51,6 +41,14 @@ bool hash_ReplaceElement(HashMap const map, char const *key, void *element);
*/
bool hash_RemoveElement(HashMap map, char const *key);
/**
* Finds an element in a hashmap, and returns a pointer to its value field.
* @param map The map to consider the elements of
* @param key The key to search an element for
* @return A pointer to the pointer to the element, or NULL if not found.
*/
void **hash_GetNode(HashMap const map, char const *key);
/**
* Finds an element in a hashmap.
* @param map The map to consider the elements of

View File

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

View File

@@ -1,7 +1,7 @@
/*
* 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
*/
@@ -12,7 +12,6 @@
#define PACKAGE_VERSION_MAJOR 0
#define PACKAGE_VERSION_MINOR 5
#define PACKAGE_VERSION_PATCH 0
#define PACKAGE_VERSION_RC 1
const char *get_package_version_string(void);

View File

@@ -44,32 +44,35 @@ struct Charmap {
static HashMap charmaps;
static struct Charmap *currentCharmap;
/*
* Store pointers to hashmap nodes, so that there is only one pointer to the memory block
* that gets reallocated.
*/
static struct Charmap **currentCharmap;
struct CharmapStackEntry {
struct Charmap *charmap;
struct Charmap **charmap;
struct CharmapStackEntry *next;
};
struct CharmapStackEntry *charmapStack;
static inline struct Charmap *charmap_Get(const char *name)
static struct Charmap *charmap_Get(const char *name)
{
return hash_GetElement(charmaps, name);
}
static inline struct Charmap *resizeCharmap(struct Charmap *map, size_t capacity)
static void resizeCharmap(struct Charmap **map, size_t capacity)
{
struct Charmap *new = realloc(map, sizeof(*map) + sizeof(*map->nodes) * capacity);
*map = realloc(*map, sizeof(**map) + sizeof(*(*map)->nodes) * capacity);
if (!new)
if (!*map)
fatalerror("Failed to %s charmap: %s\n",
map ? "create" : "resize", strerror(errno));
new->capacity = capacity;
return new;
*map ? "create" : "resize", strerror(errno));
(**map).capacity = capacity;
}
static inline void initNode(struct Charnode *node)
static void initNode(struct Charnode *node)
{
node->isTerminal = false;
memset(node->next, 0, sizeof(node->next));
@@ -95,19 +98,18 @@ struct Charmap *charmap_New(const char *name, const char *baseName)
/* Init the new charmap's fields */
if (base) {
charmap = resizeCharmap(NULL, base->capacity);
resizeCharmap(&charmap, base->capacity);
charmap->usedNodes = base->usedNodes;
memcpy(charmap->nodes, base->nodes, sizeof(base->nodes[0]) * charmap->usedNodes);
} else {
charmap = resizeCharmap(NULL, INITIAL_CAPACITY);
resizeCharmap(&charmap, INITIAL_CAPACITY);
charmap->usedNodes = 1;
initNode(&charmap->nodes[0]); /* Init the root node */
}
charmap->name = strdup(name);
hash_AddElement(charmaps, charmap->name, charmap);
currentCharmap = charmap;
currentCharmap = (struct Charmap **)hash_AddElement(charmaps, charmap->name, charmap);
return charmap;
}
@@ -120,7 +122,7 @@ void charmap_Delete(struct Charmap *charmap)
void charmap_Set(const char *name)
{
struct Charmap *charmap = charmap_Get(name);
struct Charmap **charmap = (struct Charmap **)hash_GetNode(charmaps, name);
if (charmap == NULL)
error("Charmap '%s' doesn't exist\n", name);
@@ -158,25 +160,26 @@ void charmap_Pop(void)
void charmap_Add(char *mapping, uint8_t value)
{
struct Charnode *node = &currentCharmap->nodes[0];
struct Charmap *charmap = *currentCharmap;
struct Charnode *node = &charmap->nodes[0];
for (uint8_t c; *mapping; mapping++) {
c = *mapping - 1;
if (node->next[c]) {
node = &currentCharmap->nodes[node->next[c]];
node = &charmap->nodes[node->next[c]];
} else {
/* Register next available node */
node->next[c] = currentCharmap->usedNodes;
node->next[c] = charmap->usedNodes;
/* If no more nodes are available, get new ones */
if (currentCharmap->usedNodes == currentCharmap->capacity) {
currentCharmap->capacity *= 2;
currentCharmap = resizeCharmap(currentCharmap, currentCharmap->capacity);
hash_ReplaceElement(charmaps, currentCharmap->name, currentCharmap);
if (charmap->usedNodes == charmap->capacity) {
charmap->capacity *= 2;
resizeCharmap(currentCharmap, charmap->capacity);
charmap = *currentCharmap;
}
/* Switch to and init new node */
node = &currentCharmap->nodes[currentCharmap->usedNodes++];
node = &charmap->nodes[charmap->usedNodes++];
initNode(node);
}
}
@@ -197,7 +200,8 @@ size_t charmap_Convert(char const *input, uint8_t *output)
* If no match, read a UTF-8 codepoint and output that.
*/
size_t outputLen = 0;
struct Charnode const *node = &currentCharmap->nodes[0];
struct Charmap const *charmap = *currentCharmap;
struct Charnode const *node = &charmap->nodes[0];
struct Charnode const *match = NULL;
size_t rewindDistance = 0;
@@ -209,7 +213,7 @@ size_t charmap_Convert(char const *input, uint8_t *output)
input++; /* Consume that char */
rewindDistance++;
node = &currentCharmap->nodes[node->next[c]];
node = &charmap->nodes[node->next[c]];
if (node->isTerminal) {
match = node;
rewindDistance = 0; /* Rewind from after the match */
@@ -218,7 +222,7 @@ size_t charmap_Convert(char const *input, uint8_t *output)
} else {
input -= rewindDistance; /* Rewind */
rewindDistance = 0;
node = &currentCharmap->nodes[0];
node = &charmap->nodes[0];
if (match) { /* Arrived at a dead end with a match found */
*output++ = match->value;

View File

@@ -6,6 +6,7 @@
* SPDX-License-Identifier: MIT
*/
#include <assert.h>
#include <inttypes.h>
#include <math.h>
#include <stdbool.h>
@@ -148,20 +149,24 @@ void fmt_PrintString(char *buf, size_t bufLen, struct FormatSpec const *fmt, cha
size_t len = strlen(value);
size_t totalLen = fmt->width > len ? fmt->width : len;
if (totalLen + 1 > bufLen) /* bufLen includes terminator */
if (totalLen > bufLen - 1) { /* bufLen includes terminator */
error("Formatted string value too long\n");
totalLen = bufLen - 1;
if (len > totalLen)
len = totalLen;
}
assert(len < bufLen && totalLen < bufLen && len <= totalLen);
size_t padLen = fmt->width > len ? fmt->width - len : 0;
size_t padLen = totalLen - len;
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++)
memcpy(buf, value, len);
for (size_t i = len; i < totalLen; i++)
buf[i] = ' ';
if (bufLen > padLen)
strncpy(buf + padLen, value, bufLen - padLen - 1);
} else {
for (size_t i = 0; i < padLen; i++)
buf[i] = ' ';
memcpy(buf + padLen, value, len);
}
buf[totalLen] = '\0';
@@ -221,12 +226,18 @@ void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uin
/* 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;
size_t fracWidth = fmt->hasFrac ? fmt->fracWidth : 5;
if (fracWidth) {
if (fracWidth > 255) {
error("Fractional width %zu too long, limiting to 255\n",
fracWidth);
fracWidth = 255;
}
char spec[16]; /* Max "%" + 5-char PRIu32 + ".%0255.f" + terminator */
snprintf(spec, sizeof(spec), "%%" PRIu32 ".%%0%d.f", fracWidth);
snprintf(spec, sizeof(spec), "%%" PRIu32 ".%%0%zu.f", fracWidth);
snprintf(valueBuf, sizeof(valueBuf), spec, value >> 16,
(value % 65536) / 65536.0 * pow(10, fracWidth) + 0.5);
} else {
@@ -244,55 +255,49 @@ void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uin
}
size_t len = strlen(valueBuf);
size_t numLen = len;
if (sign)
numLen++;
if (prefix)
numLen++;
size_t numLen = !!sign + !!prefix + len;
size_t totalLen = fmt->width > numLen ? fmt->width : numLen;
if (totalLen + 1 > bufLen) /* bufLen includes terminator */
if (totalLen > bufLen - 1) { /* bufLen includes terminator */
error("Formatted numeric value too long\n");
totalLen = bufLen - 1;
if (numLen > totalLen) {
len -= numLen - totalLen;
numLen = totalLen;
}
}
assert(numLen < bufLen && totalLen < bufLen && numLen <= totalLen && len <= numLen);
size_t padLen = fmt->width > numLen ? fmt->width - numLen : 0;
size_t padLen = totalLen - numLen;
size_t pos = 0;
if (fmt->alignLeft) {
size_t pos = 0;
if (sign && pos < bufLen)
if (sign)
buf[pos++] = sign;
if (prefix && pos < bufLen)
if (prefix)
buf[pos++] = prefix;
strcpy(buf + pos, valueBuf);
pos += len;
for (size_t i = 0; i < totalLen && pos + i < bufLen; i++)
buf[pos + i] = ' ';
memcpy(buf + pos, valueBuf, len);
for (size_t i = pos + len; i < totalLen; i++)
buf[i] = ' ';
} else {
size_t pos = 0;
if (fmt->padZero) {
/* sign, then prefix, then zero padding */
if (sign && pos < bufLen)
if (sign)
buf[pos++] = sign;
if (prefix && pos < bufLen)
if (prefix)
buf[pos++] = prefix;
for (size_t i = 0; i < padLen && pos < bufLen; i++)
for (size_t i = 0; i < padLen; i++)
buf[pos++] = '0';
} else {
/* space padding, then sign, then prefix */
for (size_t i = 0; i < padLen && pos < bufLen; i++)
for (size_t i = 0; i < padLen; i++)
buf[pos++] = ' ';
if (sign && pos < bufLen)
if (sign)
buf[pos++] = sign;
if (prefix && pos < bufLen)
if (prefix)
buf[pos++] = prefix;
}
if (bufLen > pos)
strcpy(buf + pos, valueBuf);
memcpy(buf + pos, valueBuf, len);
}
buf[totalLen] = '\0';

View File

@@ -175,8 +175,14 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
char const *incPath = i ? includePaths[i - 1] : "";
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... */
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;
*fullPath = realloc(*fullPath, *size);
if (!*fullPath) {
@@ -185,12 +191,14 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
break;
}
len = sprintf(*fullPath, "%s%s", incPath, path);
if (len < 0) {
error("sprintf error during include path search: %s\n",
strerror(errno));
break;
}
}
if (len < 0) {
error("snprintf error during include path search: %s\n",
strerror(errno));
} else if (isPathValid(*fullPath)) {
if (isPathValid(*fullPath)) {
printDep(*fullPath);
return true;
}
@@ -314,10 +322,14 @@ void fstk_RunInclude(char const *path)
if (!fstk_FindFile(path, &fullPath, &size)) {
free(fullPath);
if (oGeneratedMissingIncludes)
if (oGeneratedMissingIncludes) {
if (verbose)
printf("Aborting (-MG) on INCLUDE file '%s' (%s)\n",
path, strerror(errno));
oFailedOnMissingInclude = true;
else
} else {
error("Unable to open included file '%s': %s\n", path, strerror(errno));
}
return;
}
dbgPrint("Full path: \"%s\"\n", fullPath);
@@ -480,6 +492,10 @@ void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
else if (step == 0)
error("FOR cannot have a step value of 0\n");
if ((step > 0 && start > stop) || (step < 0 && start < stop))
warning(WARNING_BACKWARDS_FOR, "FOR goes backwards from %d to %d by %d\n",
start, stop, step);
if (count == 0)
return;
if (!newReptContext(reptLineNo, body, size))
@@ -532,6 +548,7 @@ void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
context->fileInfo = (struct FileStackNode *)fileInfo;
/* lineNo and reptIter are unused on the top-level context */
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->type = NODE_FILE;
memcpy(fileInfo->name, fileName, len + 1);

File diff suppressed because it is too large Load Diff

View File

@@ -170,12 +170,12 @@ void macro_ShiftCurrentArgs(int32_t count)
{
if (!macroArgs) {
error("Cannot shift macro arguments outside of a macro\n");
} else if (count > 0 && (count > macroArgs->nbArgs
} else if (count > 0 && ((uint32_t)count > macroArgs->nbArgs
|| macroArgs->shift > macroArgs->nbArgs - count)) {
warning(WARNING_MACRO_SHIFT,
"Cannot shift macro arguments past their end\n");
macroArgs->shift = macroArgs->nbArgs;
} else if (count < 0 && macroArgs->shift < -count) {
} else if (count < 0 && macroArgs->shift < (uint32_t)-count) {
warning(WARNING_MACRO_SHIFT,
"Cannot shift macro arguments past their beginning\n");
macroArgs->shift = 0;

View File

@@ -353,7 +353,8 @@ int main(int argc, char *argv[])
sect_CheckUnionClosed();
if (nbErrors != 0)
errx(1, "Assembly aborted (%u errors)!", nbErrors);
errx(1, "Assembly aborted (%u error%s)!", nbErrors,
nbErrors == 1 ? "" : "s");
// If parse aborted due to missing an include, and `-MG` was given, exit normally
if (oFailedOnMissingInclude)

View File

@@ -136,9 +136,9 @@ static uint32_t getNbFileStackNodes(void)
void out_RegisterNode(struct FileStackNode *node)
{
/* If node is not already registered, register it (and parents), and give it a unique ID */
while (node->ID == -1) {
while (node->ID == (uint32_t)-1) {
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");
node->next = fileStackNodes;
fileStackNodes = node;
@@ -266,7 +266,7 @@ static void registerSymbol(struct Symbol *sym)
*objectSymbolsTail = sym;
objectSymbolsTail = &sym->next;
out_RegisterNode(sym->src);
if (nbSymbols == -1)
if (nbSymbols == (uint32_t)-1)
fatalerror("Registered too many symbols (%" PRIu32
"); try splitting up your files\n", (uint32_t)-1);
sym->ID = nbSymbols++;
@@ -278,7 +278,7 @@ static void registerSymbol(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);
return sym->ID;
}
@@ -474,7 +474,7 @@ static void registerUnregisteredSymbol(struct Symbol *symbol, void *arg)
(void)arg; // sym_ForEach requires a void* parameter, but we are not using it.
// Check for symbol->src, to skip any built-in symbol from rgbasm
if (symbol->src && symbol->ID == -1) {
if (symbol->src && symbol->ID == (uint32_t)-1) {
registerSymbol(symbol);
}
}

View File

@@ -176,7 +176,9 @@ static void strrpl(char *dest, size_t destLen, char const *src, char const *old,
for (char const *next = strstr(src, old); next && *next; next = strstr(src, old)) {
// Copy anything before the substring to replace
memcpy(dest + i, src, next - src < destLen - i ? next - src : destLen - i);
unsigned int lenBefore = next - src;
memcpy(dest + i, src, lenBefore < destLen - i ? lenBefore : destLen - i);
i += next - src;
if (i >= destLen)
break;
@@ -333,7 +335,7 @@ static void freeDsArgList(struct DsArgList *args)
free(args->args);
}
static inline void failAssert(enum AssertionType type)
static void failAssert(enum AssertionType type)
{
switch (type) {
case ASSERT_FATAL:
@@ -347,7 +349,7 @@ static inline void failAssert(enum AssertionType type)
}
}
static inline void failAssertMsg(enum AssertionType type, char const *msg)
static void failAssertMsg(enum AssertionType type, char const *msg)
{
switch (type) {
case ASSERT_FATAL:
@@ -390,6 +392,7 @@ enum {
REG_BC = 0,
REG_DE = 1,
REG_HL = 2,
// LD/INC/ADD/DEC allow SP, PUSH/POP allow AF
REG_SP = 3,
REG_AF = 3
};
@@ -504,6 +507,8 @@ enum {
%token <tzSym> T_ID "identifier"
%token <tzSym> T_LOCAL_ID "local identifier"
%token <tzSym> T_ANON "anonymous label"
%type <tzSym> def_id
%type <tzSym> redef_id
%type <tzSym> scoped_id
%type <tzSym> scoped_anon_id
%token T_POP_EQU "EQU"
@@ -594,7 +599,6 @@ enum {
%type <nConstValue> ccode
%type <sVal> op_a_n
%type <nConstValue> op_a_r
%type <nConstValue> op_hl_ss
%type <sVal> op_mem_ind
%type <assertType> assert_type
@@ -692,6 +696,22 @@ endc : T_POP_ENDC {
}
;
def_id : T_OP_DEF {
lexer_ToggleStringExpansion(false);
} T_ID {
lexer_ToggleStringExpansion(true);
strcpy($$, $3);
}
;
redef_id : T_POP_REDEF {
lexer_ToggleStringExpansion(false);
} T_ID {
lexer_ToggleStringExpansion(true);
strcpy($$, $3);
}
;
scoped_id : T_ID | T_LOCAL_ID;
scoped_anon_id : scoped_id | T_ANON;
@@ -773,8 +793,14 @@ directive : include
| fail
| warn
| assert
| def_equ
| def_set
| def_rb
| def_rw
| def_rl
| def_equs
| redef_equs
| purge
| redef
| pops
| pushs
| popo
@@ -786,6 +812,36 @@ directive : include
trailing_comma : %empty | T_COMMA
;
equ : T_LABEL T_POP_EQU const { sym_AddEqu($1, $3); }
;
set_or_equal : T_POP_SET | T_POP_EQUAL
;
set : T_LABEL set_or_equal const { sym_AddSet($1, $3); }
;
equs : T_LABEL T_POP_EQUS string { sym_AddString($1, $3); }
;
rb : T_LABEL T_POP_RB rs_uconst {
sym_AddEqu($1, sym_GetConstantValue("_RS"));
sym_AddSet("_RS", sym_GetConstantValue("_RS") + $3);
}
;
rw : T_LABEL T_POP_RW rs_uconst {
sym_AddEqu($1, sym_GetConstantValue("_RS"));
sym_AddSet("_RS", sym_GetConstantValue("_RS") + 2 * $3);
}
;
rl : T_LABEL T_Z80_RL rs_uconst {
sym_AddEqu($1, sym_GetConstantValue("_RS"));
sym_AddSet("_RS", sym_GetConstantValue("_RS") + 4 * $3);
}
;
align : T_OP_ALIGN uconst {
if ($2 > 16)
error("Alignment must be between 0 and 16, not %u\n", $2);
@@ -893,10 +949,14 @@ rept : T_POP_REPT uconst T_NEWLINE {
}
;
for : T_POP_FOR T_ID T_COMMA for_args T_NEWLINE {
for : T_POP_FOR {
lexer_ToggleStringExpansion(false);
} T_ID {
lexer_ToggleStringExpansion(true);
} T_COMMA for_args T_NEWLINE {
lexer_CaptureRept(&captureBody);
} T_NEWLINE {
fstk_RunFor($2, $4.start, $4.stop, $4.step, captureBody.lineNo,
fstk_RunFor($3, $6.start, $6.stop, $6.step, captureBody.lineNo,
captureBody.body, captureBody.size);
}
@@ -923,10 +983,14 @@ break : T_POP_BREAK T_NEWLINE {
}
;
macrodef : T_POP_MACRO T_ID T_NEWLINE {
macrodef : T_POP_MACRO {
lexer_ToggleStringExpansion(false);
} T_ID {
lexer_ToggleStringExpansion(true);
} T_NEWLINE {
lexer_CaptureMacroBody(&captureBody);
} T_NEWLINE {
sym_AddMacro($2, captureBody.lineNo, captureBody.body, captureBody.size);
sym_AddMacro($3, captureBody.lineNo, captureBody.body, captureBody.size);
}
| T_LABEL T_COLON T_POP_MACRO T_NEWLINE {
lexer_CaptureMacroBody(&captureBody);
@@ -935,9 +999,6 @@ macrodef : T_POP_MACRO T_ID T_NEWLINE {
}
;
equs : T_LABEL T_POP_EQUS string { sym_AddString($1, $3); }
;
rsset : T_POP_RSSET uconst { sym_AddSet("_RS", $2); }
;
@@ -950,24 +1011,6 @@ rs_uconst : %empty {
| uconst
;
rl : T_LABEL T_Z80_RL rs_uconst {
sym_AddEqu($1, sym_GetConstantValue("_RS"));
sym_AddSet("_RS", sym_GetConstantValue("_RS") + 4 * $3);
}
;
rw : T_LABEL T_POP_RW rs_uconst {
sym_AddEqu($1, sym_GetConstantValue("_RS"));
sym_AddSet("_RS", sym_GetConstantValue("_RS") + 2 * $3);
}
;
rb : T_LABEL T_POP_RB rs_uconst {
sym_AddEqu($1, sym_GetConstantValue("_RS"));
sym_AddSet("_RS", sym_GetConstantValue("_RS") + $3);
}
;
union : T_POP_UNION { sect_StartUnion(); }
;
@@ -1010,6 +1053,47 @@ dl : T_POP_DL { out_Skip(4, false); }
| T_POP_DL constlist_32bit trailing_comma
;
def_equ : def_id T_POP_EQU const {
sym_AddEqu($1, $3);
}
;
def_set : def_id set_or_equal const {
sym_AddSet($1, $3);
}
| redef_id set_or_equal const {
sym_AddSet($1, $3);
}
;
def_rb : def_id T_POP_RB rs_uconst {
sym_AddEqu($1, sym_GetConstantValue("_RS"));
sym_AddSet("_RS", sym_GetConstantValue("_RS") + $3);
}
;
def_rw : def_id T_POP_RW rs_uconst {
sym_AddEqu($1, sym_GetConstantValue("_RS"));
sym_AddSet("_RS", sym_GetConstantValue("_RS") + 2 * $3);
}
;
def_rl : def_id T_Z80_RL rs_uconst {
sym_AddEqu($1, sym_GetConstantValue("_RS"));
sym_AddSet("_RS", sym_GetConstantValue("_RS") + 4 * $3);
}
;
def_equs : def_id T_POP_EQUS string {
sym_AddString($1, $3);
}
;
redef_equs : redef_id T_POP_EQUS string {
sym_RedefString($1, $3);
}
;
purge : T_POP_PURGE {
lexer_ToggleStringExpansion(false);
} purge_list trailing_comma {
@@ -1017,15 +1101,6 @@ purge : T_POP_PURGE {
}
;
redef : T_POP_REDEF {
lexer_ToggleStringExpansion(false);
} scoped_id {
lexer_ToggleStringExpansion(true);
} T_POP_EQUS string {
sym_RedefString($3, $6);
}
;
purge_list : purge_list_entry
| purge_list T_COMMA purge_list_entry
;
@@ -1043,13 +1118,6 @@ export_list : export_list_entry
export_list_entry : scoped_id { sym_Export($1); }
;
equ : T_LABEL T_POP_EQU const { sym_AddEqu($1, $3); }
;
set : T_LABEL T_POP_SET const { sym_AddSet($1, $3); }
| T_LABEL T_POP_EQUAL const { sym_AddSet($1, $3); }
;
include : T_POP_INCLUDE string {
fstk_RunInclude($2);
if (oFailedOnMissingInclude)
@@ -1097,8 +1165,14 @@ popc : T_POP_POPC { charmap_Pop(); }
print : T_POP_PRINT print_exprs trailing_comma
;
println : T_POP_PRINTLN { putchar('\n'); }
| T_POP_PRINTLN print_exprs trailing_comma { putchar('\n'); }
println : T_POP_PRINTLN {
putchar('\n');
fflush(stdout);
}
| T_POP_PRINTLN print_exprs trailing_comma {
putchar('\n');
fflush(stdout);
}
;
print_exprs : print_expr
@@ -1437,7 +1511,11 @@ string : T_STRING
strcat_args : string
| strcat_args T_COMMA string {
if (snprintf($$, sizeof($$), "%s%s", $1, $3) >= sizeof($$))
int ret = snprintf($$, sizeof($$), "%s%s", $1, $3);
if (ret == -1)
fatalerror("snprintf error in STRCAT: %s\n", strerror(errno));
else if ((unsigned int)ret >= sizeof($$))
warning(WARNING_LONG_STR, "STRCAT: String too long '%s%s'\n",
$1, $3);
}
@@ -1590,7 +1668,7 @@ z80_add : T_Z80_ADD op_a_n {
out_RelByte(&$2, 1);
}
| T_Z80_ADD op_a_r { out_AbsByte(0x80 | $2); }
| T_Z80_ADD op_hl_ss { out_AbsByte(0x09 | ($2 << 4)); }
| T_Z80_ADD T_MODE_HL T_COMMA reg_ss { out_AbsByte(0x09 | ($4 << 4)); }
| T_Z80_ADD T_MODE_SP T_COMMA reloc_8bit {
out_AbsByte(0xE8);
out_RelByte(&$4, 1);
@@ -1731,9 +1809,9 @@ z80_ld : z80_ld_mem
| z80_ld_a
;
z80_ld_hl : T_Z80_LD T_MODE_HL T_COMMA T_MODE_SP reloc_8bit {
z80_ld_hl : T_Z80_LD T_MODE_HL T_COMMA T_MODE_SP T_OP_ADD reloc_8bit {
out_AbsByte(0xF8);
out_RelByte(&$5, 1);
out_RelByte(&$6, 1);
}
| T_Z80_LD T_MODE_HL T_COMMA reloc_16bit {
out_AbsByte(0x01 | (REG_HL << 4));
@@ -1975,10 +2053,6 @@ z80_xor : T_Z80_XOR op_a_n {
op_mem_ind : T_LBRACK reloc_16bit T_RBRACK { $$ = $2; }
;
op_hl_ss : reg_ss
| T_MODE_HL T_COMMA reg_ss { $$ = $3; }
;
op_a_r : reg_r
| T_MODE_A T_COMMA reg_r { $$ = $3; }
;

View File

@@ -1,11 +1,11 @@
.\"
.\" 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
.\"
.Dd July 8, 2019
.Dd March 28, 2021
.Dt RGBASM 1
.Os
.Sh NAME
@@ -189,7 +189,7 @@ Ignoring the
prefix, entries are listed alphabetically.
.Bl -tag -width Ds
.It Fl Wno-assert
Warns when
Warn when
.Ic WARN Ns No -type
assertions fail. (See
.Dq Aborting the assembly process
@@ -197,6 +197,12 @@ in
.Xr rgbasm 5
for
.Ic ASSERT ) .
.It Fl Wbackwards-for
Warn when
.Ic FOR
loops have their start and stop values switched according to the step value.
This warning is enabled by
.Fl Wall .
.It Fl Wbuiltin-args
Warn about incorrect arguments to built-in functions, such as
.Fn STRSUB
@@ -239,7 +245,7 @@ constant or
directive are encountered.
.It Fl Wshift
Warn when shifting right a negative value.
Use a division by 2^N instead.
Use a division by 2**N instead.
.It Fl Wshift-amount
Warn when a shift's operand is negative or greater than 32.
.It Fl Wno-truncation

View File

@@ -1,11 +1,11 @@
.\"
.\" 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
.\"
.Dd December 5, 2019
.Dd March 28, 2021
.Dt RGBASM 5
.Os
.Sh NAME
@@ -25,11 +25,11 @@ Generally,
.Dq the linker
will refer to
.Xr rgblink 1 ,
but any program that processes RGB object files (described in
but any program that processes RGBDS object files (described in
.Xr rgbds 5 )
can be used in its place.
.Sh SYNTAX
The syntax is linebased, just as in any other assembler, meaning that you do one instruction or directive per line:
The syntax is line-based, just as in any other assembler, meaning that you do one instruction or directive per line:
.Pp
.Dl Oo Ar label Oc Oo Ar instruction Oc Oo Ar ;\ comment Oc
.Pp
@@ -38,7 +38,7 @@ Example:
John: ld a,87 ;Weee
.Ed
.Pp
All reserved keywords (directives, mnemonics, registers, etc.) are caseinsensitive;
All reserved keywords (directives, mnemonics, registers, etc.) are case-insensitive;
all identifiers (symbol names) are case-sensitive.
.Pp
Comments are used to give humans information about the code, such as explanations.
@@ -177,8 +177,8 @@ and
.Pp
.Ic \&!
returns 1 if the operand was 0, and 0 otherwise.
.Ss Fixedpoint Expressions
Fixed-point numbers are basically normal (32-bit) integers, which count 65536th's instead of entire units, offering better precision than integers but limiting the range of values.
.Ss Fixed-point Expressions
Fixed-point numbers are basically normal (32-bit) integers, which count 65536ths instead of entire units, offering better precision than integers but limiting the range of values.
The upper 16 bits are used for the integer part and the lower 16 bits are used for the fraction (65536ths).
Since they are still akin to integers, you can use them in normal integer expressions, and some integer operators like
.Sq +
@@ -266,21 +266,37 @@ A funky feature is
.Ql {symbol}
within a string, called
.Dq symbol interpolation .
This will paste
.Ar symbol Ap s
contents as a string.
If it's a string symbol, the string is simply inserted.
If it's a numeric symbol, its value is converted to hexadecimal notation with a dollar sign
This will paste the contents of
.Ql symbol
as if they were part of the source file.
If it's a string symbol, its characters are simply inserted.
If it's a numerical symbol, its value is converted to hexadecimal notation with a dollar sign
.Sq $
prepended.
.Bd -literal -offset indent
TOPIC equs "life, the universe, and \[rs]"everything\[rs]""
ANSWER = 42
;\ Prints "The answer to life, the universe, and "everything" is $2A"
PRINTLN "The answer to {TOPIC} is {ANSWER}"
.Ed
.Pp
Symbols can be
.Em interpolated
even in the contexts that disable
.Em expansion
of string equates:
.Ql DEF({name}) ,
.Ql DEF {name} EQU/SET/EQUS/etc ... ,
.Ql PURGE {name} ,
and
.Ql MACRO {name}
will all interpolate the contents of
.Ql {name} .
.Pp
Symbol interpolations can be nested, too!
.Bd -literal -offset indent
DEF topic EQUS "life, the universe, and \[rs]"everything\[rs]""
DEF meaning EQUS "answer"
;\ Defines answer = 42
DEF {meaning} = 42
;\ Prints "The answer to life, the universe, and "everything" is 42"
PRINTLN "The {meaning} to {topic} is {d:{meaning}}"
PURGE topic, meaning, {meaning}
.Ed
.Pp
It's possible to change the way symbols are converted by specifying a print format like so:
.Ql {fmt:symbol} .
@@ -321,7 +337,7 @@ followed by one or more
\[en]
.Ql 9 .
If specified, prints this many digits of a fixed-point fraction.
Defaults to 5 digits.
Defaults to 5 digits, maximum 255 digits.
.It Ql <type> Ta Specifies the type of value.
.El
.Pp
@@ -355,14 +371,14 @@ HINT: The
construct can also be used outside strings.
The symbol's value is again inserted directly.
.Bd -literal -offset indent
NAME equs "ITEM"
FMT equs "d"
ZERO_NUM equ 0
ZERO_STR equs "0"
def NAME equs "ITEM"
def FMT equs "d"
def ZERO_NUM equ 0
def ZERO_STR equs "0"
;\ Defines INDEX as 100
INDEX = 1{ZERO_STR}{{FMT}:ZERO_NUM}
;\ Defines ITEM_100 as "\[rs]"hundredth\[rs]""
{NAME}_{d:INDEX} equs "\[rs]"hundredth\[rs]""
def {NAME}_{d:INDEX} equs "\[rs]"hundredth\[rs]""
;\ Prints "ITEM_100 is hundredth"
PRINTLN STRCAT("{NAME}_{d:INDEX} is ", {NAME}_{d:INDEX})
;\ Purges ITEM_100
@@ -446,7 +462,7 @@ is able to compute it.
.It Fn DEF symbol Ta Returns TRUE (1) if
.Ar symbol
has been defined, FALSE (0) otherwise.
String symbols are not expanded within the parentheses.
String equates are not expanded within the parentheses.
.It Fn HIGH arg Ta Returns the top 8 bits of the operand if Ar arg No is a label or constant, or the top 8-bit register if it is a 16-bit register.
.It Fn LOW arg Ta Returns the bottom 8 bits of the operand if Ar arg No is a label or constant, or the bottom 8-bit register if it is a 16-bit register Pq Cm AF No isn't a valid register for this function .
.It Fn ISCONST arg Ta Returns 1 if Ar arg Ap s value is known by RGBASM (e.g. if it can be an argument to
@@ -590,7 +606,7 @@ depending on
.It Ic ALIGN Ns Bq Ar align , offset
Place the section at an address whose
.Ar align
leastsignificant bits are equal to
least-significant bits are equal to
.Ar offset .
(Note that
.Ic ALIGN Ns Bq Ar align
@@ -853,16 +869,7 @@ Periods
.Sq \&.
are allowed exclusively in labels, as described below.
A symbol cannot have the same name as a reserved keyword.
.Pp
Constants and string equates
.Em must not
have any whitespace before their name when they are defined;
otherwise
.Nm
will treat them as a macro invocation.
Label and macro definitions may have whitespace before them, since a leading period or a following colon distinguishes them from invoking a macro.
.Bl -tag -width indent
.It Sy Label declaration
.Ss Label declaration
One of the assembler's main tasks is to keep track of addresses for you, so you can work with meaningful names instead of "magic" numbers.
.Pp
This can be done in a number of ways:
@@ -937,56 +944,57 @@ However, if the section in which the label is declared has a fixed base address,
.Pp
.Nm
is able to compute the subtraction of two labels either if both are constant as described above, or if both belong to the same section.
.It Ic EQU
.Ss Immutable constants
.Ic EQU
allows defining constant symbols.
is used to define numerical constant symbols.
Unlike
.Ic SET
below, constants defined this way cannot be redefined.
They can, for example, be used for things such as bit definitions of hardware registers.
.Bd -literal -offset indent
SCREEN_WIDTH equ 160 ;\ In pixels
SCREEN_HEIGHT equ 144
def SCREEN_WIDTH equ 160 ;\ In pixels
def SCREEN_HEIGHT equ 144
.Ed
.Pp
Note that colons
.Ql \&:
following the name are not allowed.
.It Ic SET
.Ss Mutable constants
.Ic SET ,
or its synonym
.Ic = ,
defines constant symbols like
is used to define numerical symbols like
.Ic EQU ,
but those constants can be redefined.
but these symbols can be redefined.
This is useful for variables in macros, for counters, etc.
.Bd -literal -offset indent
ARRAY_SIZE EQU 4
COUNT SET 2
COUNT SET ARRAY_SIZE+COUNT
;\ COUNT now has the value 6
COUNT = COUNT + 1
DEF ARRAY_SIZE EQU 4
DEF COUNT SET 2
DEF COUNT SET 3
REDEF COUNT SET ARRAY_SIZE+COUNT
COUNT = COUNT*2
;\ COUNT now has the value 14
.Ed
.Pp
Note that colons
.Ql \&:
following the name are not allowed.
.It Ic RSSET , RSRESET , RB , RW
The RS group of commands is a handy way of defining structures:
.Ss Offset constants
The RS group of commands is a handy way of defining structure offsets:
.Bd -literal -offset indent
RSRESET
str_pStuff RW 1
str_tData RB 256
str_bCount RB 1
str_SIZEOF RB 0
DEF str_pStuff RW 1
DEF str_tData RB 256
DEF str_bCount RB 1
DEF str_SIZEOF RB 0
.Ed
.Pp
The example defines four constants as if by:
.Bd -literal -offset indent
str_pStuff EQU 0
str_tData EQU 2
str_bCount EQU 258
str_SIZEOF EQU 259
DEF str_pStuff EQU 0
DEF str_tData EQU 2
DEF str_bCount EQU 258
DEF str_SIZEOF EQU 259
.Ed
.Pp
There are five commands in the RS group of commands:
@@ -1008,17 +1016,26 @@ is omitted, it's assumed to be 1.
Note that colons
.Ql \&:
following the name are not allowed.
.It Ic EQUS
.Ss String equates
.Ic EQUS
is used to define string symbols.
Wherever the assembler meets a string symbol its name is replaced with its value.
If you are familiar with C you can think of it as similar to
is used to define string equate symbols.
Wherever the assembler reads a string equate, it gets
.Em expanded :
the symbol's name is replaced with its contents.
If you are familiar with C, you can think of it as similar to
.Fd #define .
This expansion is disabled in a few contexts:
.Ql DEF(name) ,
.Ql DEF name EQU/SET/EQUS/etc ... ,
.Ql PURGE name ,
and
.Ql MACRO name
will not expand string equates in their names.
.Bd -literal -offset indent
COUNTREG EQUS "[hl+]"
DEF COUNTREG EQUS "[hl+]"
ld a,COUNTREG
PLAYER_NAME EQUS "\[rs]"John\[rs]""
DEF PLAYER_NAME EQUS "\[rs]"John\[rs]""
db PLAYER_NAME
.Ed
.Pp
@@ -1028,9 +1045,9 @@ This will be interpreted as:
db "John"
.Ed
.Pp
String symbols can also be used to define small one-line macros:
String equates can also be used to define small one-line macros:
.Bd -literal -offset indent
pusha EQUS "push af\[rs]npush bc\[rs]npush de\[rs]npush hl\[rs]n"
DEF pusha EQUS "push af\[rs]npush bc\[rs]npush de\[rs]npush hl\[rs]n"
.Ed
.Pp
Note that colons
@@ -1047,7 +1064,7 @@ However, the
keyword will define or redefine a string symbol.
For example:
.Bd -literal -offset indent
s EQUS "Hello, "
DEF s EQUS "Hello, "
REDEF s EQUS "{s}world!"
; prints "Hello, world!"
PRINTT "{s}\n"
@@ -1070,7 +1087,47 @@ command-line option in
Also, a macro can contain an
.Ic EQUS
which calls the same macro, which causes the same problem.
.It Ic MACRO
.Pp
The examples above for
.Ql EQU ,
.Ql SET
or
.Ql = ,
.Ql RB ,
.Ql RW ,
.Ql RL ,
and
.Ql EQUS
all start with
.Ql DEF .
(A
.Ql SET
or
.Ql =
definition may start with
.Ql REDEF
instead, since they are redefinable.)
You may use the older syntax without
.Ql DEF ,
but then the name being defined
.Em must not
have any whitespace before it;
otherwise
.Nm
will treat it as a macro invocation.
Furthermore, without the
.Ql DEF
keyword,
string equates may be expanded for the name.
This can lead to surprising results:
.Bd -literal -offset indent
X EQUS "Y"
; this defines Y, not X!
X EQU 42
; prints "Y $2A"
PRINTLN "{X} {Y}"
.Ed
.Ss Macros
One of the best features of an assembler is the ability to write macros for it.
Macros can be called with arguments, and can react depending on input using
.Ic IF
@@ -1085,6 +1142,7 @@ ENDM
The example above defines
.Ql MyMacro
as a new macro.
String equates are not expanded within the name of the macro.
You may use the older syntax
.Ql MyMacro: MACRO
instead of
@@ -1092,6 +1150,8 @@ instead of
with a single colon
.Ql \&:
following the macro's name.
With the older syntax, string equates may be expanded for the name.
.Pp
Macros can't be exported or imported.
.Pp
Plainly nesting macro definitions is not allowed, but this can be worked around using
@@ -1108,12 +1168,11 @@ ENDM
But this will:
.Bd -literal -offset indent
MACRO outer
definition EQUS "MACRO inner\[rs]nPRINTLN \[rs]"Hello!\[rs]"\[rs]nENDM"
DEF definition EQUS "MACRO inner\[rs]nPRINTLN \[rs]"Hello!\[rs]"\[rs]nENDM"
definition
PURGE definition
ENDM
.Ed
.El
.Pp
Macro arguments support all the escape sequences of strings, as well as
.Ql \[rs],
@@ -1184,15 +1243,12 @@ I can't stress this enough,
DON'T purge a symbol that you use in expressions the linker needs to calculate.
When not sure, it's probably not safe to purge anything other than string symbols, macros, and constants.
.Bd -literal -offset indent
Kamikaze EQUS "I don't want to live anymore"
AOLer EQUS "Me too"
DEF Kamikaze EQUS "I don't want to live anymore"
DEF AOLer EQUS "Me too"
PURGE Kamikaze, AOLer
.Ed
.Pp
Note that, as an exception, string symbols in the argument list of a
.Ic PURGE
command
.Em will not be expanded .
String equates are not expanded within the symbol names.
.Ss Predeclared Symbols
The following symbols are defined by the assembler:
.Bl -column -offset indent "EQUS" "__ISO_8601_LOCAL__"
@@ -1216,6 +1272,7 @@ The following symbols are defined by the assembler:
.It Dv __RGBDS_MINOR__ Ta Ic EQU Ta Minor version number of RGBDS
.It Dv __RGBDS_PATCH__ Ta Ic EQU Ta Patch version number of RGBDS
.It Dv __RGBDS_RC__ Ta Ic EQU Ta Release candidate ID of RGBDS, not defined for final releases
.It Dv __RGBDS_VERSION__ Ta Ic EQUS Ta Version of RGBDS, as printed by Ql rgbasm --version
.El
.Pp
The current time values will be taken from the
@@ -1458,7 +1515,7 @@ MACRO LoopyMacro
ENDM
.Ed
.Pp
Now I can call the macro specifying two arguments, the first being the address and the second being a byte count.
Now you can call the macro specifying two arguments, the first being the address and the second being a byte count.
The generated code will then reset all bytes in this range.
.Bd -literal -offset indent
LoopyMacro MyVars,54
@@ -1598,6 +1655,7 @@ Everything between
and the matching
.Ic ENDR
will be repeated for each value of a given symbol.
String equates are not expanded within the symbol name.
For example, this code will produce a table of squared values from 0 to 255:
.Bd -literal -offset indent
FOR N, 256
@@ -1624,9 +1682,9 @@ You can customize the range of
values:
.Bl -column "FOR V, start, stop, step"
.It Sy Code Ta Sy Range
.It Ic FOR Ar V , stop Ta Ar V No increments from 0 to Ar stop No
.It Ic FOR Ar V , start , stop Ta Ar V No increments from Ar start No to Ar stop No
.It Ic FOR Ar V , start , stop , step Ta Ar V No goes from Ar start No to Ar stop No by Ar step No
.It Ic FOR Ar V , stop Ta Ar V No increments from 0 to Ar stop
.It Ic FOR Ar V , start , stop Ta Ar V No increments from Ar start No to Ar stop
.It Ic FOR Ar V , start , stop , step Ta Ar V No goes from Ar start No to Ar stop No by Ar step
.El
.Pp
The

View File

@@ -139,7 +139,7 @@ void rpn_BankSelf(struct Expression *expr)
if (!pCurrentSection) {
error("PC has no bank outside a section\n");
expr->nVal = 1;
} else if (pCurrentSection->bank == -1) {
} else if (pCurrentSection->bank == (uint32_t)-1) {
makeUnknown(expr, "Current section's bank is not known");
expr->nRPNPatchSize++;
*reserveSpace(expr, 1) = RPN_BANK_SELF;
@@ -165,7 +165,7 @@ void rpn_BankSymbol(struct Expression *expr, char const *tzSym)
sym = sym_Ref(tzSym);
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 */
expr->nVal = sym_GetSection(sym)->bank;
} else {
@@ -186,7 +186,7 @@ void rpn_BankSection(struct Expression *expr, char const *tzSectionName)
struct Section *pSection = out_FindSectionByName(tzSectionName);
if (pSection && pSection->bank != -1) {
if (pSection && pSection->bank != (uint32_t)-1) {
expr->nVal = pSection->bank;
} else {
makeUnknown(expr, "Section \"%s\"'s bank is not known",

View File

@@ -41,7 +41,7 @@ struct UnionStackEntry {
/*
* A quick check to see if we have an initialized section
*/
static inline void checksection(void)
static void checksection(void)
{
if (pCurrentSection == NULL)
fatalerror("Code generation before SECTION directive\n");
@@ -51,7 +51,7 @@ static inline void checksection(void)
* A quick check to see if we have an initialized section that can contain
* this much initialized data
*/
static inline void checkcodesection(void)
static void checkcodesection(void)
{
checksection();
@@ -60,7 +60,7 @@ static inline void checkcodesection(void)
pCurrentSection->name);
}
static inline void checkSectionSize(struct Section const *sect, uint32_t size)
static void checkSectionSize(struct Section const *sect, uint32_t size)
{
uint32_t maxSize = maxsize[sect->type];
@@ -72,7 +72,7 @@ static inline void checkSectionSize(struct Section const *sect, uint32_t size)
/*
* Check if the section has grown too much.
*/
static inline void reserveSpace(uint32_t delta_size)
static void reserveSpace(uint32_t delta_size)
{
/*
* This check is here to trap broken code that generates sections that
@@ -114,9 +114,9 @@ static unsigned int mergeSectUnion(struct Section *sect, enum SectionType type,
if (sect_HasData(type))
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 (sect->org != -1 && sect->org != org)
if (sect->org != (uint32_t)-1 && sect->org != org)
fail("Section already declared as fixed at different address $%04"
PRIx32 "\n", sect->org);
else if (sect->align != 0 && (mask(sect->align) & (org - sect->alignOfs)))
@@ -128,7 +128,7 @@ static unsigned int mergeSectUnion(struct Section *sect, enum SectionType type,
} else if (alignment != 0) {
/* Make sure any fixed address given is compatible */
if (sect->org != -1) {
if (sect->org != (uint32_t)-1) {
if ((sect->org - alignOffset) & mask(alignment))
fail("Section already declared as fixed at incompatible address $%04"
PRIx32 "\n", sect->org);
@@ -160,11 +160,11 @@ static unsigned int mergeFragments(struct Section *sect, enum SectionType type,
* combination of both.
* The merging is however performed at the *end* of the original section!
*/
if (org != -1) {
if (org != (uint32_t)-1) {
uint16_t curOrg = org - sect->size;
/* If both are fixed, they must be the same */
if (sect->org != -1 && sect->org != curOrg)
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);
@@ -182,7 +182,7 @@ static unsigned int mergeFragments(struct Section *sect, enum SectionType type,
curOfs += 1U << alignment;
/* Make sure any fixed address given is compatible */
if (sect->org != -1) {
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);
@@ -221,10 +221,10 @@ static void mergeSections(struct Section *sect, enum SectionType type, uint32_t
// Common checks
/* If the section's bank is unspecified, override it */
if (sect->bank == -1)
if (sect->bank == (uint32_t)-1)
sect->bank = bank;
/* 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 already declared with different bank %" PRIu32 "\n",
sect->bank);
break;
@@ -296,7 +296,7 @@ static struct Section *getSection(char const *name, enum SectionType type, uint3
// First, validate parameters, and normalize them if applicable
if (bank != -1) {
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");
@@ -316,7 +316,7 @@ static struct Section *getSection(char const *name, enum SectionType type, uint3
alignOffset = 0;
}
if (org != -1) {
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",
@@ -331,7 +331,7 @@ static struct Section *getSection(char const *name, enum SectionType type, uint3
/* It doesn't make sense to have both alignment and org set */
uint32_t mask = mask(alignment);
if (org != -1) {
if (org != (uint32_t)-1) {
if ((org - alignOffset) & mask)
error("Section \"%s\"'s fixed address doesn't match its alignment\n",
name);
@@ -339,6 +339,8 @@ static struct Section *getSection(char const *name, enum SectionType type, uint3
} else if (startaddr[type] & mask) {
error("Section \"%s\"'s alignment cannot be attained in %s\n",
name, typeNames[type]);
alignment = 0; /* Ignore it if it's unattainable */
org = 0;
} else if (alignment == 16) {
// Treat an alignment of 16 as being fixed at address 0
alignment = 0;
@@ -453,7 +455,7 @@ void sect_AlignPC(uint8_t alignment, uint16_t offset)
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) % alignSize)
error("Section's fixed address fails required alignment (PC = $%04" PRIx32
")\n", sym_GetPCValue());
@@ -472,7 +474,7 @@ void sect_AlignPC(uint8_t alignment, uint16_t offset)
}
}
static inline void growSection(uint32_t growth)
static void growSection(uint32_t growth)
{
curOffset += growth;
if (curOffset + loadOffset > pCurrentSection->size)
@@ -481,19 +483,19 @@ static inline void growSection(uint32_t growth)
currentLoadSection->size = curOffset;
}
static inline void writebyte(uint8_t byte)
static void writebyte(uint8_t byte)
{
pCurrentSection->data[sect_GetOutputOffset()] = byte;
growSection(1);
}
static inline void writeword(uint16_t b)
static void writeword(uint16_t b)
{
writebyte(b & 0xFF);
writebyte(b >> 8);
}
static inline void writelong(uint32_t b)
static void writelong(uint32_t b)
{
writebyte(b & 0xFF);
writebyte(b >> 8);
@@ -501,8 +503,7 @@ static inline void writelong(uint32_t b)
writebyte(b >> 24);
}
static inline void createPatch(enum PatchType type, struct Expression const *expr,
uint32_t pcShift)
static void createPatch(enum PatchType type, struct Expression const *expr, uint32_t pcShift)
{
out_CreatePatch(type, expr, sect_GetOutputOffset(), pcShift);
}
@@ -761,6 +762,8 @@ void out_BinaryFile(char const *s, int32_t startPos)
if (!f) {
if (oGeneratedMissingIncludes) {
if (verbose)
printf("Aborting (-MG) on INCBIN file '%s' (%s)\n", s, strerror(errno));
oFailedOnMissingInclude = true;
return;
}
@@ -777,8 +780,7 @@ void out_BinaryFile(char const *s, int32_t startPos)
if (startPos > fsize) {
error("Specified start position is greater than length of file\n");
fclose(f);
return;
goto cleanup;
}
fseek(f, startPos, SEEK_SET);
@@ -801,6 +803,7 @@ void out_BinaryFile(char const *s, int32_t startPos)
if (ferror(f))
error("Error reading INCBIN file '%s': %s\n", s, strerror(errno));
cleanup:
fclose(f);
}
@@ -824,14 +827,16 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
if (fstk_FindFile(s, &fullPath, &size))
f = fopen(fullPath, "rb");
free(fullPath);
if (!f) {
free(fullPath);
if (oGeneratedMissingIncludes) {
if (verbose)
printf("Aborting (-MG) on INCBIN file '%s' (%s)\n", s, strerror(errno));
oFailedOnMissingInclude = true;
return;
}
} else {
error("Error opening INCBIN file '%s': %s\n", s, strerror(errno));
}
return;
}
@@ -845,13 +850,13 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
if (start_pos > fsize) {
error("Specified start position is greater than length of file\n");
return;
goto cleanup;
}
if ((start_pos + length) > fsize) {
error("Specified range in INCBIN is out of bounds (%" PRIu32 " + %" PRIu32
" > %" PRIu32 ")\n", start_pos, length, fsize);
return;
goto cleanup;
}
fseek(f, start_pos, SEEK_SET);
@@ -879,8 +884,8 @@ void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length)
}
}
cleanup:
fclose(f);
free(fullPath);
}
/*

View File

@@ -43,12 +43,6 @@ static char savedTIME[256];
static char savedDATE[256];
static char savedTIMESTAMP_ISO8601_LOCAL[256];
static char savedTIMESTAMP_ISO8601_UTC[256];
static char savedDAY[3];
static char savedMONTH[3];
static char savedYEAR[20];
static char savedHOUR[3];
static char savedMINUTE[3];
static char savedSECOND[3];
static bool exportall;
bool sym_IsPC(struct Symbol const *sym)
@@ -179,7 +173,7 @@ static void updateSymbolFilename(struct Symbol *sym)
setSymbolFilename(sym);
/* If the old node was referenced, ensure the new one is */
if (oldSrc && oldSrc->referenced && oldSrc->ID != -1)
if (oldSrc && oldSrc->referenced && oldSrc->ID != (uint32_t)-1)
out_RegisterNode(sym->src);
/* TODO: unref the old node, and use `out_ReplaceNode` instead if deleting it */
}
@@ -275,9 +269,9 @@ struct Symbol const *sym_GetPC(void)
return PCSymbol;
}
static inline bool isReferenced(struct Symbol const *sym)
static bool isReferenced(struct Symbol const *sym)
{
return sym->ID != -1;
return sym->ID != (uint32_t)-1;
}
/*
@@ -315,7 +309,7 @@ uint32_t sym_GetPCValue(void)
if (!sect)
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");
else
return CallbackPC();
@@ -379,6 +373,7 @@ static struct Symbol *createNonrelocSymbol(char const *symbolName, bool numeric)
error("'%s' already defined at ", symbolName);
dumpFilename(symbol);
putc('\n', stderr);
return NULL; // Don't allow overriding the symbol, that'd be bad!
} else if (!numeric) {
// The symbol has already been referenced, but it's not allowed
error("'%s' already referenced at ", symbolName);
@@ -439,6 +434,10 @@ struct Symbol *sym_RedefString(char const *symName, char const *value)
error("'%s' already defined as non-EQUS at ", symName);
dumpFilename(sym);
putc('\n', stderr);
return NULL;
} else if (sym->isBuiltin) {
error("Built-in symbol '%s' cannot be redefined\n", symName);
return NULL;
}
/*
@@ -681,18 +680,7 @@ void sym_SetExportAll(bool set)
exportall = set;
}
/**
* Returns a pointer to the first non-zero character in a string
* Non-'0', not non-'\0'.
*/
static inline char const *removeLeadingZeros(char const *ptr)
{
while (*ptr == '0')
ptr++;
return ptr;
}
static inline struct Symbol *createBuiltinSymbol(char const *name)
static struct Symbol *createBuiltinSymbol(char const *name)
{
struct Symbol *sym = createsymbol(name);
@@ -722,13 +710,18 @@ void sym_Init(time_t now)
__LINE__Symbol->numCallback = Callback__LINE__;
__FILE__Symbol->type = SYM_EQUS;
__FILE__Symbol->strCallback = Callback__FILE__;
sym_AddSet("_RS", 0)->isBuiltin = true;
sym_AddEqu("__RGBDS_MAJOR__", PACKAGE_VERSION_MAJOR)->isBuiltin = true;
sym_AddEqu("__RGBDS_MINOR__", PACKAGE_VERSION_MINOR)->isBuiltin = true;
sym_AddEqu("__RGBDS_PATCH__", PACKAGE_VERSION_PATCH)->isBuiltin = true;
#define addNumber(name, val) sym_AddEqu(name, val)->isBuiltin = true
#define addString(name, val) sym_AddString(name, val)->isBuiltin = true
addString("__RGBDS_VERSION__", get_package_version_string());
addNumber("__RGBDS_MAJOR__", PACKAGE_VERSION_MAJOR);
addNumber("__RGBDS_MINOR__", PACKAGE_VERSION_MINOR);
addNumber("__RGBDS_PATCH__", PACKAGE_VERSION_PATCH);
#ifdef PACKAGE_VERSION_RC
sym_AddEqu("__RGBDS_RC__", PACKAGE_VERSION_RC)->isBuiltin = true;
addNumber("__RGBDS_RC__", PACKAGE_VERSION_RC);
#endif
if (now == (time_t)-1) {
@@ -750,25 +743,19 @@ void sym_Init(time_t now)
sizeof(savedTIMESTAMP_ISO8601_UTC), "\"%Y-%m-%dT%H:%M:%SZ\"",
time_utc);
strftime(savedYEAR, sizeof(savedYEAR), "%Y", time_utc);
strftime(savedMONTH, sizeof(savedMONTH), "%m", time_utc);
strftime(savedDAY, sizeof(savedDAY), "%d", time_utc);
strftime(savedHOUR, sizeof(savedHOUR), "%H", time_utc);
strftime(savedMINUTE, sizeof(savedMINUTE), "%M", time_utc);
strftime(savedSECOND, sizeof(savedSECOND), "%S", time_utc);
#define addString(name, val) sym_AddString(name, val)->isBuiltin = true
addString("__TIME__", savedTIME);
addString("__DATE__", savedDATE);
addString("__ISO_8601_LOCAL__", savedTIMESTAMP_ISO8601_LOCAL);
addString("__ISO_8601_UTC__", savedTIMESTAMP_ISO8601_UTC);
/* This cannot start with zeros */
addString("__UTC_YEAR__", savedYEAR);
addString("__UTC_MONTH__", removeLeadingZeros(savedMONTH));
addString("__UTC_DAY__", removeLeadingZeros(savedDAY));
addString("__UTC_HOUR__", removeLeadingZeros(savedHOUR));
addString("__UTC_MINUTE__", removeLeadingZeros(savedMINUTE));
addString("__UTC_SECOND__", removeLeadingZeros(savedSECOND));
addNumber("__UTC_YEAR__", time_utc->tm_year + 1900);
addNumber("__UTC_MONTH__", time_utc->tm_mon + 1);
addNumber("__UTC_DAY__", time_utc->tm_mday);
addNumber("__UTC_HOUR__", time_utc->tm_hour);
addNumber("__UTC_MINUTE__", time_utc->tm_min);
addNumber("__UTC_SECOND__", time_utc->tm_sec);
#undef addNumber
#undef addString
labelScope = NULL;

View File

@@ -15,52 +15,45 @@
#include "extern/utf8decoder.h"
/*
* 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)
char const *printChar(int c)
{
uint32_t hash = 5381;
while (*s != 0)
hash = (hash * 33) ^ (*s++);
return hash;
}
char const *print(int c)
{
static char buf[5]; /* '\xNN' + '\0' */
// "'A'" + '\0': 4 bytes
// "'\\n'" + '\0': 5 bytes
// "0xFF" + '\0': 5 bytes
static char buf[5];
if (c == EOF)
return "EOF";
if (isprint(c)) {
buf[0] = c;
buf[1] = '\0';
buf[0] = '\'';
buf[1] = c;
buf[2] = '\'';
buf[3] = '\0';
return buf;
}
buf[0] = '\\';
switch (c) {
case '\n':
buf[1] = 'n';
buf[2] = 'n';
break;
case '\r':
buf[1] = 'r';
buf[2] = 'r';
break;
case '\t':
buf[1] = 't';
buf[2] = 't';
break;
default: /* Print as hex */
buf[0] = '0';
buf[1] = 'x';
sprintf(&buf[2], "%02hhx", c);
snprintf(&buf[2], 3, "%02hhX", (uint8_t)c); // includes the '\0'
return buf;
}
buf[2] = '\0';
buf[0] = '\'';
buf[1] = '\\';
buf[3] = '\'';
buf[4] = '\0';
return buf;
}

View File

@@ -30,6 +30,7 @@ enum WarningState {
static enum WarningState const defaultWarnings[NB_WARNINGS] = {
[WARNING_ASSERT] = WARNING_ENABLED,
[WARNING_BACKWARDS_FOR] = WARNING_DISABLED,
[WARNING_BUILTIN_ARG] = WARNING_DISABLED,
[WARNING_CHARMAP_REDEF] = WARNING_DISABLED,
[WARNING_DIV] = WARNING_DISABLED,
@@ -72,6 +73,7 @@ static enum WarningState warningState(enum WarningID id)
static char const *warningFlags[NB_WARNINGS_ALL] = {
"assert",
"backwards-for",
"builtin-args",
"charmap-redef",
"div",
@@ -100,6 +102,7 @@ enum MetaWarningCommand {
/* Warnings that probably indicate an error */
static uint8_t const _wallCommands[] = {
WARNING_BACKWARDS_FOR,
WARNING_BUILTIN_ARG,
WARNING_CHARMAP_REDEF,
WARNING_EMPTY_DATA_DIRECTIVE,
@@ -119,6 +122,7 @@ static uint8_t const _wextraCommands[] = {
/* Literally everything. Notably useful for testing */
static uint8_t const _weverythingCommands[] = {
WARNING_BACKWARDS_FOR,
WARNING_BUILTIN_ARG,
WARNING_DIV,
WARNING_EMPTY_DATA_DIRECTIVE,

View File

@@ -870,11 +870,13 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
// Output ROMX if it was buffered
if (romx) {
// The value returned is either -1, or smaller than `totalRomxLen`,
// so it's fine to cast to `size_t`
writeLen = writeBytes(output, romx, totalRomxLen);
if (writeLen == -1) {
report("FATAL: Failed to write \"%s\"'s ROMX: %s\n", name, strerror(errno));
goto free_romx;
} else if (writeLen < totalRomxLen) {
} else if ((size_t)writeLen < totalRomxLen) {
report("FATAL: Could only write %ld of \"%s\"'s %ld ROMX bytes\n",
writeLen, name, totalRomxLen);
goto free_romx;
@@ -898,7 +900,9 @@ static void processFile(int input, int output, char const *name, off_t fileSize)
size_t thisLen = len > sizeof(bank) ? sizeof(bank) : len;
ssize_t ret = writeBytes(output, bank, thisLen);
if (ret != thisLen) {
// The return value is either -1, or at most `thisLen`,
// so it's fine to cast to `size_t`
if ((size_t)ret != thisLen) {
report("FATAL: Failed to write \"%s\"'s padding: %s\n",
name, strerror(errno));
break;

View File

@@ -1,11 +1,11 @@
.\"
.\" 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
.\"
.Dd December 5, 2019
.Dd March 28, 2021
.Dt RGBFIX 1
.Os
.Sh NAME

View File

@@ -1,11 +1,11 @@
.\"
.\" 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
.\"
.Dd February 23, 2018
.Dd March 28, 2021
.Dt GBZ80 7
.Os
.Sh NAME
@@ -879,9 +879,9 @@ Bytes: 2
Flags: None affected.
.Pp
This is sometimes written as
.Ql ldio [n16], a ,
.Ql LDIO [n16],A ,
or
.Ql ld [$ff00+n8], a .
.Ql LD [$FF00+n8],A .
.Ss LDH [C],A
Store value in register
.Sy A
@@ -895,9 +895,9 @@ Bytes: 1
Flags: None affected.
.Pp
This is sometimes written as
.Ql ldio [c], a ,
.Ql LDIO [C],A ,
or
.Ql ld [$ff00+c], a .
.Ql LD [$FF00+C],A .
.Ss LD A,[r16]
Load value in register
.Sy A
@@ -937,9 +937,9 @@ Bytes: 2
Flags: None affected.
.Pp
This is sometimes written as
.Ql ldio a, [n16] ,
.Ql LDIO A,[n16] ,
or
.Ql ld a, [$ff00+n8] .
.Ql LD A,[$FF00+n8] .
.Ss LDH A,[C]
Load value in register
.Sy A
@@ -953,9 +953,9 @@ Bytes: 1
Flags: None affected.
.Pp
This is sometimes written as
.Ql ldio a, [c] ,
.Ql LDIO A,[C] ,
or
.Ql ld a, [$ff00+c] .
.Ql LD A,[$FF00+C] .
.Ss LD [HLI],A
Store value in register
.Sy A
@@ -970,6 +970,11 @@ Cycles: 2
Bytes: 1
.Pp
Flags: None affected.
.Pp
This is sometimes written as
.Ql LD [HL+],A ,
or
.Ql LDI [HL],A .
.Ss LD [HLD],A
Store value in register
.Sy A
@@ -984,6 +989,11 @@ Cycles: 2
Bytes: 1
.Pp
Flags: None affected.
.Pp
This is sometimes written as
.Ql LD [HL-],A ,
or
.Ql LDD [HL],A .
.Ss LD A,[HLD]
Load value into register
.Sy A
@@ -998,6 +1008,11 @@ Cycles: 2
Bytes: 1
.Pp
Flags: None affected.
.Pp
This is sometimes written as
.Ql LD A,[HL-] ,
or
.Ql LDD A,[HL] .
.Ss LD A,[HLI]
Load value into register
.Sy A
@@ -1012,6 +1027,11 @@ Cycles: 2
Bytes: 1
.Pp
Flags: None affected.
.Pp
This is sometimes written as
.Ql LD A,[HL+], ,
or
.Ql LDI A,[HL] .
.Ss LD SP,n16
Load value
.Ar n16

View File

@@ -1,11 +1,11 @@
.\"
.\" 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
.\"
.Dd December 5, 2019
.Dd March 28, 2021
.Dt RGBGFX 1
.Os
.Sh NAME
@@ -78,7 +78,8 @@ Same as
.Fl f ,
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
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
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.

View File

@@ -46,7 +46,7 @@ static HashType hash(char const *str)
return hash;
}
bool hash_AddElement(HashMap map, char const *key, void *element)
void **hash_AddElement(HashMap map, char const *key, void *element)
{
HashType hashedKey = hash(key);
HalfHashType index = hashedKey;
@@ -61,23 +61,7 @@ bool hash_AddElement(HashMap map, char const *key, void *element)
newEntry->next = map[index];
map[index] = newEntry;
return newEntry->next != NULL;
}
bool hash_ReplaceElement(HashMap const map, char const *key, void *element)
{
HashType hashedKey = hash(key);
struct HashMapEntry *ptr = map[(HalfHashType)hashedKey];
while (ptr) {
if (hashedKey >> HALF_HASH_NB_BITS == ptr->hash
&& !strcmp(ptr->key, key)) {
ptr->content = element;
return true;
}
ptr = ptr->next;
}
return false;
return &newEntry->content;
}
bool hash_RemoveElement(HashMap map, char const *key)
@@ -99,7 +83,7 @@ bool hash_RemoveElement(HashMap map, char const *key)
return false;
}
void *hash_GetElement(HashMap const map, char const *key)
void **hash_GetNode(HashMap const map, char const *key)
{
HashType hashedKey = hash(key);
struct HashMapEntry *ptr = map[(HalfHashType)hashedKey];
@@ -107,13 +91,20 @@ void *hash_GetElement(HashMap const map, char const *key)
while (ptr) {
if (hashedKey >> HALF_HASH_NB_BITS == ptr->hash
&& !strcmp(ptr->key, key)) {
return ptr->content;
return &ptr->content;
}
ptr = ptr->next;
}
return NULL;
}
void *hash_GetElement(HashMap const map, char const *key)
{
void **node = hash_GetNode(map, key);
return node ? *node : NULL;
}
void hash_ForEach(HashMap const map, void (*func)(void *, void *), void *arg)
{
for (size_t i = 0; i < HASHMAP_NB_BUCKETS; i++) {

View File

@@ -106,12 +106,18 @@ static void processLinkerScript(void)
* @param section The section to assign
* @param location The location to assign the section to
*/
static inline void assignSection(struct Section *section,
struct MemoryLocation const *location)
static void assignSection(struct Section *section, struct MemoryLocation const *location)
{
section->org = location->address;
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--;
out_AddSection(section);

View File

@@ -205,7 +205,7 @@ static void cleanup(void)
int main(int argc, char *argv[])
{
int optionChar;
char *endptr; /* For error checking with `strtol` */
char *endptr; /* For error checking with `strtoul` */
unsigned long value; /* For storing `strtoul`'s return value */
/* Parse options */

View File

@@ -188,7 +188,7 @@ static void readFileStackNode(FILE *file, struct FileStackNode fileNodes[], uint
tryReadlong(parentID, file,
"%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,
"%s: Cannot read node #%" PRIu32 "'s line number: %s", fileName, i);
tryGetc(fileNodes[i].type, file, "%s: Cannot read node #%" PRIu32 "'s type: %s",
@@ -261,7 +261,7 @@ static void readSymbol(FILE *file, struct Symbol *symbol,
* @param i The number of the patch to report in errors
*/
static void readPatch(FILE *file, struct Patch *patch, char const *fileName, char const *sectName,
uint32_t i, struct Section *fileSections[], struct FileStackNode fileNodes[])
uint32_t i, struct FileStackNode fileNodes[])
{
uint32_t nodeID;
uint8_t type;
@@ -279,7 +279,6 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName, cha
tryReadlong(patch->pcSectionID, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
fileName, sectName, i);
patch->pcSection = patch->pcSectionID == -1 ? NULL : fileSections[patch->pcSectionID];
tryReadlong(patch->pcOffset, file,
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
fileName, sectName, i);
@@ -304,6 +303,16 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName, cha
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.
* @param file The file to read from
@@ -311,7 +320,7 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName, cha
* @param fileName The filename to report in errors
*/
static void readSection(FILE *file, struct Section *section, char const *fileName,
struct Section *fileSections[], struct FileStackNode fileNodes[])
struct FileStackNode fileNodes[])
{
int32_t tmp;
uint8_t byte;
@@ -388,12 +397,9 @@ static void readSection(FILE *file, struct Section *section, char const *fileNam
malloc(sizeof(*patches) * section->nbPatches + 1);
if (!patches)
err(1, "%s: Unable to read \"%s\"'s patches", fileName,
section->name);
for (uint32_t i = 0; i < section->nbPatches; i++) {
readPatch(file, &patches[i], fileName, section->name,
i, fileSections, fileNodes);
}
err(1, "%s: Unable to read \"%s\"'s patches", fileName, section->name);
for (uint32_t i = 0; i < section->nbPatches; i++)
readPatch(file, &patches[i], fileName, section->name, i, fileNodes);
section->patches = patches;
}
}
@@ -435,18 +441,18 @@ static void linkSymToSect(struct Symbol const *symbol, struct Section *section)
*/
static void readAssertion(FILE *file, struct Assertion *assert,
char const *fileName, uint32_t i,
struct Section *fileSections[], struct FileStackNode fileNodes[])
struct FileStackNode fileNodes[])
{
char assertName[sizeof("Assertion #4294967295")]; // UINT32_MAX
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",
fileName);
}
static inline struct Section *getMainSection(struct Section *section)
static struct Section *getMainSection(struct Section *section)
{
if (section->modifier != SECTION_NORMAL)
section = sect_GetSection(section->name);
@@ -549,7 +555,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
err(1, "%s: Couldn't create new section", fileName);
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;
if (nbSymPerSect[i]) {
fileSections[i]->symbols = malloc(nbSymPerSect[i]
@@ -567,7 +573,15 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
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++) {
int32_t sectionID = fileSymbols[i]->sectionID;
@@ -599,7 +613,8 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
if (!assertion)
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->next = assertions;
assertions = assertion;
@@ -635,7 +650,7 @@ static void freeSection(struct Section *section, void *arg)
free(section->name);
if (sect_HasData(section->type)) {
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);
}

View File

@@ -336,7 +336,7 @@ static void writeSymBank(struct SortedSections const *bankSections)
/**
* Write a bank's contents to the map file
* @param bankSections The bank's sections
* @return The bank's slack space
* @return The bank's used space
*/
static uint16_t writeMapBank(struct SortedSections const *sectList,
enum SectionType type, uint32_t bank)
@@ -350,14 +350,14 @@ static uint16_t writeMapBank(struct SortedSections const *sectList,
fprintf(mapFile, "%s bank #%" PRIu32 ":\n", typeNames[type],
bank + bankranges[type][0]);
uint16_t slack = maxsize[type];
uint16_t used = 0;
while (section || zeroLenSection) {
struct SortedSection const **pickedSection =
nextSection(&section, &zeroLenSection);
struct Section const *sect = (*pickedSection)->section;
slack -= sect->size;
used += sect->size;
if (sect->size != 0)
fprintf(mapFile, " SECTION: $%04" PRIx16 "-$%04" PRIx16 " ($%04" PRIx16 " byte%s) [\"%s\"]\n",
@@ -368,44 +368,54 @@ static uint16_t writeMapBank(struct SortedSections const *sectList,
fprintf(mapFile, " SECTION: $%04" PRIx16 " (0 bytes) [\"%s\"]\n",
sect->org, sect->name);
uint16_t org = sect->org;
while (sect) {
for (size_t i = 0; i < sect->nbSymbols; i++)
fprintf(mapFile, " $%04" PRIx32 " = %s\n",
sect->symbols[i]->offset + sect->org,
sect->symbols[i]->offset + org,
sect->symbols[i]->name);
sect = sect->nextu; // Also print symbols in the following "pieces"
}
*pickedSection = (*pickedSection)->next;
}
if (slack == maxsize[type])
if (used == 0) {
fputs(" EMPTY\n\n", mapFile);
else
} else {
uint16_t slack = maxsize[type] - used;
fprintf(mapFile, " SLACK: $%04" PRIx16 " byte%s\n\n", slack,
slack == 1 ? "" : "s");
}
return slack;
return used;
}
/**
* Write the total slack space by section type to the map file
* @param slackMap The total slack space by section type
* Write the total used space by section type to the map file
* @param usedMap The total used space by section type
*/
static void writeMapSlack(uint32_t slackMap[MIN_NB_ELMS(SECTTYPE_INVALID)])
static void writeMapUsed(uint32_t usedMap[MIN_NB_ELMS(SECTTYPE_INVALID)])
{
if (!mapFile)
return;
fputs("FREE:\n", mapFile);
fputs("USED:\n", mapFile);
for (uint8_t i = 0; i < SECTTYPE_INVALID; i++) {
enum SectionType type = typeMap[i];
// Do not output slack space for VRAM or OAM
// 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], slackMap[type], slackMap[type] == 1 ? "" : "s",
typeNames[type], usedMap[type], usedMap[type] == 1 ? "" : "s",
sections[type].nbBanks, sections[type].nbBanks == 1 ? "" : "s");
}
}
@@ -419,7 +429,7 @@ static void writeSymAndMap(void)
if (!symFileName && !mapFileName)
return;
uint32_t slackMap[SECTTYPE_INVALID] = {0};
uint32_t usedMap[SECTTYPE_INVALID] = {0};
symFile = openFile(symFileName, "w");
mapFile = openFile(mapFileName, "w");
@@ -434,11 +444,11 @@ static void writeSymAndMap(void)
struct SortedSections const *sect = &sections[type].banks[bank];
writeSymBank(sect);
slackMap[type] += writeMapBank(sect, type, bank);
usedMap[type] += writeMapBank(sect, type, bank);
}
}
writeMapSlack(slackMap);
writeMapUsed(usedMap);
closeFile(symFile);
closeFile(mapFile);

View File

@@ -37,7 +37,7 @@ struct RPNStack {
size_t capacity;
} stack;
static inline void initRPNStack(void)
static void initRPNStack(void)
{
stack.capacity = 64;
stack.values = malloc(sizeof(*stack.values) * stack.capacity);
@@ -46,7 +46,7 @@ static inline void initRPNStack(void)
err(1, "Failed to init RPN stack");
}
static inline void clearRPNStack(void)
static void clearRPNStack(void)
{
stack.size = 0;
}
@@ -92,7 +92,7 @@ static int32_t popRPN(struct FileStackNode const *node, uint32_t lineNo)
return stack.values[stack.size];
}
static inline void freeRPNStack(void)
static void freeRPNStack(void)
{
free(stack.values);
free(stack.errorFlags);
@@ -400,7 +400,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
void patch_CheckAssertions(struct Assertion *assert)
{
verbosePrint("Checking assertions...");
verbosePrint("Checking assertions...\n");
initRPNStack();
while (assert) {

View File

@@ -1,11 +1,11 @@
.\"
.\" 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
.\"
.Dd November 26, 2019
.Dd March 28, 2021
.Dt RGBLINK 1
.Os
.Sh NAME

View File

@@ -1,11 +1,11 @@
.\"
.\" 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
.\"
.Dd November 26, 2019
.Dd March 28, 2021
.Dt RGBLINK 5
.Os
.Sh NAME

View File

@@ -78,12 +78,12 @@ static bool popFile(void)
return true;
}
static inline bool isWhiteSpace(int c)
static bool isWhiteSpace(int c)
{
return c == ' ' || c == '\t';
}
static inline bool isNewline(int c)
static bool isNewline(int c)
{
return c == '\r' || c == '\n';
}

View File

@@ -194,11 +194,7 @@ void sect_AddSection(struct Section *section)
section->name, typeNames[section->type]);
} else {
/* If not, add it */
bool collided = hash_AddElement(sections, section->name,
section);
if (beVerbose && collided)
warnx("Section hashmap collision occurred!");
hash_AddElement(sections, section->name, section);
}
}
@@ -248,7 +244,7 @@ static void doSanityChecks(struct Section *section, void *ptr)
* Check if alignment is reasonable, this is important to avoid UB
* 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;
/* Too large an alignment may not be satisfiable */

View File

@@ -54,10 +54,7 @@ void sym_AddSymbol(struct Symbol *symbol)
}
/* If not, add it */
bool collided = hash_AddElement(symbols, symbol->name, symbol);
if (beVerbose && collided)
warnx("Symbol hashmap collision occurred!");
hash_AddElement(symbols, symbol->name, symbol);
}
struct Symbol *sym_GetSymbol(char const *name)

View File

@@ -1,11 +1,11 @@
.\"
.\" 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
.\"
.Dd January 26, 2018
.Dd March 28, 2021
.Dt RGBDS 5
.Os
.Sh NAME
@@ -22,11 +22,11 @@ This toolchain is in development and new features may require adding more inform
The following types are used:
.Pp
.Ar LONG
is a 32bit integer stored in littleendian format.
is a 32-bit integer stored in little-endian format.
.Ar BYTE
is an 8bit integer.
is an 8-bit integer.
.Ar STRING
is a 0terminated string of
is a 0-terminated string of
.Ar BYTE .
.Bd -literal
; Header

View File

@@ -1,11 +1,11 @@
.\"
.\" 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
.\"
.Dd March 7, 2018
.Dd March 28, 2021
.Dt RGBDS 7
.Os
.Sh NAME

View File

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

View File

@@ -0,0 +1,3 @@
SECTION UNION "X", WRAM0
SECTION UNION "X", WRAM0, ALIGN[16]

View File

@@ -0,0 +1,3 @@
ERROR: align-unattainable.asm(3):
Section "X"'s alignment cannot be attained in WRAM0
error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: assert-nosect-bank.asm(1):
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):
PC has no value outside a section
error: Assembly aborted (1 errors)!
error: Assembly aborted (1 error)!

View File

@@ -0,0 +1,43 @@
macro tickle
; There once was a bug where overwriting worked only on the second try, so
; try everything twice for good measure
; Skip this syntax for EQUS, as it is invalid
IF \2
\1 = 0
\1 = 0
PRINTLN \1
\1 EQU 0
\1 EQU 0
PRINTLN \1
ENDC
PURGE \1
PURGE \1
PRINTLN \1
DEF \1 EQU 0
DEF \1 EQU 0
PRINTLN \1
DEF \1 = 0
DEF \1 = 0
PRINTLN \1
DEF \1 EQUS "hello"
DEF \1 EQUS "hello"
PRINTLN \1
REDEF \1 = 0
REDEF \1 = 0
PRINTLN \1
REDEF \1 EQUS "hello"
REDEF \1 EQUS "hello"
PRINTLN \1
endm
; Representative numeric and string builtins
tickle __LINE__, 1
tickle __FILE__, 0

View File

@@ -0,0 +1,57 @@
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(7):
'__LINE__' already defined as constant at <builtin>
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(8):
'__LINE__' already defined as constant at <builtin>
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(11):
'__LINE__' already defined at <builtin>
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(12):
'__LINE__' already defined at <builtin>
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(16):
Built-in symbol '__LINE__' cannot be purged
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(17):
Built-in symbol '__LINE__' cannot be purged
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(20):
'__LINE__' already defined at <builtin>
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(21):
'__LINE__' already defined at <builtin>
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(24):
'__LINE__' already defined as constant at <builtin>
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(25):
'__LINE__' already defined as constant at <builtin>
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(28):
'__LINE__' already defined at <builtin>
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(29):
'__LINE__' already defined at <builtin>
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(32):
'__LINE__' already defined as constant at <builtin>
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(33):
'__LINE__' already defined as constant at <builtin>
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(36):
'__LINE__' already defined as non-EQUS at <builtin>
ERROR: builtin-overwrite.asm(42) -> builtin-overwrite.asm::tickle(37):
'__LINE__' already defined as non-EQUS at <builtin>
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(16):
Built-in symbol '__FILE__' cannot be purged
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(17):
Built-in symbol '__FILE__' cannot be purged
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(20):
'__FILE__' already defined at <builtin>
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(21):
'__FILE__' already defined at <builtin>
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(24):
'__FILE__' already defined as constant at <builtin>
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(25):
'__FILE__' already defined as constant at <builtin>
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(28):
'__FILE__' already defined at <builtin>
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(29):
'__FILE__' already defined at <builtin>
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(32):
'__FILE__' already defined as constant at <builtin>
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(33):
'__FILE__' already defined as constant at <builtin>
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(36):
Built-in symbol '__FILE__' cannot be redefined
ERROR: builtin-overwrite.asm(43) -> builtin-overwrite.asm::tickle(37):
Built-in symbol '__FILE__' cannot be redefined
error: Assembly aborted (28 errors)!

View File

@@ -0,0 +1,14 @@
$9
$D
$12
$16
$1A
$1E
$22
$26
builtin-overwrite.asm
builtin-overwrite.asm
builtin-overwrite.asm
builtin-overwrite.asm
builtin-overwrite.asm
builtin-overwrite.asm

29
test/asm/def.asm Normal file
View File

@@ -0,0 +1,29 @@
def variable = 1
println variable
def variable set 2
println variable
redef variable = 3
println variable
redef variable set 4
println variable
DEF constant EQU 42
println constant
DEF string EQUS "here"
println "{string}"
rsreset
def _x rb
def _y rw 2
def _z rl
def _size rb 0
println "{_x} {_y} {_z} {_size}"
def constant equ 6*7 ; fails
println constant
redef string equs "there"
println "{string}"
redef constant equ 6*9 ; syntax error

5
test/asm/def.err Normal file
View File

@@ -0,0 +1,5 @@
ERROR: def.asm(23):
'constant' already defined at def.asm(10)
ERROR: def.asm(29):
syntax error, unexpected EQU, expecting SET or = or EQUS
error: Assembly aborted (2 errors)!

9
test/asm/def.out Normal file
View File

@@ -0,0 +1,9 @@
$1
$2
$3
$4
$2A
here
$0 $1 $5 $9
$2A
there

5
test/asm/def.simple.err Normal file
View File

@@ -0,0 +1,5 @@
ERROR: def.asm(23):
'constant' already defined at def.asm(10)
ERROR: def.asm(29):
syntax error
error: Assembly aborted (2 errors)!

View File

@@ -2,4 +2,4 @@ warning: deprecated-pi.asm(2): [-Wobsolete]
`_PI` is deprecated; use 3.14159
ERROR: deprecated-pi.asm(3):
Built-in symbol '_PI' cannot be purged
error: Assembly aborted (1 errors)!
error: Assembly aborted (1 error)!

View File

@@ -1,36 +1,62 @@
_ASM equ 0
def _ASM equ 0
test: MACRO
; Test RGBASM
V equs "_ASM +"
; Test RGBASM
redef V equs "_ASM +"
static_assert \#
PURGE V
; Test RGBLINK
V equs "_LINK +"
; Test RGBLINK
redef V equs "_LINK +"
assert \#
PURGE V
ENDM
for x, -300, 301
for y, -x - 1, x + 2
if y != 0
q = x / y
r = x % y
test_mod: MACRO
def x = \1 ; dividend
def y = \2 ; divisor
shift 2
def q = x / y ; quotient
def r = x % y ; remainder
; identity laws
test (V (q * y + r)) == (V x)
test (V (x + y) % y) == (V r)
test (V (x - y) % y) == (V r)
endc
endr
endr
ENDM
for x, -300, 301
for p, 31
y = 2 ** p
r = x % y
m = x & (y - 1)
test_each_mod: MACRO
test_mod (\1), (\2)
test_mod (\1), -(\2)
test_mod -(\1), (\2)
test_mod -(\1), -(\2)
ENDM
test_pow: MACRO
def x = \1 ; dividend
def y = 2 ** \2 ; divisor
def r = x % y ; remainder
def m = x & (y - 1) ; mask
; identity law
test (V r) == (V m)
endr
endr
ENDM
test_each_pow: MACRO
test_pow (\1), (\2)
test_pow -(\1), (\2)
ENDM
test_each_mod 0, 1
test_each_mod 7, 5
test_each_mod 42, 256
test_each_mod 567, 256
test_each_mod 256, 512
test_each_mod 1, 65535
test_each_mod 100, 65535
test_each_mod 10000, 65535
test_each_mod 1000000, 65535
test_each_pow 5, 1
test_each_pow 42, 8
test_each_pow 567, 8
test_each_pow 12345, 16
test_each_pow 99999, 16
SECTION "LINK", ROM0
_LINK::

View File

@@ -2,4 +2,4 @@ ERROR: ds-bad.asm(3):
Expected constant expression: 'unknown' is not constant at assembly time
warning: ds-bad.asm(4): [-Wtruncation]
Expression must be 8-bit
error: Assembly aborted (1 errors)!
error: Assembly aborted (1 error)!

View File

@@ -0,0 +1,23 @@
if 1
println "taken if"
elif 2 / 0 ; avoided fatal "Division by zero" error
println "untaken elif"
elif 3 / 0 ; avoided fatal "Division by zero" error
println "untaken after untaken"
endc
if 0
println "untaken if"
elif 1
println "taken elif"
elif !@#$ ; avoided fatal syntax error
println "untaken elif"
elif %^&* ; avoided fatal syntax error
println "untaken after untaken"
endc
if 0
println "untaken if"
elif 1 / 0 ; fatal "Division by zero" error
println "unreached elif"
endc

View File

@@ -0,0 +1,2 @@
FATAL: elif-after-taken-if.asm(21):
Division by zero

View File

@@ -0,0 +1,2 @@
taken if
taken elif

View File

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

View File

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

View File

@@ -0,0 +1,6 @@
test: MACRO
v equs "X"
X equs "" ; should not be expanded
\1
ENDM
test v 0

View File

@@ -0,0 +1,3 @@
ERROR: expand-empty-string.asm(6) -> expand-empty-string.asm::test(4):
syntax error, unexpected number
error: Assembly aborted (1 error)!

View File

View File

@@ -0,0 +1,3 @@
ERROR: expand-empty-string.asm(6) -> expand-empty-string.asm::test(4):
syntax error
error: Assembly aborted (1 error)!

5
test/asm/ff00-plus-c.asm Normal file
View File

@@ -0,0 +1,5 @@
SECTION "test", ROM0[0]
ld [ $ff00 + c ], a
; 257 spaces exceeds both LEXER_BUF_SIZE (42) and uint8_t limit (255)
ld [ $ff00 + c ], a
ld [ $ff00 + c ], a

0
test/asm/ff00-plus-c.err Normal file
View File

0
test/asm/ff00-plus-c.out Normal file
View File

View File

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

View File

@@ -8,13 +8,17 @@ for v, 0
endr
for v, 2, 1
print "unreached"
print "backwards"
endr
for v, 1, 2, 0
print "unreached"
endr
for v, 1, 2, -1
print "backwards"
endr
for x, 1, 5+1
print "{d:x} "
endr
@@ -33,7 +37,7 @@ endr
println "-> {d:q}"
s EQUS "x"
for s, 3, 30, 3
for {s}, 3, 30, 3
print "{d:x} "
endr
println "-> {d:x}"

View File

@@ -1,6 +1,10 @@
warning: for.asm(12): [-Wbackwards-for]
FOR goes backwards from 2 to 1 by 1
ERROR: for.asm(16):
FOR cannot have a step value of 0
ERROR: for.asm(41) -> for.asm::REPT~4(47):
'v' already defined as constant at for.asm(41) -> for.asm::REPT~4(45)
FATAL: for.asm(41) -> for.asm::REPT~4(47):
warning: for.asm(20): [-Wbackwards-for]
FOR goes backwards from 1 to 2 by -1
ERROR: for.asm(45) -> for.asm::REPT~4(51):
'v' already defined as constant at for.asm(45) -> for.asm::REPT~4(49)
FATAL: for.asm(45) -> for.asm::REPT~4(51):
Failed to update FOR symbol value

View File

@@ -0,0 +1,15 @@
num equ 42
fix equ 123.0
str equs "hello"
println "{#0260x:num}"
println "{#-260x:num}"
println "{0280.260f:fix}"
println "{260s:str}"
println "{-260s:str}"
println "<{#0260x:num}>"
println "<{#-260x:num}>"
println "<{0280.260f:fix}>"
println "<{260s:str}>"
println "<{-260s:str}>"

View File

@@ -0,0 +1,35 @@
ERROR: format-truncation.asm(5):
Formatted numeric value too long
ERROR: format-truncation.asm(6):
Formatted numeric value too long
ERROR: format-truncation.asm(7):
Fractional width 260 too long, limiting to 255
ERROR: format-truncation.asm(7):
Formatted numeric value too long
ERROR: format-truncation.asm(8):
Formatted string value too long
ERROR: format-truncation.asm(9):
Formatted string value too long
ERROR: format-truncation.asm(11):
Formatted numeric value too long
warning: format-truncation.asm(11): [-Wlong-string]
String constant too long
ERROR: format-truncation.asm(12):
Formatted numeric value too long
warning: format-truncation.asm(12): [-Wlong-string]
String constant too long
ERROR: format-truncation.asm(13):
Fractional width 260 too long, limiting to 255
ERROR: format-truncation.asm(13):
Formatted numeric value too long
warning: format-truncation.asm(13): [-Wlong-string]
String constant too long
ERROR: format-truncation.asm(14):
Formatted string value too long
warning: format-truncation.asm(14): [-Wlong-string]
String constant too long
ERROR: format-truncation.asm(15):
Formatted string value too long
warning: format-truncation.asm(15): [-Wlong-string]
String constant too long
error: Assembly aborted (12 errors)!

View File

@@ -0,0 +1,10 @@
$0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002a
$2a
123.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
hello
hello
<$0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002
<$2a
<123.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
< hell
<hello

View File

@@ -1,3 +1,3 @@
ERROR: fragment-align.asm(25):
Section's alignment fails required alignment (offset from section start = $0004)
error: Assembly aborted (1 errors)!
error: Assembly aborted (1 error)!

View File

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

View File

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

View File

@@ -1,3 +1,3 @@
ERROR: incbin-empty-bad.asm(3):
Specified range in INCBIN is out of bounds (0 + 1 > 0)
error: Assembly aborted (1 errors)!
error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: incbin-end-bad.asm(3):
Specified range in INCBIN is out of bounds (123 + 1 > 123)
error: Assembly aborted (1 errors)!
error: Assembly aborted (1 error)!

View File

@@ -0,0 +1,4 @@
; It seems that \1 was the easiest way to notice the memory corruption that
; resulted from this overflow
x = 0
{.99999999f:x}\1

View File

@@ -0,0 +1,9 @@
ERROR: interpolation-overflow.asm(4):
Fractional width 99999999 too long, limiting to 255
ERROR: interpolation-overflow.asm(4):
Formatted numeric value too long
warning: interpolation-overflow.asm(4): [-Wlarge-constant]
Precision of fixed-point constant is too large
while expanding symbol "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
FATAL: interpolation-overflow.asm(4):
Macro argument '\1' not defined

View File

View File

@@ -0,0 +1,2 @@
recurse EQUS "\{recurse\}"
{recurse}

View File

@@ -0,0 +1,67 @@
FATAL: interpolation-recursion.asm(2):
Recursion limit (64) exceeded
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"
while expanding symbol "{recurse}"

View File

View File

@@ -1,3 +1,3 @@
ERROR: label-outside-section.asm(1):
Label "bad" created outside of a SECTION
error: Assembly aborted (1 errors)!
error: Assembly aborted (1 error)!

View File

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

View File

@@ -1,3 +1,3 @@
ERROR: line-continuation-whitespace.asm(7):
Label "foo" created outside of a SECTION
error: Assembly aborted (1 errors)!
error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: line-continuation.asm(7):
Label "foo" created outside of a SECTION
error: Assembly aborted (1 errors)!
error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: load-rom.asm(3):
`LOAD` blocks cannot create a ROM section
error: Assembly aborted (1 errors)!
error: Assembly aborted (1 error)!

View File

@@ -1,3 +1,3 @@
ERROR: local-purge.asm(8):
Interpolated symbol ".loc" does not exist
error: Assembly aborted (1 errors)!
error: Assembly aborted (1 error)!

View File

@@ -0,0 +1,11 @@
SECTION "Test", ROM0
MACRO a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
println "truncated :("
ENDM
a012:
a012.local
a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012:
a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012.local

View File

@@ -0,0 +1,7 @@
warning: local-truncated.asm(10): [-Wlong-string]
Symbol name too long, got truncated
ERROR: local-truncated.asm(10):
'a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001' already defined at local-truncated.asm(3)
warning: local-truncated.asm(11): [-Wlong-string]
Symbol name too long, got truncated
error: Assembly aborted (1 error)!

View File

@@ -0,0 +1 @@
truncated :(

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