1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
//! Minimal run-time system, which does I/O. //! //! In Bendersky's first JIT, the program uses Linux system calls, but that's //! insufficiently portable. And maybe I could figure out Darwin system calls, but //! I’d rather not write retry loops anyway. The technique here is from [the `dynlib-rs` //! tutorial]. Instead, we store trait objects in [a struct](struct.RtsState.html), pass a pointer //! to that struct to the generated program, and then have the generated program pass the pointer //! to that struct to the RTS’s read and write functions. //! //! [the `dynlib-rs` tutorial]:(https://censoredusername.github.io/dynasm-rs/language/tutorial.html#advanced-usage) use std::io::{Read, Write}; /// The object code terminated successfully. pub const OKAY: u64 = 0; /// The pointer would have pointed below the allocated buffer had the program continued. pub const UNDERFLOW: u64 = 1; /// The pointer would have pointed above the allocated buffer had the program continued. pub const OVERFLOW: u64 = 2; /// Minimal state for our minimal run-time system. /// /// Trait objects providing channels for standard input and output. pub struct RtsState<'a> { /// Input channel for the `,` operation. input: &'a mut Read, /// Output channel for the `.` operation. output: &'a mut Write, } impl<'a> RtsState<'a> { pub fn new<R: Read, W: Write>(input: &'a mut R, output: &'a mut W) -> Self { RtsState { input, output, } } pub extern "win64" fn read(&mut self) -> u8 { let mut buf = [0]; let _ = self.input.read_exact(&mut buf); buf[0] } pub extern "win64" fn write(&mut self, byte: u8) { let _ = self.output.write_all(&[byte]); } pub extern "C" fn read_c(&mut self) -> u8 { let mut buf = [0]; let _ = self.input.read_exact(&mut buf); buf[0] } pub extern "C" fn write_c(&mut self, byte: u8) { let _ = self.output.write_all(&[byte]); } }