| //// | |
| // Yes, yes, manual override *all* the things! | |
| #![no_std] | |
| #![no_main] | |
| #![feature(lang_items)] | |
| #![feature(asm)] | |
| #![feature(core_panic_info)] | |
| #![cfg(target_arch = "x86_64")] | |
| #![feature(link_args)] | |
| #![link_args = "-nostartfiles"] | |
| use core::{ptr, panic}; | |
| //// | |
| // Constants which other languages would put in a header file. | |
| // System call numbers | |
| const SYS_EXIT: u64 = 60; | |
| const SYS_WRITE: u64 = 1; | |
| const SYS_READ: u64 = 0; | |
| // Error numbers. | |
| const EAGAIN: i64 = 4; | |
| const EINTR: i64 = 11; | |
| // Standard file descriptors. | |
| const STDIN: i64 = 0; | |
| const STDOUT: i64 = 1; | |
| //// | |
| // Error handling: live dangerously and die explosivly | |
| fn trigger_segfault() -> ! { unsafe { | |
| let nowhere = 16usize as *mut u32; | |
| ptr::write_volatile(nowhere, 0xdeadbeef); | |
| loop {} | |
| } } | |
| #[panic_handler] | |
| fn panic(_: &panic::PanicInfo) -> ! { trigger_segfault(); } | |
| #[lang = "eh_personality"] | |
| fn meh_personality() -> ! { trigger_segfault(); } | |
| //// | |
| // Exit process | |
| fn exit(error_code: i32) -> ! { unsafe { | |
| let error_code = error_code as i64; | |
| asm!("syscall" | |
| : | |
| : "{rax}"(SYS_EXIT), "{rdi}"(error_code) | |
| : "rax" | |
| : "volatile" ); | |
| // Unreachable | |
| trigger_segfault(); | |
| } } | |
| //// | |
| // Write to stdout. | |
| fn say(mut x: &[u8]) { unsafe { | |
| while x.len() != 0 { | |
| let base_addr: *const u8 = x.as_ptr(); | |
| let count: usize = x.len(); | |
| let result: i64; | |
| asm!("syscall" | |
| : "={rax}"(result) | |
| : "{rax}"(SYS_WRITE), "{rdi}"(STDOUT), "{rsi}"(base_addr), "{rdx}"(count) | |
| : | |
| : "volatile" ); | |
| if result > 0 { | |
| x = &x[result as usize..] | |
| } else if result == -EAGAIN || result == -EINTR { | |
| continue | |
| } else { | |
| trigger_segfault() | |
| } | |
| } | |
| } } | |
| //// | |
| // Tastefully buffered getch. NOT THREAD SAFE. | |
| fn getch() -> Option<u8> { unsafe { | |
| const BUFFER_SIZE: usize = 128; | |
| static mut BUFFER: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; | |
| static mut FILLED: usize = 0; | |
| static mut READ: usize = 0; | |
| while READ == FILLED { | |
| let base_addr = BUFFER.as_ptr(); | |
| let result: i64; | |
| asm!("syscall" | |
| : "={rax}"(result) | |
| : "{rax}"(SYS_READ), "{rdi}"(STDIN), "{rsi}"(base_addr), "{rdx}"(BUFFER_SIZE) | |
| : "memory" | |
| : "volatile" ); | |
| if result > 0 { | |
| READ = 0; | |
| FILLED = result as usize; | |
| } else if result == -EINTR || result == -EAGAIN { | |
| continue | |
| } else if result == 0 { | |
| return None | |
| } else { | |
| trigger_segfault(); | |
| } | |
| } | |
| let ch = BUFFER[READ]; | |
| READ += 1; | |
| return Some(ch) | |
| } } | |
| //// | |
| // Entry point. | |
| #[no_mangle] | |
| fn _start() -> ! { | |
| let mut total: i64 = 0; | |
| let mut current_number: i64 = 0; | |
| while let Some(ch) = getch() { | |
| match ch { | |
| b'0' ... b'9' => { | |
| current_number *= 10; | |
| current_number += (ch - b'0') as i64; | |
| }, | |
| _ => { | |
| total += current_number; | |
| current_number = 0; | |
| } | |
| } | |
| } | |
| total += current_number; | |
| let mut output_buffer = [b' '; 30]; | |
| const MESSAGE_TOTAL: &str = "Total:"; | |
| let mut output_n = 28; | |
| output_buffer[0..MESSAGE_TOTAL.len()].copy_from_slice(MESSAGE_TOTAL.as_bytes()); | |
| output_buffer[29] = b'\n'; | |
| if total == 0 { output_buffer[28] = b'0' } | |
| while total != 0 { | |
| let d = (total % 10) as u8; | |
| output_buffer[output_n] = b'0' + d; | |
| total /= 10; | |
| output_n -= 1; | |
| } | |
| say(&output_buffer); | |
| exit(0) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment