Implementation of the UM-32 "Universal Machine" as described by the Cult of the Bound Variable
at main 191 lines 6.3 kB view raw
1// Copyright (C) 2025 Thom Hayward. 2// 3// This program is free software: you can redistribute it and/or modify it under 4// the terms of the GNU General Public License as published by the Free Software 5// Foundation, version 3. 6// 7// This program is distributed in the hope that it will be useful, but WITHOUT 8// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 9// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 10// details. 11// 12// You should have received a copy of the GNU General Public License along with 13// this program. If not, see <https://www.gnu.org/licenses/>. 14// 15 16use crate::reg::Register; 17 18#[derive(Clone, Copy, Debug, PartialEq, Eq)] 19pub enum Operation { 20 /// Operator #0. Conditional Move. 21 /// 22 /// The register A receives the value in register B, 23 /// unless the register C contains 0. 24 ConditionalMove { 25 a: Register, 26 b: Register, 27 c: Register, 28 }, 29 /// Operator #1: Array Index. 30 /// 31 /// The register A receives the value stored at offset 32 /// in register C in the array identified by B. 33 ArrayIndex { 34 a: Register, 35 b: Register, 36 c: Register, 37 }, 38 /// Operator #2. Array Amendment. 39 /// 40 /// The array identified by A is amended at the offset 41 /// in register B to store the value in register C. 42 ArrayAmendment { 43 a: Register, 44 b: Register, 45 c: Register, 46 }, 47 /// Operator #3. Addition. 48 /// 49 /// The register A receives the value in register B plus 50 /// the value in register C, modulo 2^32. 51 Addition { 52 a: Register, 53 b: Register, 54 c: Register, 55 }, 56 /// Operator #4. Multiplication. 57 /// 58 /// The register A receives the value in register B times 59 /// the value in register C, modulo 2^32. 60 Multiplication { 61 a: Register, 62 b: Register, 63 c: Register, 64 }, 65 /// Operator #5. Division. 66 /// 67 /// The register A receives the value in register B 68 /// divided by the value in register C, if any, where 69 /// each quantity is treated as an unsigned 32 bit number. 70 Division { 71 a: Register, 72 b: Register, 73 c: Register, 74 }, 75 /// Operator #6. Not-And. 76 /// 77 /// Each bit in the register A receives the 1 bit if 78 /// either register B or register C has a 0 bit in that 79 /// position. Otherwise the bit in register A receives 80 /// the 0 bit. 81 NotAnd { 82 a: Register, 83 b: Register, 84 c: Register, 85 }, 86 /// Operator #7. Halt. 87 /// 88 /// The universal machine stops computation. 89 Halt, 90 /// Operator #8. Allocation. 91 /// 92 /// A new array is created with a capacity of platters 93 /// commensurate to the value in the register C. This 94 /// new array is initialized entirely with platters 95 /// holding the value 0. A bit pattern not consisting of 96 /// exclusively the 0 bit, and that identifies no other 97 /// active allocated array, is placed in the B register. 98 Allocation { 99 b: Register, 100 c: Register, 101 }, 102 /// Operator #9. Abandonment. 103 /// 104 /// The array identified by the register C is abandoned. 105 /// Future allocations may then reuse that identifier. 106 Abandonment { 107 c: Register, 108 }, 109 /// Operator #10. Output. 110 /// 111 /// The value in the register C is displayed on the console 112 /// immediately. Only values between and including 0 and 255 113 /// are allowed. 114 Output { 115 c: Register, 116 }, 117 /// Operator #11. Input. 118 /// 119 /// The universal machine waits for input on the console. 120 /// When input arrives, the register C is loaded with the 121 /// input, which must be between and including 0 and 255. 122 /// If the end of input has been signaled, then the 123 /// register C is endowed with a uniform value pattern 124 /// where every place is pregnant with the 1 bit. 125 Input { 126 c: Register, 127 }, 128 /// Operator #12. Load Program. 129 /// 130 /// The array identified by the B register is duplicated 131 /// and the duplicate shall replace the '0' array, 132 /// regardless of size. The execution finger is placed 133 /// to indicate the platter of this array that is 134 /// described by the offset given in C, where the value 135 /// 0 denotes the first platter, 1 the second, et 136 /// cetera. 137 /// 138 /// The '0' array shall be the most sublime choice for 139 /// loading, and shall be handled with the utmost 140 /// velocity. 141 LoadProgram { 142 b: Register, 143 c: Register, 144 }, 145 /// Operator #13. Orthography. 146 /// 147 /// The value indicated is loaded into the register A 148 /// forthwith. 149 Orthography { 150 a: Register, 151 value: u32, 152 }, 153 IllegalInstruction, 154} 155 156impl From<u32> for Operation { 157 fn from(value: u32) -> Self { 158 let a = Register::from_u8(((value >> 6) & 0x07) as u8); 159 let b = Register::from_u8(((value >> 3) & 0x07) as u8); 160 let c = Register::from_u8((value & 0x07) as u8); 161 match value & 0xf000_0000 { 162 0x0000_0000 => Self::ConditionalMove { a, b, c }, 163 0x1000_0000 => Self::ArrayIndex { a, b, c }, 164 0x2000_0000 => Self::ArrayAmendment { a, b, c }, 165 0x3000_0000 => Self::Addition { a, b, c }, 166 0x4000_0000 => Self::Multiplication { a, b, c }, 167 0x5000_0000 => Self::Division { a, b, c }, 168 0x6000_0000 => Self::NotAnd { a, b, c }, 169 0x7000_0000 => Self::Halt, 170 0x8000_0000 => Self::Allocation { b, c }, 171 0x9000_0000 => Self::Abandonment { c }, 172 0xa000_0000 => Self::Output { c }, 173 0xb000_0000 => Self::Input { c }, 174 0xc000_0000 => Self::LoadProgram { b, c }, 175 0xd000_0000 => { 176 let a = Register::from_u8(((value >> 25) & 0x07) as u8); 177 let value = value & 0x01ff_ffff; 178 Self::Orthography { a, value } 179 } 180 _ => Self::IllegalInstruction, 181 } 182 } 183} 184 185/// Decode a Universal Machine program into a [`Vec`] of [`Operation`]s. 186#[must_use] 187pub fn decode(ops: &[u32]) -> Vec<Operation> { 188 ops.iter() 189 .map(|&encoded| Operation::from(encoded)) 190 .collect() 191}