# BIOS * **Address Span:** `0x0` to `0x3FFF` (16k) The [BIOS](https://en.wikipedia.org/wiki/BIOS) of the GBA is a small read-only portion of memory at the very base of the address space. However, it is also hardware protected against reading, so if you try to read from BIOS memory when the program counter isn't pointed into the BIOS (eg: any time code _you_ write is executing) then you get [basically garbage data](https://problemkaputt.de/gbatek.htm#gbaunpredictablethings) back. So we're not going to spend time here talking about what bits to read or write within BIOS memory like we do with the other sections. Instead we're going to spend time talking about [inline assembly](https://doc.rust-lang.org/unstable-book/language-features/asm.html) ([tracking issue](https://github.com/rust-lang/rust/issues/29722)) and then use it to call the [GBA BIOS Functions](https://problemkaputt.de/gbatek.htm#biosfunctions). Note that BIOS calls have _more overhead than normal function calls_, so don't go using them all over the place if you don't have to. I'd like to take a moment to thank [Marc Brinkmann](https://github.com/mbr) (with contributions from [Oliver Schneider](https://github.com/oli-obk) and [Philipp Oppermann](https://github.com/phil-opp)) for writing [this blog post](http://embed.rs/articles/2016/arm-inline-assembly-rust/). It's at least ten times the tutorial quality as the `asm` entry in the Unstable Book has. In their defense, the actual spec of how inline ASM works in rust is "basically what clang does", and that's specified as "basically what GCC does", and that's basically not specified at all despite GCC being like 30 years old. So we're in for a very slow, careful, and pedantic ride on this one. ## Inline ASM The inline asm docs describe an asm call as looking like this: ```rust asm!(assembly template : output operands : input operands : clobbers : options ); ``` And once you stick a lot of stuff in there it can _absolutely_ be hard to remember the ordering of the elements. So we'll start with a code block that has some commends throw in on each line: ```rust asm!(/* ASM */ TODO :/* OUT */ TODO :/* INP */ TODO :/* CLO */ TODO :/* OPT */ ); ``` Note: it's possible to use an inline ASM style where you allow LLVM to determine the exact register placement. We will _not_ do that in this section because each BIOS call has specific input and output slots that we must follow. However, if you want to use inline asm for other purposes elsewhere in your code you can use it then. * **ASM:** The actual asm instructions to use. * When writing inline asm, remember that we're writing for 16-bit THUMB mode because that's what all of our Rust code is compiled to. You can switch to 32-bit ARM mode on the fly, but be sure to switch back before the inline ASM block ends or things will go _bad_. * You can write code for specific registers (`r0` through `r7` are available in THUMB mode) or you can write code for _register slots_ and let LLVM pick what actual registers to assign to what slots. In this case, you'd instead write `$0`, `$1` and so on (however many you need). Outputs take up one slot each, followed by inputs taking up one slot each. * **OUT:** The output variables, if any. Comma separated list. * Output is specified as `"constraint" (binding)` * A constraint is either `=` (write), `+` (read and write), or `&` (early clobber) followed by either the name of a specific register in curly braces, such as `{r0}`, or simply `r` if you want to let LLVM assign it. * If you're writing to `r0` you'd use `={r0}`, if you're read writing from `r3` you'd use `+{r3}` and so on. * Bindings named in the outputs must be mutable bindings or bindings that are declared but not yet assigned to. * GBA registers are 32-bit, and you must always use an appropriately sized type for the binding. * LLVM assumes when selecting registers for you that no output is written to until all inputs are read. If this is not the case you need to use the `&` designation on your output to give LLVM the heads up so that LLVM doesn't assign it as an input register. * **INP:** The inputs, if any. Comma separated list. * Similar to outputs, the input format is `"constraint" (binding)` * Inputs don't have a symbol prefix, you simply name the specific register in curly braces or use `r` to let LLVM pick. * Inputs should always be 32-bit types. (TODO: can you use smaller types and have it 'just work'?) * **CLO:** This is possibly _the most important part to get right_. The "clobbers" part describes what registers are affected by this use of asm. The compiler will use this to make sure that you don't accidentally destroy any of your data. * The clobbers list is a comma separated series of string literals that each name one of the registers clobbered. * Example: "r0", "r1", "r3" * **OPT:** This lets us specify any options. At the moment the only option we care about is that some asm calls will need to be "volatile". As with reads and writes, the compiler will attempt to eliminate asm that it thinks isn't necessary, so if there's no output from an asm block we'll need to mark it volatile to make sure that it gets done. That seems like a whole lot, but since we're only handling BIOS calls in this section we can tone it down quite a bit: * Inputs are always `r0`, `r1`, `r2`, and/or `r3`, depending on function. * Outputs are always zero or more of `r0`, `r1`, and `r3`. * Any of the output registers that aren't actually used should be marked as clobbered. * All other registers are unaffected. All of the GBA BIOS calls are performed using the [swi](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0068b/BABFCEEG.html) instruction, combined with a value depending on what BIOS function you're trying to invoke. If you're in 16-bit code you use the value directly, and if you're in 32-bit mode you shift the value up by 16 bits first. ### Example BIOS Function: Division The GBA doesn't have hardware division. You have to do it in software. You can implement that yourself (we might get around to trying that, i was even sent [a link to a paper](https://www.microsoft.com/en-us/research/wp-content/uploads/2008/08/tr-2008-141.pdf) that I promptly did not read), or you can call the BIOS to do it for you and trust that it's being as efficient as possible. GBATEK gives a very clear explanation of it: ```txt Signed Division, r0/r1. r0 signed 32bit Number r1 signed 32bit Denom Return: r0 Number DIV Denom ;signed r1 Number MOD Denom ;signed r3 ABS (Number DIV Denom) ;unsigned For example, incoming -1234, 10 should return -123, -4, +123. The function usually gets caught in an endless loop upon division by zero. ``` Of course, the math folks tell me that the `r1` value should be properly called the "remainder" not the "modulus". We'll go with that for our function, doesn't hurt to use the correct names. The function itself is a single assert, then we name some bindings without giving them a value, make the asm call, and then return what we got. ```rust pub fn div_rem(numerator: i32, denominator: i32) -> (i32, i32) { assert!(denominator != 0); let div_out: i32; let rem_out: i32; unsafe { asm!(/* ASM */ "swi 0x06" :/* OUT */ "={r0}"(div_out), "={r1}"(rem_out) :/* INP */ "{r0}"(numerator), "{r1}"(denominator) :/* CLO */ "r3" :/* OPT */ ); } (div_out, rem_out) } ``` I _hope_ this makes sense by now.