Getting Rust on the GBA 3

Posted on November 21, 2015

Alright, I have removed the remaining C code from the project. This was an adventure.

I followed the advice from pragmatic bare metal rust, and emitted both an .rlib and a .o file for the core library with:

rustc --target=target.json -Z no-landing-pads ../rust/src/libcore/lib.rs --out-dir libs/ --emit link,obj

Also, I've removed landing pads, since we are not going to be recovering from panics at all.

I switched from a -o flag to --out-dir, as we output both libcore.rlib and core.o.

Then, when compiling rust-gba.rs, I also request an object file

rustc -L libs -Z no-landing-pads --target=target.json --emit obj  rust-gba.rs

and no landing pads.

We still link everything together with clang.

clang -nostdlib -target arm-none-eabi -Tgba_cart.ld \
    -Wl,--gc-sections \
    rust-gba.o libs/core.o \
    gba_crt0.o crti.o -o rust-gba.elf

arm-none-eabi-objcopy -O binary rust-gba.elf rust-gba.gba
gbafix rust-gba.gba

But, we also need to pass --gc-sections to the linker, hence -Wl,--gc-sections. Otherwise, we need to have floating point support, as core defines f32 and f64, which we don't have hardware support for on the GBA. We get a ton of linker errors otherwise, this way we only get one:

ld: section .got loaded at [08004a18,08004a23] overlaps section .pad loaded at [08004a18,08004a1f]

which is utterly baffling until I happened to read this article on linkers.

The .got section is the Global Offset Table, and the PLT is the Procedure Linkage Scheme Table. These are used when there is some dynamic linking going on, which is a little odd, since I am attempting to statically link everything into one gba file, but ok compiler if you really want to have them, I can give you some bytes in the ROM.

The gba_cart.ld file already placed .plt in rom, but forgot about .got, so simply adding it fixed the problem.

EDIT: Luqman pointed out that adding

	"relocation-model"    : "static",

to the target.json prevents rustc from trying to produce those segments at all, which is a far better solution.

Now, more excitingly I can write readable code, and less of this magic constants as addresses business.

#![feature(lang_items, no_std)]

#![no_std]
#![crate_type = "staticlib"]

Now, this time we use no_std instead of no_core, and we automatically have extern crate core added for us. Also, things link best if I say crate_type = "staticlib".

#[lang = "panic_fmt"]
pub extern fn panic_fmt() -> ! { loop {} }

#[lang = "stack_exhausted"]
pub extern fn stack_exhausted() -> ! { loop {} }

#[lang = "eh_personality"]
pub extern fn eh_personality() -> ! { loop {} }

Someday, I will improve panic_fmt, and throw something up on the screen when everything goes wrong. Till then, just loop forever on crash.

// // I'm not 100% sure what this function does, but references to it are compiled
// // into the program by the Rust compiler. I think it would be called in the case
// // of a program panic.
#[no_mangle]
pub fn __aeabi_unwind_cpp_pr0() {
    loop {}
}
#[no_mangle]
pub fn __aeabi_unwind_cpp_pr1() {
    loop {}
}

Now there's two of these strange functions. I don't think we're going to be unwinding anytime soon.

mod gfx;

use gfx::Color;

Oh, what's this, a second file? Nice. Lets take a look in there before finishing the main file.

use core::slice;


pub struct Color (u16);

impl Color {
    //! Creates a color with 16 bits, 5 bits for each channel.
    pub fn rgb15(red: u32, green: u32, blue: u32) -> Color {
        Color((red | (green << 5) | (blue << 10)) as u16)
    }
}

Cool, a nice way to create colors. And since the name is rbg15, I can hardly forget the order of arguments.


/// Check out http://www.coranac.com/tonc/text/bitmaps.htm for more details on
/// different modes.
///
/// Mode3 is a one buffer, with width 240, height 160, and 16 bits per pixel (bpp).
/// Is it the most basic of modes.
pub struct Mode3;

impl Mode3 {
    /// Calling this invalidates all other modes and enters Mode3.
    pub fn new () -> Mode3 {
        unsafe {
            *(0x04000000 as *mut u32)= 0x0403;
        }
        Mode3
    }

    /// Draw a dot at co-ordinates (x, y), and of color `color`.
    pub fn dot(&mut self, x: u32, y: u32, color: Color) {

        assert!(x < 240);
        assert!(y < 160);

        let buff : &mut [u16] = unsafe {
            slice::from_raw_parts_mut(0x06000000 as *mut u16, 240 * 160)
        };
        buff[(x+y*240) as usize] = color.0;
    }
}

Back to the main file rust-gba.rs, and see how this gets put to use:


#[no_mangle]
pub extern "C" fn main() {
    let mut m = gfx::Mode3::new();
    m.dot(120, 80, Color::rgb15(31, 0, 0));
    m.dot(136, 80, Color::rgb15(0, 31, 0));
    m.dot(120, 96, Color::rgb15(0, 0, 31));
}

This is pretending to be the main from that C file still, but it works, so that's fine with me.

I'm about ready to try and pull things out into a separate crate for the library code and main, but work out a few more of TONC's examples first.

Well, you can check out the rest of the files on github now.

See you next time.