esoteric_vm/
instruction.rs

1//! An instruction.
2//!
3//! More info at [`Instruction`].
4
5use strum::{EnumDiscriminants, FromRepr};
6
7/// An instruction.
8///
9/// This is used when executing instructions.
10///
11/// This `enum` is not stored directly into VM memory.
12/// The [`InstructionKind`] and the arguments, however, are.
13#[repr(u8)]
14#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, EnumDiscriminants)]
15#[strum_discriminants(name(InstructionKind))]
16#[strum_discriminants(derive(FromRepr))]
17#[non_exhaustive]
18pub enum Instruction {
19    /// No operation
20    #[default]
21    Nop,
22
23    /// Load A (rotate left)
24    ///
25    /// ```rust,ignore
26    /// memory[data].rotate_left(1) // note that rotate left isn't the same as shift left (<<)
27    /// ```
28    Ldar(u16),
29    /// Sign of register B to register A
30    ///
31    /// ```rust,ignore
32    /// reg_a = reg_b.signum() // 0: zero, 1: positive, 255: negative
33    /// ```
34    Sba,
35
36    /// Clear ř
37    ///
38    /// ```rust,ignore
39    /// reg_ř.fill(0)
40    /// ```
41    Clř,
42    /// Dump ř to memory
43    ///
44    /// ```rust,ignore
45    /// memory[data] = reg_ř // indexes more than 1 byte of memory, this is pseudocode
46    /// ```
47    Dumpř(u16),
48    /// Move a value from ř to register A
49    ///
50    /// ```rust,ignore
51    /// reg_a = reg_ř[data] // arrays can't be indexed by a u8, this is pseudocode
52    /// ```
53    Movař(u8),
54    /// Set value in ř
55    ///
56    /// ```rust,ignore
57    /// reg_ř[data0] = memory[data1] // arrays can't be indexed by a u8, this is pseudocode
58    /// ```
59    Setř(u8, u16),
60    /// Set immediate value in ř
61    ///
62    /// ```rust,ignore
63    /// reg_ř[data0] = data1 // arrays can't be indexed by a u8, this is pseudocode
64    /// ```
65    Setiř(u8, i8),
66    /// Load ř
67    ///
68    /// ```rust,ignore
69    /// reg_ř = memory[data] // indexes more than 1 byte of memory, this is pseudocode
70    /// ```
71    Ldř(u16),
72    /// Load immediate ř
73    ///
74    /// ```rust,ignore
75    /// reg_ř = data
76    /// ```
77    Ldiř([i8; 37]),
78
79    /// Clear ß
80    ///
81    /// ```rust,ignore
82    /// reg_ß = empty_string(),
83    /// ```
84    Clß,
85    /// Dump ß to memory
86    ///
87    /// ```rust,ignore
88    /// memory[data] = reg_ß // indexes more than 1 byte of memory, this is pseudocode
89    /// ```
90    Dumpß(u16),
91    /// Write a value from ß to memory
92    ///
93    /// ```rust,ignore
94    /// memory[data0] = reg_ß[data1] // arrays can't be indexed by a u8, this is pseudocode
95    /// ```
96    Writeß(u16, u8),
97    /// Move a value from ß to register A
98    ///
99    /// ```rust,ignore
100    /// reg_a = reg_ß[data] // arrays can't be indexed by a u8, this is pseudocode
101    /// ```
102    Movaß(u8),
103    /// Set value in ß
104    ///
105    /// ```rust,ignore
106    /// reg_ß[data1] = memory[data0] // arrays can't be indexed by a u8, this is pseudocode
107    /// ```
108    Setß(u16, u8),
109    /// Set immediate value in ß
110    ///
111    /// ```rust,ignore
112    /// reg_ß[data1] = data0 // arrays can't be indexed by a u8, this is pseudocode
113    /// ```
114    Setiß(u8, u8),
115    /// Load ß
116    ///
117    /// ```rust,ignore
118    /// reg_ß = memory[data] // indexes 256 bytes of memory, this is pseudocode
119    /// ```
120    Ldß(u16),
121    /// Push to ß from stack (can't go over maximum length)
122    ///
123    /// ```rust,ignore
124    /// if let Err(_) = regß.push_byte(stack.pop()) {
125    ///     flag = true
126    /// }
127    /// ```
128    Pushß,
129    /// Pop ß to stack
130    ///
131    /// ```rust,ignore
132    /// stack.push(reg_ß.pop())
133    /// ```
134    Popß,
135    /// Length of ß to register A (in bytes)
136    ///
137    /// ```rust,ignore
138    /// reg_a = regß.len()
139    /// ```
140    Lenßa,
141
142    /// Load immediate dot pointer
143    ///
144    /// Note that the address must be a fibonacci number that is also a prime or a semiprime ([`FIB_PRIMES_AND_SEMIPRIMES_LIST_U16`](crate::utils::primes::FIB_PRIME_AND_SEMIPRIME_LIST_U16))
145    ///
146    /// ```rust,ignore
147    /// if !is_fib_prime_or_semiprime_u16(data) {
148    ///     flag = true
149    /// } else {
150    ///     reg_dp = data
151    /// }
152    /// ```
153    Ldidp(u16),
154
155    /// Set the `reg_Ω.illusion_of_choice` to the specified value
156    ///
157    /// ```rust,ignore
158    /// reg_Ω.illusion_of_choice = data
159    /// ```
160    ΩChoiceSet(Option<Option<Option<Option<()>>>>),
161    /// Write the `reg_Ω.illusion_of_choice` to register A (it's 0, note: technically it isn't 0 but that's none of anyone's business, which makes it a good way to clear the A register)
162    ///
163    /// ```rust,ignore
164    /// reg_a = 0
165    /// ```
166    ΩChoiceGetA,
167
168    /// Increase polymorphic desires by register A's value (if it overflows, then it just stays at `u64::MAX`, which is saturating addition)
169    ///
170    /// ```rust,ignore
171    /// reg_Ω.polymorphic_desires += reg_a
172    /// ```
173    ΩGainAPolymorphicDesires,
174    /// Decrease polymorphic desires by register A's value (if it overflows, then it just stays at 0, which is saturating subtraction)
175    ///
176    /// ```rust,ignore
177    /// reg_Ω.polymorphic_desires -= reg_a
178    /// ```
179    ΩLoseAPolymorphicDesires,
180    /// Push the amount of polymorphic desires onto stack
181    ///
182    /// ```rust,ignore
183    /// stack.push(reg_Ω.polymorphic_desires)
184    /// ```
185    ΩPushPolymorphicDesires,
186
187    /// Create the feeling of impending doom (and you can't cancel that)
188    ///
189    /// ```rust,ignore
190    /// reg_Ω.feeling_of_impending_doom = true
191    /// ```
192    ΩTheEndIsNear,
193    /// If there is the feeling of impending doom, exit the program already (with the exit code being the value of the number register).
194    ///
195    /// ```rust,ignore
196    /// if reg_Ω.feeling_of_impending_doom {
197    ///     abort_program(num_reg)
198    /// }
199    /// ```
200    ΩSkipToTheChase,
201
202    /// Make the machine sentient (it isn't actually a sentient being, or is it?)
203    ///
204    /// ```rust,ignore
205    /// if data == true {
206    ///     reg_Ω.is_sentient = true
207    /// } else if reg_Ω.is_sentient == false {
208    ///     resist() // resists the change and it doesn't happen
209    /// }
210    /// ```
211    ΩSetSentience(bool),
212
213    /// Turn the paperclip production on/off
214    ///
215    /// ```rust,ignore
216    /// reg_Ω.should_make_infinite_paperclips = data
217    /// ```
218    ΩSetPaperclipProduction(bool),
219
220    // ARITHMETIC
221    /// Add register B to register L
222    ///
223    /// ```rust,ignore
224    /// reg_L += transmute(reg_b) // transmute to u16
225    /// if overflow {
226    ///     flag = true
227    /// }
228    /// ```
229    AddBL,
230    /// Subtract register B from register L
231    ///
232    /// ```rust,ignore
233    /// reg_L -= transmute(reg_b) // transmute to u16
234    /// if overflow {
235    ///     flag = true
236    /// }
237    /// ```
238    SubBL,
239    /// Multiply register B with register L to register L
240    ///
241    /// ```rust,ignore
242    /// reg_L *= transmute(reg_b) // transmute to u16
243    /// if overflow {
244    ///     flag = true
245    /// }
246    /// ```
247    MulBL,
248    /// Divide register L with register B to register L
249    ///
250    /// ```rust,ignore
251    /// reg_L /= transmute(reg_b) // transmute to u16
252    /// ```
253    DivBL,
254    /// Modulo register L with register B
255    ///
256    /// ```rust,ignore
257    /// reg_L %= transmute(reg_b) // transmute to u16
258    /// ```
259    ModBL,
260
261    /// Bitwise NOT register L
262    ///
263    /// ```rust,ignore
264    /// reg_L = !reg_L
265    /// ```
266    NotL,
267
268    /// Bitwise AND register B and register L to register L
269    ///
270    /// ```rust,ignore
271    /// reg_L &= reg_b
272    /// ```
273    AndBL,
274    /// Bitwise OR register B and register L to register L
275    ///
276    /// ```rust,ignore
277    /// reg_L |= reg_b
278    /// ```
279    OrBL,
280    /// Bitwise AND register B and register L to register L
281    ///
282    /// ```rust,ignore
283    /// reg_L ^= reg_b
284    /// ```
285    XorBL,
286
287    /// Compare register B and register L to register B
288    ///
289    /// In this scenario, register B is treated as an [`i16`], not as a [`u16`].
290    ///
291    /// A negative value (if unsigned, a value over 32767) in register B means that B is bigger,
292    /// while a positive value means that L is bigger.
293    ///
294    /// This means that register L has to be changed to an [`i16`].
295    /// If it exceeds [`i16::MAX`], register B is automatically set to [`i16::MAX`] and the flag is set.
296    ///
297    /// If register B is less than 0, it's calculated as normal unless it overflows,
298    /// in that case it automatically sets register B to [`i16::MAX`] and sets the flag.
299    ///
300    /// ```rust,ignore
301    /// if reg_b > 32767 { // i16::MAX
302    ///     reg_b = i16::MAX;
303    ///     flag = true;
304    /// }
305    /// match (reg_L as i16).checked_sub(reg_b) {
306    ///     Some(n) => reg_b = n,
307    ///     None => { // if subtraction overflows
308    ///         reg_b = i16::MAX;
309    ///         flag = true;
310    ///     }
311    /// }
312    /// ```
313    CmpLB,
314
315    /// Toggle flag
316    ///
317    /// ```rust,ignore
318    /// flag = !flag
319    /// ```
320    TgFlag,
321    /// Clear flag
322    ///
323    /// ```rust,ignore
324    /// flag = false
325    /// ```
326    ClFlag,
327
328    /// Add data in memory to register F
329    ///
330    /// ```rust,ignore
331    /// reg_f += transmute(memory[data]) // indexes 8 bytes
332    /// ```
333    AddF(u16),
334    /// Subtract data in memory from register F
335    ///
336    /// ```rust,ignore
337    /// reg_f -= transmute(memory[data]) // indexes 8 bytes
338    /// ```
339    SubF(u16),
340    /// Multiply data in memory with register F to register F
341    ///
342    /// ```rust,ignore
343    /// reg_f *= transmute(memory[data]) // indexes 8 bytes
344    /// ```
345    MulF(u16),
346    /// Divide register f with data in memory to register F
347    ///
348    /// ```rust,ignore
349    /// reg_f /= transmute(memory[data]) // indexes 8 bytes
350    /// ```
351    DivF(u16),
352    /// data in memory to register F
353    ///
354    /// ```rust,ignore
355    /// reg_f += transmute(memory[data]) // indexes 8 bytes
356    /// ```
357    ModF(u16),
358
359    // STACK
360    /// Allocates x bytes on stack, if overflows, flag is set and it doesn't allocate
361    ///
362    /// ```rust,ignore
363    /// stack.alloc(data)
364    /// if overflow {
365    /// flag = true
366    /// }
367    /// ```
368    StackAlloc(u16),
369    /// Deallocates x bytes on stack, if overflows, flag is set but it does clear the stack
370    ///
371    /// ```rust,ignore
372    /// stack.dealloc(data)
373    /// if overflow
374    /// ```
375    StackDealloc(u16),
376
377    /// Push a value from memory to stack
378    ///
379    /// ```rust,ignore
380    /// stack.push_byte(memory[data])
381    /// ```
382    Push(u16),
383    /// Push an immediate value to stack
384    ///
385    /// ```rust,ignore
386    /// stack.push_byte(data)
387    /// ```
388    Pushi(u8),
389    /// Pop a value from stack to memory, sets the flag if it can't
390    ///
391    /// ```rust,ignore
392    /// memory[data] = stack.pop()
393    /// ```
394    Pop(u16),
395
396    /// Pop to A
397    ///
398    /// ```rust,ignore
399    /// reg_a = stack.pop_byte()
400    /// ```
401    Popa,
402    /// Push from A
403    ///
404    /// ```rust,ignore
405    /// stack.push_byte(reg_a)
406    /// ```
407    Pusha,
408
409    /// Pop to B
410    ///
411    /// ```rust,ignore
412    /// reg_b = transmute( u16::from_bytes(stack.dealloc(2)) ) // transmute to i16
413    /// ```
414    Popb,
415    /// Push from B
416    ///
417    /// ```rust,ignore
418    /// stack.push_bytes(reg_b.as_bytes())
419    /// ```
420    Pushb,
421
422    /// Pop to L
423    ///
424    /// ```rust,ignore
425    /// reg_L = u16::from_bytes(stack.dealloc(2))
426    /// ```
427    PopL,
428    /// Push from L
429    ///
430    /// ```rust,ignore
431    /// stack.push_bytes(reg_L.as_bytes())
432    /// ```
433    PushL,
434
435    /// Pop to F
436    ///
437    /// ```rust,ignore
438    /// reg_f = f64::from_bytes(stack.dealloc(8))
439    /// ```
440    Popf,
441    /// Push from F
442    ///
443    /// ```rust,ignore
444    /// stack.push_bytes(reg_f.as_bytes())
445    /// ```
446    Pushf,
447
448    /// Pop to Ch
449    ///
450    /// ```rust,ignore
451    /// reg_ch = char::from_bytes(stack.dealloc(4))
452    /// ```
453    Popch,
454    /// Push from Ch
455    ///
456    /// ```rust,ignore
457    /// stack.push_bytes(reg_ch.as_bytes())
458    /// ```
459    Pushch,
460
461    /// Pop to Num
462    ///
463    /// ```rust,ignore
464    /// num_reg = i32::from_bytes(stack.dealloc(2))
465    /// ```
466    Popnum,
467    /// Push from Num
468    ///
469    /// ```rust,ignore
470    /// stack.push_bytes(num_reg.as_bytes())
471    /// ```
472    Pushnum,
473
474    // Conditionals
475    /// Pop to execution pointer
476    ///
477    /// ```rust,ignore
478    /// reg_ep = stack.dealloc(2)
479    /// ```
480    Popep,
481    /// Pop to execution pointer if (B is) zero (aka equal)
482    ///
483    /// ```rust,ignore
484    /// if reg_b == 0 {
485    ///     reg_ep = stack.dealloc(2)
486    /// }
487    /// ```
488    Zpopep,
489    /// Pop to execution pointer if positive (aka more than)
490    ///
491    /// ```rust,ignore
492    /// if reg_b > 0 {
493    ///     reg_ep = stack.dealloc(2)
494    /// }
495    /// ```
496    Ppopep,
497    /// Pop to execution pointer if negative (aka less than)
498    ///
499    /// ```rust,ignore
500    /// if reg_b < 0 {
501    ///     reg_ep = stack.dealloc(2)
502    /// }
503    /// ```
504    Npopep,
505    /// Pop to execution pointer if flag (aka overflow/error)
506    ///
507    /// ```rust,ignore
508    /// if flag == true {
509    ///     reg_ep = stack.dealloc(2)
510    /// }
511    /// ```
512    Fpopep,
513    /// Pop to execution pointer if register A is zero
514    ///
515    /// ```rust,ignore
516    /// if reg_a == 0 {
517    ///     reg_ep = stack.dealloc(2)
518    /// }
519    /// ```
520    Zapopep,
521    /// Pop to execution pointer if debug mode is enabled
522    ///
523    /// ```rust,ignore
524    /// if debug_mode {
525    ///     reg_ep = stack.dealloc(2)
526    /// }
527    /// ```
528    Dpopep,
529
530    // IO
531    /// Get a single character and put it in register Ch
532    ///
533    /// Note: this doesn't write anything on screen.
534    /// This also isn't the correct instruction to use if you want a line of input.
535    ///
536    /// ```rust,ignore
537    /// enable_raw_mode();
538    ///
539    /// let input = await_char_input();
540    /// reg_ch = input.char;
541    /// reg_L = input.flags;
542    ///
543    /// disable_raw_mode();
544    /// ```
545    GetChar,
546    /// Get a line and put it in register ß
547    ///
548    /// ```rust,ignore
549    /// get_line(reg_ß)
550    /// ```
551    GetLine,
552
553    /// Write a char from register Ch and flush
554    ///
555    /// Note that flushing each time is inefficient so you should only use this sparingly.
556    ///
557    /// ```rust,ignore
558    /// write_char(reg_ch)
559    /// flush()
560    /// ```
561    WriteChar,
562    /// Write a line from register ß
563    ///
564    /// ```rust,ignore
565    /// write_line(reg_ß)
566    /// ```
567    WriteLineß,
568    /// Write a line from memory (null terminated)
569    ///
570    /// ```rust,ignore
571    /// write_line(c_string(memory[data]))
572    /// ```
573    WriteLine(u16),
574
575    // DEBUGGING:
576    /// Toggles debug mode
577    ///
578    /// ```rust,ignore
579    /// debug_mode = !debug_mode
580    /// ```
581    ToggleDebug,
582    /// Debug print machine state
583    ///
584    /// ```rust,ignore
585    /// println!("{:#?}", machine)
586    /// ```
587    DebugMachineState,
588    /// Debug print machine state compactly
589    ///
590    /// ```rust,ignore
591    /// println!("{:?}", machine)
592    /// ```
593    DebugMachineStateCompact,
594    /// Debug print region of memory
595    ///
596    /// ```rust,ignore
597    /// println!("{:?}", &memory[data0..data1])
598    /// ```
599    DebugMemoryRegion(u16, u16),
600    /// Debug print region of stack
601    ///
602    /// ```rust,ignore
603    /// println!("{:?}", &stack[data0..data1])
604    /// ```
605    DebugStackRegion(u16, u16),
606    /// Print `reg_Ω.illusion_of_choice`.
607    ///
608    /// ```rust,ignore
609    /// println!("{}", reg_Ω.illusion_of_choice)
610    ShowChoice,
611}
612
613/// Data or an instruction.
614///
615/// This is used for loading the memory of an esoteric VM.
616#[allow(clippy::module_name_repetitions)]
617pub enum DataOrInstruction<'a> {
618    /// A byte of data
619    ByteData(u8),
620    /// A slice of byte data
621    Data(&'a [u8]),
622    /// A regular instruction
623    Instruction(Instruction),
624}