aboutsummaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAgeFilesLines
* Prevent out of bounds panics on range_to_human()Miquel Sabaté Solà2026-07-091-1/+1
| | | | | | | | | | | | | | | | On range_to_human(), if the range is completely broken for some reason, we might get a start = 0 and end = 0. In this case, then we would get into an 'end - 1' computation for an 'usize', resulting in bad arithmetics. This can be avoided altogether if the 'start + 1' computation from the start of the function not only results to be equal, but also larger than 'end'. This, of course, doesn't make much sense, and it's most probably a bandaid; but it has been detected via fuzzy testing with wild inputs. Hence, it's fine if this function doesn't return a coherent string representation for deranged inputs. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* scripts: separate fuzzy testing into its own fileMiquel Sabaté Solà2026-07-092-9/+13
| | | | | | | This way we can simply run fuzzy testing multiple times from the shell without having to run the full tests too. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Minor style fixes from an upgraded clippyMiquel Sabaté Solà2026-07-093-3/+3
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Add support for the asan:fixed-segments commentMiquel Sabaté Solà2026-07-098-24/+370
| | | | | | | | This magic comment allows for the definition of segments that are fixed and for which references are always safe. This goes in tandem with the asan:safe comment, which can then be used more sporadically. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Add a warning for unknown cross-mapping referencesMiquel Sabaté Solà2026-07-089-2/+286
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Some segments, like the 'vectors' one, will reference code that is outside of its mapping. But in some other configurations, segments cannot make these cross-mapping references so happily. Imagine: .segment "SWAPPABLE" .proc foo rts .endproc .segment "FIXED" jsr foo Here the assembler will properly detect the address of 'foo' in the context of the 'SWAPPABLE' segment. But what this assembler doesn't know is that this segment is swappable (e.g. UNROM chip). Hence, if the bank being mapped right now is not the one containing the 'SWAPPABLE' segment, then the address computed for 'foo' and used in that 'jsr' instruction will point to something else entirely. This would be similar to a use-after-free bug. This is something that can only be inspected at runtime, and so the assembler cannot be of much help here. Hence, this commit adds a warning so the programmer can understand the potentially dangerous operation. All of that being said, this commit also adds support for "asan:safe" or "check:safe", which is a magic comment that the programmer can write to re-assure the assembler that this operation is fine (e.g. there is a guarantee that the mapped bank is that one we are expecting). Hence, the code above could now be written like so: jsr foo ; check:safe Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Add the SectionType::Vector enum valueMiquel Sabaté Solà2026-07-082-3/+14
| | | | | | | It's actually valuable to distinguish that part of PRG-ROM that is part of the vectors segment. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* lib: add descriptions in Cargo.toml filesMiquel Sabaté Solà2026-07-082-1/+3
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Upgrade to the 2024 edition of RustMiquel Sabaté Solà2026-07-0813-289/+384
| | | | | | | And also adjust the code so the clippy from the 2024 edition is fine with it. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Let arguments be able to reference their macroMiquel Sabaté Solà2026-07-076-37/+69
| | | | | | | | | | | | | | The enum type for macro arguments has been adjusted so it accepts a String. This is then used to store the name of the macro for this argument. This is a bit of a circlejerk, but in the end it stems from the fact that macro arguments were sort of a hack defined ad-hoc each time they were needed. That being said, if we want to report macro arguments being unused, we need to reference the actual macro definition, not the macro call. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Be more specific on unused objectsMiquel Sabaté Solà2026-07-072-3/+8
| | | | | | | | | | It was downright lazy from my end to not inspect the line of the object that was being unused. Fix this by trying to fetch the bundle's node. This right now applies to variable definitions. Objects like macro arguments are pending to be done. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Preserve the 'accessed' member on macro argumentsMiquel Sabaté Solà2026-07-075-1/+46
| | | | | | | | | | | | | | | Macro arguments get tampered with every time a new call has to be bundled. Deep down this is done by cloning the underlying object, but this resets any increase on the 'accessed' member, giving always the impression that macro arguments are never accessed. Fix this by at least preserving this member when cloning a macro argument. Note that this is neither the most elegant solution, and probably not the most correct. As in, it will probably get into a non-precise count after some calls. But at least it will be a non-zero value, which is more than enough to what this is actually used for. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Do not optimize away unresolved non-jumpsMiquel Sabaté Solà2026-07-071-1/+31
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | In the following code: .macro MACRO ADDR adc ADDR .endmacro MACRO label label: nop The evaluation of the 'adc' instruction went into being re-sized to 1 despite indications of being an address (and hence hinting to a size of 2). This was done because of the optimization to shrink a single byte left arm from absolute to relative addressing. But on unresolved bundles this is bad, as everything may still be zeroed out, and the instruction itself doesn't hint on the end size. This shrinking would then mess with the segment's offset, and we would end up with an offset of 2 instead of 3 for the label 'label'. Note that this is in the same spirit as commit ed3d34b0afb1 ("Only shrink the addressing on resolved bundles"), but now applied to the get_from_left() function which apparently was spared for whatever reason. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Refer to the original file for unused objectsMiquel Sabaté Solà2026-06-056-22/+49
| | | | | | | | | | | | | | | | | | | When warning users on unused objects (e.g. variables, proc's), we have to pick up the source of origin for this warning. In places where the original PNode is not available, then we go with the last source we have at hand, as that's the usual way to go (i.e. the error occurred at the current source/context). That being said, for unused objects that's not desirable, because we might otherwise claim the error to happen on the file we first targetted, but it's way more useful to understand where the object was defined. Hence, when defining a variable, address or proc, let's actually store the PNode associated with it as well. This way the checker can hopefully get the source from this PNode and be more clear to the user. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* tests: check on the existence of jetpac.nesMiquel Sabaté Solà2026-04-301-1/+6
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* xa65: sort the flags on the help messageMiquel Sabaté Solà2026-04-301-1/+1
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Fix tabulation on --allow-unused help's messageMiquel Sabaté Solà2026-04-301-1/+1
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* ci: override LC_ALL for a more consistent 'sort'Miquel Sabaté Solà2026-04-302-2/+2
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* tests: fix the assembler fuzzerMiquel Sabaté Solà2026-04-301-2/+16
| | | | | | We needed to pass the new 'allow_unused' parameter. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Rename "raw address" to "label"Miquel Sabaté Solà2026-04-302-3/+3
| | | | | | This is much clearer to the programmer. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* nasm: add documentation to the 'unused' checksMiquel Sabaté Solà2026-04-301-0/+23
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* tests: adapt to the new 'unused' checksMiquel Sabaté Solà2026-04-304-19/+35
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Issue a warning on unused macrosMiquel Sabaté Solà2026-04-301-0/+33
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Don't do the 'accessed' check if "check:ignore" was givenMiquel Sabaté Solà2026-04-301-2/+5
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Allow a magic "check:ignore" commentMiquel Sabaté Solà2026-04-301-1/+1
| | | | | | | This is basically the same as "asan:ignore", but to the programmer it will sound less weird on non-ASAN uses. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* nasm/xa65: add the --allow-unused optionMiquel Sabaté Solà2026-04-303-2/+31
| | | | | | | | | Since the addition of the check() function in commit 6a9dea943c07 ("Add a new 'check' stage in assemble()"), it can be annoying to some users that the check for unused/unreferenced object applies by default. Hence, add a flag so this can be disabled. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Add a new 'check' stage in assemble()Miquel Sabaté Solà2026-04-302-108/+108
| | | | | | | | This is actually an expansion from asan(), but it also checks for stuff that goes outside of the realm of address sanitation. In this aspect, asan() has been integrated inside of the new check() function. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Send an error for unused .proc'sMiquel Sabaté Solà2026-04-285-42/+111
| | | | | | | | | | | | | | | | | We cannot safely detect all scenarios in which there is dead code, but we can safely do it for .proc's. Even if they are not called directly, they might be referenced via jump tables and shenanigans like that. Long story short, if you are not referencing a .proc in any meaningful way, then we have dead code. A pattern could also be given in which a function that was too long has been splitted into smaller functions which are not called directly. This is, in my opinion, an anti-pattern (as we generally expect an rts or a jmp from .proc's), and anyways can be avoided via the use of __fallthrough__ if the programmer is really set to write this kind of code. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* nasm: add documentation on the exit status codeMiquel Sabaté Solà2026-04-281-0/+9
| | | | | | | I apparently never bothered to do so. It's a good idea to have this as it might not be all that expected. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* nasm: call exit() right after error detectionMiquel Sabaté Solà2026-04-281-90/+88
| | | | | | | | The 'if error_count == 0' branch came from old code that accumulated over and over. Since this was the only branch taken before exit, let's prefer the other way around so we can remove one indentation level. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* nasm: use buffered writing for full ROMs as wellMiquel Sabaté Solà2026-04-281-4/+8
| | | | | | | | This was already implemented for the --split-segments option. Not let's bring this when building full ROM files, as writing things byte by byte is slow. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* nasm: avoid an unnecessary loop when writing bytesMiquel Sabaté Solà2026-04-281-5/+3
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* nasm: add the --split-segments optionMiquel Sabaté Solà2026-04-282-33/+81
| | | | | | | | | | | | This allows the programmer to tell nasm to leave each segment into its own .out file. This can be useful when writing some specific segments into different chips. The caveat is that filling is not applied, and hence it's up to the programmer to account for that if two or more segments happened to be on the same mapping and needed some alignment in between. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* nasm: add a description on the README.md fileMiquel Sabaté Solà2026-04-251-0/+14
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* tests: update submodules to their latest commitsMiquel Sabaté Solà2026-04-253-0/+0
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Add the context's name into memory rangesMiquel Sabaté Solà2026-04-252-4/+8
| | | | | | | | This is applied whenever a given memory range name is not under the global context. The end result is that scopes will be shown into the memory.txt file when using '--write-info'. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Add the .min and the .max control expressionsMiquel Sabaté Solà2026-04-243-0/+91
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Allow scoped names in __fallthrough__Miquel Sabaté Solà2026-04-243-1/+11
| | | | | | Fixes 4f1a9c660108 ("Add the .fallthrough control statement") Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Add a big endian control statementMiquel Sabaté Solà2026-04-243-5/+73
| | | | | | | | | | | By default everything is little endian, and that should always be the case. That being said, if a literal is better expressed in big endian format for whatever reason, you can now use the .be or the .bigendian control statements. The terribly named .dbyt control statement can also be used for this purpose, but that's just to be compatible with the implementation from ca65. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Add the .version pseudo-variableMiquel Sabaté Solà2026-04-243-0/+45
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* ci: upgrade to actions/checkout@v6Miquel Sabaté Solà2026-04-241-5/+5
| | | | | | | This fixes a deprecation warning on an outdated Node.js version being used. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Update the code style to the latest stable RustMiquel Sabaté Solà2026-04-244-22/+15
| | | | | | | | | | This mostly involves applying the collapsible_match rule[1] where appropiate, and using sort_by_key() as pointed out by the latest stable version of clippy. [1] https://rust-lang.github.io/rust-clippy/rust-1.95.0/index.html#collapsible_match Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* tests: add jetpac.nes to the end-to-end testsMiquel Sabaté Solà2026-04-243-0/+23
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Fix outdated comment on __fallthrough__Miquel Sabaté Solà2026-04-241-4/+4
| | | | | | | | | | Since commit 31dd071e927a ("Change .fallthrough to __fallthrough__") this pseudo control statement is declared via underscores, not like a proper control statement. That was done in order to avoid clashes with ca65, and in order to allow nasm's --prelude to produce a .macro replacement for it when using ca65. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Fix calls with arguments containing addressesMiquel Sabaté Solà2026-04-241-1/+46
| | | | | | | | | | | | | | | | | | | | | | | | | Some macro calls could contain addresses, like this: MACRO_CALL @address Before this commit this was resolved at the "crunch" stage, but only because we were grabbing the value from the part that was not resolved. However, if this unresolved object came from a node which is not just a value (e.g. an arithmetic operation), then it just applied the computation from before the "crunch" stage. So, for something like this: MACRO_CALL @address + 1 the argument would have been computed as simply "1", as that unresolved object only contained the "+ 1" known part. Fix this by re-evaluating the node at "crunch" stage whenever we have an attached "node" member to the given pending definition. This way, we can evaluate the full node and get the proper value from the arithmetic computation. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Ensure expressions in arguments are fully parsedMiquel Sabaté Solà2026-04-232-0/+33
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | Imagine the following scenario: lda #.lobyte(@address + 1) Here we have an immediate which comes from the .lobyte call. That being said, since this is using the '#' symbol, we look ahead after noticing it just in case we need to disambiguate some scenarios. When a parenthesis is found, in extract_paren_expression() we temporarily discard the looking ahead status as expressions inside of parenthesis are no longer affected by any hypothetical external ambiguity. This, though, was not being done in parse_arguments(), which is the function that would be called in the above scenario. Hence, ensure that we temporarily decrease the look ahead status for the inner arguments, and then increase it back when we are done. This way, when the parser finds '@address', it will no longer go the "we have found a 'value', refrain from ambiguities and return early" route, and it will check for any binary expression. Note that all this dance actually applied here, as binary operations are a source of ambiguities, and the look ahead mechanism was brought about because of these kinds of expressions. Hence, it's not like the look ahead mechanism is troublesome in on itself, we just missed the special handling on this specific scenario. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* xa65: fix indentation on the help messageMiquel Sabaté Solà2026-04-231-3/+1
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Add documentation for asan:stackMiquel Sabaté Solà2026-04-081-0/+23
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Implement the asan:stack statementMiquel Sabaté Solà2026-04-0813-7/+348
| | | | | | | | | | | | | | This statement allows programmers to reserve a memory range for the stack. This is useful to reserve a memory region which is not attached to any variable, and: 1. It is a way to safely reserve space on the $0100 page. 2. The --stats/--write-info flags will be able to add up the memory space reserved for the stack. 3. Future tooling might be able to use this value as a way to detect stack overflows. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Re-create the values for bundle call argumentsMiquel Sabaté Solà2026-03-105-4/+94
| | | | | | | | | | | | | | | | | | Bundle call arguments were fine most of the times, when arguments could be processed as-is and there were no issues with arguments being overwritten by successive calls. This was not the case, though, whenever a given instruction was delayed into a PendingNode status. In this case, the argument would get the last value, and in some extreme cases that definition might not have been there any more. Prevent all of this by providing a list of PendingDefine's, which are a way to re-create these call-only arguments as they were initially found. These PendingDefine's are then created on "crunch" on each PendingNode. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Ignore GLOBAL_CONTEXT on force_context_switch()Miquel Sabaté Solà2026-03-091-0/+6
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>