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}