Implementation of the UM-32 "Universal Machine" as described by the Cult of the Bound Variable
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}