Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Fixes #192, or "How I Learned To Stop Worrying And Love The Linker".
Also adds SDK-side
get_chr_bank
/set_chr_bank
helpers to CNROM, now that this is actually possible to do safely; as well as an UNROM 512 macro to enable a "self-flashing" board, now that this is no longer the default.(Other INES macros will be added via #193)
I'm giving this one a longer write-up because I think the level of hacks in the PR necessitates an explanation.
The Problem
The NES features mapper bus conflicts. Simpler mappers, based around discrete logic chips, do not prevent the ROM chip from being enabled when the CPU is writing to the cartridge's data bus - this creates a conflict, as both the CPU and ROM are outputting values on the same bus at the same time.
The correct way to work around this is to ensure that the value output by the CPU is the same as outputting the ROM. This is typically done by creating a byte array
A
where, for any given valueN
,A[N] == N
; then, whenever one wants to writeN
to ROM, it is done to the address ofA[N]
.However, this byte array would normally take up 256 bytes of the 16384 bytes typically present in the "fixed" ROM - this level of wastefulness is unacceptable. Many mappers only require a far smaller table; for example, an UNROM cartridge typically only has 16 banks, so the only values one would need to write to the ROM bus will be between 0 and 15. We'd like the LLVM-MOS SDK to be able to dynamically allocate, at link time, the right-sized array - of
M
bytes - based on the INES header configuration (the bank sizes, etc).The Answer
Naturally, it'd be nice to have an unified solution for all the affected mappers. By sheer coincidence, all the discrete mappers with bus conflicts which llvm-mos-sdk wishes to target have just one writable memory-mapped port, which is always reachable from the "fixed" ROM space. Nice! As such, a
rompoke.h
header is defined with the functionvoid rom_poke_safe(char value);
, which performs theA[N] == N
operation.Next, we need to create the dynamically allocated table.
The first idea was to generate one table - with a distinct section and symbol - per table size (from 0 to 256 inclusive - mappers with bus conflicts often have non-conflicting variants, which we can then avoid paying the table's size cost on completely), like so:
Unfortunately, LLD will retain in ROM all symbols used in such an equation - which is worse than just having one table in the first place.
The Terrible, Terrible Answer
Something we can dynamically control, though, is section sizes; we can also control the byte value used to fill such a section.
So, technically speaking, one could define 256 sections - one for each byte of
A
- and simply set the firstM
sections to a size of 1 byte, with the remaining256 - M
sections set to a size of 0 bytes.But who would ever do such a thing? Anyhow, here's the pull request.
The Less Terrible But Actually No Good Answer
That's when I found that GNU's linker, which LLD claims drop-in compatibility with, states the following in its manual:
If the fill expression is a simple hex number, ie. a string of hex digit starting with ‘0x’ and without a trailing ‘k’ or ‘M’, then an arbitrarily long sequence of hex digits can be used to specify the fill pattern; Leading zeros become part of the pattern too.
This isn't that well-known - older versions of
ld
were limited to just two- or four-byte fill patterns.Either way, that's it! I can have just one section which holds an arbitrary number of bytes based on a fill pattern, then set the section's size using
. = . + __rom_poke_table_size;
! This is great! It will certainly-... If this works on the GNU linker (I checked with binutils 2.39), does this technically constitute an LLVM bug?
The Summary
The PR is a terrible hack, but it does get the job done and it seemingly passes all the tests. Please be merciful.