├── .gitignore ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── gba.json ├── gbaimg ├── Cargo.toml └── src │ └── lib.rs └── src ├── alloc └── mod.rs ├── boxed ├── boxed.rs └── mod.rs ├── collections ├── arr.rs ├── mod.rs └── static_arr.rs ├── graphics ├── mod.rs ├── sprites.rs └── tiled_bg.rs ├── input └── mod.rs ├── interrupt.rs ├── lang.rs ├── lib.rs ├── mem └── mod.rs ├── ptr └── mod.rs └── reg.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/* 2 | /.idea/ 3 | Cargo.lock 4 | /target/ 5 | /target/* 6 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gbalib" 3 | version = "0.1.0" 4 | authors = ["Joshua Karns"] 5 | 6 | [dependencies] 7 | "gbaimg" = {path = "gbaimg", version = "*"} 8 | 9 | [lib] 10 | name = "gbalib" 11 | path = "src/lib.rs" 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright 2018, Joshua Karns 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 6 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation the 7 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit 8 | persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 13 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 14 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 15 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build 2 | build: 3 | xargo build --target gba --release 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # stdgba 2 | A small rust standard library for the GBA. As of right now, I likely wont touch this project for a while. Hopefully someone finds it to be a useful reference to make rust libraries for the GBA, and the related projects I've made may help people create a working GBA rom using rust. 3 | 4 | If you have any questions about using this, email me at josh@mail.rit.edu :) 5 | 6 | This library intends to provide basic necessities needed to program Rust on the GBA. 7 | That would include: 8 | 9 | - [x] (very simple) memory allocator 10 | - [x] dynamic allocated types (arrays and boxes) 11 | - [x] input handling 12 | - [x] image-to-tile macros (i.e. load images into static slices at runtime) 13 | - [ ] a complete API to the GBA's graphics functionality (partially done) 14 | - [ ] bindings to the GBA's link cable functionality 15 | - [ ] documentation and examples 16 | 17 | Possible future goals would be: 18 | - better API design 19 | - math utilities 20 | - high-level graphics functionality 21 | 22 | Help would be greatly appreciated, and suggestions are welcome - but currently the library is being developed in my limited freetime. 23 | -------------------------------------------------------------------------------- /gba.json: -------------------------------------------------------------------------------- 1 | { 2 | "data-layout" : "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64", 3 | "llvm-target" : "arm-none-eabi", 4 | "ar" : "arm-none-eabi-ar", 5 | "linker" : "arm-none-eabi-ld", 6 | "target-endian" : "little", 7 | "target-pointer-width": "32", 8 | "target-c-int-width": "32", 9 | "linker-flavor": "ld", 10 | "cpu" : "arm7tdmi", 11 | "arch" : "arm", 12 | "relocation-model" : "static", 13 | "dynamic-linking" : "false", 14 | "os" : "none", 15 | "executables" : true, 16 | "no-compiler-rt" : true, 17 | "no-default-libraries": true, 18 | "archive-format" : "gnu", 19 | "pre-link-args" : {"ld": [ 20 | "-specs=gba.specs", 21 | "-lsysbase", 22 | "-lc" 23 | ]} 24 | } 25 | -------------------------------------------------------------------------------- /gbaimg/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gbaimg" 3 | version = "0.1.0" 4 | authors = ["Joshua Karns"] 5 | 6 | [dependencies] 7 | image = "*" 8 | syn = "*" 9 | quote = "*" 10 | 11 | [lib] 12 | name = "gbaimg" 13 | path = "src/lib.rs" 14 | proc-macro = true -------------------------------------------------------------------------------- /gbaimg/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(proc_macro, rustc_private)] 2 | extern crate proc_macro; 3 | extern crate syn; 4 | use syn::{ DeriveInput, LitStr }; 5 | 6 | extern crate syntax; 7 | use syntax::parse::token; 8 | 9 | use proc_macro::{ TokenStream, TokenTree, }; 10 | 11 | extern crate image; 12 | 13 | #[macro_use] 14 | extern crate quote; 15 | use quote::*; 16 | 17 | use std::mem::transmute; 18 | 19 | fn process_tokens(input: TokenStream) -> String { 20 | let input: LitStr = syn::parse(input).unwrap(); 21 | input.value() 22 | } 23 | 24 | #[proc_macro] 25 | pub fn img_as_palleted_sprite_4bpp(input: TokenStream) -> TokenStream { 26 | let path = process_tokens(input); 27 | 28 | let mut colors = Vec::::with_capacity(1 << 4); 29 | colors.push(0x8000); 30 | 31 | let img = load_img(path); 32 | let (width, height) = img.dimensions(); 33 | 34 | if (width * height) & 1 == 1 { 35 | panic!("image must have an even number of pixels in order to convert it to 4bpp") 36 | } 37 | 38 | let mut pixels = vec![0u8; (width * height) as usize >> 1]; 39 | let mut index = 0usize; 40 | //for sy in 0..height / sprite_height { for sx in 0..width / sprite_width { 41 | for iy in 0..height / 8 { for ix in 0..width / 8 { 42 | for y in 0..8 { for x in 0..8 { 43 | let rgb = img.get_pixel(/*sx * sprite_width +*/ ix * 8 + x, /*sy * sprite_height +*/ iy * 8 + y).data; 44 | //print!("Pixel at ({}, {}): {:?}; ", ix * 8 + x, iy * 8 + y, rgb); 45 | let converted_red = ((rgb[0] as f32 / 255.0f32) * 31.0f32) as u16; 46 | let converted_green = ((rgb[1] as f32 / 255.0f32) * 31.0f32) as u16; 47 | let converted_blue = ((rgb[2] as f32 / 255.0f32) * 31.0f32) as u16; 48 | 49 | let color = (converted_blue << 10) | (converted_green << 5) | converted_red; 50 | let color_index = 51 | if let Some(color_index) = colors.iter().position(|&item| item == color) { 52 | if color_index > 15 { 53 | panic!("image contains more than 16 colors: a 4bpp image can only have 16 colors"); 54 | } 55 | color_index 56 | } else { 57 | let color_index = colors.len(); 58 | if color_index == 16 { 59 | panic!("image contains more than 16 colors: a 4bpp image can only have 16 colors"); 60 | } 61 | 62 | colors.push(color); 63 | color_index 64 | }; 65 | if index & 1 == 0 { 66 | pixels[index >> 1] |= color_index as u8; 67 | } else { 68 | pixels[index >> 1] |= (color_index << 4) as u8; 69 | } 70 | index += 1; 71 | }} 72 | }} 73 | //}} 74 | 75 | /* 76 | let mut newln = 0; 77 | print!("palette: {{ "); 78 | for x in colors.iter() { 79 | if newln == 16 { 80 | newln = 0; 81 | println!(""); 82 | } 83 | print!("{:x}, ", x); 84 | newln += 1; 85 | } 86 | newln = 0; 87 | println!("}}\nimg: "); 88 | for x in pixels.iter() { 89 | if newln == 32 { 90 | newln = 0; 91 | println!(""); 92 | } 93 | print!("{:02x}, ", x); 94 | newln += 1; 95 | } 96 | println!("}}"); 97 | */ 98 | 99 | (quote! { (&[#(#colors),*], &[#(#pixels),*]) }).into() 100 | } 101 | 102 | 103 | #[proc_macro] 104 | pub fn img_as_palleted_sprite_8bpp(input: TokenStream) -> TokenStream { 105 | let path = process_tokens(input); 106 | 107 | let mut colors = Vec::::with_capacity(1 << 8); 108 | colors.push(0); 109 | 110 | let img = load_img(path); 111 | let (width, height) = img.dimensions(); 112 | 113 | if (width * height) & 1 == 1 { 114 | panic!("image must have an even number of pixels in order to convert it to 4bpp") 115 | } 116 | 117 | 118 | 119 | let mut pixels = vec![0u8; (width * height) as usize]; 120 | let mut index = 0usize; 121 | //for sy in 0..height / sprite_height { for sx in 0..width / sprite_width { 122 | for iy in 0..height / 8 { for ix in 0..width / 8 { 123 | for y in 0..8 { for x in 0..8 { 124 | //println!("sx: {}, sy: {}, ix: {}, iy: {}, x: {}, y: {}", sx, sy, ix, iy, x, y); 125 | let rgb = img.get_pixel(/*sx * sprite_width +*/ ix * 8 + x, /*sy * sprite_height +*/ iy * 8 + y).data; 126 | //print!("Pixel at ({}, {}): {:?}; ", ix * 8 + x, iy * 8 + y, rgb); 127 | let converted_red = ((rgb[0] as f32 / 255.0f32) * 31.0f32) as u16; 128 | let converted_green = ((rgb[1] as f32 / 255.0f32) * 31.0f32) as u16; 129 | let converted_blue = ((rgb[2] as f32 / 255.0f32) * 31.0f32) as u16; 130 | 131 | let color = (converted_blue << 10) | (converted_green << 5) | converted_red; 132 | let color_index = 133 | if let Some(color_index) = colors.iter().position(|&item| item == color) { 134 | if color_index > (1 << 8) - 1 { 135 | panic!("image contains more than 256 colors: a 8bpp image can only have 256 colors"); 136 | } 137 | color_index 138 | } else { 139 | let color_index = colors.len(); 140 | if color_index == 1 << 8 { 141 | panic!("image contains more than 256 colors: a 8bpp image can only have 256 colors"); 142 | } 143 | colors.push(color); 144 | color_index 145 | }; 146 | pixels[index] |= color_index as u8; 147 | index += 1; 148 | }} 149 | }} 150 | //}} 151 | 152 | /* 153 | let mut newln = 0; 154 | print!("palette: {{ "); 155 | for x in colors.iter() { 156 | if newln == 16 { 157 | newln = 0; 158 | println!(""); 159 | } 160 | print!("{:x}, ", x); 161 | newln += 1; 162 | } 163 | newln = 0; 164 | println!("}}\nimg: "); 165 | for x in pixels.iter() { 166 | if newln == 32 { 167 | newln = 0; 168 | println!(""); 169 | } 170 | print!("{:02x}, ", x); 171 | newln += 1; 172 | } 173 | println!("}}"); 174 | */ 175 | let p = (quote! { (&[#(#colors),*], &[#(#pixels),*]) }); 176 | p.into() 177 | } 178 | 179 | #[proc_macro] 180 | pub fn img_as_palleted_sprite_16bpp(_input: TokenStream) -> TokenStream { 181 | panic!("aa") 182 | } 183 | 184 | fn load_img(file_path: String) -> image::RgbImage { 185 | 186 | let img_res = image::open(&file_path); 187 | let img = match img_res { 188 | Ok(i) => i.to_rgb(), 189 | _ => { 190 | panic!(format!("could not find image in the specified path '{}'", file_path)); 191 | } 192 | }; 193 | 194 | img 195 | } 196 | 197 | -------------------------------------------------------------------------------- /src/alloc/mod.rs: -------------------------------------------------------------------------------- 1 | /// This is a very simple memory allocator that works with the slow RAM offered by the GBA. It has 2 | /// not been extensively tested, and it probably isn't a great idea to use in the same way a 3 | /// memory allocator would be used on a non-embedded system. The best use is to assign memory 4 | /// chunks of memory that will remain there for the entire lifetime of the program; memory can be 5 | /// freed, however doing so will lead to segmentation that may not be recoverable. 6 | /// 7 | /// Writing beyond the size of a memory chunk can prevent future allocation or deallocation from working 8 | /// properly. 9 | use core::{ mem, iter }; 10 | use core::option::*; 11 | 12 | use ptr::Ptr; 13 | 14 | 15 | // first 8 bytes are used to point to free / used lists 16 | const RAM_START: u32 = 0x02000010; 17 | const RAM_END: u32 = 0x0203FFFF; 18 | 19 | const FREE_HEAD_LOC: u32 = 0x02000000; 20 | const USED_HEAD_LOC: u32 = 0x02000004; 21 | 22 | const BLOCK_SIZE_SHIFT: u32 = 7; 23 | const BLOCK_SIZE: u32 = 1 << BLOCK_SIZE_SHIFT; 24 | 25 | /// A meta data struct that is found before every pointer returned by the allocator. It is used to link 26 | /// free regions together, and stores information about possible extra length in an allocation 27 | #[derive(Copy, Clone)] 28 | struct Chunk { 29 | /// Points to the next Chunk in the list 30 | next: Ptr, 31 | /// Points to the previous Chunk in the list! 32 | prev: Ptr, 33 | /// Size of a sector, not including the space kept up by the Chunk struct 34 | len: u32, 35 | //// Used for debugging the allocator, otherwise it should be commented out. 36 | // magic: u32 37 | } 38 | 39 | impl Chunk { 40 | 41 | #[inline(always)] 42 | pub unsafe fn get_free_head() -> Ptr> { Ptr::from_u32(FREE_HEAD_LOC) } 43 | 44 | /// Should only be used when the current head is null! 45 | pub unsafe fn set_free_head(ptr: Ptr) { 46 | let current_head: * mut u32 = FREE_HEAD_LOC as * mut u32; 47 | *current_head = ptr.num; 48 | } 49 | 50 | #[inline(always)] 51 | pub unsafe fn get_used_head() -> Ptr> { Ptr::from_u32(USED_HEAD_LOC) } 52 | 53 | /// Should only be used when the current head is null! 54 | pub unsafe fn set_used_head(ptr: Ptr) { 55 | let current_head: * mut u32 = USED_HEAD_LOC as * mut u32; 56 | *current_head = ptr.num; 57 | } 58 | 59 | pub const unsafe fn of_size(size: u32) -> Chunk { 60 | Chunk { 61 | next: Ptr::null(), 62 | prev: Ptr::null(), 63 | len: size, 64 | // magic: 0xDEADBABE 65 | } 66 | } 67 | 68 | pub unsafe fn initialize(&mut self) { 69 | // self.magic = 0xCAFEBABE; 70 | self.next = Ptr::null(); 71 | self.prev = Ptr::null(); 72 | self.len = 0; 73 | } 74 | 75 | /// Attempts to concatenate two Chunks. The memory address of self should be less than other, 76 | /// even if they cannot be concatenated. This function will deinitialize other, so all data it 77 | /// contains will be lost. 78 | pub unsafe fn try_concatenate(mut self: Ptr, mut other: Ptr) -> bool { 79 | // If self ends right before other 80 | if self.after().num == other.num { 81 | (*self).len += (*other).len + mem::size_of::() as u32; 82 | (*self).next = (*other).next; 83 | (*(*self).next).prev = self; 84 | (*other).deinitialize(); 85 | true 86 | } else { 87 | false 88 | } 89 | } 90 | 91 | /// Call drop... Mostly for debugging to see deinitialized chunks in memory 92 | pub unsafe fn deinitialize(&mut self) { 93 | // self.magic = 0xDEADBEEF; 94 | self.next.num = 0; 95 | self.prev.num = 0; 96 | self.len = 0; 97 | } 98 | 99 | pub unsafe fn append_to_used(mut ptr: Ptr) { 100 | let head_ptr = Chunk::get_used_head(); 101 | let head: Ptr = *head_ptr; 102 | if ! head.is_null() { 103 | // Sort the pointers! 104 | let mut current: Ptr = head; 105 | while !(*current).next.is_null() && current.num < ptr.num { 106 | current = (*current).next; 107 | } 108 | let current_prev: Ptr = current.prev; 109 | (*current).prev = ptr; 110 | (*ptr).prev = current_prev; 111 | (*ptr).next = current; 112 | } else { 113 | Chunk::set_used_head(ptr); 114 | } 115 | } 116 | 117 | pub unsafe fn append_to_free(mut ptr: Ptr) { 118 | let mut head = Chunk::get_free_head(); 119 | if ! head.is_null() { 120 | // Sort the pointers and concatenate the given ptr if any adjacent blocks are found. 121 | let mut current: Ptr = *head; 122 | if current.num > ptr.num { 123 | (*head).num = ptr.num; 124 | (*ptr).next.num = current.num; 125 | (*ptr).prev = Ptr::null(); 126 | } else { 127 | while !(*current).next.is_null() && current.num < ptr.num { 128 | current = (*current).next; 129 | } 130 | } 131 | 132 | let mut current_prev: Ptr = current.prev; 133 | 134 | // Link the chunks together so that ptr is in the middle 135 | (*current).prev = ptr; 136 | (*ptr).prev = current_prev; 137 | (*ptr).next = current; 138 | if ! current_prev.is_null() { 139 | (*current_prev).next = ptr; 140 | // Try to concatenate the previous chunk and ptr 141 | if current_prev.try_concatenate(ptr) { 142 | // The two were successfully concatenated so try appending the new chunk with 143 | // the following chunk in the list. 144 | // Result doesn't matter here 145 | let _ = current_prev.try_concatenate(current); 146 | } 147 | } else { // Try to concatenate ptr and the following chunk 148 | let _ = ptr.try_concatenate(current); 149 | } 150 | } else { 151 | Chunk::set_free_head(ptr); 152 | } 153 | } 154 | 155 | /// after returns a pointer to the first byte after the data section 156 | pub unsafe fn after(&self) -> Ptr { 157 | let len = self.len; 158 | let mut self_ptr = Ptr::::from_ref(self); 159 | self_ptr.num += len + mem::size_of::() as u32; 160 | self_ptr.transmute::() 161 | } 162 | 163 | /// get_data_ptr returns a pointer to the beginning of the data section after the chunk. 164 | #[inline(always)] 165 | pub unsafe fn get_data_ptr(&self) -> Ptr { 166 | let mut ptr = Ptr::::from_ref(self).transmute::(); 167 | ptr.num += mem::size_of::() as u32; 168 | ptr 169 | } 170 | 171 | #[inline(always)] 172 | pub unsafe fn as_gba_ptr(&mut self) -> Ptr { Ptr::from_mut_ref(self) } 173 | 174 | #[inline(always)] 175 | pub const fn as_ptr(&self) -> * const Chunk { self as * const Chunk } 176 | 177 | #[inline(always)] 178 | pub unsafe fn as_ptr_mut(&mut self) -> * mut Chunk { self as * mut Chunk} 179 | 180 | /// Remove this chunk from the list it is contained in. 181 | pub unsafe fn remove_from_free_list(&mut self) { 182 | let mut head = Chunk::get_free_head(); 183 | 184 | let (next_null, prev_null) = (self.next.is_null(), self.prev.is_null()); 185 | 186 | // Current node is: 187 | match (next_null, prev_null) { 188 | // Only member in the list 189 | (true, true) => { 190 | Chunk::set_free_head(Ptr::null()); 191 | }, 192 | // End of the list 193 | (true, false) => { 194 | (*self.prev).next = Ptr::null(); 195 | }, 196 | // Head of list 197 | (false, true) => { 198 | *head = self.next; 199 | (*self.next).prev = Ptr::null(); 200 | }, 201 | // Somewhere in the middle 202 | (false, false) => { 203 | let mut next = self.next; 204 | *next.prev = *self.prev; 205 | (*self.prev).next = next; 206 | } 207 | } 208 | 209 | self.next = Ptr::::null(); 210 | self.prev = Ptr::::null(); 211 | } 212 | 213 | pub unsafe fn remove_from_used_list(&mut self) { 214 | let mut head = Chunk::get_used_head(); 215 | 216 | let (next_null, prev_null) = (self.next.is_null(), self.prev.is_null()); 217 | 218 | // Current node is: 219 | match (next_null, prev_null) { 220 | // Only member in the list 221 | (true, true) => { 222 | Chunk::set_used_head(Ptr::null()); 223 | }, 224 | // End of the list 225 | (true, false) => { 226 | (*self.prev).next = Ptr::null(); 227 | }, 228 | // Head of list 229 | (false, true) => { 230 | *head = self.next; 231 | (*self.next).prev = Ptr::null(); 232 | }, 233 | // Somewhere in the middle 234 | (false, false) => { 235 | let mut next = self.next; 236 | *next.prev = *self.prev; 237 | (*self.prev).next = next; 238 | } 239 | } 240 | 241 | self.next = Ptr::::null(); 242 | self.prev = Ptr::::null(); 243 | } 244 | 245 | #[inline(always)] 246 | pub unsafe fn try_alloc(&mut self, buf_len: u32) -> Ptr { 247 | // If there this block is too small... 248 | if self.len <= buf_len { 249 | return Ptr::::null() 250 | } 251 | let num_whole_blocks = buf_len >> BLOCK_SIZE_SHIFT; 252 | let len: u32; 253 | 254 | if num_whole_blocks << BLOCK_SIZE_SHIFT == buf_len { 255 | // Perfect fit! 256 | len = num_whole_blocks << BLOCK_SIZE_SHIFT; 257 | } else if buf_len <= BLOCK_SIZE { 258 | // This is a small allocation - only allocate the required number of whole-words 259 | len = match buf_len & 3 { 260 | 0 => buf_len, 261 | 1 => buf_len + 3, 262 | 2 => buf_len + 2, 263 | 3 => buf_len + 1, 264 | _ => unreachable!() 265 | }; 266 | } else { 267 | // Since there is a partial block after this, account for it. 268 | len = (num_whole_blocks + 1) << 2; 269 | } 270 | 271 | // If the extra block space is too much, return null 272 | if len >= self.len { 273 | Ptr::::null() 274 | } else { 275 | // Remaining space in this chunk, after accounting for a new Chunk and the len 276 | // of the requested buffer. 277 | let remaining_space = self.len - mem::size_of::() as u32 - len; 278 | 279 | // If the remaining space isn't a whole-block or more, use that space too. 280 | if remaining_space < BLOCK_SIZE { 281 | // The current Chunk is good as is 282 | self.remove_from_free_list(); 283 | Chunk::append_to_used(self.as_gba_ptr()); 284 | self.as_gba_ptr() 285 | } else { 286 | // Use the calculated amount of needed space, with some left over in new_chunk 287 | // create new chunk that will hold remaining space 288 | let mut new_chunk: Ptr = self.as_gba_ptr(); 289 | new_chunk.num += mem::size_of::() as u32 + len; 290 | 291 | Chunk::initialize(&mut *new_chunk); 292 | 293 | // new_chunk is a new node in the linked list - place it after the current node 294 | new_chunk.next = self.next; 295 | if !self.next.is_null() { 296 | self.next.prev = new_chunk; 297 | } 298 | new_chunk.prev = Ptr::::from_mut_ref(self); 299 | 300 | // New chunk will have any remaining space that is in this chunk 301 | new_chunk.len = remaining_space; 302 | self.len = len; 303 | 304 | self.next = new_chunk; 305 | 306 | self.remove_from_free_list(); 307 | 308 | let ptr = self.as_gba_ptr(); 309 | Chunk::append_to_used(ptr); 310 | ptr 311 | } 312 | } 313 | } 314 | } 315 | 316 | struct ChunkIterator { 317 | current: Option> 318 | } 319 | 320 | impl<'a> iter::IntoIterator for &'a Chunk { 321 | type Item = Chunk; 322 | type IntoIter = ChunkIterator; 323 | 324 | fn into_iter(self) -> Self::IntoIter { 325 | let next = self.next; 326 | ChunkIterator { current: Some(next.prev) } 327 | } 328 | } 329 | 330 | impl iter::Iterator for ChunkIterator { 331 | type Item = Chunk; 332 | 333 | fn next(&mut self) -> Option { 334 | if self.current.is_none() { 335 | None 336 | } else { 337 | let ch: Chunk = *(self.current.unwrap()); 338 | if ch.next.is_null() { 339 | self.current = None; 340 | } else { 341 | self.current = Some(ch.next); 342 | } 343 | Some(ch) 344 | } 345 | } 346 | } 347 | 348 | pub unsafe fn alloc_initialize() { 349 | let mut free_head: Ptr> = Chunk::get_free_head(); 350 | (*free_head).num = RAM_START; 351 | (**free_head).initialize(); 352 | (**free_head).len = RAM_END - RAM_START - mem::size_of::() as u32; 353 | } 354 | 355 | pub unsafe fn alloc(len: u32) -> Ptr { 356 | let len = mem::size_of::() as u32 * len; 357 | let head: Ptr> = Chunk::get_free_head(); 358 | let mut current = (*head).transmute::(); 359 | while ! current.is_null() { 360 | let result: Ptr = current.try_alloc(len); 361 | if result.is_null() { 362 | current = (*current).next; 363 | continue 364 | } 365 | return (*result).get_data_ptr::(); 366 | } 367 | Ptr::::null() 368 | } 369 | 370 | pub unsafe fn free(ptr: &mut Ptr) { 371 | if ptr.is_null() { 372 | return; 373 | } else { 374 | let mut chunk: Ptr = Ptr::::from_u32(ptr.num - (mem::size_of::() as u32)); 375 | (*chunk).remove_from_used_list(); 376 | Chunk::append_to_free(chunk); 377 | ptr.num = 0; 378 | } 379 | } 380 | -------------------------------------------------------------------------------- /src/boxed/boxed.rs: -------------------------------------------------------------------------------- 1 | use ptr::Ptr; 2 | use alloc::{ alloc, free }; 3 | use core::mem; 4 | use core::ops::{ Deref, DerefMut, Drop }; 5 | use core::intrinsics::volatile_store; 6 | 7 | pub struct Box { 8 | inner: Ptr 9 | } 10 | 11 | impl Box { 12 | pub fn new(item: T) -> Self { 13 | unsafe { 14 | let inner = alloc::(1); 15 | volatile_store(inner.ptr_mut, item); 16 | Box { inner } 17 | } 18 | } 19 | } 20 | 21 | impl Deref for Box { 22 | type Target = T; 23 | 24 | fn deref(&self) -> &T { 25 | unsafe { mem::transmute::<* const T, &T>(self.inner.ptr) } 26 | } 27 | } 28 | 29 | impl DerefMut for Box { 30 | fn deref_mut(&mut self) -> &mut T { 31 | unsafe { mem::transmute::<* mut T, &mut T>(self.inner.ptr_mut) } 32 | } 33 | } 34 | 35 | impl Drop for Box { 36 | fn drop(&mut self) { 37 | unsafe { free(&mut self.inner) } 38 | } 39 | } -------------------------------------------------------------------------------- /src/boxed/mod.rs: -------------------------------------------------------------------------------- 1 | mod boxed; 2 | 3 | pub use self::boxed::*; -------------------------------------------------------------------------------- /src/collections/arr.rs: -------------------------------------------------------------------------------- 1 | use core::ops::{ Index, IndexMut }; 2 | use core::mem::size_of; 3 | 4 | use ptr::Ptr; 5 | use alloc::{ alloc, free }; 6 | 7 | pub struct Arr { 8 | ptr: Ptr, 9 | len: u32, 10 | } 11 | 12 | impl Arr { 13 | pub fn new(len: u32) -> Arr { 14 | unsafe { 15 | let data_ptr: Ptr = alloc::(len); 16 | Arr { 17 | ptr: data_ptr, 18 | len: len * size_of::() as u32, 19 | } 20 | } 21 | } 22 | 23 | pub fn len(&self) -> u32 { self.len } 24 | 25 | pub fn free(mut self) { 26 | unsafe { 27 | free(&mut self.ptr); 28 | } 29 | } 30 | } 31 | 32 | impl Index for Arr 33 | where Item: Sized, 34 | Ind: Sized + Into { 35 | type Output = Item; 36 | 37 | fn index(&self, index: Ind) -> &Item { 38 | let i: u32 = index.into(); 39 | unsafe { 40 | let x = Ptr::::from_u32(self.ptr.num + i * size_of::() as u32); 41 | let y: &mut Item; 42 | asm!("mov $0, $1" 43 | : "=&r" (y) 44 | : "r"(x) 45 | : 46 | : ); 47 | y 48 | } 49 | } 50 | } 51 | 52 | impl IndexMut for Arr 53 | where Item: Sized, 54 | Ind: Sized + Into { 55 | 56 | fn index_mut(&mut self, index: Ind) -> &mut Item { 57 | let i: u32 = index.into(); 58 | unsafe { 59 | let x = Ptr::::from_u32(self.ptr.num + i * size_of::() as u32); 60 | let y: &mut Item; 61 | asm!("mov $0, $1" 62 | : "=&r" (y) 63 | : "r"(x) 64 | : 65 | : ); 66 | y 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /src/collections/mod.rs: -------------------------------------------------------------------------------- 1 | mod arr; 2 | mod static_arr; 3 | 4 | pub use self::arr::Arr; 5 | pub use self::static_arr::StaticArr; -------------------------------------------------------------------------------- /src/collections/static_arr.rs: -------------------------------------------------------------------------------- 1 | use core::ops::{ Index, IndexMut }; 2 | use core::mem::transmute; 3 | use ptr::Ptr; 4 | 5 | pub struct StaticArr where Ptr: Clone + Copy { 6 | ptr: Ptr, 7 | len: u32, 8 | } 9 | 10 | unsafe impl Send for StaticArr {} 11 | unsafe impl Sync for StaticArr {} 12 | 13 | impl StaticArr { 14 | pub const fn new(ptr: Ptr, len: u32) -> StaticArr { 15 | StaticArr { ptr, len } 16 | } 17 | 18 | pub fn len(&self) -> u32 { self.len } 19 | 20 | pub fn zero(&self) -> u32 { 21 | panic!() 22 | } 23 | 24 | pub fn as_ptr(&self) -> Ptr { 25 | self.ptr 26 | } 27 | } 28 | 29 | impl Index for StaticArr 30 | where Item: Sized + Clone + Copy, 31 | Ind: Sized + Into { 32 | type Output = Item; 33 | 34 | fn index(&self, index: Ind) -> &'static Item { 35 | unsafe { 36 | let p: Ptr<_> = self.ptr.offset(index.into()); 37 | transmute(p) 38 | } 39 | } 40 | } 41 | 42 | impl IndexMut for StaticArr 43 | where Item: Sized + Clone + Copy, 44 | Ind: Sized + Into { 45 | 46 | fn index_mut(&mut self, index: Ind) -> &'static mut Item { 47 | unsafe { 48 | let p: Ptr<_> = self.ptr.offset(index.into()); 49 | transmute(p) 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /src/graphics/mod.rs: -------------------------------------------------------------------------------- 1 | use core::mem; 2 | use core::default::Default; 3 | use core::intrinsics::volatile_store; 4 | use reg; 5 | 6 | pub mod tiled_bg; 7 | pub mod sprites; 8 | pub use self::sprites::*; 9 | 10 | pub fn vsync_busy() { 11 | unsafe { 12 | while reg::REG_VCOUNT.volatile_load() >= 160 {} 13 | while reg::REG_VCOUNT.volatile_load() < 160 {} 14 | 15 | } 16 | } 17 | 18 | pub fn vsync_int() { 19 | interrupt!(0x05); 20 | } 21 | 22 | #[derive(Copy, Clone)] 23 | #[repr(u16)] 24 | pub enum ColorMode { 25 | _4bpp = 0x0_u16, 26 | _8bpp = 0x2000_u16, 27 | } 28 | 29 | #[derive(Clone, Copy)] 30 | #[repr(u8)] 31 | pub enum VideoMode { 32 | Mode0 = 0, 33 | Mode1 = 1, 34 | Mode2 = 2, 35 | Mode3 = 3, 36 | Mode4 = 4, 37 | Mode5 = 5, 38 | } 39 | 40 | impl VideoMode { 41 | const MASK: u8 = 0b0000_0111_u8; 42 | 43 | pub fn set(self, val: u32) -> u32 { 44 | let p = self as u32; 45 | (val & !(Self::MASK as u32)) | p 46 | } 47 | } 48 | 49 | #[derive(Copy, Clone)] 50 | #[repr(u8)] 51 | pub enum FrameBufferStart { 52 | /// The FrameBuffer should start at the address 0x06000000 53 | Base = 0b0000_0000, 54 | /// The FrameBuffer should start at the address 0x0600A000 55 | Offset = 0b0001_0000, 56 | } 57 | 58 | impl FrameBufferStart { 59 | const MASK: u8 = 0b0001_0000_u8; 60 | 61 | pub fn set(self, val: u32) -> u32 { 62 | let p = self as u32; 63 | (val & !(Self::MASK as u32)) | p 64 | } 65 | } 66 | 67 | #[derive(Copy, Clone)] 68 | #[repr(u8)] 69 | pub enum SpriteStorageMode { 70 | _2D = 0b0000_0000, 71 | _1D = 0b0100_0000 72 | } 73 | 74 | impl SpriteStorageMode { 75 | const MASK: u8 = 0b0100_0000_u8; 76 | 77 | pub fn set(self, val: u32) -> u32 { 78 | let p = self as u32; 79 | (val & !(Self::MASK as u32)) | p 80 | } 81 | } 82 | 83 | #[derive(Copy, Clone)] 84 | #[repr(u8)] 85 | pub enum HBlankProcessing { 86 | None = 0b0000_0000_u8, 87 | Force = 0b0010_0000_u8, 88 | } 89 | 90 | impl HBlankProcessing { 91 | const MASK: u8 = 0b0010_0000_u8; 92 | 93 | pub fn set(self, val: u32) -> u32 { 94 | let p = self as u32; 95 | (val & !(Self::MASK as u32)) | p 96 | } 97 | } 98 | 99 | #[derive(Copy, Clone)] 100 | #[repr(u8)] 101 | pub enum DisplayState { 102 | Blank = 0b1000_0000_u8, 103 | On = 0b0000_0000_u8, 104 | } 105 | 106 | impl DisplayState { 107 | const MASK: u8 = 0b1000_0000_u8; 108 | 109 | pub fn set(self, val: u32) -> u32 { 110 | let p = self as u32; 111 | (val & !(Self::MASK as u32)) | p 112 | } 113 | } 114 | 115 | pub struct GraphicsMode { 116 | pub vm: VideoMode, 117 | pub frame_buffer_start: FrameBufferStart, 118 | pub hblank_policy: HBlankProcessing, 119 | pub sprite_storage_mode: SpriteStorageMode, 120 | pub display_state: DisplayState, 121 | pub bg0_enabled: bool, 122 | pub bg1_enabled: bool, 123 | pub bg2_enabled: bool, 124 | pub bg3_enabled: bool, 125 | pub sprites_enabled: bool, 126 | pub window0_enabled: bool, 127 | pub window1_enabled: bool, 128 | pub sprite_windows_enabled: bool, 129 | } 130 | 131 | impl GraphicsMode { 132 | 133 | const BG0_MASK: u16 = 0x0100; 134 | const BG1_MASK: u16 = 0x0200; 135 | const BG2_MASK: u16 = 0x0400; 136 | const BG3_MASK: u16 = 0x0800; 137 | const SPRITES_MASK: u16 = 0x1000; 138 | const WINDOW0_MASK: u16 = 0x2000; 139 | const WINDOW1_MASK: u16 = 0x2000; 140 | const SPRITE_WINDOWS_MASK: u16 = 0x2000; 141 | 142 | 143 | pub fn current() -> GraphicsMode { 144 | unsafe { GraphicsMode::from_u16(mem::transmute(*reg::REG_GRAPHICS_MODE)) } 145 | } 146 | 147 | pub fn from_u16(n: u16) -> GraphicsMode { 148 | GraphicsMode { 149 | vm: VideoMode::Mode0, 150 | frame_buffer_start: FrameBufferStart::Base, 151 | hblank_policy: HBlankProcessing::None, 152 | sprite_storage_mode: SpriteStorageMode::_2D, 153 | display_state: DisplayState::On, 154 | bg0_enabled: (n & GraphicsMode::BG0_MASK) != 0, 155 | bg1_enabled: (n & GraphicsMode::BG1_MASK) != 0, 156 | bg2_enabled: (n & GraphicsMode::BG2_MASK) != 0, 157 | bg3_enabled: (n & GraphicsMode::BG3_MASK) != 0, 158 | sprites_enabled: (n & GraphicsMode::SPRITES_MASK) != 0, 159 | window0_enabled: (n & GraphicsMode::WINDOW0_MASK) != 0, 160 | window1_enabled: (n & GraphicsMode::WINDOW1_MASK) != 0, 161 | sprite_windows_enabled: (n & GraphicsMode::SPRITE_WINDOWS_MASK) != 0, 162 | } 163 | } 164 | 165 | pub fn set(&self) { 166 | let mut reg = 0u16; 167 | reg |= self.vm as u16; 168 | reg |= self.frame_buffer_start as u16; 169 | reg |= self.hblank_policy as u16; 170 | reg |= self.sprite_windows_enabled as u16; 171 | reg |= self.display_state as u16; 172 | reg |= self.sprite_storage_mode as u16; 173 | 174 | let bg0 = if self.bg0_enabled { GraphicsMode::BG0_MASK } else { 0 }; 175 | let bg1 = if self.bg1_enabled { GraphicsMode::BG1_MASK } else { 0 }; 176 | let bg2 = if self.bg2_enabled { GraphicsMode::BG2_MASK } else { 0 }; 177 | let bg3 = if self.bg3_enabled { GraphicsMode::BG3_MASK } else { 0 }; 178 | let sprites = if self.sprites_enabled { GraphicsMode::SPRITES_MASK } else { 0 }; 179 | let window0 = if self.window0_enabled { GraphicsMode::WINDOW0_MASK } else { 0 }; 180 | let window1= if self.window1_enabled { GraphicsMode::WINDOW1_MASK } else { 0 }; 181 | let sprite_windows = if self.sprite_windows_enabled { GraphicsMode::SPRITE_WINDOWS_MASK} else { 0 }; 182 | 183 | reg |= bg0 | bg1 | bg2 | bg3 | sprites | window0 | window1 | sprite_windows; 184 | 185 | // The * mut _ is to prevent a weird warning, that may be a bug in rustc 186 | // when using reg::REG_GRAPHICS_MODE.ptr_mut, a warning as thrown that says: 187 | // 188 | // --> src\graphics\mod.rs:164:33 189 | // | 190 | // 164 | unsafe { volatile_store(reg::REG_GRAPHICS_MODE.ptr_mut, reg) } 191 | // | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 192 | // | 193 | // = note: #[warn(const_err)] on by default 194 | 195 | unsafe { volatile_store(reg::REG_GRAPHICS_MODE.num as * mut _, reg) } 196 | } 197 | 198 | } 199 | 200 | impl Default for GraphicsMode { 201 | fn default() -> Self { 202 | GraphicsMode::from_u16(0) 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /src/graphics/sprites.rs: -------------------------------------------------------------------------------- 1 | use core::mem::transmute; 2 | use collections::StaticArr; 3 | use ptr::Ptr; 4 | use graphics::*; 5 | 6 | /// Further documentation sprite related gba things can be found here: https://www.cs.rit.edu/~tjh8300/CowBite/CowBiteSpec.htm#Graphics%20Hardware%20Overview 7 | /// and also here: https://www.coranac.com/tonc/text/regobj.htm 8 | 9 | 10 | pub const OBJECT_SPRITE_ATTRIBUTES: StaticArr = unsafe { StaticArr::new(Ptr::from_u32(0x07000000), 128) }; 11 | pub const OBJECT_SPRITE_AFFINE: StaticArr = unsafe { StaticArr::new(Ptr::from_u32(0x07000000), 32) }; 12 | 13 | pub type Charblock = [u8; 0x4000]; 14 | pub const TILE_MEMORY: StaticArr = unsafe { StaticArr::new(Ptr::from_u32(0x06000000), 6) }; 15 | pub const PALETTE_MEMORY: StaticArr = unsafe { StaticArr::new(Ptr::from_u32(0x05000200), 256) }; 16 | 17 | pub fn oam_clear() { 18 | let p = SpriteAttributes::default(); 19 | for i in 0..128 { 20 | p.set(i); 21 | } 22 | } 23 | 24 | #[derive(Copy, Clone)] 25 | #[repr(u16)] 26 | pub enum SpriteMode { 27 | /// Enables normal sprite rendering 28 | Normal = 0x0000_u16, 29 | 30 | /// Enables alpha blending 31 | Alpha = 0x0400_u16, 32 | 33 | /// As per TONC: "Object is part of the object window. The sprite itself isn't rendered, but 34 | /// serves as a mask for bgs and other sprites. (I think, haven't used it yet)" 35 | Masked = 0x0800_u16, 36 | 37 | /// This value is invalid / unused, but is here so it can be used if someone is interested in 38 | /// testing it. 39 | Forbidden = 0x0C00_u16 40 | } 41 | 42 | #[derive(Copy, Clone)] 43 | #[repr(u16)] 44 | pub enum AffineMode { 45 | /// Enables normal affine rendering 46 | Normal = 0x0000_u16, 47 | 48 | /// Sprite is an affine sprite and uses the specified affine matrix 49 | Affine = 0x0100_u16, 50 | 51 | /// Sprite is hidden 52 | Disabled = 0x0200_u16, 53 | 54 | /// Doubles the size of the sprite (I think?) 55 | Doubled = 0x0300_u16 56 | } 57 | 58 | /// An enum that represents a sprite's dimensions (width then height). Since there are two attributes that need to be 59 | /// set to set the dimensions, this enum can be converted into a tuple: the first element is the bits to 60 | /// be masked into attribute_0, and the latter into attribute into attribute_1. 61 | #[derive(Copy, Clone)] 62 | #[repr(u32)] 63 | pub enum SpriteDimensions { 64 | _8x8 = 0x0000_0000_u32, 65 | _16x16 = 0x0000_4000_u32, 66 | _32x32 = 0x0000_8000_u32, 67 | _64x64 = 0x0000_C000_u32, 68 | _16x8 = 0x4000_0000_u32, 69 | _32x8 = 0x4000_4000_u32, 70 | _32x16 = 0x4000_8000_u32, 71 | _64x32 = 0x4000_C000_u32, 72 | _8x16 = 0x8000_0000_u32, 73 | _8x32 = 0x8000_4000_u32, 74 | _16x32 = 0x8000_8000_u32, 75 | _32x64 = 0x8000_C000_u32 76 | } 77 | 78 | impl SpriteDimensions { 79 | 80 | pub const fn into_tuple(self) -> (u16, u16) { 81 | ((self as u32 >> 16) as u16, self as u32 as u16) 82 | } 83 | 84 | /* If you remove the const qualifier, you could do 85 | 86 | pub fn into_tuple(self) -> (u16, u16) { 87 | mem::transmute(self as u32) 88 | } 89 | 90 | but I think the const will allow for more optimizations 91 | */ 92 | } 93 | 94 | #[derive(Copy, Clone)] 95 | #[repr(u16)] 96 | pub enum SpritePriority { 97 | Last = 0x0000_u16, 98 | Background = 0x0400_u16, 99 | Foreground = 0x0800_u16, 100 | First = 0x0C00_u16, 101 | } 102 | 103 | #[derive(Copy, Clone)] 104 | pub struct SpriteAttributes { 105 | /// Attribute 0 (attribute_0) 106 | a0: u16, 107 | 108 | /// Attribute 1 (attribute_1) 109 | a1: u16, 110 | 111 | /// Attribute 2 (attribute_2) 112 | a2: u16, 113 | /// Filler to make the struct word aligned 114 | pub filler: u16 115 | } 116 | 117 | #[allow(unused)] 118 | impl SpriteAttributes { 119 | /// All masks used for attribute_0 120 | 121 | /// The 8 bits that are used to set the Y coordinate of the sprite in attribute_0 122 | const Y_COORD_MASK: u16 = 0x00FF_u16; 123 | /// The two bits that determine the what affine is used for this sprite; located in attribute_0 124 | const AFFINE_MODE_MASK: u16 = 0x0300_u16; 125 | /// The two bits that determine the what special effects are enabled for this sprite; located in 126 | /// attribute_0 127 | const SPRITE_MODE_MASK: u16 = 0x0C00_u16; 128 | /// If this bit is set to 1 in attribute_0, then mosaic effects are enabled 129 | const MOSAIC_MASK: u16 = 0x1000_u16; 130 | /// The bit that determine what color mode is used in attribute_0. If it is 0, then it is 4bpp 131 | /// (16 color), otherwise it is 8bpp (256 colors). 132 | const COLOR_MODE_MASK: u16 = 0x2000_u16; 133 | /// The first attribute that determines the dimensions of the sprite, in attribute_0 (first element 134 | /// in the tuple from SpriteDimensions). 135 | const SPRITE_SHAPE_MASK: u16 = 0xC000_u16; 136 | 137 | /// All masks used for attribute_1 138 | 139 | /// The 9 (yes 9) bits that are used to set the X coordinate of the sprite in attribute_1 140 | const X_COORD_MASK: u16 = 0x01FF_u16; 141 | /// The affine index bits in attribute_1. Should only be set if AFFINE_MODE is set to Affine 142 | const AFFINE_INDEX_MASK: u16 = 0x3E00_u16; 143 | /// The bit to be set if this sprite should be horizontally flipped, in attribute_1. 144 | const HORIZONTAL_FLIP_MASK: u16 = 0x1000_u16; 145 | /// The bit to be set if this sprite should be vertically flipped, in attribute_1. 146 | const VERTICAL_FLIP_MASK: u16 = 0x2000_u16; 147 | /// The second attribute that determines the dimensions of the sprite, in attribute_1 (second 148 | /// element in the tuple from SpriteDimensions) 149 | const SPRITE_SIZE_MASK: u16 = 0xC000_u16; 150 | 151 | /// All masks used for attribute_2 152 | 153 | const TILE_INDEX_MASK: u16 = 0x03FF_u16; 154 | const PRIORITY_MASK: u16 = 0x0C00_u16; 155 | const PALETTE_BANK_INDEX_MASK: u16 = 0xF000_u16; 156 | 157 | /// Copy this SpriteAttributes into the n'th SA slot. 158 | pub fn set(self, n: u32) { 159 | OBJECT_SPRITE_ATTRIBUTES[n as i32] = self; 160 | } 161 | 162 | pub fn default() -> Self { SpriteAttributes { a0: 0, a1: 0, a2: 0, filler: 0 } } 163 | 164 | pub fn new( x: i16, y: i16, affine_mode: AffineMode, sprite_mode: SpriteMode, 165 | dimensions: SpriteDimensions, color_mode: ColorMode, mosaic_enabled: bool, 166 | horizontal_flipped: bool, vertical_flipped: bool, priority: SpritePriority, 167 | palette_bank_index: u16, tile_index: u16) -> Self { 168 | 169 | let mut result = Self::default(); 170 | result.set_x(x); 171 | result.set_y(y); 172 | result.set_priority(priority); 173 | result.set_dimensions(dimensions); 174 | result.set_tile_index(tile_index); 175 | result.set_color_mode(color_mode); 176 | result.set_affine_mode(affine_mode); 177 | result.set_sprite_mode(sprite_mode); 178 | result.set_mosaic_enabled(mosaic_enabled); 179 | result.set_vertically_flipped(vertical_flipped); 180 | result.set_palette_bank_index(palette_bank_index); 181 | result.set_horizontally_flipped(horizontal_flipped); 182 | 183 | result 184 | } 185 | 186 | pub fn set_x(&mut self, mut x: i16) { 187 | unsafe { 188 | x &= transmute::(Self::X_COORD_MASK); 189 | self.a1 &= !SpriteAttributes::X_COORD_MASK; 190 | self.a1 |= transmute::(x); 191 | } 192 | } 193 | 194 | pub fn set_y(&mut self, mut y: i16) { 195 | unsafe { 196 | y &= transmute::(Self::X_COORD_MASK); 197 | self.a0 &= !SpriteAttributes::Y_COORD_MASK; 198 | self.a0 |= transmute::(y); 199 | } 200 | } 201 | 202 | pub fn set_priority(&mut self, priority: SpritePriority) { 203 | let pr = priority as u16; 204 | self.a2 &= !SpriteAttributes::PRIORITY_MASK; 205 | self.a2 |= pr; 206 | } 207 | 208 | pub fn set_dimensions(&mut self, dim: SpriteDimensions) { 209 | let (width,height) = dim.into_tuple(); 210 | 211 | self.a0 &= !SpriteAttributes::SPRITE_SHAPE_MASK; 212 | self.a0 |= width; 213 | 214 | self.a1 &= !SpriteAttributes::SPRITE_SIZE_MASK; 215 | self.a1 |= height; 216 | } 217 | 218 | pub fn set_color_mode(&mut self, color_mode: ColorMode) { 219 | self.a0 &= !SpriteAttributes::COLOR_MODE_MASK; 220 | self.a0 |= color_mode as u16; 221 | } 222 | 223 | pub fn set_affine_mode(&mut self, affine_mode: AffineMode) { 224 | let am = affine_mode as u16; 225 | self.a0 &= !SpriteAttributes::AFFINE_MODE_MASK; 226 | self.a0 |= am; 227 | } 228 | 229 | pub fn set_sprite_mode(&mut self, sprite_mode: SpriteMode) { 230 | let sm = sprite_mode as u16; 231 | self.a0 &= !SpriteAttributes::SPRITE_MODE_MASK; 232 | self.a0 |= sm; 233 | } 234 | 235 | pub fn set_mosaic_enabled(&mut self, enabled: bool) { 236 | let p = (enabled as u16) << 12; 237 | self.a0 &= !SpriteAttributes::MOSAIC_MASK; 238 | self.a0 |= p; 239 | } 240 | 241 | pub fn set_vertically_flipped(&mut self, flipped: bool) { 242 | let p = (flipped as u16) << 13; 243 | self.a1 &= !SpriteAttributes::VERTICAL_FLIP_MASK; 244 | self.a1 |= p; 245 | } 246 | 247 | pub fn set_horizontally_flipped(&mut self, flipped: bool) { 248 | let p = (flipped as u16) << 12; 249 | self.a1 &= !SpriteAttributes::HORIZONTAL_FLIP_MASK; 250 | self.a1 |= p; 251 | } 252 | 253 | pub fn set_palette_bank_index(&mut self, mut index: u16) { 254 | index <<= 12; 255 | self.a2 &= !SpriteAttributes::PALETTE_BANK_INDEX_MASK; 256 | self.a2 |= index; 257 | } 258 | 259 | pub fn set_tile_index(&mut self, mut index: u16) { 260 | index &= SpriteAttributes::TILE_INDEX_MASK; 261 | self.a2 &= !SpriteAttributes::TILE_INDEX_MASK; 262 | self.a2 |= index; 263 | } 264 | } 265 | 266 | #[allow(unused)] 267 | #[derive(Clone, Copy)] 268 | pub struct SpriteAffine { 269 | fill0: [u16; 3], 270 | pa: i16, 271 | fill1: [u16; 3], 272 | pb: i16, 273 | fill2: [u16; 3], 274 | pc: i16, 275 | fill3: [u16; 3], 276 | pd: i16 277 | } 278 | -------------------------------------------------------------------------------- /src/graphics/tiled_bg.rs: -------------------------------------------------------------------------------- 1 | use reg; 2 | use graphics::ColorMode; 3 | use core::mem::transmute; 4 | use ptr::Ptr; 5 | 6 | /// Represents a Background control object as per http://www.coranac.com/tonc/text/regbg.htm 7 | #[derive(Copy, Clone)] 8 | pub struct BgControl(u16); 9 | 10 | impl BgControl { 11 | 12 | /// Returns the n'th background control. There are only 4, so only values of n [0,3]are 13 | /// valid. 14 | pub fn get(mut n: u32) -> &'static mut BgControl { 15 | n &= 3; 16 | unsafe { reg::REG_BGCNT.transmute::().offset(n as i32).as_mut() } 17 | } 18 | 19 | pub fn set_priority(&mut self, mut priority: u16) -> &mut Self { 20 | self.0 &= !0b11; 21 | priority &= 0b11; 22 | self.0 |= priority; 23 | self 24 | } 25 | 26 | pub fn set_character_base_block(&mut self, mut block_n: u16) -> &mut Self { 27 | self.0 &= !0b1100; 28 | block_n &= 0b11; 29 | self.0 |= block_n << 2; 30 | self 31 | } 32 | 33 | pub fn set_color_mode(&mut self, color_mode: ColorMode) -> &mut Self { 34 | self.0 &= !0x80; 35 | self.0 |= (color_mode as u16) >> 6; 36 | self 37 | } 38 | 39 | pub fn set_mosaic_enabled(&mut self, mosaic: bool) -> &mut Self { 40 | let p = mosaic as u16; 41 | self.0 &= !0b01000000; 42 | self.0 |= p << 6; 43 | self 44 | } 45 | 46 | pub fn set_affine_wrapping_enabled(&mut self, wrapping: bool) -> &mut Self { 47 | let p = wrapping as u16; 48 | self.0 &= !0x2000; 49 | self.0 |= p << 13; 50 | self 51 | } 52 | 53 | pub fn set_screen_base_block(&mut self, mut block_n: u16) -> &mut Self { 54 | block_n &= 0b11111; 55 | self.0 |= block_n << 8; 56 | self 57 | } 58 | 59 | pub fn set_bg_size(&mut self, bg_size: Bg) -> &mut Self { 60 | self.0 &= 0x3FFF; 61 | self.0 |= bg_size.into_bg_bits(); 62 | self 63 | } 64 | } 65 | 66 | pub trait BgSize { fn into_bg_bits(self) -> u16; } 67 | 68 | /// Represents a regular background size in terms of tiles (that is, an 8x8 image). 69 | #[repr(u16)] 70 | pub enum RegularBgSize { 71 | /// 256x256 pixels 72 | _32x32 = 0x0000, 73 | 74 | /// 512x256 pixels 75 | _64x32 = 0x4000, 76 | 77 | /// 256x512 pixels 78 | _32x64 = 0x8000, 79 | 80 | /// 512x512 pixels 81 | _64x64 = 0xC000, 82 | } 83 | 84 | impl BgSize for RegularBgSize { fn into_bg_bits(self) -> u16 { self as u16 } } 85 | 86 | #[repr(u16)] 87 | pub enum AffineBgSize { 88 | _16x16 = 0x0000, 89 | _32x32 = 0x4000, 90 | _64x64 = 0x8000, 91 | _128x128 = 0xC000, 92 | } 93 | 94 | impl BgSize for AffineBgSize { fn into_bg_bits(self) -> u16 { self as u16 } } 95 | 96 | #[repr(C)] 97 | #[derive(Copy, Clone)] 98 | struct BgOffsetInternal { x: i16, y: i16 } 99 | 100 | /// Represents the offset of a background. Since the x and y fields in BgOffsetInternal are write 101 | /// only, we keep a copy of x and y in this struct (since we can't read from the registers to use 102 | /// normal arithmetic operators like +=). 103 | /// 104 | /// The x and y coordinates of the background offset will be `mod mapsize`. 105 | #[derive(Clone)] 106 | struct BgOffset { 107 | x: i16, 108 | y: i16, 109 | inner: Ptr, 110 | } 111 | 112 | impl BgOffset { 113 | pub fn get(x: i16, y: i16, mut n: u32) -> BgOffset { 114 | n &= 3; 115 | BgOffset { 116 | x, y, 117 | inner: unsafe { reg::REG_BG_OFS.transmute::().offset(transmute(n)).transmute() }, 118 | } 119 | } 120 | 121 | pub fn set_x(&mut self, x: i16) -> &mut BgOffset { 122 | self.x = x; 123 | self.inner.x = x; 124 | self 125 | } 126 | 127 | pub fn set_y(&mut self, y: i16) -> &mut BgOffset { 128 | self.y = y; 129 | self.inner.y = y; 130 | self 131 | } 132 | 133 | pub fn set(&mut self, x: i16, y: i16) -> &mut BgOffset { 134 | self.x = x; 135 | self.inner.x = x; 136 | self.y = y; 137 | self.inner.y = y; 138 | self 139 | } 140 | 141 | pub fn translate(&mut self, x: i16, y: i16) -> &mut BgOffset { 142 | self.x += x; 143 | self.y += y; 144 | self.inner.x = self.x; 145 | self.inner.y = self.y; 146 | self 147 | // self.set_x(self.x + x) 148 | // .set_y(self.y + y) 149 | } 150 | 151 | /// Same as `set_x` except it doesn't return &mut BgOffset, hence nc: 'no-chain' 152 | pub fn set_x_nc(&mut self, x: i16) { 153 | self.inner.x = x; 154 | self.x = x; 155 | } 156 | 157 | /// Same as `set_y` except it doesn't return &mut BgOffset, hence nc: 'no-chain' 158 | pub fn set_y_nc(&mut self, y: i16) { 159 | self.y = y; 160 | self.inner.y = y; 161 | } 162 | 163 | /// Same as `set` except it doesn't return &mut BgOffset, hence nc: 'no-chain' 164 | pub fn set_nc(&mut self, x: i16, y: i16) { 165 | self.x = x; 166 | self.inner.x = x; 167 | self.y = y; 168 | self.inner.y = y; 169 | } 170 | 171 | /// Same as `translate` except it doesn't return &mut BgOffset, hence nc: 'no-chain' 172 | pub fn translate_nc(&mut self, x: i16, y: i16) { 173 | self.x += x; 174 | self.y += y; 175 | self.inner.x = self.x; 176 | self.inner.y = self.y; 177 | // self.set_x(self.x + x) 178 | // .set_y(self.y + y) 179 | } 180 | } 181 | 182 | /// Holds metadata about a tile at a position. The position of the tile is determined by the 183 | /// TileEntry's location (index) in the Screenblock array. 184 | pub struct TileEntry(u16); 185 | 186 | impl TileEntry { 187 | 188 | } 189 | 190 | /// A `Screenblock` is used to store a tilemap (i.e. indices into a tile set - maps position to a 191 | /// tile + palette entry) 192 | pub type Screenblock = [TileEntry; 0x400]; 193 | 194 | pub struct Tilemap { 195 | inner: Ptr, 196 | 197 | } 198 | 199 | impl Tilemap { 200 | /// Creates a new Tilemap, without initializing any memory to zero (i.e. do it yourself if you 201 | /// need to). Since there are 32 screenblocks in total, n = n (mod 32) 202 | pub fn new(mut n: i32) -> Self { 203 | n &= 31; 204 | let inner = unsafe { reg::VRAM.transmute::().offset(n).transmute() }; 205 | Tilemap { inner } 206 | } 207 | 208 | pub fn entry(&mut self, n: u32) -> &'static mut TileEntry { 209 | unsafe { self.inner.cpy().offset(transmute(n)).transmute::().as_mut() } 210 | } 211 | } 212 | 213 | 214 | #[repr(C)] 215 | pub struct BgAffine { 216 | _padding: [u16; 4], 217 | dx: i32, 218 | dy: i32 219 | } 220 | -------------------------------------------------------------------------------- /src/input/mod.rs: -------------------------------------------------------------------------------- 1 | use reg::REG_KEY_INPUT; 2 | use core::mem; 3 | use core::ops::Deref; 4 | 5 | #[derive(Copy, Clone)] 6 | pub struct InputState(pub u16); 7 | 8 | impl InputState { 9 | pub fn current() -> Self { 10 | InputState(*REG_KEY_INPUT) 11 | } 12 | 13 | pub fn all_keys_down>(self, keys: T) -> bool { 14 | let keys = keys.into(); 15 | !self.0 & keys.0 == keys.0 16 | } 17 | 18 | pub fn any_keys_down>(self, keys: T) -> bool { 19 | let keys = keys.into(); 20 | !self.0 & keys.0 != 0 21 | } 22 | 23 | pub fn pressed_keys(self) -> KeySet { 24 | KeySet(!self.0) 25 | } 26 | 27 | pub fn key_down(self, key: Key) -> bool { 28 | !self.0 & key as u16 == key as u16 29 | } 30 | } 31 | 32 | /// A set of keys 33 | #[derive(Copy, Clone)] 34 | pub struct KeySet(pub u16); 35 | 36 | const ALL_KEYS: KeySet = KeySet(0x03FF); 37 | 38 | impl KeySet { 39 | pub fn empty() -> KeySet { 40 | KeySet(0) 41 | } 42 | 43 | pub fn all() -> KeySet { 44 | ALL_KEYS 45 | } 46 | 47 | pub fn contains(self, key: Key) -> bool { 48 | self.0 & *key != 0 49 | } 50 | 51 | pub fn add(mut self, key: Key) -> Self { 52 | self.0 |= *key; 53 | self 54 | } 55 | } 56 | 57 | #[repr(u16)] 58 | #[derive(Copy, Clone)] 59 | pub enum Key { 60 | A = 0x0001, 61 | B = 0x0002, 62 | Select = 0x0004, 63 | Start = 0x0008, 64 | Right = 0x0010, 65 | Left = 0x0020, 66 | Up = 0x0040, 67 | Down = 0x0080, 68 | R = 0x0100, 69 | L = 0x0200, 70 | } 71 | 72 | impl Deref for Key { 73 | type Target = u16; 74 | 75 | fn deref(&self) -> &u16 { 76 | unsafe { mem::transmute::<&Key, &u16>(self) } 77 | } 78 | } 79 | 80 | impl Into for Key { 81 | fn into(self) -> KeySet { 82 | KeySet(*self) 83 | } 84 | } -------------------------------------------------------------------------------- /src/interrupt.rs: -------------------------------------------------------------------------------- 1 | macro_rules! interrupt { 2 | ($x:expr) => (unsafe { asm!(concat!("swi ", $x) : : : "r0", "r1", "r2", "r3" : "volatile"); }) 3 | } 4 | -------------------------------------------------------------------------------- /src/lang.rs: -------------------------------------------------------------------------------- 1 | use core::panic::PanicInfo; 2 | 3 | #[lang = "panic_impl"] 4 | #[no_mangle] 5 | pub extern fn panic_fmt(_info: &PanicInfo) -> ! { 6 | loop {} 7 | } 8 | 9 | #[lang = "eh_personality"] 10 | pub extern fn eh_personality() -> ! { loop {} } 11 | 12 | #[no_mangle] 13 | pub extern "C" fn __aeabi_unwind_cpp_pr0() { loop {} } 14 | #[no_mangle] 15 | pub extern "C" fn __aeabi_unwind_cpp_pr1() { loop {} } 16 | 17 | #[no_mangle] 18 | pub extern "C" fn __multi3(a: i32, b: i32) -> i32 { a * b } 19 | 20 | #[no_mangle] 21 | pub extern "C" fn __udivti3(a: u32, b: u32) -> u32 { a / b } 22 | 23 | #[no_mangle] 24 | pub extern "C" fn __umodti3(a: u32, b: u32) -> u32 { a % b } 25 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(asm, lang_items, core_intrinsics, const_fn, untagged_unions, arbitrary_self_types, const_fn_union)] 3 | 4 | #![allow(dead_code)] 5 | 6 | pub extern crate gbaimg; 7 | pub use gbaimg::{ img_as_palleted_sprite_8bpp, img_as_palleted_sprite_4bpp }; 8 | 9 | mod lang; 10 | pub use lang::*; 11 | 12 | #[macro_use] 13 | pub mod interrupt; 14 | pub mod reg; 15 | pub mod ptr; 16 | pub mod input; 17 | pub mod alloc; 18 | pub mod boxed; 19 | pub mod collections; 20 | pub mod mem; 21 | pub mod graphics; 22 | 23 | -------------------------------------------------------------------------------- /src/mem/mod.rs: -------------------------------------------------------------------------------- 1 | use ptr::Ptr; 2 | use core::mem::{ size_of }; 3 | 4 | // const debug: Ptr = Ptr::from_u32(0x03000000); 5 | pub unsafe fn memcpy(dst: Ptr, src: Ptr, items: u32) -> Ptr where Ptr: Clone + Copy { 6 | // *debug = 0; 7 | let mut p = Ptr::::from_u32(0x03000004); 8 | if items == 0 || dst.is_null() || src.is_null() { 9 | *p = 0xBEEF; 10 | return dst; 11 | } 12 | 13 | //*debug = items * size_of::() as u32; 14 | 15 | let mut count; 16 | let mut dst16; 17 | let mut src8 = src.transmute::(); 18 | 19 | let mut len = items * (size_of::() as u32); 20 | 21 | // Copy data 4 words at a time - except for the tail, which is 0, 1, 2, or 3 words 22 | if (src.num | dst.num) & 4 == 0 && len >= 4 { 23 | let mut src32 = src.transmute::(); 24 | let mut dst32 = dst.transmute::(); 25 | 26 | count = len / 4; 27 | let mut tail_words = count & 3; 28 | count /= 4; 29 | 30 | while tail_words != 0 { 31 | *dst32 = *src32; 32 | dst32 = dst32.offset(1); 33 | src32 = src32.offset(1); 34 | tail_words-= 1; 35 | } 36 | 37 | while count != 0 { 38 | *dst32 = *src32; 39 | dst32 = dst32.offset(1); 40 | src32 = src32.offset(1); 41 | 42 | *dst32 = *src32; 43 | dst32 = dst32.offset(1); 44 | src32 = src32.offset(1); 45 | 46 | *dst32 = *src32; 47 | dst32 = dst32.offset(1); 48 | src32 = src32.offset(1); 49 | 50 | *dst32 = *src32; 51 | dst32 = dst32.offset(1); 52 | src32 = src32.offset(1); 53 | 54 | count -= 1; 55 | } 56 | 57 | len &= 3; 58 | if len == 0 { 59 | *p = 0xDEAD; 60 | return dst; 61 | } 62 | 63 | src8 = src32.transmute(); 64 | dst16 = dst32.transmute(); 65 | } else { 66 | let dst_offset = dst.num & 1; 67 | dst16 = Ptr::::from_u32(dst.num - dst_offset); 68 | 69 | if dst_offset != 0 { 70 | *dst16 = (*dst16 & 0xFF) | ((*src8 as u16) << 8); 71 | src8 = src8.offset(1); 72 | dst16 = dst16.offset(1); 73 | len -= 1; 74 | if len == 0 { 75 | *p = 0xCAFE; 76 | return dst; 77 | } 78 | } 79 | } 80 | 81 | count = len / 2; 82 | 83 | while count != 0 { 84 | *dst16 = src8[0] as u16 | ((src8[1] as u16) << 8); 85 | dst16 = dst16.offset(1); 86 | src8 = src8.offset(2); 87 | len -= 1; 88 | count -= 1; 89 | } 90 | 91 | 92 | if len & 1 != 0 { 93 | *dst16 = (*dst16 & !0xFF) | *src8 as u16; 94 | } 95 | 96 | *p = 0xBABE; 97 | return dst 98 | } 99 | -------------------------------------------------------------------------------- /src/ptr/mod.rs: -------------------------------------------------------------------------------- 1 | use core::ops::{ Deref, DerefMut, Index, IndexMut }; 2 | use core::mem; 3 | use core::intrinsics::{ volatile_store, volatile_load }; 4 | 5 | #[derive(Clone, Copy)] 6 | pub union Ptr { 7 | pub ptr: * const T, 8 | pub ptr_mut: * mut T, 9 | pub num: u32, 10 | pub signed: i32 11 | } 12 | 13 | impl Ptr { 14 | 15 | pub const unsafe fn from_u32(i: u32) -> Self { Ptr { num: i } } 16 | 17 | pub const unsafe fn from_ptr(ptr: * const T) -> Self { Ptr { ptr: ptr } } 18 | 19 | pub const unsafe fn from_mut_ptr(ptr_mut: * mut T) -> Self { Ptr { ptr_mut } } 20 | 21 | pub const unsafe fn from_ref(const_ref: &T) -> Self { Ptr { ptr: const_ref as * const T } } 22 | 23 | pub unsafe fn from_mut_ref(mut_ref: &mut T) -> Self { Ptr { ptr_mut: mut_ref as * mut T } } 24 | 25 | pub const unsafe fn null() -> Self { Ptr { num: 0 } } 26 | 27 | pub unsafe fn transmute(self) -> Ptr { 28 | Ptr::::from_u32(self.num) 29 | } 30 | 31 | pub const fn is_null(&self) -> bool { unsafe { self.num == 0 } } 32 | 33 | pub unsafe fn as_ref(self) -> &'static T { mem::transmute(self.ptr) } 34 | 35 | pub unsafe fn as_mut(self) -> &'static mut T { mem::transmute(self.ptr_mut) } 36 | 37 | pub unsafe fn offset(mut self, n: i32) -> Self { 38 | self.signed += n * mem::size_of::() as i32; 39 | self 40 | } 41 | 42 | #[inline(always)] 43 | pub unsafe fn volatile_load(&self) -> T { volatile_load(self.ptr) } 44 | 45 | #[inline(always)] 46 | pub unsafe fn volatile_store(&mut self, dat: T) { volatile_store(self.ptr_mut, dat); } 47 | 48 | pub unsafe fn cpy(&self) -> Self { Ptr { num: self.num } } 49 | } 50 | 51 | impl Deref for Ptr { 52 | type Target = T; 53 | 54 | fn deref(&self) -> &T { 55 | unsafe { mem::transmute::<* const T, &T>(self.ptr) } 56 | } 57 | } 58 | 59 | impl DerefMut for Ptr { 60 | fn deref_mut(&mut self) -> &mut T { 61 | unsafe { mem::transmute::<* mut T, &mut T>(self.ptr_mut) } 62 | } 63 | } 64 | 65 | impl> IndexMut for Ptr { 66 | 67 | fn index_mut(&mut self, index: Ind) -> &'static mut T { 68 | let i: i32 = index.into(); 69 | unsafe { 70 | let x = Ptr::::from_u32(self.num).offset(i); 71 | x.as_mut() 72 | } 73 | } 74 | } 75 | 76 | impl> Index for Ptr { 77 | type Output = T; 78 | 79 | fn index(&self, index: Ind) -> &'static T { 80 | let i: i32 = index.into(); 81 | unsafe { 82 | let x = Ptr::::from_u32(self.num).offset(i); 83 | x.as_ref() 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/reg.rs: -------------------------------------------------------------------------------- 1 | use ptr::Ptr; 2 | 3 | pub const REG_GRAPHICS_MODE: Ptr = unsafe { Ptr::from_u32(0x04000000) }; 4 | pub const REG_BG_AFFINE: Ptr = unsafe { Ptr::from_u32(0x04000000) }; 5 | pub const REG_VCOUNT: Ptr = unsafe { Ptr::from_u32(0x04000006) }; 6 | pub const REG_BGCNT: Ptr = unsafe { Ptr::from_u32(0x04000008) }; 7 | pub const REG_BG_OFS: Ptr = unsafe { Ptr::from_u32(0x04000010) }; 8 | pub const REG_BG_VOFS: Ptr = unsafe { Ptr::from_u32(0x04000012) }; 9 | pub const REG_DATA_IN0: Ptr = unsafe { Ptr::from_u32(0x04000120) }; 10 | pub const REG_DATA_IN1: Ptr = unsafe { Ptr::from_u32(0x04000122) }; 11 | pub const REG_DATA_IN2: Ptr = unsafe { Ptr::from_u32(0x04000124) }; 12 | pub const REG_DATA_IN3: Ptr = unsafe { Ptr::from_u32(0x04000126) }; 13 | pub const REG_SIOCNT: Ptr = unsafe { Ptr::from_u32(0x04000128) }; 14 | pub const REG_DATA_OUT: Ptr = unsafe { Ptr::from_u32(0x0400012A) }; 15 | pub const REG_KEY_INPUT: Ptr = unsafe { Ptr::from_u32(0x04000130) }; 16 | pub const REG_RCNT: Ptr = unsafe { Ptr::from_u32(0x04000134) }; 17 | pub const REG_IE: Ptr = unsafe { Ptr::from_u32(0x04000200) }; 18 | pub const REG_IME: Ptr = unsafe { Ptr::from_u32(0x04000208) }; 19 | 20 | pub const VRAM: Ptr = unsafe { Ptr::from_u32(0x06000000) }; 21 | pub const OAM: Ptr = unsafe { Ptr::from_u32(0x07000000) }; 22 | --------------------------------------------------------------------------------