Replies: 3 comments 1 reply
-
This idea is interesting, but I'm unsure how well it will work. I can see a few main issues. Each ROM bank has 32 KiB. How does the user know the final size of their function? There are a lot of compiler optimizations that change functions in hard-to-predict ways. Function inlining, for example, will completely erase some functions and put their contents into the calling function. Another problem is generic functions. Through (monomorphization)[https://en.wikipedia.org/wiki/Monomorphization, a copy of a generic function will be created for each type it is called with. There are parts of this idea that I really like. It seems like if you have several functions that are in the same bank, you don't need to worry about switching between banks if you call them in a row. I think this can be done through post-processing. I think we will need to write a tool to look at the object files, break them apart, organize them into banks, and pass the result to the GB assembler. If we do this, we can even optimize the banks. This would be a lot of work, but I think it is easier than trying to force function locations into the type system. |
Beta Was this translation helpful? Give feedback.
-
Thinking about this a bit more. We certainly need to solve this through post-processing. Consider this example: pub struct Foo(u8);
impl Foo {
const fn new() -> Self {
Self(0)
}
}
pub fn get_foo() -> &'static Foo {
static FOO: Foo = Foo::new();
&FOO
} The reference to |
Beta Was this translation helpful? Give feedback.
-
I think it's very hard to find a heuristic algorithm to separate functions and constants into several ROMs as you mentioned. For example, consider a situation where a very large constant is used in several functions. This "very large constant" must be 1) located in the same ROM as the function that uses it, or 2) used only by the function in the first ROM. If the sum of the sizes of the functions and constants does not fit in one ROM, it can also be divided into two ROMs, consisting of (constant and function set 1) and (constant and function set 2) In addition, if certain functions run frequently in succession, they should be placed in the same ROM for optimization. Similarly, if (function 1/ function 2) and (function 1/ function 3) are frequently used together, both ROMs may contain function 1. Considering the calling relationships between functions, it is necessary to set a cost and optimize the separation of each function into different ROMs. I think it's going to resemble an unbounded multiple/multi-objective 0-1 knapsack problem. Maybe we can construct a polynomial-time algorithm, but it will be a really difficult problem. In contrast, my solution is much easier to implement. All we need to do is simply replace text and separate files based on symbols. All ROM optimization and capacity guarantees are left to the user, so there is no need to create optimization algorithms. How about implementing my solution first, and then developing an auto-ROM separating algorithm later? ROM banking is a must-have feature, but there is still too much work to do to devote a lot of time to it in the early stages of the project. |
Beta Was this translation helpful? Give feedback.
-
We are discussing about RAM banking and non-linear memory safety in #2
However, Game Boy also has non-linear ROM space, which means we need to do ROM banking to create programs over 32KB.
In GBDK, banking is done using one C file for one bank. But this is not the proper way for Rust, and it is very likely that Undefined Behavior will occur.
Therefore, I suggest the following method of using one struct per one ROM bank.
The underlying structure is similar to the nonlinear RAM banking proposed by @TylerBloom.
There are
BankProvider
trait andRomBankMap
, where you can define structs to be banked. In themain()
function, you will create only oneRomBankMap
at the top, similar toMemoryMap.
RomBankMap has 64 types and switch_to_bank functions (depending on the MBC type).
User creates a struct and demonstrates that this struct will be banked by implementing the
RomBank
trait.The bank constant can be used by importing &'static into the field via
RomRef
.Then, you can do safe banking as below.
You can check complete prototype in https://github.com/zlfn/rust-gb/tree/rom-banking/source/src
It works, but there is no process yet to parse the C file and divide it into several ROMs, so banking is not actually done.
Beta Was this translation helpful? Give feedback.
All reactions