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

rename types

tjh 92e16e4c 52cc7a16

+62 -59
+62 -59
src/main.rs
··· 20 20 } 21 21 22 22 type Platter = u32; 23 - type Register = u8; 23 + type Parameter = u8; 24 24 25 25 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 26 - enum Op { 26 + enum Operation { 27 27 ConditionalMove { 28 - a: Register, 29 - b: Register, 30 - c: Register, 28 + a: Parameter, 29 + b: Parameter, 30 + c: Parameter, 31 31 }, 32 32 ArrayIndex { 33 - a: Register, 34 - b: Register, 35 - c: Register, 33 + a: Parameter, 34 + b: Parameter, 35 + c: Parameter, 36 36 }, 37 37 ArrayAmendment { 38 - a: Register, 39 - b: Register, 40 - c: Register, 38 + a: Parameter, 39 + b: Parameter, 40 + c: Parameter, 41 41 }, 42 42 Addition { 43 - a: Register, 44 - b: Register, 45 - c: Register, 43 + a: Parameter, 44 + b: Parameter, 45 + c: Parameter, 46 46 }, 47 47 Multiplication { 48 - a: Register, 49 - b: Register, 50 - c: Register, 48 + a: Parameter, 49 + b: Parameter, 50 + c: Parameter, 51 51 }, 52 52 Division { 53 - a: Register, 54 - b: Register, 55 - c: Register, 53 + a: Parameter, 54 + b: Parameter, 55 + c: Parameter, 56 56 }, 57 57 NotAnd { 58 - a: Register, 59 - b: Register, 60 - c: Register, 58 + a: Parameter, 59 + b: Parameter, 60 + c: Parameter, 61 61 }, 62 62 Halt, 63 63 Allocation { 64 - b: Register, 65 - c: Register, 64 + b: Parameter, 65 + c: Parameter, 66 66 }, 67 67 Abandonment { 68 - c: Register, 68 + c: Parameter, 69 69 }, 70 70 Output { 71 - c: Register, 71 + c: Parameter, 72 72 }, 73 73 Input { 74 - c: Register, 74 + c: Parameter, 75 75 }, 76 76 LoadProgram { 77 - b: Register, 78 - c: Register, 77 + b: Parameter, 78 + c: Parameter, 79 79 }, 80 80 Orthography { 81 - a: Register, 81 + a: Parameter, 82 82 value: u32, 83 83 }, 84 84 IllegalInstruction, 85 85 } 86 86 87 - impl From<Platter> for Op { 87 + impl From<Platter> for Operation { 88 88 fn from(value: Platter) -> Self { 89 - let a = ((value >> 6) & 0x07) as Register; 90 - let b = ((value >> 3) & 0x07) as Register; 91 - let c = ((value >> 0) & 0x07) as Register; 89 + let a = ((value >> 6) & 0x07) as Parameter; 90 + let b = ((value >> 3) & 0x07) as Parameter; 91 + let c = ((value >> 0) & 0x07) as Parameter; 92 92 93 93 match value & 0xf0000000 { 94 94 0x00000000 => Self::ConditionalMove { a, b, c }, ··· 105 105 0xb0000000 => Self::Input { c }, 106 106 0xc0000000 => Self::LoadProgram { b, c }, 107 107 0xd0000000 => { 108 - let a = ((value >> 25) & 0x07) as Register; 108 + let a = ((value >> 25) & 0x07) as Parameter; 109 109 let value = value & 0x01ffffff; 110 110 Self::Orthography { a, value } 111 111 } ··· 114 114 } 115 115 } 116 116 117 - fn decode_ops(ops: &[Platter]) -> Vec<Op> { 118 - ops.iter().map(|encoded| Op::from(*encoded)).collect() 117 + fn decode_ops(ops: &[Platter]) -> Vec<Operation> { 118 + ops.iter() 119 + .map(|&encoded| Operation::from(encoded)) 120 + .collect() 119 121 } 120 122 121 123 #[derive(Default)] ··· 128 130 memory: Vec<Vec<Platter>>, 129 131 #[cfg(feature = "reclaim-memory")] 130 132 free_blocks: Vec<Platter>, 131 - ops: Vec<Op>, 133 + ops: Vec<Operation>, 132 134 stdin: Option<Box<dyn std::io::Read>>, 133 135 stdout: Option<Box<dyn std::io::Write>>, 134 136 } 135 137 136 138 impl Um { 137 - /// Construct a new Universal Machine with the specified program. 139 + /// Initialise a Universal Machine with the specified program scroll. 138 140 pub fn new(program: Vec<Platter>) -> Self { 139 141 let ops = decode_ops(&program); 140 142 Self { ··· 144 146 } 145 147 } 146 148 147 - /// Construct a new Universal Machine from a program represented in bytes. 149 + /// Initialise a Universal Machine with a program read from a legacy 150 + /// unsigned 8-bit character scroll. 148 151 pub fn from_bytes(program: impl AsRef<[u8]>) -> Self { 149 152 let bytes = program.as_ref(); 150 153 let mut program = Vec::with_capacity(bytes.len().div_ceil(size_of::<Platter>())); ··· 165 168 Self::new(program) 166 169 } 167 170 168 - /// Sets the output for the univeral machine. 171 + /// Sets the output for the universal machine. 169 172 pub fn stdout(mut self, stdout: impl std::io::Write + 'static) -> Self { 170 173 self.stdout.replace(Box::new(stdout)); 171 174 self ··· 188 191 // 189 192 // The register A receives the value in register B, 190 193 // unless the register C contains 0. 191 - Op::ConditionalMove { a, b, c } => { 194 + Operation::ConditionalMove { a, b, c } => { 192 195 if self.load_register(c) != 0 { 193 196 self.save_register(a, self.load_register(b)); 194 197 } ··· 198 201 // 199 202 // The register A receives the value stored at offset 200 203 // in register C in the array identified by B. 201 - Op::ArrayIndex { a, b, c } => { 204 + Operation::ArrayIndex { a, b, c } => { 202 205 let block = self.load_register(b); 203 206 let offset = self.load_register(c); 204 207 self.save_register(a, self.load_memory(block, offset)); ··· 208 211 // 209 212 // The array identified by A is amended at the offset 210 213 // in register B to store the value in register C. 211 - Op::ArrayAmendment { a, b, c } => { 214 + Operation::ArrayAmendment { a, b, c } => { 212 215 let block = self.load_register(a); 213 216 let offset = self.load_register(b); 214 217 let value = self.load_register(c); ··· 219 222 // 220 223 // The register A receives the value in register B plus 221 224 // the value in register C, modulo 2^32. 222 - Op::Addition { a, b, c } => { 225 + Operation::Addition { a, b, c } => { 223 226 self.save_register( 224 227 a, 225 228 self.load_register(b).wrapping_add(self.load_register(c)), ··· 230 233 // 231 234 // The register A receives the value in register B times 232 235 // the value in register C, modulo 2^32. 233 - Op::Multiplication { a, b, c } => { 236 + Operation::Multiplication { a, b, c } => { 234 237 self.save_register( 235 238 a, 236 239 self.load_register(b).wrapping_mul(self.load_register(c)), ··· 242 245 // The register A receives the value in register B 243 246 // divided by the value in register C, if any, where 244 247 // each quantity is treated as an unsigned 32 bit number. 245 - Op::Division { a, b, c } => { 248 + Operation::Division { a, b, c } => { 246 249 self.save_register( 247 250 a, 248 251 self.load_register(b).wrapping_div(self.load_register(c)), ··· 255 258 // either register B or register C has a 0 bit in that 256 259 // position. Otherwise the bit in register A receives 257 260 // the 0 bit. 258 - Op::NotAnd { a, b, c } => { 261 + Operation::NotAnd { a, b, c } => { 259 262 self.save_register(a, !(self.load_register(b) & self.load_register(c))); 260 263 } 261 264 262 265 // Operator #7. Halt. 263 266 // 264 267 // The universal machine stops computation. 265 - Op::Halt => break, 268 + Operation::Halt => break, 266 269 267 270 // Operator #8. Allocation. 268 271 // ··· 272 275 // holding the value 0. A bit pattern not consisting of 273 276 // exclusively the 0 bit, and that identifies no other 274 277 // active allocated array, is placed in the B register. 275 - Op::Allocation { b, c } => { 278 + Operation::Allocation { b, c } => { 276 279 let length = self.load_register(c); 277 280 let index = self.allocate_memory(length); 278 281 self.save_register(b, index); ··· 282 285 // 283 286 // The array identified by the register C is abandoned. 284 287 // Future allocations may then reuse that identifier. 285 - Op::Abandonment { c } => { 288 + Operation::Abandonment { c } => { 286 289 let block = self.load_register(c); 287 290 self.free_memory(block); 288 291 } ··· 292 295 // The value in the register C is displayed on the console 293 296 // immediately. Only values between and including 0 and 255 294 297 // are allowed. 295 - Op::Output { c } => { 298 + Operation::Output { c } => { 296 299 let value = self.load_register(c); 297 300 if let Some(stdout) = self.stdout.as_mut() { 298 301 let buffer = [(value & 0xff) as u8]; ··· 308 311 // If the end of input has been signaled, then the 309 312 // register C is endowed with a uniform value pattern 310 313 // where every place is pregnant with the 1 bit. 311 - Op::Input { c } => { 314 + Operation::Input { c } => { 312 315 if let Some(stdin) = self.stdin.as_mut() { 313 316 let mut buffer = vec![0]; 314 317 match stdin.read_exact(&mut buffer) { ··· 333 336 // The '0' array shall be the most sublime choice for 334 337 // loading, and shall be handled with the utmost 335 338 // velocity. 336 - Op::LoadProgram { b, c } => { 339 + Operation::LoadProgram { b, c } => { 337 340 let block = self.load_register(b); 338 341 339 342 // Source array is always copied to array[0], but there ··· 352 355 // 353 356 // The value indicated is loaded into the register A 354 357 // forthwith. 355 - Op::Orthography { a, value } => { 358 + Operation::Orthography { a, value } => { 356 359 self.save_register(a, value); 357 360 } 358 361 359 - Op::IllegalInstruction => self.panic(), 362 + Operation::IllegalInstruction => self.panic(), 360 363 } 361 364 362 365 self.program_counter += 1; ··· 367 370 } 368 371 369 372 /// Loads the value from the specified register. 370 - fn load_register(&self, index: Register) -> Platter { 373 + fn load_register(&self, index: Parameter) -> Platter { 371 374 assert!(index < 8, "register index out of bounds"); 372 375 self.registers[index as usize] 373 376 } 374 377 375 378 /// Saves a value to the specified register. 376 - fn save_register(&mut self, index: Register, value: Platter) { 379 + fn save_register(&mut self, index: Parameter, value: Platter) { 377 380 assert!(index < 8, "register index out of bounds"); 378 381 self.registers[index as usize] = value; 379 382 }