├── .gitignore ├── Makefile ├── README.md ├── image.jpg └── src ├── console.rs ├── gl ├── font.rs ├── mailbox.rs └── mod.rs ├── gpio.rs ├── interrupts.rs ├── interrupts_asm.s ├── keyboard ├── mod.rs └── ps2.rs ├── main.rs ├── memmap ├── reset.rs ├── ringbuf.rs ├── start.s ├── timer.rs └── uart.rs /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.elf 3 | *.img 4 | target 5 | Cargo.lock 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OBJCOPY=arm-none-eabi-objcopy 2 | OBJDUMP=arm-none-eabi-objdump 3 | 4 | TARGET=arm-unknown-linux-gnueabihf 5 | 6 | SOURCES := $(shell find src -name '*.rs') 7 | 8 | # Files 9 | NAME=kernel 10 | 11 | .PHONY: build clean listing $(OUT_FILE) 12 | 13 | all: clean kernel.img kernel.list 14 | 15 | kernel.img: kernel.elf 16 | $(OBJCOPY) kernel.elf -O binary kernel.img 17 | 18 | kernel.list: kernel.img 19 | $(OBJDUMP) -d kernel.elf > kernel.list 20 | 21 | kernel.elf: src/start.o src/interrupts_asm.s src/main.o 22 | arm-none-eabi-gcc -O0 -g -Wl,-gc-sections -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s -nostdlib $^ -o $@ 23 | 24 | %.o: %.rs $(SOURCES) 25 | rustc --target arm-unknown-linux-gnueabihf -g -L /Users/osnr/dev --crate-type="staticlib" $< -o $@ 26 | 27 | %.o: %.s 28 | arm-none-eabi-as $< -o $@ 29 | 30 | install: clean kernel.img 31 | rpi-install.py kernel.img 32 | 33 | install-screen: install 34 | sleep 5 35 | screen /dev/tty.SLAB_USBtoUART 115200 36 | 37 | clean: 38 | rm -f kernel.img 39 | rm -f kernel.elf 40 | rm -f src/*.o 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | I wanted to learn Rust, and I know how to work the Pi from C and have 2 | a bunch of code that does that, so I thought it would be fun to try 3 | and build an interactive computer out of the Raspberry Pi using Rust 4 | instead of C. 5 | 6 | **No guarantees that this is idiomatic Rust -- it's mostly 7 | hand-transliterated C.** I think that the PS/2 driver and the generic 8 | ring buffer are fairly nice, though. 9 | 10 | ## What it does 11 | 12 | ![Image](image.jpg) 13 | 14 | This is a bare-metal program that runs on the Raspberry Pi. It's like 15 | an operating system. (If an operating system has two jobs -- 16 | hardware abstraction and multiplexing resources -- we might say that 17 | this thing does one of the two.) 18 | 19 | You copy it to a SD card, put it in the Pi, and turn the Pi on (_note: 20 | I think you actually need to use a bootloader right now_), and plug it 21 | into an HDMI monitor and a PS/2 keyboard. 22 | 23 | It gives you a console. It talks to the Pi's video card, to the UART 24 | peripheral on the board, and to a PS/2 keyboard. You can type and see 25 | things on the screen (I bundled a [font](src/gl/font.rs)). 26 | 27 | The console has one actual command so far: `reset` (which I 28 | implemented so that I could recompile and update it fast!) 29 | 30 | All the code running on the Pi (except some standard library functions 31 | that format strings and stuff) should just be Rust code in this 32 | project. 33 | 34 | (I'd like to strip dead code too, to get rid of unused library code in 35 | the binary. Ideally, everything running on the thing would be 36 | obvious.) 37 | 38 | ## How I built it 39 | 40 | I mostly worked off of reference C code I had lying around. I wrote 41 | drivers in Rust for the GPU framebuffer, for the serial peripheral, 42 | and for the GPIO pins. 43 | 44 | I made a basic console and found a bitmap font online to display 45 | letters. 46 | 47 | I wrote a PS/2 client that uses the serial line to read scancodes from 48 | a PS/2 keyboard, so you can type. 49 | 50 | (I set up interrupts and install an interrupt handler so that the PS/2 51 | driver is nonblocking.) 52 | 53 | ## Weird stuff 54 | 55 | The hardest part was getting it to build; that took like most of a 56 | day, when I went beyond 57 | [blink](http://blog.thiago.me/raspberry-pi-bare-metal-programming-with-rust/) 58 | and started actually trying to use the Rust standard library and it 59 | started complaining. 60 | 61 | The key insight there was using `gc-sections` in the linker command 62 | and then stubbing out the remaining undefined symbols by hand. 63 | 64 | Some things in C but not Rust that I had to hack around: 65 | 66 | - I had a ton of trouble aligning global variables in memory. I did 67 | [something with SIMD and padding](src/gl/mod.rs) that seems to work 68 | reliably, but Rust doesn't have a real align attribute for variable 69 | declarations. 70 | 71 | - Hard to get data from the linker into the program. I couldn't 72 | `extern` variables in Rust, so had to push things like the interrupt 73 | table start position as arguments to an 74 | [`extern` initializer function](src/main.rs). 75 | 76 | - No `volatile` attribute, but `volatile_load` and `volatile_store` 77 | are OK (though Rust claims they're an unstable interface). 78 | 79 | The code is probably really un-idiomatic Rust. Tons of unsafe 80 | blocks -- but at least I know what to clean up later. 81 | 82 | The I/O stuff, talking to hardware, looks messier than in C, but the 83 | programming on top of the I/O layer is hopefully nicer in Rust. 84 | 85 | ## Cool stuff 86 | 87 | Having the Rust core library available is pretty useful. 88 | 89 | The PS/2 driver I wrote is a ton nicer in Rust than it is in C, 90 | because it's basically a state machine, and Rust has way more 91 | expressive types. 92 | 93 | It's cool that Rust does bounds and null checking and can panic, and I 94 | set up a way for them to get reported over serial and onscreen, 95 | too. Surprisingly civilized. 96 | 97 | ## Further steps 98 | 99 | I was working on connecting to an ESP8266 but the power draw is too 100 | much to power it directly off the Pi, I think. That would let you do 101 | web browsing and wireless connection to a real computer, without 102 | needing to also write a USB driver, Wi-Fi dongle driver, and network 103 | stack. You could update it over the air, maybe. 104 | 105 | Add a dynamic memory allocator. 106 | 107 | Onboard programming language -- some kind of Lisp variant? This would 108 | better test whether Rust is better for general programming than C. 109 | 110 | GUI? PS/2 mouse support would be a natural next step. 111 | 112 | Improve style, use the concurrency stuff in Rust and make things 113 | safer. Figure out where unsafe really belongs. Get better types on 114 | things. 115 | 116 | Some kind of persistent storage, like on the SD card? The main thing 117 | MS-DOS has that I don't right now. 118 | -------------------------------------------------------------------------------- /image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osnr/rpi-kernel/d7aac9b9554b5c81c15b2f2cd843f367d7558f77/image.jpg -------------------------------------------------------------------------------- /src/console.rs: -------------------------------------------------------------------------------- 1 | use core::str; 2 | use core::fmt; 3 | use core::fmt::Write; 4 | 5 | use uart; 6 | use keyboard; 7 | use gl; 8 | use gl::font; 9 | use reset; 10 | 11 | use gpio; 12 | 13 | pub struct Console { 14 | row: usize, 15 | col: usize, 16 | } 17 | 18 | impl Console { 19 | pub fn new() -> Console { 20 | Console { row: 0, col: 0 } 21 | } 22 | 23 | pub fn run(&mut self) { 24 | self.println("hello"); 25 | 26 | loop { 27 | self.print("> "); 28 | 29 | let mut buf: [u8; 100] = [0; 100]; 30 | 31 | self.readln(&mut buf); 32 | self.eval(unsafe { str::from_utf8_unchecked(&buf) }); 33 | } 34 | } 35 | 36 | fn eval(&self, s: &str) { 37 | if s.starts_with("reset") { 38 | reset::reset(); 39 | } 40 | } 41 | 42 | pub fn println(&mut self, s: &str) { 43 | self.print(s); 44 | self.print("\n"); 45 | } 46 | fn print(&mut self, s: &str) { 47 | for c in s.bytes() { 48 | self.putchar(c); 49 | } 50 | } 51 | fn putchar(&mut self, c: u8) { 52 | if self.row * (font::HEIGHT + 1) >= gl::HEIGHT { 53 | gl::clear(); 54 | self.row = 0; 55 | self.col = 0; 56 | } 57 | 58 | if c >= 32 && (c - 32) < 95 { 59 | gl::put_char(c, self.col * font::WIDTH, self.row * font::HEIGHT); 60 | 61 | self.col += 1; 62 | if self.col * font::WIDTH >= gl::WIDTH { 63 | self.row += 1; 64 | self.col = 0; 65 | } 66 | } else if c == '\n' as u8 { 67 | self.row += 1; 68 | self.col = 0; 69 | } 70 | } 71 | 72 | fn readln(&mut self, buf: &mut [u8; 100]) { 73 | let mut i: usize = 0; 74 | loop { 75 | let c = if uart::hasc() { 76 | uart::getc() 77 | } else if keyboard::has_char() { 78 | keyboard::read_char() 79 | } else { 80 | continue 81 | }; 82 | 83 | uart::putc(c); 84 | 85 | match c as char { 86 | '\x08' => { 87 | // Backspace. 88 | if self.col >= 2 { 89 | self.col -= 1; 90 | self.putchar(' ' as u8); 91 | self.col -= 1; 92 | 93 | i -= 1; 94 | } else { 95 | self.col = 0; 96 | i = 0; 97 | } 98 | }, 99 | '\n' => { 100 | self.putchar(c); 101 | return; 102 | }, 103 | _ => { 104 | self.putchar(c); 105 | 106 | buf[i] = c; 107 | i += 1; 108 | } 109 | } 110 | } 111 | } 112 | } 113 | 114 | 115 | impl fmt::Write for Console { 116 | fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { 117 | self.print(s); 118 | return Ok(()); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/gl/font.rs: -------------------------------------------------------------------------------- 1 | pub const WIDTH: usize = 8; 2 | pub const HEIGHT: usize = 13; 3 | 4 | // From http://stackoverflow.com/a/23130671 5 | pub const FONT: [[u8; HEIGHT]; 95] = [ 6 | [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],// space :32 7 | [0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18],// ! :33 8 | [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36], 9 | [0x00, 0x00, 0x00, 0x66, 0x66, 0xff, 0x66, 0x66, 0xff, 0x66, 0x66, 0x00, 0x00], 10 | [0x00, 0x00, 0x18, 0x7e, 0xff, 0x1b, 0x1f, 0x7e, 0xf8, 0xd8, 0xff, 0x7e, 0x18], 11 | [0x00, 0x00, 0x0e, 0x1b, 0xdb, 0x6e, 0x30, 0x18, 0x0c, 0x76, 0xdb, 0xd8, 0x70], 12 | [0x00, 0x00, 0x7f, 0xc6, 0xcf, 0xd8, 0x70, 0x70, 0xd8, 0xcc, 0xcc, 0x6c, 0x38], 13 | [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x1c, 0x0c, 0x0e], 14 | [0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c], 15 | [0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30], 16 | [0x00, 0x00, 0x00, 0x00, 0x99, 0x5a, 0x3c, 0xff, 0x3c, 0x5a, 0x99, 0x00, 0x00], 17 | [0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0xff, 0xff, 0x18, 0x18, 0x18, 0x00, 0x00], 18 | [0x00, 0x00, 0x30, 0x18, 0x1c, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], 19 | [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00], 20 | [0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], 21 | [0x00, 0x60, 0x60, 0x30, 0x30, 0x18, 0x18, 0x0c, 0x0c, 0x06, 0x06, 0x03, 0x03], 22 | [0x00, 0x00, 0x3c, 0x66, 0xc3, 0xe3, 0xf3, 0xdb, 0xcf, 0xc7, 0xc3, 0x66, 0x3c], 23 | [0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78, 0x38, 0x18], 24 | [0x00, 0x00, 0xff, 0xc0, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0xe7, 0x7e], 25 | [0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0x7e, 0x07, 0x03, 0x03, 0xe7, 0x7e], 26 | [0x00, 0x00, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0xff, 0xcc, 0x6c, 0x3c, 0x1c, 0x0c], 27 | [0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0xff], 28 | [0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc7, 0xfe, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e], 29 | [0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x03, 0x03, 0xff], 30 | [0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e], 31 | [0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x03, 0x7f, 0xe7, 0xc3, 0xc3, 0xe7, 0x7e], 32 | [0x00, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x00, 0x00], 33 | [0x00, 0x00, 0x30, 0x18, 0x1c, 0x1c, 0x00, 0x00, 0x1c, 0x1c, 0x00, 0x00, 0x00], 34 | [0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x60, 0x30, 0x18, 0x0c, 0x06], 35 | [0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00], 36 | [0x00, 0x00, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60], 37 | [0x00, 0x00, 0x18, 0x00, 0x00, 0x18, 0x18, 0x0c, 0x06, 0x03, 0xc3, 0xc3, 0x7e], 38 | [0x00, 0x00, 0x3f, 0x60, 0xcf, 0xdb, 0xd3, 0xdd, 0xc3, 0x7e, 0x00, 0x00, 0x00], 39 | [0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xff, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18], 40 | [0x00, 0x00, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe], 41 | [0x00, 0x00, 0x7e, 0xe7, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e], 42 | [0x00, 0x00, 0xfc, 0xce, 0xc7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc7, 0xce, 0xfc], 43 | [0x00, 0x00, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xc0, 0xff], 44 | [0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfc, 0xc0, 0xc0, 0xc0, 0xff], 45 | [0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xcf, 0xc0, 0xc0, 0xc0, 0xc0, 0xe7, 0x7e], 46 | [0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xff, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3], 47 | [0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7e], 48 | [0x00, 0x00, 0x7c, 0xee, 0xc6, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06], 49 | [0x00, 0x00, 0xc3, 0xc6, 0xcc, 0xd8, 0xf0, 0xe0, 0xf0, 0xd8, 0xcc, 0xc6, 0xc3], 50 | [0x00, 0x00, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0], 51 | [0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xff, 0xff, 0xe7, 0xc3], 52 | [0x00, 0x00, 0xc7, 0xc7, 0xcf, 0xcf, 0xdf, 0xdb, 0xfb, 0xf3, 0xf3, 0xe3, 0xe3], 53 | [0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xe7, 0x7e], 54 | [0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe], 55 | [0x00, 0x00, 0x3f, 0x6e, 0xdf, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c], 56 | [0x00, 0x00, 0xc3, 0xc6, 0xcc, 0xd8, 0xf0, 0xfe, 0xc7, 0xc3, 0xc3, 0xc7, 0xfe], 57 | [0x00, 0x00, 0x7e, 0xe7, 0x03, 0x03, 0x07, 0x7e, 0xe0, 0xc0, 0xc0, 0xe7, 0x7e], 58 | [0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff], 59 | [0x00, 0x00, 0x7e, 0xe7, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3], 60 | [0x00, 0x00, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3], 61 | [0x00, 0x00, 0xc3, 0xe7, 0xff, 0xff, 0xdb, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3], 62 | [0x00, 0x00, 0xc3, 0x66, 0x66, 0x3c, 0x3c, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3], 63 | [0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3], 64 | [0x00, 0x00, 0xff, 0xc0, 0xc0, 0x60, 0x30, 0x7e, 0x0c, 0x06, 0x03, 0x03, 0xff], 65 | [0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3c], 66 | [0x00, 0x03, 0x03, 0x06, 0x06, 0x0c, 0x0c, 0x18, 0x18, 0x30, 0x30, 0x60, 0x60], 67 | [0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c], 68 | [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18], 69 | [0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], 70 | [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x30, 0x70], 71 | [0x00, 0x00, 0x7f, 0xc3, 0xc3, 0x7f, 0x03, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00], 72 | [0x00, 0x00, 0xfe, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0], 73 | [0x00, 0x00, 0x7e, 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00], 74 | [0x00, 0x00, 0x7f, 0xc3, 0xc3, 0xc3, 0xc3, 0x7f, 0x03, 0x03, 0x03, 0x03, 0x03], 75 | [0x00, 0x00, 0x7f, 0xc0, 0xc0, 0xfe, 0xc3, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00], 76 | [0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x33, 0x1e], 77 | [0x7e, 0xc3, 0x03, 0x03, 0x7f, 0xc3, 0xc3, 0xc3, 0x7e, 0x00, 0x00, 0x00, 0x00], 78 | [0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0xc0, 0xc0, 0xc0, 0xc0], 79 | [0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x18, 0x00], 80 | [0x38, 0x6c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x0c, 0x00], 81 | [0x00, 0x00, 0xc6, 0xcc, 0xf8, 0xf0, 0xd8, 0xcc, 0xc6, 0xc0, 0xc0, 0xc0, 0xc0], 82 | [0x00, 0x00, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x78], 83 | [0x00, 0x00, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xfe, 0x00, 0x00, 0x00, 0x00], 84 | [0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xfc, 0x00, 0x00, 0x00, 0x00], 85 | [0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00], 86 | [0xc0, 0xc0, 0xc0, 0xfe, 0xc3, 0xc3, 0xc3, 0xc3, 0xfe, 0x00, 0x00, 0x00, 0x00], 87 | [0x03, 0x03, 0x03, 0x7f, 0xc3, 0xc3, 0xc3, 0xc3, 0x7f, 0x00, 0x00, 0x00, 0x00], 88 | [0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xe0, 0xfe, 0x00, 0x00, 0x00, 0x00], 89 | [0x00, 0x00, 0xfe, 0x03, 0x03, 0x7e, 0xc0, 0xc0, 0x7f, 0x00, 0x00, 0x00, 0x00], 90 | [0x00, 0x00, 0x1c, 0x36, 0x30, 0x30, 0x30, 0x30, 0xfc, 0x30, 0x30, 0x30, 0x00], 91 | [0x00, 0x00, 0x7e, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00], 92 | [0x00, 0x00, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00], 93 | [0x00, 0x00, 0xc3, 0xe7, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00], 94 | [0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00], 95 | [0xc0, 0x60, 0x60, 0x30, 0x18, 0x3c, 0x66, 0x66, 0xc3, 0x00, 0x00, 0x00, 0x00], 96 | [0x00, 0x00, 0xff, 0x60, 0x30, 0x18, 0x0c, 0x06, 0xff, 0x00, 0x00, 0x00, 0x00], 97 | [0x00, 0x00, 0x0f, 0x18, 0x18, 0x18, 0x38, 0xf0, 0x38, 0x18, 0x18, 0x18, 0x0f], 98 | [0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18], 99 | [0x00, 0x00, 0xf0, 0x18, 0x18, 0x18, 0x1c, 0x0f, 0x1c, 0x18, 0x18, 0x18, 0xf0], 100 | [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x8f, 0xf1, 0x60, 0x00, 0x00, 0x00], 101 | ]; 102 | -------------------------------------------------------------------------------- /src/gl/mailbox.rs: -------------------------------------------------------------------------------- 1 | use core::intrinsics::{volatile_load, volatile_store}; 2 | 3 | const MAILBOX_BASE: u32 = 0x2000B880; 4 | 5 | #[allow(dead_code)] 6 | pub enum Channel { 7 | PowerManagement = 0, 8 | Framebuffer = 1, 9 | VirtualUart = 2, 10 | Vchiq = 3, 11 | Leds = 4, 12 | Buttons = 5, 13 | Touchscreen = 6, 14 | Unused = 7, 15 | TagsArmToVc = 8, 16 | TagsVcToArm = 9, 17 | } 18 | 19 | const MAILBOX_FULL: u32 = 1 << 31; 20 | const MAILBOX_EMPTY: u32 = 1 << 30; 21 | 22 | #[allow(dead_code)] 23 | #[repr(C)] 24 | struct Mailbox { 25 | read: u32, 26 | _padding: [u32; 3], 27 | peek: u32, 28 | sender: u32, 29 | status: u32, 30 | configuration: u32, 31 | write: u32, 32 | } 33 | 34 | pub fn write(channel: Channel, addr: u32) { 35 | // addr must be a multiple of 16. 36 | if (addr & 0xFu32) != 0 { 37 | panic!(); 38 | } 39 | 40 | let mailbox = MAILBOX_BASE as *mut Mailbox; 41 | unsafe { 42 | let mailbox_status = &mut(*mailbox).status as *mut u32; 43 | // TODO Can I do this better? 44 | while volatile_load(mailbox_status) & MAILBOX_FULL != 0 { 45 | asm!(""); 46 | } 47 | 48 | let mailbox_write = &mut(*mailbox).write as *mut u32; 49 | volatile_store(mailbox_write, addr + (channel as u32)); 50 | } 51 | } 52 | 53 | pub fn read(channel: Channel) -> bool { 54 | let mailbox = MAILBOX_BASE as *mut Mailbox; 55 | unsafe { 56 | let mailbox_status = &mut(*mailbox).status as *const u32; 57 | while volatile_load(mailbox_status) & MAILBOX_EMPTY != 0 { 58 | asm!(""); 59 | } 60 | 61 | let ra = volatile_load(&(*mailbox).read as *const u32); 62 | if (ra & 0xF) == (channel as u32) { 63 | return (ra >> 4) == 0; 64 | } 65 | } 66 | 67 | return false; 68 | } 69 | -------------------------------------------------------------------------------- /src/gl/mod.rs: -------------------------------------------------------------------------------- 1 | use core::slice; 2 | 3 | use core::intrinsics::{volatile_store}; 4 | 5 | mod mailbox; 6 | pub mod font; 7 | 8 | const GPU_NOCACHE: u32 = 0x40000000; 9 | 10 | pub const WIDTH: usize = 640; 11 | pub const HEIGHT: usize = 480; 12 | 13 | // The simd is a giant alignment hack. 14 | #[allow(dead_code)] 15 | #[repr(C, simd)] 16 | struct FbConfig { 17 | width: u32, 18 | height: u32, 19 | 20 | virtual_width: u32, 21 | virtual_height: u32, 22 | 23 | pitch: u32, 24 | depth: u32, 25 | 26 | x_offset: u32, 27 | y_offset: u32, 28 | 29 | framebuffer: u32, 30 | 31 | size: u32, 32 | 33 | _align1: u32, 34 | _align2: u32, 35 | } 36 | 37 | static mut fb_config: FbConfig = FbConfig { 38 | width: WIDTH as u32, 39 | height: HEIGHT as u32, 40 | 41 | virtual_width: WIDTH as u32, 42 | virtual_height: HEIGHT as u32, 43 | 44 | depth: 32, 45 | x_offset: 0, 46 | y_offset: 0, 47 | 48 | // The GPU will fill these in. 49 | pitch: 0, 50 | framebuffer: 0, 51 | size: 0, 52 | 53 | _align1: 0, 54 | _align2: 0, 55 | }; 56 | 57 | pub fn init() { 58 | let uncached_fb_config_addr = unsafe { (&mut fb_config as *mut FbConfig as u32) + GPU_NOCACHE }; 59 | 60 | mailbox::write(mailbox::Channel::Framebuffer, uncached_fb_config_addr); 61 | mailbox::read(mailbox::Channel::Framebuffer); 62 | } 63 | 64 | fn get_fb() -> &'static mut [u32] { 65 | return unsafe { slice::from_raw_parts_mut(fb_config.framebuffer as *mut u32, (fb_config.size / 4) as usize) }; 66 | } 67 | 68 | fn get_width() -> usize { 69 | return unsafe { fb_config.width as usize }; 70 | } 71 | 72 | pub fn put_pixel(color: u32, x: usize, y: usize) { 73 | let pixel: *mut u32 = &mut get_fb()[(y * get_width()) + x] as *mut u32; 74 | unsafe { volatile_store(pixel, color); } 75 | } 76 | 77 | pub fn put_char(c: u8, x: usize, y: usize) { 78 | let glyph: [u8; 13] = font::FONT[c as usize - 32]; 79 | 80 | for row in 0..font::HEIGHT { 81 | let glyph_row = glyph[row]; 82 | 83 | for col in 0..font::WIDTH { 84 | let pixel_bw = (glyph_row >> col) & 1; 85 | 86 | let color = 87 | if pixel_bw == 0 { 88 | 0xFF000000 89 | } else { 90 | 0xFFFFFFFF 91 | }; 92 | 93 | put_pixel(color, x + font::WIDTH - 1 - col, y + font::HEIGHT - 1 - row); 94 | } 95 | } 96 | } 97 | 98 | pub fn clear() { 99 | for y in 0..HEIGHT { 100 | for x in 0..WIDTH { 101 | put_pixel(0xFF000000, x, y); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/gpio.rs: -------------------------------------------------------------------------------- 1 | use core::intrinsics::{volatile_load, volatile_store}; 2 | 3 | const GPIO_BASE: u32 = 0x20200000; 4 | 5 | // Pin output set: Set an output pin to be 1 6 | // const GPSET0: u32 = 0x2020001C; 7 | // const GPSET1: u32 = 0x20200020; 8 | // Pin output clear: Set an output pin to be 0 9 | // const GPCLR0: u32 = 0x20200028; 10 | // const GPCLR1: u32 = 0x2020002C; 11 | // Pin level: read a pin (high or low) 12 | const GPLEV0: u32 = 0x20200034; 13 | const GPLEV1: u32 = 0x20200038; 14 | // Pin event detect status (has the event occured) 15 | const GPEDS0: u32 = 0x20200040; 16 | const GPEDS1: u32 = 0x20200044; 17 | // Pin rising edge detect 18 | // const GPREN0: u32 = 0x2020004C; 19 | // const GPREN1: u32 = 0x20200050; 20 | // Pin falling edge detect 21 | const GPFEN0: u32 = 0x20200058; 22 | const GPFEN1: u32 = 0x2020005C; 23 | // Pin high detect 24 | // const GPHEN0: u32 = 0x20200064; 25 | // const GPHEN1: u32 = 0x20200068; 26 | // Pin low detect 27 | // const GPLEN0: u32 = 0x20200070; 28 | // const GPLEN1: u32 = 0x20200074; 29 | // Pin async rising edge detect 30 | // const GPAREN0: u32 = 0x2020007C; 31 | // const GPAREN1: u32 = 0x20200080; 32 | // Pin async falling edge detect 33 | // const GPAFEN0: u32 = 0x20200088; 34 | // const GPAFEN1: u32 = 0x2020008C; 35 | 36 | // Pin pull-up/pull-down enable 37 | // const GPPUD: u32 = 0x20200094; 38 | // Pin pull-up/pull-down enabe clock 39 | // const GPPUDCLK0: u32 = 0x20200098; 40 | // const GPPUDCLK1: u32 = 0x2020009C; 41 | 42 | 43 | #[allow(dead_code)] 44 | #[derive(Copy, Clone)] 45 | pub enum Pin { 46 | Tx = 14, 47 | Rx = 15, 48 | TwentyThree = 23, 49 | TwentyFour = 24, 50 | } 51 | 52 | #[allow(dead_code)] 53 | pub enum Mode { 54 | Input = 0b000, 55 | Output = 0b001, 56 | Alt5 = 0b010, 57 | } 58 | 59 | pub fn write(pin: Pin, value: bool) { 60 | let gpio = GPIO_BASE as *const u32; 61 | let pin_num = pin as u32; 62 | 63 | if value { 64 | let led_on = unsafe { gpio.offset(8) as *mut u32 }; 65 | unsafe { volatile_store(led_on, 1 << pin_num); } 66 | } else { 67 | let led_off = unsafe { gpio.offset(11) as *mut u32 }; 68 | unsafe { volatile_store(led_off, 1 << pin_num); } 69 | } 70 | } 71 | pub fn act() { // Used for debugging. 72 | write(Pin::Rx, true); 73 | } 74 | 75 | pub fn read(pin: Pin) -> bool { 76 | let pin_num = pin as u32; 77 | let reg = if pin_num <= 31 { 78 | GPLEV0 79 | } else { 80 | GPLEV1 81 | } as *mut u32; 82 | 83 | system_memory_read_barrier(); 84 | 85 | let mut val = unsafe { volatile_load(reg) }; 86 | val = val >> (pin_num % 32); 87 | 88 | return (val & 1) != 0; 89 | } 90 | 91 | pub fn set_mode(pin: Pin, mode: Mode) { 92 | let gpio = GPIO_BASE as *const u32; 93 | 94 | let pin_num = pin as isize; 95 | let mode_val = mode as u32; 96 | 97 | unsafe { 98 | let gpio_pin_control = gpio.offset(pin_num / 10) as *mut u32; 99 | volatile_store(gpio_pin_control, volatile_load(gpio_pin_control) | mode_val << ((pin_num * 3) % 30)); 100 | } 101 | } 102 | 103 | fn system_memory_read_barrier() { 104 | // DSB: data synchronization barrier 105 | unsafe { asm!("mcr p15, 0, $0, c7, c10, 4" : : "r" (0) : "memory" : "volatile"); } 106 | } 107 | fn system_memory_write_barrier() { 108 | // DSB: data synchronization barrier 109 | unsafe { asm!("mcr p15, 0, $0, c7, c10, 4" : : "r" (0) : "memory" : "volatile"); } 110 | } 111 | 112 | pub fn detect_falling_edge(pin: Pin) { 113 | let reg = if pin as u32 <= 31 { 114 | GPFEN0 115 | } else { 116 | GPFEN1 117 | } as *mut u32; 118 | let offset = (pin as u32) % 32; 119 | 120 | system_memory_read_barrier(); 121 | 122 | let mut val = unsafe { volatile_load(reg) }; 123 | val |= 1 << offset; 124 | unsafe { volatile_store(reg, val); } 125 | 126 | system_memory_write_barrier(); 127 | } 128 | 129 | 130 | fn pin_to_event_register(pin: Pin) -> u32 { 131 | let pin_num = pin as u32; 132 | if pin_num <= 31 { 133 | return GPEDS0; 134 | } else { 135 | return GPEDS1; 136 | } 137 | } 138 | pub fn check_and_clear_event(pin: Pin) -> u32 { 139 | let reg = pin_to_event_register(pin) as *mut u32; 140 | let offset = (pin as u32) % 32; 141 | 142 | system_memory_read_barrier(); 143 | 144 | let val: u32 = unsafe { volatile_load(reg) }; 145 | let mask: u32 = 1 << offset; 146 | unsafe { volatile_store(reg, mask); } // Writing a 1 clears the bit 147 | 148 | system_memory_write_barrier(); 149 | 150 | return val & mask; 151 | } 152 | -------------------------------------------------------------------------------- /src/interrupts.rs: -------------------------------------------------------------------------------- 1 | use keyboard; 2 | 3 | pub fn enable() { 4 | unsafe { 5 | asm!("mrs r0, cpsr"); 6 | asm!("bic r0, r0, #0x80"); 7 | asm!("msr cpsr_c, r0"); 8 | } 9 | } 10 | 11 | #[no_mangle] pub unsafe extern fn interrupt_vector(_pc: u32) { 12 | keyboard::interrupt(); 13 | } 14 | 15 | #[no_mangle] pub unsafe extern fn fast_interrupt_vector(_pc: u32) {} 16 | #[no_mangle] pub unsafe extern fn software_interrupt_vector(_pc: u32) {} 17 | 18 | #[no_mangle] pub unsafe extern fn reset_vector(_pc: u32) {} 19 | #[no_mangle] pub unsafe extern fn undefined_instruction_vector(_pc: u32) {} 20 | #[no_mangle] pub unsafe extern fn prefetch_abort_vector(_pc: u32) {} 21 | #[no_mangle] pub unsafe extern fn data_abort_vector(_pc: u32) {} 22 | -------------------------------------------------------------------------------- /src/interrupts_asm.s: -------------------------------------------------------------------------------- 1 | 2 | .globl _table 3 | .globl _table_end 4 | 5 | _table: 6 | ldr pc, _reset_asm 7 | ldr pc, _undefined_instruction_asm 8 | ldr pc, _software_interrupt_asm 9 | ldr pc, _prefetch_abort_asm 10 | ldr pc, _data_abort_asm 11 | ldr pc, _reset_asm 12 | ldr pc, _interrupt_asm 13 | fast_interrupt_asm: 14 | sub lr, lr, #4 @First instr of FIQ handler 15 | push {lr} 16 | push {r0-r12} 17 | mov r0, lr @ Pass old pc 18 | bl fast_interrupt_vector @ C function 19 | pop {r0-r12} 20 | ldm sp!, {pc}^ 21 | 22 | _reset_asm: .word reset_asm 23 | _undefined_instruction_asm: .word undefined_instruction_asm 24 | _software_interrupt_asm: .word software_interrupt_asm 25 | _prefetch_abort_asm: .word prefetch_abort_asm 26 | _data_abort_asm: .word data_abort_asm 27 | _interrupt_asm: .word interrupt_asm 28 | 29 | _table_end: 30 | 31 | interrupt_asm: 32 | sub lr, lr, #4 33 | push {lr} 34 | push {r0-r12} 35 | mov r0, lr @ Pass old pc 36 | bl interrupt_vector @ C function 37 | pop {r0-r12} 38 | ldm sp!, {pc}^ 39 | 40 | 41 | reset_asm: 42 | sub lr, lr, #4 43 | push {lr} 44 | push {r0-r12} 45 | mov r0, lr @ Pass old pc 46 | bl reset_vector @ C function 47 | pop {r0-r12} 48 | ldm sp!, {pc}^ 49 | 50 | undefined_instruction_asm: 51 | sub lr, lr, #4 52 | push {lr} 53 | push {r0-r12} 54 | mov r0, lr @ Pass old pc 55 | bl undefined_instruction_vector @ C function 56 | pop {r0-r12} 57 | ldm sp!, {pc}^ 58 | 59 | software_interrupt_asm: 60 | sub lr, lr, #4 61 | push {lr} 62 | push {r0-r12} 63 | mov r0, lr @ Pass old pc 64 | bl software_interrupt_vector @ C function 65 | pop {r0-r12} 66 | ldm sp!, {pc}^ 67 | 68 | prefetch_abort_asm: 69 | sub lr, lr, #4 70 | push {lr} 71 | push {r0-r12} 72 | mov r0, lr @ Pass old pc 73 | bl prefetch_abort_vector @ C function 74 | pop {r0-r12} 75 | ldm sp!, {pc}^ 76 | 77 | data_abort_asm: 78 | sub lr, lr, #4 79 | push {lr} 80 | push {r0-r12} 81 | mov r0, lr @ Pass old pc 82 | bl data_abort_vector @ C function 83 | pop {r0-r12} 84 | ldm sp!, {pc}^ 85 | -------------------------------------------------------------------------------- /src/keyboard/mod.rs: -------------------------------------------------------------------------------- 1 | mod ps2; 2 | 3 | pub fn init() { 4 | ps2::init(); 5 | } 6 | 7 | pub unsafe fn interrupt() { ps2::interrupt(); } 8 | 9 | pub fn wait_for_next_scan() -> u8 { 10 | unsafe { 11 | while ps2::scanbuf.empty() { asm!(""); } 12 | } 13 | 14 | return unsafe { ps2::scanbuf.pop() }; 15 | } 16 | 17 | const SCAN_RELEASE: u8 = 0xF0; 18 | const SCAN_SPECIAL: u8 = 0xE0; 19 | 20 | // const SCAN_SPECIAL_LEFT: u8 = 0x6B; 21 | // const SCAN_SPECIAL_RIGHT: u8 = 0x74; 22 | // const SCAN_SPECIAL_UP: u8 = 0x75; 23 | // const SCAN_SPECIAL_DOWN: u8 = 0x72; 24 | 25 | const SCAN_LSHIFT: u8 = 0x12; 26 | const SCAN_RSHIFT: u8 = 0x59; 27 | const SCAN_CAPSLOCK: u8 = 0x58; 28 | 29 | static mut shift: bool = false; 30 | static mut capslock: bool = false; 31 | static mut last_char: Option = None; 32 | 33 | pub fn has_char() -> bool { 34 | unsafe { 35 | if ps2::scanbuf.empty() { return false; } 36 | if last_char.is_some() { return true; } 37 | } 38 | 39 | let scan = unsafe { ps2::scanbuf.pop() }; 40 | 41 | match scan { 42 | SCAN_RELEASE => { 43 | let scan = wait_for_next_scan(); 44 | 45 | if scan == SCAN_LSHIFT || scan == SCAN_RSHIFT { 46 | unsafe { shift = false; } 47 | } else if scan == SCAN_SPECIAL { 48 | wait_for_next_scan(); // Discard next one too. 49 | } 50 | 51 | return has_char(); 52 | }, 53 | 54 | SCAN_SPECIAL => { 55 | wait_for_next_scan(); 56 | 57 | // TODO: Handle arrow keys here. 58 | 59 | return has_char(); 60 | }, 61 | 62 | SCAN_LSHIFT | SCAN_RSHIFT => { 63 | unsafe { shift = true; } 64 | return has_char(); 65 | }, 66 | 67 | SCAN_CAPSLOCK => { 68 | unsafe { capslock = !capslock; } 69 | return has_char(); 70 | }, 71 | 72 | _ => { 73 | unsafe { 74 | if shift != capslock { 75 | last_char = Some(ps2::SHIFT_SCAN_TABLE[scan as usize]); 76 | } else { 77 | last_char = Some(ps2::SCAN_TABLE[scan as usize]); 78 | } 79 | } 80 | 81 | return true; 82 | } 83 | } 84 | } 85 | 86 | pub fn read_char() -> u8 { 87 | let c = unsafe { last_char.unwrap() }; 88 | unsafe { last_char = None; } 89 | 90 | return c as u8; 91 | } 92 | -------------------------------------------------------------------------------- /src/keyboard/ps2.rs: -------------------------------------------------------------------------------- 1 | use gpio; 2 | use core::intrinsics::volatile_store; 3 | 4 | use ringbuf; 5 | use ringbuf::Buf; 6 | 7 | const CLOCK: gpio::Pin = gpio::Pin::TwentyThree; 8 | const DATA: gpio::Pin = gpio::Pin::TwentyFour; 9 | 10 | const INTERRUPT_ENABLE_2: u32 = 0x2000b214; 11 | const INTERRUPT_DISABLE_1: u32 = 0x2000b21c; 12 | const INTERRUPT_DISABLE_2: u32 = 0x2000b220; 13 | 14 | pub fn init() { 15 | unsafe { 16 | volatile_store(INTERRUPT_DISABLE_1 as *mut u32, 0xFFFFFFFF); 17 | volatile_store(INTERRUPT_DISABLE_2 as *mut u32, 0xFFFFFFFF); 18 | } 19 | 20 | gpio::set_mode(CLOCK, gpio::Mode::Input); 21 | gpio::set_mode(DATA, gpio::Mode::Input); 22 | 23 | gpio::detect_falling_edge(CLOCK); 24 | 25 | unsafe { 26 | volatile_store(INTERRUPT_ENABLE_2 as *mut u32, 1 << (52 - 32)); 27 | } 28 | } 29 | 30 | pub static mut scanbuf: Buf = Buf { 31 | elems: [None; ringbuf::SIZE], 32 | head: 0, 33 | tail: 0, 34 | }; 35 | 36 | enum Ps2 { 37 | AwaitingStart, 38 | Started, 39 | GotData { code: u8, pos: i32, parity: u8 }, 40 | GotParity { code: u8 }, 41 | } 42 | 43 | static mut scan: Ps2 = Ps2::AwaitingStart; 44 | 45 | pub unsafe fn interrupt() { 46 | match scan { 47 | Ps2::AwaitingStart => { 48 | if !gpio::read(DATA) { 49 | // Start bit was low. Good. 50 | scan = Ps2::Started; 51 | } else { 52 | // Failure. Start bit was high. 53 | scan = Ps2::AwaitingStart; 54 | } 55 | }, 56 | Ps2::Started => { 57 | let bit = gpio::read(DATA) as u8; 58 | scan = Ps2::GotData { 59 | code: bit, 60 | pos: 1, 61 | parity: bit, 62 | }; 63 | }, 64 | Ps2::GotData { code, pos, parity } => { 65 | let bit = gpio::read(DATA) as u8; 66 | if pos < 8 { 67 | scan = Ps2::GotData { 68 | code: code | (bit << pos), 69 | pos: pos + 1, 70 | parity: parity + bit, 71 | }; 72 | 73 | } else { 74 | // Handle parity bit. 75 | if (parity + bit) % 2 != 1 { 76 | // Failure. 77 | scan = Ps2::AwaitingStart; 78 | } else { 79 | scan = Ps2::GotParity { code: code }; 80 | } 81 | } 82 | }, 83 | Ps2::GotParity { code } => { 84 | if gpio::read(DATA) { 85 | // Stop bit was high. Good. 86 | scanbuf.push(code); 87 | scan = Ps2::AwaitingStart; 88 | } else { 89 | scan = Ps2::AwaitingStart; 90 | } 91 | }, 92 | } 93 | 94 | gpio::check_and_clear_event(CLOCK); 95 | } 96 | 97 | pub const SHIFT_SCAN_TABLE: [char; 128] = [ 98 | ' ', // 0x00 99 | ' ', 100 | ' ', 101 | ' ', 102 | ' ', 103 | ' ', 104 | ' ', 105 | ' ', 106 | ' ', // 0x08 107 | ' ', 108 | ' ', 109 | ' ', 110 | ' ', 111 | '\t', 112 | '~', 113 | ' ', 114 | ' ', // 0x10 115 | ' ', 116 | ' ', 117 | ' ', 118 | ' ', 119 | 'Q', 120 | '!', 121 | ' ', 122 | ' ', // 0x18 123 | ' ', 124 | 'Z', 125 | 'S', 126 | 'A', 127 | 'W', 128 | '@', 129 | ' ', 130 | ' ', // 0x20 131 | 'C', 132 | 'X', 133 | 'D', 134 | 'E', 135 | '$', 136 | '#', 137 | ' ', 138 | ' ', // 0x28 139 | ' ', 140 | 'V', 141 | 'F', 142 | 'T', 143 | 'R', 144 | '%', 145 | ' ', 146 | ' ', // 0x30 147 | 'N', 148 | 'B', 149 | 'H', 150 | 'G', 151 | 'Y', 152 | '^', 153 | ' ', 154 | ' ', // 0x38 155 | ' ', 156 | 'M', 157 | 'J', 158 | 'U', 159 | '&', 160 | '*', 161 | ' ', 162 | ' ', // 0x40 163 | '<', 164 | 'L', 165 | 'I', 166 | 'O', 167 | ')', 168 | '(', 169 | ' ', 170 | ' ', // 0x48 171 | '>', 172 | '?', 173 | 'L', 174 | ':', 175 | 'P', 176 | '_', 177 | ' ', 178 | ' ', // 0x50 179 | ' ', 180 | '\"', 181 | ' ', 182 | '{', 183 | '+', 184 | ' ', 185 | ' ', 186 | ' ', // 0x58 187 | ' ', 188 | '\n', 189 | '}', 190 | ' ', 191 | '|', 192 | ' ', 193 | ' ', 194 | ' ', // 0x60 195 | ' ', 196 | ' ', 197 | ' ', 198 | ' ', 199 | ' ', 200 | '\x08', 201 | ' ', 202 | ' ', // 0x68 203 | '1', 204 | ' ', 205 | '4', 206 | '7', 207 | ' ', 208 | ' ', 209 | ' ', 210 | '0', // 0x70 211 | '.', 212 | '2', 213 | '5', 214 | '6', 215 | '8', 216 | ' ', 217 | ' ', 218 | ' ', // 0x78 219 | '+', 220 | '3', 221 | '-', 222 | '*', 223 | '9', 224 | ' ', 225 | ' ' 226 | ]; 227 | 228 | pub const SCAN_TABLE: [char; 128] = [ 229 | ' ', // 0x00 230 | ' ', 231 | ' ', 232 | ' ', 233 | ' ', 234 | ' ', 235 | ' ', 236 | ' ', 237 | ' ', // 0x08 238 | ' ', 239 | ' ', 240 | ' ', 241 | ' ', 242 | '\t', 243 | '`', 244 | ' ', 245 | ' ', // 0x10 246 | ' ', 247 | ' ', 248 | ' ', 249 | ' ', 250 | 'q', 251 | '1', 252 | ' ', 253 | ' ', // 0x18 254 | ' ', 255 | 'z', 256 | 's', 257 | 'a', 258 | 'w', 259 | '2', 260 | ' ', 261 | ' ', // 0x20 262 | 'c', 263 | 'x', 264 | 'd', 265 | 'e', 266 | '4', 267 | '3', 268 | ' ', 269 | ' ', // 0x28 270 | ' ', 271 | 'v', 272 | 'f', 273 | 't', 274 | 'r', 275 | '5', 276 | ' ', 277 | ' ', // 0x30 278 | 'n', 279 | 'b', 280 | 'h', 281 | 'g', 282 | 'y', 283 | '6', 284 | ' ', 285 | ' ', // 0x38 286 | ' ', 287 | 'm', 288 | 'j', 289 | 'u', 290 | '7', 291 | '8', 292 | ' ', 293 | ' ', // 0x40 294 | ',', 295 | 'k', 296 | 'i', 297 | 'o', 298 | '0', 299 | '9', 300 | ' ', 301 | ' ', // 0x48 302 | '.', 303 | '/', 304 | 'l', 305 | ';', 306 | 'p', 307 | '-', 308 | ' ', 309 | ' ', // 0x50 310 | ' ', 311 | '\'', 312 | ' ', 313 | '[', 314 | '=', 315 | ' ', 316 | ' ', 317 | ' ', // 0x58 318 | ' ', 319 | '\n', 320 | ']', 321 | ' ', 322 | '\\', 323 | ' ', 324 | ' ', 325 | ' ', // 0x60 326 | ' ', 327 | ' ', 328 | ' ', 329 | ' ', 330 | ' ', 331 | '\x08', 332 | ' ', 333 | ' ', // 0x68 334 | '1', 335 | ' ', 336 | '4', 337 | '7', 338 | ' ', 339 | ' ', 340 | ' ', 341 | '0', // 0x70 342 | '.', 343 | '2', 344 | '5', 345 | '6', 346 | '8', 347 | ' ', 348 | ' ', 349 | ' ', // 0x78 350 | '+', 351 | '3', 352 | '-', 353 | '*', 354 | '9', 355 | ' ', 356 | ' ' 357 | ]; 358 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(lang_items, asm, repr_simd, core_intrinsics)] 2 | #![no_std] 3 | 4 | pub mod interrupts; 5 | 6 | mod gpio; 7 | mod timer; 8 | #[macro_use] mod uart; 9 | mod reset; 10 | mod ringbuf; 11 | mod keyboard; 12 | 13 | mod console; 14 | use self::console::Console; 15 | 16 | use core::fmt::Arguments; 17 | 18 | mod gl; 19 | 20 | static mut global_console: Option = None; 21 | 22 | #[no_mangle] 23 | pub extern fn main() { 24 | timer::sleep(500000); 25 | 26 | uart::init(); 27 | keyboard::init(); 28 | gl::init(); 29 | 30 | interrupts::enable(); 31 | 32 | unsafe { 33 | global_console = Some(Console::new()); 34 | global_console.as_mut().unwrap().run(); 35 | } 36 | } 37 | 38 | const RPI_VECTOR_START: u32 = 0x0; 39 | 40 | #[no_mangle] 41 | pub extern fn prologue(table_start: isize, table_end: isize) { 42 | let vector: *mut u32 = RPI_VECTOR_START as *mut u32; 43 | 44 | let mut table = table_start; 45 | while table < table_end { 46 | let there = unsafe { vector.offset((table - table_start) / 4) }; 47 | let here = table as *mut u32; 48 | 49 | unsafe { *there = *here; } 50 | 51 | table += 4; 52 | } 53 | } 54 | 55 | #[lang = "eh_personality"] extern fn eh_personality() {} 56 | #[lang = "panic_fmt"] extern fn panic_fmt(fmt: Arguments, file: &str, line: u32) { 57 | gpio::act(); 58 | 59 | let console = unsafe { global_console.as_mut().unwrap() }; 60 | use core::fmt::Write; 61 | 62 | console.write_fmt(format_args!("\n\nPANIC in {} at line {}:", file, line)); 63 | console.write_fmt(format_args!("\n {}", fmt)); 64 | 65 | loop { 66 | println!("\n\nPANIC in {} at line {}:", file, line); 67 | println!(" {}", fmt); 68 | 69 | timer::sleep(5000000); 70 | reset::reset(); 71 | } 72 | } 73 | #[no_mangle] pub extern fn __aeabi_unwind_cpp_pr0() {} 74 | #[no_mangle] pub extern fn __aeabi_unwind_cpp_pr1() {} 75 | 76 | #[no_mangle] 77 | pub unsafe extern fn memcpy(dest: *mut u8, src: *const u8, 78 | n: usize) -> *mut u8 { 79 | let mut i = 0; 80 | while i < n { 81 | *dest.offset(i as isize) = *src.offset(i as isize); 82 | i += 1; 83 | } 84 | return dest; 85 | } 86 | #[no_mangle] 87 | pub unsafe extern fn memset(s: *mut u8, c: i32, n: usize) -> *mut u8 { 88 | let mut i = 0; 89 | while i < n { 90 | *s.offset(i as isize) = c as u8; 91 | i += 1; 92 | } 93 | return s; 94 | } 95 | #[no_mangle] 96 | pub unsafe extern fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 { 97 | let mut i = 0; 98 | while i < n { 99 | let a = *s1.offset(i as isize); 100 | let b = *s2.offset(i as isize); 101 | if a != b { 102 | return a as i32 - b as i32 103 | } 104 | i += 1; 105 | } 106 | return 0; 107 | } 108 | -------------------------------------------------------------------------------- /src/memmap: -------------------------------------------------------------------------------- 1 | MEMORY 2 | { 3 | ram : ORIGIN = 0x8000, 4 | LENGTH = 0x10000 5 | } 6 | 7 | SECTIONS 8 | { 9 | .text : { 10 | start.o (.text) 11 | *(.text*) 12 | . = ALIGN(4); 13 | } > ram 14 | .data : { 15 | *(.data*) 16 | . = ALIGN(16); 17 | fb_config = . 18 | . = ALIGN(320) 19 | } > ram 20 | .rodata : { *(.rodata*) } > ram 21 | .bss : { 22 | __bss_start__ = .; 23 | *(.bss) *(COMMON) 24 | . = ALIGN(4); 25 | __bss_end__ = .; 26 | __stab_start__ = .; 27 | } > ram 28 | } 29 | -------------------------------------------------------------------------------- /src/reset.rs: -------------------------------------------------------------------------------- 1 | use core::intrinsics::volatile_store; 2 | 3 | use timer; 4 | 5 | const PM_RSTC: u32 = 0x2010001c; 6 | const PM_WDOG: u32 = 0x20100024; 7 | const PM_PASSWORD: u32 = 0x5a000000; 8 | const PM_RSTC_WRCFG_FULL_RESET: u32 = 0x00000020; 9 | 10 | pub fn reset() { 11 | timer::sleep(100000); 12 | 13 | // timeout = 1/16th of a second? (whatever) 14 | unsafe { 15 | volatile_store(PM_WDOG as *mut u32, PM_PASSWORD | 1); 16 | volatile_store(PM_RSTC as *mut u32, PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET); 17 | } 18 | 19 | loop { unsafe { asm!(""); } } 20 | } 21 | -------------------------------------------------------------------------------- /src/ringbuf.rs: -------------------------------------------------------------------------------- 1 | pub const SIZE: usize = 128; 2 | 3 | // TODO: Make a real initializer. 4 | pub struct Buf { 5 | pub elems: [Option; SIZE], 6 | 7 | pub head: usize, 8 | pub tail: usize, 9 | } 10 | 11 | impl Buf { 12 | pub fn empty(&self) -> bool { 13 | return self.tail == self.head; 14 | } 15 | 16 | #[allow(dead_code)] 17 | pub fn full(&self) -> bool { 18 | let next_tail = (self.tail + 1) % SIZE; 19 | return next_tail == self.head; 20 | } 21 | 22 | pub fn pop(&mut self) -> T { 23 | let elem = self.elems[self.head].unwrap(); 24 | self.elems[self.head] = None; 25 | 26 | self.head = (self.head + 1) % SIZE; 27 | 28 | return elem; 29 | } 30 | 31 | pub fn push(&mut self, elem: T) { 32 | let next_tail = (self.tail + 1) % SIZE; 33 | if next_tail == self.head { 34 | panic!(); 35 | } 36 | 37 | self.elems[self.tail] = Some(elem); 38 | self.tail = next_tail; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/start.s: -------------------------------------------------------------------------------- 1 | .section .text 2 | .globl _start 3 | _start: 4 | mov r0, #0xD2 @ IRQ mode 5 | msr cpsr_c, r0 @ Put in IRQ mode, don't clear C bits 6 | mov sp, #0x8000 @ Set IRQ stack pointer 7 | mov r0, #0xD3 @ SVC mode 8 | msr cpsr_c, r0 @ Put in SVC mode, don't clear C bits 9 | mov sp, #0x7000 @ Set SVC stack pointer 10 | 11 | ldr r0, =_table 12 | ldr r1, =_table_end 13 | bl prologue 14 | 15 | bl main @ Jump to C start routine 16 | 17 | hang: 18 | b hang 19 | -------------------------------------------------------------------------------- /src/timer.rs: -------------------------------------------------------------------------------- 1 | pub fn sleep(value: u32) { 2 | for _ in 1..value { 3 | unsafe { asm!(""); } 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /src/uart.rs: -------------------------------------------------------------------------------- 1 | use gpio; 2 | use core::intrinsics::volatile_load; 3 | 4 | use core::fmt; 5 | 6 | #[allow(dead_code)] 7 | #[repr(C)] 8 | pub struct Uart { 9 | data: u32, // I/O Data 10 | ier: u32, // Interupt enable 11 | iir: u32, // Interupt identify and fifo enables/clears 12 | lcr: u32, // line control register 13 | mcr: u32, // modem control register 14 | lsr: u32, // line status 15 | msr: u32, // line status 16 | scratch: u32, 17 | cntl: u32, // control register 18 | stat: u32, // status register 19 | baud: u32, // baud rate register 20 | } 21 | 22 | // AUX bits 23 | const AUX_ENABLES: u32 = 0x20215004; 24 | const AUX_MU_ENABLE: u32 = 0x00000001; 25 | #[allow(dead_code)] 26 | const AUX_SPI0_ENABLE: u32 = 0x00000002; 27 | #[allow(dead_code)] 28 | const AUX_SPI1_ENABLE: u32 = 0x00000004; 29 | 30 | // Mini UART bits 31 | const AUX_MU_UART: u32 = 0x20215040; 32 | 33 | const AUX_MU_IIR_RX_FIFO_CLEAR: u32 = 0x00000002; 34 | const AUX_MU_IIR_TX_FIFO_CLEAR: u32 = 0x00000004; 35 | const AUX_MU_IIR_RX_FIFO_ENABLE: u32 = 0x00000008; 36 | const AUX_MU_IIR_TX_FIFO_ENABLE: u32 = 0x00000004; 37 | 38 | const AUX_MU_LCR_8BIT: u32 = 0x00000003; 39 | 40 | const AUX_MU_LSR_RX_READY: u32 = 0x00000001; 41 | #[allow(dead_code)] 42 | const AUX_MU_LSR_TX_READY: u32 = 0x00000010; 43 | const AUX_MU_LSR_TX_EMPTY: u32 = 0x00000020; 44 | 45 | const AUX_MU_CNTL_TX_ENABLE: u32 = 0x00000002; 46 | const AUX_MU_CNTL_RX_ENABLE: u32 = 0x00000001; 47 | 48 | pub fn get_uart() -> &'static mut Uart { 49 | let uart = AUX_MU_UART as *mut Uart; 50 | let uart_ref = unsafe { &mut *uart }; 51 | 52 | return uart_ref; 53 | } 54 | 55 | pub fn init() { 56 | let uart = get_uart(); 57 | let aux = AUX_ENABLES as *mut u32; 58 | unsafe { *aux = AUX_MU_ENABLE; } 59 | 60 | uart.ier = 0; 61 | uart.cntl = 0; 62 | uart.lcr = AUX_MU_LCR_8BIT; 63 | uart.mcr = 0; 64 | uart.ier = 0; 65 | uart.iir = AUX_MU_IIR_RX_FIFO_CLEAR| 66 | AUX_MU_IIR_RX_FIFO_ENABLE| 67 | AUX_MU_IIR_TX_FIFO_CLEAR| 68 | AUX_MU_IIR_TX_FIFO_ENABLE; 69 | uart.baud = 270; 70 | 71 | gpio::set_mode(gpio::Pin::Tx, gpio::Mode::Alt5); 72 | gpio::set_mode(gpio::Pin::Rx, gpio::Mode::Alt5); 73 | 74 | uart.cntl = AUX_MU_CNTL_TX_ENABLE|AUX_MU_CNTL_RX_ENABLE; 75 | } 76 | 77 | pub fn hasc() -> bool { 78 | let lsr = &get_uart().lsr as *const u32; 79 | return unsafe { volatile_load(lsr) } & AUX_MU_LSR_RX_READY != 0; 80 | } 81 | 82 | pub fn getc() -> u8 { 83 | let uart = get_uart(); 84 | while uart.lsr & AUX_MU_LSR_RX_READY == 0 { 85 | unsafe { asm!(""); } 86 | } 87 | 88 | return (uart.data & 0xFFu32) as u8; 89 | } 90 | 91 | pub fn putc(c: u8) { 92 | let uart = get_uart(); 93 | if c == '\n' as u8 { 94 | putc('\r' as u8); 95 | } 96 | 97 | while uart.lsr & AUX_MU_LSR_TX_EMPTY == 0 { 98 | unsafe { asm!(""); } 99 | } 100 | 101 | uart.data = c as u32; 102 | } 103 | 104 | impl fmt::Write for Uart { 105 | fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> { 106 | for c in s.bytes() { 107 | putc(c); 108 | } 109 | return Ok(()); 110 | } 111 | } 112 | 113 | #[macro_export] 114 | macro_rules! print { 115 | ($($arg:tt)*) => (uart::get_uart().write_fmt(format_args!($($arg)*)).ok()); 116 | } 117 | 118 | #[macro_export] 119 | macro_rules! println { 120 | ($fmt:expr) => (print!(concat!($fmt, "\n"))); 121 | ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); 122 | } 123 | --------------------------------------------------------------------------------