rust-console / gba

A crate that helps you make GBA games
https://docs.rs/gba
Apache License 2.0
638 stars 50 forks source link

Support building Multiboot-ROMs #122

Open phijor opened 3 years ago

phijor commented 3 years ago

I'm currently dabbling with the GBA's support for loading ROMs from serial (i.e. multibooting). To build a Multiboot ROM, one must put .text and .rodata into IWRAM EWRAM (with the entrypoint at 0x0200_0000).

Would it be possible to support this usecase in this crate?

I can imagine the following approach:

  1. Supply a Multiboot start script (rsrt0_multiboot.S).
  2. Build it by default in build.rs
  3. Instruct the user to adapt their linker scripts to include the multiboot rutime instead of rsrt0.o and put everything in IWRAM EWRAM.
Lokathor commented 3 years ago

Yes.

I don't recall who, but I recall one user getting multi-boot working with this crate by modifying the linker script and boot shim. They were able to make some pixels show up on actual GBA hardware by using a GBA/GCN link cable hooked to a Wii using some sort of Homebrew thing to send the rom from the Wii to the GBA.

So, it's possible.

phijor commented 3 years ago

I got it to work:

Multiboot ROM output

Looking at the libgba linker scripts, the only modifications I had to make were the following (in linker.ld):

  1. Change VMA for sections .text and .rodata from >rom to >ewram
  2. Change LMA for section .iwram from AT>rom to AT>ewram so .data gets loaded from EWRAM to IWRAM

I did not have to touch the start script (rsrt0.S). For some reason mgba would not load the ROM, but I guess this is just Multiboot autodetection being flakey; it works on real hardware.

Should this be documented somewhere? I can make a PR if you point me in the correct direction.

Lokathor commented 3 years ago

I for now just make a file called multiboot.md in the repository root and write up the steps there. Keeping things like this in markdown files seems like the simplest way to get started with it.

TheHans255 commented 2 years ago

I've been dabbling in this lately - I recently wrote a sample that deploys an already-written multiboot payload to another GBA over MultiPlay: https://github.com/TheHans255/rust-gba-multiboot-test. Ideally, we'd have a way to build a multiboot executable that shares code with a cartridge executable to let us efficiently make updates to both, which would likely happen via a shared library crate and two executable crates.

I did not have to touch the start script (rsrt0.S). For some reason mgba would not load the ROM, but I guess this is just Multiboot autodetection being flakey; it works on real hardware.

I believe this is correct for SPI mode, but not for MultiPlay (GBA Link Cable) or JOYBUS (Wii/GameCube) mode. Execution starts at 0x0200_00c0 for a Multiboot payload from SPI or MultiPlay, but the GBA BIOS writes to 0x0200_00c4 and 0x0200_00c5 in MultiPlay mode to indicate the boot mode and the player number, respectively, which will corrupt any instructions written there. As for JOYBUS mode, execution starts further down at 0x0200_00e0. A proper rsrt0.S start script will have its header augmented to add b instructions to those locations.

TheHans255 commented 2 years ago

Also, an idea that I saw in the GBATEK document (https://www.problemkaputt.de/gbatek.htm#biosmultibootsinglegamepak) is that if a game is less than 256K, it could be compiled in a "hybrid" mode where the same payload is valid for both cartridge and multiboot - in this mode, the cartridge entry point at 0x0800_0000 leads to a small shim that copies the cartridge into EWRAM. If we need to provide sample linker scripts and start scripts for multiboot, it might be interesting to consider adding samples for the hybrid mode as well.