Implementation of the UM-32 "Universal Machine" as described by the Cult of the Bound Variable

refactor `Um::run()`

tjh 7fff4bb8 28045747

+185 -179
+76
src/lib.rs
··· 3 3 4 4 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 5 5 pub enum Operation { 6 + /// Operator #0. Conditional Move. 7 + /// 8 + /// The register A receives the value in register B, 9 + /// unless the register C contains 0. 6 10 ConditionalMove { 7 11 a: Parameter, 8 12 b: Parameter, 9 13 c: Parameter, 10 14 }, 15 + /// Operator #1: Array Index. 16 + /// 17 + /// The register A receives the value stored at offset 18 + /// in register C in the array identified by B. 11 19 ArrayIndex { 12 20 a: Parameter, 13 21 b: Parameter, 14 22 c: Parameter, 15 23 }, 24 + /// Operator #2. Array Amendment. 25 + /// 26 + /// The array identified by A is amended at the offset 27 + /// in register B to store the value in register C. 16 28 ArrayAmendment { 17 29 a: Parameter, 18 30 b: Parameter, 19 31 c: Parameter, 20 32 }, 33 + /// Operator #3. Addition. 34 + /// 35 + /// The register A receives the value in register B plus 36 + /// the value in register C, modulo 2^32. 21 37 Addition { 22 38 a: Parameter, 23 39 b: Parameter, 24 40 c: Parameter, 25 41 }, 42 + /// Operator #4. Multiplication. 43 + /// 44 + /// The register A receives the value in register B times 45 + /// the value in register C, modulo 2^32. 26 46 Multiplication { 27 47 a: Parameter, 28 48 b: Parameter, 29 49 c: Parameter, 30 50 }, 51 + /// Operator #5. Division. 52 + /// 53 + /// The register A receives the value in register B 54 + /// divided by the value in register C, if any, where 55 + /// each quantity is treated as an unsigned 32 bit number. 31 56 Division { 32 57 a: Parameter, 33 58 b: Parameter, 34 59 c: Parameter, 35 60 }, 61 + /// Operator #6. Not-And. 62 + /// 63 + /// Each bit in the register A receives the 1 bit if 64 + /// either register B or register C has a 0 bit in that 65 + /// position. Otherwise the bit in register A receives 66 + /// the 0 bit. 36 67 NotAnd { 37 68 a: Parameter, 38 69 b: Parameter, 39 70 c: Parameter, 40 71 }, 72 + /// Operator #7. Halt. 73 + /// 74 + /// The universal machine stops computation. 41 75 Halt, 76 + /// Operator #8. Allocation. 77 + /// 78 + /// A new array is created with a capacity of platters 79 + /// commensurate to the value in the register C. This 80 + /// new array is initialized entirely with platters 81 + /// holding the value 0. A bit pattern not consisting of 82 + /// exclusively the 0 bit, and that identifies no other 83 + /// active allocated array, is placed in the B register. 42 84 Allocation { 43 85 b: Parameter, 44 86 c: Parameter, 45 87 }, 88 + /// Operator #9. Abandonment. 89 + /// 90 + /// The array identified by the register C is abandoned. 91 + /// Future allocations may then reuse that identifier. 46 92 Abandonment { 47 93 c: Parameter, 48 94 }, 95 + /// Operator #10. Output. 96 + /// 97 + /// The value in the register C is displayed on the console 98 + /// immediately. Only values between and including 0 and 255 99 + /// are allowed. 49 100 Output { 50 101 c: Parameter, 51 102 }, 103 + /// Operator #11. Input. 104 + /// 105 + /// The universal machine waits for input on the console. 106 + /// When input arrives, the register C is loaded with the 107 + /// input, which must be between and including 0 and 255. 108 + /// If the end of input has been signaled, then the 109 + /// register C is endowed with a uniform value pattern 110 + /// where every place is pregnant with the 1 bit. 52 111 Input { 53 112 c: Parameter, 54 113 }, 114 + /// Operator #12. Load Program. 115 + /// 116 + /// The array identified by the B register is duplicated 117 + /// and the duplicate shall replace the '0' array, 118 + /// regardless of size. The execution finger is placed 119 + /// to indicate the platter of this array that is 120 + /// described by the offset given in C, where the value 121 + /// 0 denotes the first platter, 1 the second, et 122 + /// cetera. 123 + /// 124 + /// The '0' array shall be the most sublime choice for 125 + /// loading, and shall be handled with the utmost 126 + /// velocity. 55 127 LoadProgram { 56 128 b: Parameter, 57 129 c: Parameter, 58 130 }, 131 + /// Operator #13. Orthography. 132 + /// 133 + /// The value indicated is loaded into the register A 134 + /// forthwith. 59 135 Orthography { 60 136 a: Parameter, 61 137 value: u32,
+109 -179
src/main.rs
··· 109 109 110 110 loop { 111 111 match self.ops[self.program_counter as usize] { 112 - // Operator #0. Conditional Move. 113 - // 114 - // The register A receives the value in register B, 115 - // unless the register C contains 0. 116 - Operation::ConditionalMove { a, b, c } => { 117 - if self.load_register(c) != 0 { 118 - self.save_register(a, self.load_register(b)); 119 - } 120 - } 121 - 122 - // Operator #1: Array Index. 123 - // 124 - // The register A receives the value stored at offset 125 - // in register C in the array identified by B. 126 - Operation::ArrayIndex { a, b, c } => { 127 - let block = self.load_register(b); 128 - let offset = self.load_register(c); 129 - self.save_register(a, self.load_memory(block, offset)); 130 - } 131 - 132 - // Operator #2. Array Amendment. 133 - // 134 - // The array identified by A is amended at the offset 135 - // in register B to store the value in register C. 136 - Operation::ArrayAmendment { a, b, c } => { 137 - let block = self.load_register(a); 138 - let offset = self.load_register(b); 139 - let value = self.load_register(c); 140 - self.store_memory(block, offset, value); 141 - } 142 - 143 - // Operator #3. Addition. 144 - // 145 - // The register A receives the value in register B plus 146 - // the value in register C, modulo 2^32. 147 - Operation::Addition { a, b, c } => { 148 - self.save_register( 149 - a, 150 - self.load_register(b).wrapping_add(self.load_register(c)), 151 - ); 152 - } 153 - 154 - // Operator #4. Multiplication. 155 - // 156 - // The register A receives the value in register B times 157 - // the value in register C, modulo 2^32. 158 - Operation::Multiplication { a, b, c } => { 159 - self.save_register( 160 - a, 161 - self.load_register(b).wrapping_mul(self.load_register(c)), 162 - ); 163 - } 164 - 165 - // Operator #5. Division. 166 - // 167 - // The register A receives the value in register B 168 - // divided by the value in register C, if any, where 169 - // each quantity is treated as an unsigned 32 bit number. 170 - Operation::Division { a, b, c } => { 171 - self.save_register( 172 - a, 173 - self.load_register(b).wrapping_div(self.load_register(c)), 174 - ); 175 - } 176 - 177 - // Operator #6. Not-And. 178 - // 179 - // Each bit in the register A receives the 1 bit if 180 - // either register B or register C has a 0 bit in that 181 - // position. Otherwise the bit in register A receives 182 - // the 0 bit. 183 - Operation::NotAnd { a, b, c } => { 184 - self.save_register(a, !(self.load_register(b) & self.load_register(c))); 185 - } 186 - 187 - // Operator #7. Halt. 188 - // 189 - // The universal machine stops computation. 112 + Operation::ConditionalMove { a, b, c } => self.conditional_move(a, b, c), 113 + Operation::ArrayIndex { a, b, c } => self.array_index(a, b, c), 114 + Operation::ArrayAmendment { a, b, c } => self.array_amendment(a, b, c), 115 + Operation::Addition { a, b, c } => self.addition(a, b, c), 116 + Operation::Multiplication { a, b, c } => self.multiplication(a, b, c), 117 + Operation::Division { a, b, c } => self.division(a, b, c), 118 + Operation::NotAnd { a, b, c } => self.not_and(a, b, c), 190 119 Operation::Halt => break, 191 - 192 - // Operator #8. Allocation. 193 - // 194 - // A new array is created with a capacity of platters 195 - // commensurate to the value in the register C. This 196 - // new array is initialized entirely with platters 197 - // holding the value 0. A bit pattern not consisting of 198 - // exclusively the 0 bit, and that identifies no other 199 - // active allocated array, is placed in the B register. 200 - Operation::Allocation { b, c } => { 201 - let length = self.load_register(c); 202 - let index = self.allocate_memory(length); 203 - self.save_register(b, index); 204 - } 205 - 206 - // Operator #9. Abandonment. 207 - // 208 - // The array identified by the register C is abandoned. 209 - // Future allocations may then reuse that identifier. 210 - Operation::Abandonment { c } => { 211 - let block = self.load_register(c); 212 - self.free_memory(block); 213 - } 214 - 215 - // Operator #10. Output. 216 - // 217 - // The value in the register C is displayed on the console 218 - // immediately. Only values between and including 0 and 255 219 - // are allowed. 220 - Operation::Output { c } => { 221 - let value = self.load_register(c); 222 - if let Some(stdout) = self.stdout.as_mut() { 223 - let buffer = [(value & 0xff) as u8]; 224 - stdout.write_all(&buffer).unwrap(); 225 - } 226 - } 227 - 228 - // Operator #11. Input. 229 - // 230 - // The universal machine waits for input on the console. 231 - // When input arrives, the register C is loaded with the 232 - // input, which must be between and including 0 and 255. 233 - // If the end of input has been signaled, then the 234 - // register C is endowed with a uniform value pattern 235 - // where every place is pregnant with the 1 bit. 236 - Operation::Input { c } => { 237 - if let Some(stdin) = self.stdin.as_mut() { 238 - let mut buffer = vec![0]; 239 - match stdin.read_exact(&mut buffer) { 240 - Ok(()) => self.save_register(c, buffer[0] as u32), 241 - Err(_) => self.save_register(c, 0xff), 242 - } 243 - } else { 244 - self.save_register(c, 0xff); 245 - } 246 - } 247 - 248 - // Operator #12. Load Program. 249 - // 250 - // The array identified by the B register is duplicated 251 - // and the duplicate shall replace the '0' array, 252 - // regardless of size. The execution finger is placed 253 - // to indicate the platter of this array that is 254 - // described by the offset given in C, where the value 255 - // 0 denotes the first platter, 1 the second, et 256 - // cetera. 257 - // 258 - // The '0' array shall be the most sublime choice for 259 - // loading, and shall be handled with the utmost 260 - // velocity. 120 + Operation::Allocation { b, c } => self.allocation(b, c), 121 + Operation::Abandonment { c } => self.abandonment(c), 122 + Operation::Output { c } => self.output(c), 123 + Operation::Input { c } => self.input(c), 261 124 Operation::LoadProgram { b, c } => { 262 - let block = self.load_register(b); 263 - 264 - // Source array is always copied to array[0], but there 265 - // is no point copying array[0] to array[0]. 266 - if block != 0 { 267 - let duplicated = self.duplicate_memory(block); 268 - let ops = um::decode_ops(duplicated); 269 - self.ops = ops; 270 - } 271 - 272 - self.program_counter = self.load_register(c); 125 + self.load_program(b, c); 273 126 continue; 274 127 } 275 - 276 - // Operator #13. Orthography. 277 - // 278 - // The value indicated is loaded into the register A 279 - // forthwith. 280 - Operation::Orthography { a, value } => { 281 - self.save_register(a, value); 282 - } 283 - 284 - Operation::IllegalInstruction => self.panic(), 128 + Operation::Orthography { a, value } => self.orthography(a, value), 129 + Operation::IllegalInstruction => self.illegal_instruction(), 285 130 } 286 - 287 131 self.program_counter += 1; 288 132 } 289 133 ··· 305 149 self.registers[index.into_index()] = value; 306 150 } 307 151 152 + pub fn conditional_move(&mut self, a: Parameter, b: Parameter, c: Parameter) { 153 + if self.load_register(c) != 0 { 154 + self.save_register(a, self.load_register(b)); 155 + } 156 + } 157 + 158 + pub fn array_index(&mut self, a: Parameter, b: Parameter, c: Parameter) { 159 + let block = self.load_register(b); 160 + let offset = self.load_register(c); 161 + self.save_register(a, self.load_memory(block, offset)); 162 + } 163 + 164 + pub fn array_amendment(&mut self, a: Parameter, b: Parameter, c: Parameter) { 165 + let block = self.load_register(a); 166 + let offset = self.load_register(b); 167 + let value = self.load_register(c); 168 + self.store_memory(block, offset, value); 169 + } 170 + 171 + pub fn addition(&mut self, a: Parameter, b: Parameter, c: Parameter) { 172 + self.save_register(a, self.load_register(b).wrapping_add(self.load_register(c))); 173 + } 174 + 175 + pub fn multiplication(&mut self, a: Parameter, b: Parameter, c: Parameter) { 176 + self.save_register(a, self.load_register(b).wrapping_mul(self.load_register(c))); 177 + } 178 + 179 + pub fn division(&mut self, a: Parameter, b: Parameter, c: Parameter) { 180 + self.save_register(a, self.load_register(b).wrapping_div(self.load_register(c))); 181 + } 182 + 183 + pub fn not_and(&mut self, a: Parameter, b: Parameter, c: Parameter) { 184 + self.save_register(a, !(self.load_register(b) & self.load_register(c))); 185 + } 186 + 187 + pub fn allocation(&mut self, b: Parameter, c: Parameter) { 188 + let length = self.load_register(c); 189 + let index = self.allocate_memory(length); 190 + self.save_register(b, index); 191 + } 192 + 193 + pub fn abandonment(&mut self, c: Parameter) { 194 + let block = self.load_register(c); 195 + self.free_memory(block); 196 + } 197 + 198 + pub fn output(&mut self, c: Parameter) { 199 + let value = self.load_register(c); 200 + if let Some(stdout) = self.stdout.as_mut() { 201 + let buffer = [(value & 0xff) as u8]; 202 + stdout.write_all(&buffer).unwrap(); 203 + } 204 + } 205 + 206 + pub fn input(&mut self, c: Parameter) { 207 + if let Some(stdin) = self.stdin.as_mut() { 208 + let mut buffer = vec![0]; 209 + match stdin.read_exact(&mut buffer) { 210 + Ok(()) => self.save_register(c, buffer[0] as u32), 211 + Err(_) => self.save_register(c, 0xff), 212 + } 213 + } else { 214 + self.save_register(c, 0xff); 215 + } 216 + } 217 + 218 + pub fn load_program(&mut self, b: Parameter, c: Parameter) { 219 + let block = self.load_register(b); 220 + 221 + // Source array is always copied to array[0], but there 222 + // is no point copying array[0] to array[0]. 223 + if block != 0 { 224 + let duplicated = self.duplicate_memory(block); 225 + let ops = um::decode_ops(duplicated); 226 + self.ops = ops; 227 + } 228 + 229 + self.program_counter = self.load_register(c); 230 + } 231 + 232 + pub fn orthography(&mut self, a: Parameter, value: Platter) { 233 + self.save_register(a, value); 234 + } 235 + 236 + #[cold] 237 + #[inline(never)] 238 + fn illegal_instruction(&self) -> ! { 239 + panic!( 240 + "illegal instruction: {:08x}, pc: {:08x}, r: {:08x?}", 241 + self.memory[0][self.program_counter.into_index()], 242 + self.program_counter, 243 + self.registers 244 + ) 245 + } 246 + 308 247 fn load_memory(&self, block: Platter, offset: Platter) -> Platter { 309 248 let block = block.into_index(); 310 249 let offset = offset.into_index(); ··· 350 289 self.free_blocks.push(block); 351 290 self.memory[block.into_index()] = Self::new_block(0); 352 291 } 353 - } 354 - 355 - #[cold] 356 - #[inline(never)] 357 - fn panic(&self) -> ! { 358 - panic!( 359 - "universal machine failure: instruction: {:08x}, program_counter: {:08x}, registers: {:08x?}", 360 - self.memory[0][self.program_counter.into_index()], self.program_counter, self.registers 361 - ) 362 292 } 363 293 364 294 fn new_block(len: usize) -> SmallVec<[Platter; SMALLVEC_SIZE]> {