aboutsummaryrefslogtreecommitdiff
path: root/lib
Commit message (Collapse)AuthorAgeFilesLines
* Add an option for setting a cycle limitMiquel Sabaté Solà2026-09-081-0/+30
| | | | | | | | This can be quite handy when it comes to checking that a piece of code doesn't exceed a given amount of cycles, or when you are tuning a block of code in order to make it go beyond the current amount of cycles. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* vnf: refuse to proceed if the machine is inactiveMiquel Sabaté Solà2026-09-081-1/+13
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* vnf: validate the minimum stack valueMiquel Sabaté Solà2026-09-071-5/+10
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* vnf: display also the break mark on report()Miquel Sabaté Solà2026-09-071-1/+9
| | | | | | | | When reporting the final instruction, if that was actually a brk (and hence we stopped because of it), show the breaking mark as that could be relevant when debugging. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* vnf: add the 'Machine.halt_on_brk' fieldMiquel Sabaté Solà2026-09-071-0/+9
| | | | | | | | This allows developers to use 'brk' as asserts which can then be debugged. This is true by default but it can be turned off via the --no-halting-brk option from runrom. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* vnf: convert StatusRegister.break_mark to Option<u8>Miquel Sabaté Solà2026-09-071-8/+10
| | | | | | | Instead of assuming a default value, it's safer to just implement it in Rust's native Option<_> type. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* vnf: improve documentationMiquel Sabaté Solà2026-09-072-5/+57
| | | | | | | Provide documentation for the library, and improve the documentation for some of the structs and interfaces. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* vnf: properly document how branching worksMiquel Sabaté Solà2026-09-061-10/+11
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* vnf: don't flip the given joypad inputsMiquel Sabaté Solà2026-09-061-5/+1
| | | | | | | | | | I am not sure from where I got this bit flipping part, but I believe things worked because of another bug (see 9c685d5b6ca1 ("vnf: also set the carry on a zero compare")). Fixes: ad77503d9533 ("vnf: add support for standard joypads") Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* vnf: also set the carry on a zero compareMiquel Sabaté Solà2026-09-061-1/+1
| | | | | | Add also a test to further check corner cases on compare instructions. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* vnf: add support for standard joypadsMiquel Sabaté Solà2026-09-061-19/+93
| | | | | | | | This means adding constants so developers can actually trigger which key presses are available at any given read, as well as a simple interface that allows to add button presses and clear them. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* vnf: implement the randomized initialization for memoryMiquel Sabaté Solà2026-09-062-2/+2
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* vnf: remove tests modMiquel Sabaté Solà2026-09-061-7/+0
| | | | | | | For now tests on vnf are provided via e2e tests, and via the vnf-tests binary. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Implement the bvc and bvs instructionsMiquel Sabaté Solà2026-09-061-4/+11
| | | | | | They were the last ones marked with the todo!() macro. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Remove the 'affected_on_page' memberMiquel Sabaté Solà2026-09-064-315/+0
| | | | | | | | | | | It was created a long time ago in the hopes that it would be useful whenever I wanted to create an emulator, but when that happened in the form of 'runrom' I've seen that it's really not needed. Remove it as it just pollutes the source code with hundreds of useless lines. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Change the first argument of Machine::from() into a &PathMiquel Sabaté Solà2026-08-271-2/+6
| | | | | | | | This makes for a saner API, and some users (e.g. vnf-tests) don't have to go over backwards to provide a parameter they already had as a PathBuf. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* vnf: add support for all stack instructionsMiquel Sabaté Solà2026-08-271-23/+156
| | | | | | | | | | | | This also includes brk and rti, for which we are also storing and exporting the break mark, just in case it can be useful to library users in case they want to add error codes in break marks. This commit also includes a fix for accounting the Z and N flags on transfer instructions, which couldn't easily be split into a separate commit, but it's a fairly simple change. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Implement the Default trait for MemoryInitialValueMiquel Sabaté Solà2026-08-271-0/+11
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Fix cycle count for the plp instructionMiquel Sabaté Solà2026-08-271-2/+2
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* vnf: don't report stack push/pop twice in verboseMiquel Sabaté Solà2026-08-271-10/+12
| | | | | | | | On situations like jsr or rts, we perform two pushes/pops, but this is not to be reported in verbose mode as it's not that clear. Instead, let's report the full address in a single stack line. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* vnf: fix the accounting of cycles on branchesMiquel Sabaté Solà2026-08-251-2/+10
| | | | | | This was broken since the very beginning. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* vnf: push/pop the proper address on jsr/rtsMiquel Sabaté Solà2026-08-251-5/+13
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* vnf: fix documentation on 'page_penalty'Miquel Sabaté Solà2026-08-251-1/+1
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Do not account for stack reads on 'rts'Miquel Sabaté Solà2026-08-251-10/+14
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Stop execution if not active in until_address()Miquel Sabaté Solà2026-08-251-1/+1
| | | | | | | | | | | Even if the desired thing is to run until the provided address is reached, abort execution as well if the machine stops being active as well. This is a common scenario when running in function mode and the target end address is something passed that. Fixes: e6c41303c2da ("runrom: add the --until-address option") Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* runrom: add the --until-address optionMiquel Sabaté Solà2026-08-251-15/+1
| | | | | | | | | This allows the user to run execution until a given address is reached. This can, like with the --start option, be coupled with the --nasm option in order to be able to provide an identifier instead of a regular hexadecimal value. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Do not mark some valid identifiers as invalidMiquel Sabaté Solà2026-08-181-0/+27
| | | | | | | | | | | | | Some identifiers like "@aa" were marked as invalid as "aa" is a potentially hexadecimal value, but the "@" symbol actually disambiguates this situation. Hence, if a non-alphanumeric character (that the parser liked) is actually found, ensure it cannot be considered as an hexadecimal value. Provide also a test of is_valid_identifier(). The list is certainly not exhaustive, but it should be good enough coupled with end-to-end tests. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Validate labels when walking through the contextMiquel Sabaté Solà2026-08-181-3/+32
| | | | | | | | | | | | | | The parser does not validate label names completely (it only takes care of validating that it something that makes sense syntactically). The assembler can tell whether the label is actually taking a reserved name, or an invalid hexadecimal constant, etc. Before this commit a user would get a cryptic "invalid identifier" message when referencing an invalid label (e.g. "jmp 1234"). Fix this by validating the identifier there, but also when the label was defined before any of this. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Add global labelsMiquel Sabaté Solà2026-08-184-21/+94
| | | | | | | | | | | | These are labels that are declared by prefixing a '#' symbol to the name, and it allows the label to be declared at the global scope instead of the current one. This is a feature which is not to be abused so to not make scopes pointless, but it can be quite handy with some optimizations while not abandoning scopes completely. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Fix the opcode for the 'sed' instructionMiquel Sabaté Solà2026-08-171-2/+2
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Add the runrom crate and the vnf libraryMiquel Sabaté Solà2026-08-173-0/+1194
| | | | | | | | | | | | | | | | | | | The vnf library supports the runrom crate and they both combined enable users to "run" a ROM file. This is basically an emulator, but with two key differences: 1. It is to be run programatically. That is, you are not expected to play games with this, but to run code by steps, start at a given address, run a function, etc. 2. It is headless: there are no graphics displayed on screen, nor sound being delivered. Thus, the target for both these things are developers, not players. This way developers can validate code paths without needing a full blown emulator. You can write tests with this and be able to run some checks as part of your testing infrastructure. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Remove .github directoryMiquel Sabaté Solà2026-07-221-1/+1
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* readrom: implement the -d/--disassemble optionMiquel Sabaté Solà2026-07-152-1/+2181
| | | | | | | | This option can also be coupled with -n/--nasm-directory, and you can then get a human-readable disassembling of any proc or label you might be thinking on. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Save known addresses into .nasm/addresses.txtMiquel Sabaté Solà2026-07-151-5/+81
| | | | | | | | | This new file contains all the addresses that are known to the assembler, belonging to either proc's or plain labels. For the former we will further be able to tell the "end" of the proc. With that, the file follows a simple CSV format, with the name, start and end. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Generalize MemoryRange into RangeMiquel Sabaté Solà2026-07-141-65/+69
| | | | | | | Leading up to also having address ranges exported to callers, which should be using the same infrastructure. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Use 'full_name' everywhere in check()Miquel Sabaté Solà2026-07-141-5/+1
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* readrom: print the usual PRG-RAM size if availableMiquel Sabaté Solà2026-07-131-1/+1
| | | | | | | | | | If 'has_persistent_memory' has been set on the ROM header, then we must print it even if bytes 8/10 was never set by the header. In fact, the vast majority of ROM files don't have these bytes set but they report a true value for 'has_persistent_memory'. Hence, assume the usual PRG-RAM size in these cases, which is (I hope) the correct one in all cases. Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* 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>
* Minor style fixes from an upgraded clippyMiquel Sabaté Solà2026-07-091-1/+1
| | | | Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
* Add support for the asan:fixed-segments commentMiquel Sabaté Solà2026-07-093-24/+189
| | | | | | | | 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-084-1/+105
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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-087-281/+378
| | | | | | | 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-072-32/+63
| | | | | | | | | | | | | | 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-071-2/+7
| | | | | | | | | | 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-071-1/+12
| | | | | | | | | | | | | | | 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-052-22/+28
| | | | | | | | | | | | | | | | | | | 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: 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>