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//
15use crate::reg::Register;
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub enum Operation {
19 /// Operator #0. Conditional Move.
20 ///
21 /// The register A receives the value in register B,
22 /// unless the register C contains 0.
23 ConditionalMove {
24 a: Register,
25 b: Register,
26 c: Register,
27 },
28 /// Operator #1: Array Index.
29 ///
30 /// The register A receives the value stored at offset
31 /// in register C in the array identified by B.
32 ArrayIndex {
33 a: Register,
34 b: Register,
35 c: Register,
36 },
37 /// Operator #2. Array Amendment.
38 ///
39 /// The array identified by A is amended at the offset
40 /// in register B to store the value in register C.
41 ArrayAmendment {
42 a: Register,
43 b: Register,
44 c: Register,
45 },
46 /// Operator #3. Addition.
47 ///
48 /// The register A receives the value in register B plus
49 /// the value in register C, modulo 2^32.
50 Addition {
51 a: Register,
52 b: Register,
53 c: Register,
54 },
55 /// Operator #4. Multiplication.
56 ///
57 /// The register A receives the value in register B times
58 /// the value in register C, modulo 2^32.
59 Multiplication {
60 a: Register,
61 b: Register,
62 c: Register,
63 },
64 /// Operator #5. Division.
65 ///
66 /// The register A receives the value in register B
67 /// divided by the value in register C, if any, where
68 /// each quantity is treated as an unsigned 32 bit number.
69 Division {
70 a: Register,
71 b: Register,
72 c: Register,
73 },
74 /// Operator #6. Not-And.
75 ///
76 /// Each bit in the register A receives the 1 bit if
77 /// either register B or register C has a 0 bit in that
78 /// position. Otherwise the bit in register A receives
79 /// the 0 bit.
80 NotAnd {
81 a: Register,
82 b: Register,
83 c: Register,
84 },
85 /// Operator #7. Halt.
86 ///
87 /// The universal machine stops computation.
88 Halt,
89 /// Operator #8. Allocation.
90 ///
91 /// A new array is created with a capacity of platters
92 /// commensurate to the value in the register C. This
93 /// new array is initialized entirely with platters
94 /// holding the value 0. A bit pattern not consisting of
95 /// exclusively the 0 bit, and that identifies no other
96 /// active allocated array, is placed in the B register.
97 Allocation {
98 b: Register,
99 c: Register,
100 },
101 /// Operator #9. Abandonment.
102 ///
103 /// The array identified by the register C is abandoned.
104 /// Future allocations may then reuse that identifier.
105 Abandonment {
106 c: Register,
107 },
108 /// Operator #10. Output.
109 ///
110 /// The value in the register C is displayed on the console
111 /// immediately. Only values between and including 0 and 255
112 /// are allowed.
113 Output {
114 c: Register,
115 },
116 /// Operator #11. Input.
117 ///
118 /// The universal machine waits for input on the console.
119 /// When input arrives, the register C is loaded with the
120 /// input, which must be between and including 0 and 255.
121 /// If the end of input has been signaled, then the
122 /// register C is endowed with a uniform value pattern
123 /// where every place is pregnant with the 1 bit.
124 Input {
125 c: Register,
126 },
127 /// Operator #12. Load Program.
128 ///
129 /// The array identified by the B register is duplicated
130 /// and the duplicate shall replace the '0' array,
131 /// regardless of size. The execution finger is placed
132 /// to indicate the platter of this array that is
133 /// described by the offset given in C, where the value
134 /// 0 denotes the first platter, 1 the second, et
135 /// cetera.
136 ///
137 /// The '0' array shall be the most sublime choice for
138 /// loading, and shall be handled with the utmost
139 /// velocity.
140 LoadProgram {
141 b: Register,
142 c: Register,
143 },
144 /// Operator #13. Orthography.
145 ///
146 /// The value indicated is loaded into the register A
147 /// forthwith.
148 Orthography {
149 a: Register,
150 value: u32,
151 },
152 IllegalInstruction,
153}
154
155impl From<u32> for Operation {
156 fn from(value: u32) -> Self {
157 let a = Register::from_u8(((value >> 6) & 0x07) as u8);
158 let b = Register::from_u8(((value >> 3) & 0x07) as u8);
159 let c = Register::from_u8((value & 0x07) as u8);
160 match value & 0xf0000000 {
161 0x00000000 => Self::ConditionalMove { a, b, c },
162 0x10000000 => Self::ArrayIndex { a, b, c },
163 0x20000000 => Self::ArrayAmendment { a, b, c },
164 0x30000000 => Self::Addition { a, b, c },
165 0x40000000 => Self::Multiplication { a, b, c },
166 0x50000000 => Self::Division { a, b, c },
167 0x60000000 => Self::NotAnd { a, b, c },
168 0x70000000 => Self::Halt,
169 0x80000000 => Self::Allocation { b, c },
170 0x90000000 => Self::Abandonment { c },
171 0xa0000000 => Self::Output { c },
172 0xb0000000 => Self::Input { c },
173 0xc0000000 => Self::LoadProgram { b, c },
174 0xd0000000 => {
175 let a = Register::from_u8(((value >> 25) & 0x07) as u8);
176 let value = value & 0x01ffffff;
177 Self::Orthography { a, value }
178 }
179 _ => Self::IllegalInstruction,
180 }
181 }
182}
183
184pub fn decode(ops: &[u32]) -> Vec<Operation> {
185 ops.iter()
186 .map(|&encoded| Operation::from(encoded))
187 .collect()
188}