aboutsummaryrefslogtreecommitdiff
path: root/lib/xixanta/src/assembler.rs
Commit message (Collapse)AuthorAgeFilesLines
...
* Apply suggested style fixes from clippyMiquel Sabaté Solà2025-08-171-25/+17
| | | | Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Implement echo control statementsMiquel Sabaté Solà2025-01-221-1/+30
| | | | Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* nasm: Implement the -D flagMiquel Sabaté Solà2025-01-221-4/+64
| | | | | | | This allows users to define variables directly from the command line, which is useful for testing purposes. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Introduce looking ahead when parsing literalsMiquel Sabaté Solà2025-01-211-1/+1
| | | | | | | | | | | | | | | | | | | | | | There were certain situations in which syntax ambiguity could arise. For example, the parser as it stood could treat a valid expression such as 'lda #$80 >> 2' in an unexpected 'lda #$(80 >> 2)'. This is of course bad, and it came from the fact that literals don't have enclosing characters, and white spaces are not enough to provide disambiguation in some cases. Because of this fact, the parser now has a "look ahead" capability similar to many other parsers, and it's applied for now only to literals. This looking ahead actually honors parenthesis, so these can be added if the programmer wants to explicitely disambiguate an expression. This involved quite the heavy lifting, and some functions like 'parse_expression_with_identifier' had to be removed with the rewrite. This had the side effect of having (hopefully) more sane functions all around, and the parser also has a better capability to differentiate between regular Values and Calls. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Whitelist valid identifier charactersMiquel Sabaté Solà2025-01-161-5/+14
| | | | | | | | Instead of making up a list of characters that end an identifier, do the other way around since it's far less cumbersome and it prevents from silly bugs such as "var+1" being considered a single identifier. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Permit 16-bit decimal valuesMiquel Sabaté Solà2025-01-161-9/+18
| | | | | | | | | | As a remnant of old code, the 'parse_decimal' function was not allowing for decimal values larger than 8-bits. This was not the case in other areas such as 'parse_hexadecimal', and in the rest of the code we already cover that immediates are not too big in instructions. Hence, this restriction can be lift up and allow up to 16-bit decimal literals. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Implement .ifdef/.ifndef statementsMiquel Sabaté Solà2025-01-161-0/+49
| | | | | | | They are just synonyms for ".if .defined" and ".if !.defined" respectively. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Implement .def/.defined expressionsMiquel Sabaté Solà2025-01-161-0/+53
| | | | | | | This can be combined with .if/.elsif statements just like any other expression. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Implement .if/.elsif/.else statementsMiquel Sabaté Solà2025-01-161-11/+133
| | | | Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Add boolean operatorsMiquel Sabaté Solà2025-01-161-7/+60
| | | | | | | | | | | Allow for expressions that evaluate to a boolean expression. This in turn mean that the value is just set to 0 or 1 depending on the given condition. As with other assemblers, only a value of 0 evaluates to 0, and others go to 1. So, something like "1 && 2" evaluates to 1 even if it doesn't make much sense at first glance (as an assembler we just assume that the programmer knows what it's doing). Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Move check for ASCII-only strings into the parserMiquel Sabaté Solà2025-01-151-9/+2
| | | | Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Implement .asciiz control statementMiquel Sabaté Solà2025-01-141-10/+69
| | | | Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Implement the .res control statementMiquel Sabaté Solà2025-01-141-0/+97
| | | | | | | | | | | | | Similarly to other assemblers, this allows the programmer to write a definite amount of bytes with the same values. Compared to other assemblers there are two things to notice. First, there is a limit to it (i.e. whatever can fit in 2 bytes). Second, if the fill value is not provided, then it will default to the current mapping's fill value, or just 0x00 if the current mapping doesn't define one of its own. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* tests: Update code.nes so -Werror can be passedMiquel Sabaté Solà2025-01-131-20/+2
| | | | | | | | | Update the underlying code.nes test data so it uses a tighter configuration for NROM which in turn frees nasm from complaining about empty segments. This makes room to allo for -Werror so we catch warnings that might appear in the future. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Improve the message on bad file includesMiquel Sabaté Solà2025-01-101-3/+2
| | | | | | Also remove some pending TODOs. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Properly fill the context stack when forcing itMiquel Sabaté Solà2025-01-101-2/+48
| | | | | | | | | | | | | When calling `force_context_switch` the stack was mindlessly pushing the given name without taking into consideration how scopes are to be laid out. This made some variables/addresses that were previously preserved no longer reachable when crunching pending nodes. This patch also makes `force_context_switch` reset the stack before doing anything at all, which means that `force_context_pop` was no longer relevant. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Improve error message on unknown variable/scopeMiquel Sabaté Solà2025-01-091-10/+8
| | | | Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Prevent assignments on relative referencesMiquel Sabaté Solà2025-01-091-1/+17
| | | | | | | | | | | | It is a really weird thing to do, but on a twisted way I can see how someone could think of such a monstrosity. Panicking on such a case is not valid because that's not the fault from the assembler but from the programmer, and so a proper message should be displayed instead. This was detected via the fuzzer, but it was a side effect from e7c5e63d04f6 ("Evaluate bare numbers as decimal values"). Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Reset the literal mode on each macro argumentMiquel Sabaté Solà2025-01-091-0/+20
| | | | Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Evaluate bare numbers as decimal valuesMiquel Sabaté Solà2025-01-091-6/+11
| | | | | | | | | | In places like macro calls, the programmer might actually prefer to pass a numeric argument as is, without any prefixes. In these cases, just evaluate it as a decimal. In fact, this was already covered when evaluating the context because the same thing happens to assignments. Hence, I just needed to expand the scope of this. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Rename SourceInfo::working_directory to directoryMiquel Sabaté Solà2025-01-091-2/+2
| | | | | | Also documented the struct as it is exported. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Merge all error types into a single oneMiquel Sabaté Solà2025-01-091-132/+134
| | | | | | | | They started with good intentions, but in the end they were all pretty much alike. Hence, it makes sense to simplify everything and provide a single struct. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Remove ContextErrorReason as it was unusedMiquel Sabaté Solà2025-01-091-10/+1
| | | | Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Implement the .include statementMiquel Sabaté Solà2025-01-091-81/+228
| | | | | | | | | | | | | | This needed some heavy lifting when it comes to how files were located. This means that statements like .include/.incbin now take into consideration a new list made out of SourceInfo, which holds enough information to translate from which file a node comes from. This has also been added into errors, so they are more informative on what went wrong. In order to tests this, besides all the regular unit tests, a new e2e test has been added. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Reset the literal mode on assignmentsMiquel Sabaté Solà2025-01-081-0/+15
| | | | Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Try absolute addressing on bad zeropage addressingMiquel Sabaté Solà2025-01-081-0/+31
| | | | | | | | | | | | | | | | | | | | | | | | Sometimes, out of clarity, the programmer may have written something along the lines of: lda $40, y This is invalid because the `lda` instruction does not allow zeropage y-indexing addressing mode. That being said, it does allow for absolute y-indexing addressing mode. This commit allows this syntax by transforming code like the previous one into: lda $0040, y This cannot always be done, but the assembler should at least try if it's possible and not trouble the programmer. That being said, this is otherwise a bit shady since the programmer might think that it's a 2-byte instruction when it's a 3-byte one. Hence, maybe a future linter can pick up code like this and suggest a more explicit writing. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Add the .repeat control statementMiquel Sabaté Solà2025-01-071-28/+232
| | | | | | | | | | | | | | | | | | | | | | | | This is a control statement which acts similarly as .proc/.macro/.scope, in which an inner block is allocated for it. Hence, all the previous work from 1f8a6becc7cd ("parser: Implement block bodies") and ec8b709fa24c ("Implement block bodies inside of the assembler") make this one out possible, as .repeat statements don't have an identifier that can be used for hashing. From the parser perspective this introduction raises two new things. First of all this control statement also needed a differentiation between the amount of required arguments, and the allowed ones, since there is a second optional argument to it. And second, even the identifier is not given, we have to generate one so to add a context for it. This was at first not needed, but introducing .repeat-only variables means that we have to have inner contexts which need to be named somehow so we can retrieve the context later when picking up the value for them again. This last thing brought the need for a new dependency: rand. This is used to generate a random string to identify the .repeat block. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Store a reference for macros instead of an indexMiquel Sabaté Solà2025-01-071-862/+689
| | | | | | | | | | | | | | | | | It is not safe to store a node index for macros since the list of nodes that is passed down during assembly might change depending on whether an inner block is being evaluated. Hence, the previous implementation would break on a simple macro call inside of a .proc. This also raised some concerns on the design around the API, since the lifetime of references for internal assembler data needed an explicit lifetime now, and as a side-effect functions like `assemble` had to be moved out of the inner impl Assembler. This is in retrospect also a better design choice. Fixes: ec8b709fa24c ("Implement block bodies inside of the assembler"). Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Implement block bodies inside of the assemblerMiquel Sabaté Solà2025-01-051-167/+180
| | | | | | | | | | | Following 1f8a6becc7cd ("parser: Implement block bodies"), the support for the new way of managing block bodies have also been added into the assembler. There are still some things to iron out, but they will be fixed in later commits. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* parser: Implement block bodiesMiquel Sabaté Solà2025-01-041-2/+4
| | | | | | | | | | These are bodies which are the right node of some proc controls. This way the parser comes back to sanity for statements like .macro and the likes, and behaves more like a usual parser. This was not done in the past because I thought things could have been simpler this way, but it ended up making the assembler way more complicated that it needed to. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Add a test on jumps and labels inside of procsMiquel Sabaté Solà2024-12-241-2/+61
| | | | Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Fix usage of labels inside of macrosMiquel Sabaté Solà2024-12-241-35/+72
| | | | | | | | Instead of calling `evaluate_node` for each node on a macro, just call `Assembler::bundle` for the list of nodes so the context is preserved and labels and other statements can be catched as usual. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Forbid creating named labels inside of macrosMiquel Sabaté Solà2024-12-241-0/+37
| | | | | | | This is just prone to errors and it is confusing all around. Just prohibit developers doing that. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Prevent missplaced start for procs and scopesMiquel Sabaté Solà2024-12-231-4/+85
| | | | Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Prevent early .end statementsMiquel Sabaté Solà2024-12-231-3/+98
| | | | | | | | | | Prevent a missmatch on .end{macro,proc,scope}. This was more or less already covered when there was a bad context_pop call, but it was prone to errors. Check this in eval_context as it should've always been done. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Make more explicit segment/macros are only globalMiquel Sabaté Solà2024-12-231-21/+40
| | | | | | | | Force .segment and .macro statements to be on the global scope since this is how they are meant. Hence, if the programmer tries to do this, just error out. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Transform mapping configurations into toml filesMiquel Sabaté Solà2024-12-221-28/+30
| | | | | | | | This will allow the creation of configuration files that can live outside of this tree, so developers can fine tune configuration files of their own without having to pick up whatever is currently available. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Prevent a division by zeroMiquel Sabaté Solà2024-12-201-0/+51
| | | | | | Fixes: 8b5feeed96b3 ("assembler: Implement and add tests for operators") Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Only shrink the addressing on resolved bundlesMiquel Sabaté Solà2024-12-201-3/+35
| | | | | | | | | | | | | Commit ea5f0f81b8a7 ("Shrink some absolute instructions by one byte") applied the optimization in all cases, but we cannot perform it on bundles which are yet to be resolved. This is because in unresolved bundles the value is only an offset, which usually will fit on a single byte and hence the optimization would've been carried out. That being said, whenever we resolve this it might just be the case the it wouldn't have fit in that single byte, and hence we end up with an artificially shrinked instruction for a 16-bit address. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Shrink some absolute instructions by one byteMiquel Sabaté Solà2024-12-201-5/+27
| | | | | | | | | | | Some instructions which make use of absolute addressing can actually be further compressed to act like zeropage indexing. This can be done if the immediate being used by that instruction can actually fit into a single byte. If that's the case, we will then "correct" the programmer by using zeropage indexing instead of an absolute one, thus reducing one byte for that instruction. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Improve the format of error messagesMiquel Sabaté Solà2024-12-201-56/+75
| | | | | | | | | | | It was weird to show warnings which also showed "Error: " as a message, and likewise it was weird for errors to display their kind, since users simply do not care about this kind of information. Hence, streamline the format to something closer to what it's done by modern assemblers/compilers. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Add support for warningsMiquel Sabaté Solà2024-12-201-14/+41
| | | | | | | | | | | Warnings are mere xixanta::error::Error's which are not pushed into the Err of Result. That is, instead they are accumulated into an internal `warnings` vector inside of Assembler. On the binary side we now show warnings as well, and there is an option to turn warnings into errors. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Expect macros to overwrite parameter valuesMiquel Sabaté Solà2024-12-201-32/+8
| | | | | | | This is normal when calling the same macro multiple times and in which parameter values need to be updated on each case. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Find variable values on parent scopesMiquel Sabaté Solà2024-12-201-0/+34
| | | | | | | If a given variable cannot be found on the current scope, attempt to go up the context hierarchy to find it. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* assembler: Implement and add tests for operatorsMiquel Sabaté Solà2024-12-191-6/+193
| | | | Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Implement the .incbin control statementMiquel Sabaté Solà2024-12-191-3/+138
| | | | | | | | This also forced us to add the current working directory to the `Assembler::assemble` public function, as otherwise this control statement and others wouldn't know how to resolve relative paths. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Merge code handling for procs and labelsMiquel Sabaté Solà2024-12-181-55/+75
| | | | | | | | | | | | | The handling of labels and proc's both when evaluating the context and when bundling is almost identical. The only change is that proc's need to change the context afterwards, but otherwise they need to create a label just as regular ones. Merge things as much as possible on both these cases, while also taking the chance to do some much needed clean up around these areas, and adding a bit of helpful comments in between. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Reset literal mode on pending nodesMiquel Sabaté Solà2024-12-181-3/+5
| | | | | | | | | Failing to do so would spill over the previous literal mode into the evaluation of nodes pending to be crunched. Fixes: a9f50efdef40 ("Fix label references on control statements") Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Add support for jumping into .proc'sMiquel Sabaté Solà2024-12-181-6/+66
| | | | | | | | Up until now defining a proc only involved pushing/popping the context. Here we also allow it to create a label so it can be referenced by instructions like jsr. Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
* Fix label references on control statementsMiquel Sabaté Solà2024-12-181-1/+47
| | | | Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>