Skip to main content

endbasic_core/
bytecode.rs

1// EndBASIC
2// Copyright 2026 Julio Merino
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU Affero General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU Affero General Public License for more details.
13//
14// You should have received a copy of the GNU Affero General Public License
15// along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17//! Bytecode for a compiled EndBASIC program.
18
19use crate::ast::{ArgSep, ExprType};
20use crate::num::{
21    unchecked_u32_as_u8, unchecked_u32_as_u16, unchecked_u32_as_usize, unchecked_u64_as_u8,
22};
23use std::convert::TryFrom;
24use std::fmt;
25
26/// Representation of the various register scopes.
27#[derive(Debug)]
28pub enum RegisterScope {
29    /// Global scope for variables visible from any scope.
30    Global,
31
32    /// Local scope for variables visible only within a function or subroutine.
33    Local,
34
35    /// Temporary scope for intermediate values during expression evaluation.
36    Temp,
37}
38
39impl fmt::Display for RegisterScope {
40    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41        match self {
42            Self::Global => write!(f, "global"),
43            Self::Local => write!(f, "local"),
44            Self::Temp => write!(f, "temp"),
45        }
46    }
47}
48
49/// Error to indicate an invalid `END` exit code.
50#[derive(Debug, thiserror::Error)]
51#[error("Exit code must be in the 0..127 range")]
52pub struct InvalidExitCodeError(());
53
54/// Error to indicate that we have run out of registers.
55#[derive(Debug, thiserror::Error)]
56#[error("Out of registers")]
57pub(crate) struct OutOfRegistersError(());
58
59/// Error to indicate that an array has too many dimensions.
60#[derive(Debug, thiserror::Error)]
61#[error("Too many dimensions")]
62pub(crate) struct TooManyArrayDimensionsError(());
63
64/// Error types for bytecode parsing.
65#[derive(Debug, thiserror::Error)]
66pub(crate) enum ParseError {
67    /// The type tag in the bytecode is not recognized.
68    #[error("{0}: Invalid type tag {0}")]
69    InvalidTypeTag(u64),
70}
71
72/// Result type for bytecode parsing operations.
73pub(crate) type ParseResult<T> = Result<T, ParseError>;
74
75/// Program exit code carried by the `END` instruction.
76#[derive(Clone, Copy, Debug, Eq, PartialEq)]
77pub struct ExitCode(u8);
78
79impl ExitCode {
80    /// Returns true if this code represents successful execution.
81    pub fn is_success(self) -> bool {
82        self.0 == 0
83    }
84
85    /// Creates an `ExitCode` from an integer.
86    pub fn try_new(value: i32) -> Result<Self, InvalidExitCodeError> {
87        if (0..128).contains(&value) {
88            Ok(Self(value as u8))
89        } else {
90            Err(InvalidExitCodeError(()))
91        }
92    }
93
94    /// Converts this exit code to an integer.
95    pub fn to_i32(self) -> i32 {
96        i32::from(self.0)
97    }
98}
99
100impl TryFrom<i32> for ExitCode {
101    type Error = InvalidExitCodeError;
102
103    fn try_from(value: i32) -> Result<Self, Self::Error> {
104        Self::try_new(value)
105    }
106}
107
108/// Conversions between a primitive type and a `u32` for insertion into an instruction.
109trait RawValue: Sized {
110    /// Converts a `u32` to the primitive type `Self`.
111    ///
112    /// This operation is only performed to _parse_ bytecode and we assume that the bytecode is
113    /// correctly formed.  As a result, this does not perform any range checks.
114    fn from_u32(v: u32) -> Self;
115
116    /// Converts the primitive type `Self` to a u32.
117    ///
118    /// This operation is only performed to _generate_ bytecode during compilation, and all
119    /// instruction definitions need to have fields that always fit in a u32.  Consequently,
120    /// this operation is always safe.
121    fn to_u32(self) -> u32;
122}
123
124/// Implements `RawValue` for an unsigned primitive type that is narrower than `u32`.
125macro_rules! impl_raw_value {
126    ( $ty:ty, $from_u32_conv:ident ) => {
127        impl RawValue for $ty {
128            fn from_u32(v: u32) -> Self {
129                $from_u32_conv(v)
130            }
131
132            fn to_u32(self) -> u32 {
133                u32::from(self)
134            }
135        }
136    };
137}
138
139impl_raw_value!(u8, unchecked_u32_as_u8);
140impl_raw_value!(u16, unchecked_u32_as_u16);
141
142/// Representation of a register number.
143///
144/// Registers are represented as `u8` integers where the first `Self::MAX_GLOBAL` values
145/// correspond to global registers and the numbers after those correspond to local registers.
146///
147/// During compilation, local register numbers are assigned starting from "logical 0" for
148/// every scope in the call stack.  During execution, local register numbers must be interpreted
149/// in the context of the Frame Pointer (FP) register, which indicates the offset in the register
150/// bank where local registers start for the current scope.
151#[derive(Clone, Copy, Debug, Eq, PartialEq)]
152pub(crate) struct Register(pub(crate) u8);
153
154impl fmt::Display for Register {
155    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156        write!(f, "R{}", self.0)
157    }
158}
159
160impl RawValue for Register {
161    fn from_u32(v: u32) -> Self {
162        Self(unchecked_u32_as_u8(v))
163    }
164
165    fn to_u32(self) -> u32 {
166        u32::from(self.0)
167    }
168}
169
170impl Register {
171    /// Maximum number of supported registers.
172    pub(crate) const MAX: u8 = u8::MAX;
173
174    /// Maximum number of supported global registers.
175    pub(crate) const MAX_GLOBAL: u8 = 64;
176
177    /// Constructs an instance of `Register` to represent the global register `reg`.  Returns an
178    /// error if we have run out of global registers.
179    pub(crate) fn global(reg: u8) -> Result<Self, OutOfRegistersError> {
180        if reg < Self::MAX_GLOBAL { Ok(Self(reg)) } else { Err(OutOfRegistersError(())) }
181    }
182
183    /// Constructs an instance of `Register` to represent the local register `reg`.  Returns an
184    /// error if we have run out of local registers.
185    pub(crate) fn local(reg: u8) -> Result<Self, OutOfRegistersError> {
186        match reg.checked_add(Self::MAX_GLOBAL) {
187            Some(num) => Ok(Self(num)),
188            None => Err(OutOfRegistersError(())),
189        }
190    }
191
192    /// Breaks apart the internal register representation and returns a tuple indicating if the
193    /// register is global or not and its logical index.
194    pub(crate) fn to_parts(self) -> (bool, u8) {
195        if self.0 < Self::MAX_GLOBAL { (true, self.0) } else { (false, self.0 - Self::MAX_GLOBAL) }
196    }
197}
198
199/// A tagged reference to a variable (register) encoding the register's absolute address
200/// and the type of the value it holds.
201///
202/// The encoding stores the `ExprType` tag in the upper 32 bits and the absolute register
203/// address in the lower 32 bits of a `u64`.
204///
205/// This is distinct from `DatumPtr`, which points to data in the constant pool or heap
206/// rather than to a register in the register file.
207pub(crate) struct TaggedRegisterRef(u64);
208
209impl TaggedRegisterRef {
210    /// Creates a new tagged register reference from a register, frame pointer, and type.
211    pub(crate) fn new(reg: Register, fp: usize, vtype: ExprType) -> Self {
212        let (is_global, index) = reg.to_parts();
213        let mut index = usize::from(index);
214        if !is_global {
215            index += fp;
216        }
217
218        let index = u32::try_from(index).expect("Cannot support that many registers");
219        Self(u64::from(vtype as u8) << 32 | u64::from(index))
220    }
221
222    /// Parses a tagged register reference from a raw `u64`, returning the absolute register
223    /// index and the type of the value it holds.
224    ///
225    /// Panics if the type tag is invalid.
226    pub(crate) fn parse(self) -> (usize, ExprType) {
227        let vtype: ExprType = {
228            #[allow(unsafe_code)]
229            unsafe {
230                let v = unchecked_u64_as_u8(self.0 >> 32);
231                assert!(v <= ExprType::Text as u8);
232                std::mem::transmute(v)
233            }
234        };
235
236        let index = unchecked_u32_as_usize((self.0 & 0xffffffff) as u32);
237
238        (index, vtype)
239    }
240
241    /// Returns the raw `u64` encoding.
242    pub(crate) fn as_u64(&self) -> u64 {
243        self.0
244    }
245
246    /// Wraps a raw `u64` as a `TaggedRegisterRef`.
247    pub(crate) fn from_u64(v: u64) -> Self {
248        Self(v)
249    }
250}
251
252/// A packed representation of an array's element type and number of dimensions.
253///
254/// The encoding stores the `ExprType` in the upper 4 bits and the dimension count in the
255/// lower 4 bits of a single `u8`.
256#[derive(Clone, Copy, Debug, Eq, PartialEq)]
257pub(crate) struct PackedArrayType(u8);
258
259impl PackedArrayType {
260    /// Creates a new packed array type from a subtype and dimension count.
261    pub(crate) fn new(
262        subtype: ExprType,
263        ndims: usize,
264    ) -> Result<Self, TooManyArrayDimensionsError> {
265        if ndims > 15 {
266            return Err(TooManyArrayDimensionsError(()));
267        }
268        let ndims = ndims as u8;
269        Ok(Self(((subtype as u8) << 4) | (ndims & 0x0f)))
270    }
271
272    /// Returns the element type.
273    pub(crate) fn subtype(self) -> ExprType {
274        ExprType::from_u32(u32::from(self.0 >> 4))
275    }
276
277    /// Returns the number of dimensions.
278    pub(crate) fn ndims(self) -> u8 {
279        self.0 & 0x0f
280    }
281}
282
283impl fmt::Display for PackedArrayType {
284    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
285        write!(f, "[{}]{}", self.ndims(), self.subtype().annotation())
286    }
287}
288
289impl RawValue for PackedArrayType {
290    fn from_u32(v: u32) -> Self {
291        Self(unchecked_u32_as_u8(v))
292    }
293
294    fn to_u32(self) -> u32 {
295        u32::from(self.0)
296    }
297}
298
299impl RawValue for ExprType {
300    fn from_u32(v: u32) -> Self {
301        #[allow(unsafe_code)]
302        unsafe {
303            let v = unchecked_u32_as_u8(v);
304            assert!(v <= ExprType::Text as u8);
305            std::mem::transmute(v)
306        }
307    }
308
309    fn to_u32(self) -> u32 {
310        u32::from(self as u8)
311    }
312}
313
314/// Modes for the error handler configured by `ON ERROR`.
315#[derive(Clone, Copy, Debug, Eq, PartialEq)]
316pub(crate) enum ErrorHandlerMode {
317    /// Disable error handling.
318    None,
319
320    /// Resume execution at the next statement after an error.
321    ResumeNext,
322
323    /// Jump to a specific handler address after an error.
324    Jump,
325}
326
327impl RawValue for ErrorHandlerMode {
328    fn from_u32(v: u32) -> Self {
329        #[allow(unsafe_code)]
330        unsafe {
331            let v = unchecked_u32_as_u8(v);
332            assert!(v <= ErrorHandlerMode::Jump as u8);
333            std::mem::transmute(v)
334        }
335    }
336
337    fn to_u32(self) -> u32 {
338        u32::from(self as u8)
339    }
340}
341
342impl fmt::Display for ErrorHandlerMode {
343    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344        match self {
345            Self::None => write!(f, "NONE"),
346            Self::ResumeNext => write!(f, "RESUME_NEXT"),
347            Self::Jump => write!(f, "JUMP"),
348        }
349    }
350}
351
352/// Generates functions to construct an instruction's bytecode representation for the compiler's
353/// benefit, to parse it for the VM's benefit, and to format it for debugging purposes.
354macro_rules! instr {
355    ( $opcode:expr, $name:expr,
356      $make:ident, $parse:ident, $format:ident,
357    ) => {
358        pub(crate) fn $make() -> u32 {
359            ($opcode as u32) << 24
360        }
361
362        pub(crate) fn $parse(op: u32) {
363            debug_assert_eq!($opcode as u32, op >> 24);
364        }
365
366        pub(crate) fn $format(op: u32) -> String {
367            $parse(op);
368            $name.to_owned()
369        }
370    };
371
372    ( $opcode:expr, $name: expr,
373      $make:ident, $parse:ident, $format:ident,
374      $type1:ty, $mask1:expr, $offset1:expr,
375    ) => {
376        pub(crate) fn $make(v1: $type1) -> u32 {
377            let v1 = (RawValue::to_u32(v1) & $mask1) << $offset1;
378            (($opcode as u32) << 24) | v1
379        }
380
381        pub(crate) fn $parse(op: u32) -> $type1 {
382            debug_assert_eq!($opcode as u32, op >> 24);
383            let v1 = RawValue::from_u32((op >> $offset1) & $mask1);
384            v1
385        }
386
387        pub(crate) fn $format(op: u32) -> String {
388            let v1 = $parse(op);
389            format!("{:11} {}", $name, v1)
390        }
391    };
392
393    ( $opcode:expr, $name:expr,
394      $make:ident, $parse:ident, $format:ident,
395      $type1:ty, $mask1:expr, $offset1:expr,
396      $type2:ty, $mask2:expr, $offset2:expr,
397    ) => {
398        pub(crate) fn $make(v1: $type1, v2: $type2) -> u32 {
399            let v1 = (RawValue::to_u32(v1) & $mask1) << $offset1;
400            let v2 = (RawValue::to_u32(v2) & $mask2) << $offset2;
401            (($opcode as u32) << 24) | v1 | v2
402        }
403
404        pub(crate) fn $parse(op: u32) -> ($type1, $type2) {
405            debug_assert_eq!($opcode as u32, op >> 24);
406            let v1 = RawValue::from_u32((op >> $offset1) & $mask1);
407            let v2 = RawValue::from_u32((op >> $offset2) & $mask2);
408            (v1, v2)
409        }
410
411        pub(crate) fn $format(op: u32) -> String {
412            let (v1, v2) = $parse(op);
413            format!("{:11} {}, {}", $name, v1, v2)
414        }
415    };
416
417    ( $opcode:expr, $name:expr,
418      $make:ident, $parse:ident, $format:ident,
419      $type1:ty, $mask1:expr, $offset1:expr,
420      $type2:ty, $mask2:expr, $offset2:expr,
421      $type3:ty, $mask3:expr, $offset3:expr,
422    ) => {
423        pub(crate) fn $make(v1: $type1, v2: $type2, v3: $type3) -> u32 {
424            let v1 = (RawValue::to_u32(v1) & $mask1) << $offset1;
425            let v2 = (RawValue::to_u32(v2) & $mask2) << $offset2;
426            let v3 = (RawValue::to_u32(v3) & $mask3) << $offset3;
427            (($opcode as u32) << 24) | v1 | v2 | v3
428        }
429
430        pub(crate) fn $parse(op: u32) -> ($type1, $type2, $type3) {
431            debug_assert_eq!($opcode as u32, op >> 24);
432            let v1 = RawValue::from_u32((op >> $offset1) & $mask1);
433            let v2 = RawValue::from_u32((op >> $offset2) & $mask2);
434            let v3 = RawValue::from_u32((op >> $offset3) & $mask3);
435            (v1, v2, v3)
436        }
437
438        pub(crate) fn $format(op: u32) -> String {
439            let (v1, v2, v3) = $parse(op);
440            format!("{:11} {}, {}, {}", $name, v1, v2, v3)
441        }
442    };
443}
444
445/// Enumeration of all valid instruction types (opcodes).
446///
447/// The specific numbers assigned to each instruction are not important at this moment because
448/// we expect bytecode execution to always be coupled with generation (which means there is no
449/// need to worry about stable values over time).
450#[repr(u8)]
451pub(crate) enum Opcode {
452    /// Adds two doubles and stores the result into a third one.
453    AddDouble,
454
455    /// Adds two integers and stores the result into a third one.
456    AddInteger,
457
458    /// Allocates an object on the heap.
459    Alloc,
460
461    /// Allocates a multidimensional array on the heap.
462    AllocArray,
463
464    /// Computes the bitwise AND of two integers and stores the result into a third one.
465    BitwiseAnd,
466
467    /// Computes the bitwise NOT of an integer value in place.
468    BitwiseNot,
469
470    /// Computes the bitwise OR of two integers and stores the result into a third one.
471    BitwiseOr,
472
473    /// Computes the bitwise XOR of two integers and stores the result into a third one.
474    BitwiseXor,
475
476    /// Calls an address relative to the PC.
477    Call,
478
479    /// Concatenates two strings and stores the pointer to the result into a third one.
480    Concat,
481
482    /// Divides two doubles and stores the result into a third one.
483    DivideDouble,
484
485    /// Divides two integers and stores the result into a third one.
486    DivideInteger,
487
488    /// Converts the double value in a register to an integer.
489    DoubleToInteger,
490
491    /// Compares two booleans for equality and stores the result into a third one.
492    EqualBoolean,
493
494    /// Compares two doubles for equality and stores the result into a third one.
495    EqualDouble,
496
497    /// Compares two integers for equality and stores the result into a third one.
498    EqualInteger,
499
500    /// Compares two strings for equality and stores the result into a third one.
501    EqualText,
502
503    /// Jumps to a subroutine at an address relative to the PC.
504    Gosub,
505
506    /// Compares two doubles for greater-than and stores the result into a third one.
507    GreaterDouble,
508
509    /// Compares two doubles for greater-than-or-equal and stores the result into a third one.
510    GreaterEqualDouble,
511
512    /// Compares two integers for greater-than-or-equal and stores the result into a third one.
513    GreaterEqualInteger,
514
515    /// Compares two strings for greater-than-or-equal and stores the result into a third one.
516    GreaterEqualText,
517
518    /// Compares two integers for greater-than and stores the result into a third one.
519    GreaterInteger,
520
521    /// Compares two strings for greater-than and stores the result into a third one.
522    GreaterText,
523
524    /// Converts the integer value in a register to a double.
525    IntegerToDouble,
526
527    /// Jumps to an address relative to the PC.
528    Jump,
529
530    /// Jumps to an address relative to the PC if the condition register is false (0).
531    JumpIfFalse,
532
533    /// Compares two doubles for less-than and stores the result into a third one.
534    LessDouble,
535
536    /// Compares two doubles for less-than-or-equal and stores the result into a third one.
537    LessEqualDouble,
538
539    /// Compares two integers for less-than-or-equal and stores the result into a third one.
540    LessEqualInteger,
541
542    /// Compares two strings for less-than-or-equal and stores the result into a third one.
543    LessEqualText,
544
545    /// Compares two integers for less-than and stores the result into a third one.
546    LessInteger,
547
548    /// Compares two strings for less-than and stores the result into a third one.
549    LessText,
550
551    /// Loads an element from an array.
552    LoadArray,
553
554    /// Loads a constant into a register.
555    LoadConstant,
556
557    /// Loads an integer immediate into a register.
558    LoadInteger,
559
560    /// Loads a register pointer into a register.
561    LoadRegisterPointer,
562
563    /// Computes the modulo of two doubles and stores the result into a third one.
564    ModuloDouble,
565
566    /// Computes the modulo of two integers and stores the result into a third one.
567    ModuloInteger,
568
569    /// Moves (copies) data between two registers.
570    Move,
571
572    /// Multiplies two doubles and stores the result into a third one.
573    MultiplyDouble,
574
575    /// Multiplies two integers and stores the result into a third one.
576    MultiplyInteger,
577
578    /// Negates a double value in place.
579    NegateDouble,
580
581    /// Negates an integer value in place.
582    NegateInteger,
583
584    /// Compares two booleans for inequality and stores the result into a third one.
585    NotEqualBoolean,
586
587    /// Compares two doubles for inequality and stores the result into a third one.
588    NotEqualDouble,
589
590    /// Compares two integers for inequality and stores the result into a third one.
591    NotEqualInteger,
592
593    /// Compares two strings for inequality and stores the result into a third one.
594    NotEqualText,
595
596    /// The "null" instruction, used by the compiler to pad the code for fixups.
597    Nop,
598
599    /// Computes the power of two doubles and stores the result into a third one.
600    PowerDouble,
601
602    /// Computes the power of two integers and stores the result into a third one.
603    PowerInteger,
604
605    /// Returns from a previous `Call`.
606    Return,
607
608    /// Sets the error handler mode and target address.
609    SetErrorHandler,
610
611    /// Shifts an integer left by a number of bits without rotation, storing the result into
612    /// a third register.
613    ShiftLeft,
614
615    /// Shifts an integer right by a number of bits without rotation, storing the result into
616    /// a third register.
617    ShiftRight,
618
619    /// Stores a value into an array element.
620    StoreArray,
621
622    /// Subtracts two doubles and stores the result into a third one.
623    SubtractDouble,
624
625    /// Subtracts two integers and stores the result into a third one.
626    SubtractInteger,
627
628    /// Terminates execution with an explicit exit code.
629    End,
630
631    /// Requests the execution of an upcall, stopping VM execution.
632    Upcall,
633
634    /// Requests the execution of an asynchronous upcall, stopping VM execution.
635    UpcallAsync,
636
637    /// Terminates execution due to natural fallthrough.
638    // KEEP THIS LAST.
639    Eof,
640}
641
642#[rustfmt::skip]
643instr!(
644    Opcode::AddDouble, "ADDD",
645    make_add_double, parse_add_double, format_add_double,
646    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
647    Register, 0x000000ff, 8,  // Left hand side value.
648    Register, 0x000000ff, 0,  // Right hand side value.
649);
650
651#[rustfmt::skip]
652instr!(
653    Opcode::AddInteger, "ADDI",
654    make_add_integer, parse_add_integer, format_add_integer,
655    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
656    Register, 0x000000ff, 8,  // Left hand side value.
657    Register, 0x000000ff, 0,  // Right hand side value.
658);
659
660#[rustfmt::skip]
661instr!(
662    Opcode::Alloc, "ALLOC",
663    make_alloc, parse_alloc, format_alloc,
664    Register, 0x000000ff, 8,  // Destination register in which to store the heap pointer.
665    ExprType, 0x000000ff, 0,  // Type of the object to allocate.
666);
667
668#[rustfmt::skip]
669instr!(
670    Opcode::AllocArray, "ALLOCA",
671    make_alloc_array, parse_alloc_array, format_alloc_array,
672    Register, 0x000000ff, 16,  // Destination register to store the array pointer.
673    PackedArrayType, 0x000000ff, 8,  // Packed element type and dimension count.
674    Register, 0x000000ff, 0,  // First register containing dimension sizes.
675);
676
677#[rustfmt::skip]
678instr!(
679    Opcode::BitwiseAnd, "AND",
680    make_bitwise_and, parse_bitwise_and, format_bitwise_and,
681    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
682    Register, 0x000000ff, 8,  // Left hand side value.
683    Register, 0x000000ff, 0,  // Right hand side value.
684);
685
686#[rustfmt::skip]
687instr!(
688    Opcode::BitwiseNot, "NOT",
689    make_bitwise_not, parse_bitwise_not, format_bitwise_not,
690    Register, 0x000000ff, 0,  // Register with the value to NOT in place.
691);
692
693#[rustfmt::skip]
694instr!(
695    Opcode::BitwiseOr, "OR",
696    make_bitwise_or, parse_bitwise_or, format_bitwise_or,
697    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
698    Register, 0x000000ff, 8,  // Left hand side value.
699    Register, 0x000000ff, 0,  // Right hand side value.
700);
701
702#[rustfmt::skip]
703instr!(
704    Opcode::BitwiseXor, "XOR",
705    make_bitwise_xor, parse_bitwise_xor, format_bitwise_xor,
706    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
707    Register, 0x000000ff, 8,  // Left hand side value.
708    Register, 0x000000ff, 0,  // Right hand side value.
709);
710
711#[rustfmt::skip]
712instr!(
713    Opcode::Call, "CALL",
714    make_call, parse_call, format_call,
715    Register, 0x000000ff, 16,  // Destination register for the return value, if any.
716    u16, 0x0000ffff, 0,  // Target address.
717);
718
719#[rustfmt::skip]
720instr!(
721    Opcode::Concat, "CONCAT",
722    make_concat, parse_concat, format_concat,
723    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
724    Register, 0x000000ff, 8,  // Left hand side value.
725    Register, 0x000000ff, 0,  // Right hand side value.
726);
727
728#[rustfmt::skip]
729instr!(
730    Opcode::DivideDouble, "DIVD",
731    make_divide_double, parse_divide_double, format_divide_double,
732    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
733    Register, 0x000000ff, 8,  // Left hand side value.
734    Register, 0x000000ff, 0,  // Right hand side value.
735);
736
737#[rustfmt::skip]
738instr!(
739    Opcode::DivideInteger, "DIVI",
740    make_divide_integer, parse_divide_integer, format_divide_integer,
741    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
742    Register, 0x000000ff, 8,  // Left hand side value.
743    Register, 0x000000ff, 0,  // Right hand side value.
744);
745
746#[rustfmt::skip]
747instr!(
748    Opcode::DoubleToInteger, "DTOI",
749    make_double_to_integer, parse_double_to_integer, format_double_to_integer,
750    Register, 0x000000ff, 0,  // Register with the value to convert.
751);
752
753#[rustfmt::skip]
754instr!(
755    Opcode::EqualBoolean, "CMPEQB",
756    make_equal_boolean, parse_equal_boolean, format_equal_boolean,
757    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
758    Register, 0x000000ff, 8,  // Left hand side value.
759    Register, 0x000000ff, 0,  // Right hand side value.
760);
761
762#[rustfmt::skip]
763instr!(
764    Opcode::EqualDouble, "CMPEQD",
765    make_equal_double, parse_equal_double, format_equal_double,
766    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
767    Register, 0x000000ff, 8,  // Left hand side value.
768    Register, 0x000000ff, 0,  // Right hand side value.
769);
770
771#[rustfmt::skip]
772instr!(
773    Opcode::EqualInteger, "CMPEQI",
774    make_equal_integer, parse_equal_integer, format_equal_integer,
775    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
776    Register, 0x000000ff, 8,  // Left hand side value.
777    Register, 0x000000ff, 0,  // Right hand side value.
778);
779
780#[rustfmt::skip]
781instr!(
782    Opcode::EqualText, "CMPEQS",
783    make_equal_text, parse_equal_text, format_equal_text,
784    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
785    Register, 0x000000ff, 8,  // Left hand side value.
786    Register, 0x000000ff, 0,  // Right hand side value.
787);
788
789#[rustfmt::skip]
790instr!(
791    Opcode::End, "END",
792    make_end, parse_end, format_end,
793    Register, 0x000000ff, 0,  // Register with the return code.
794);
795
796#[rustfmt::skip]
797instr!(
798    Opcode::Eof, "EOF",
799    make_eof, parse_eof, format_eof,
800);
801
802#[rustfmt::skip]
803instr!(
804    Opcode::Gosub, "GOSUB",
805    make_gosub, parse_gosub, format_gosub,
806    u16, 0x0000ffff, 0,  // Target address.
807);
808
809#[rustfmt::skip]
810instr!(
811    Opcode::GreaterDouble, "CMPGTD",
812    make_greater_double, parse_greater_double, format_greater_double,
813    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
814    Register, 0x000000ff, 8,  // Left hand side value.
815    Register, 0x000000ff, 0,  // Right hand side value.
816);
817
818#[rustfmt::skip]
819instr!(
820    Opcode::GreaterEqualDouble, "CMPGED",
821    make_greater_equal_double, parse_greater_equal_double, format_greater_equal_double,
822    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
823    Register, 0x000000ff, 8,  // Left hand side value.
824    Register, 0x000000ff, 0,  // Right hand side value.
825);
826
827#[rustfmt::skip]
828instr!(
829    Opcode::GreaterEqualInteger, "CMPGEI",
830    make_greater_equal_integer, parse_greater_equal_integer, format_greater_equal_integer,
831    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
832    Register, 0x000000ff, 8,  // Left hand side value.
833    Register, 0x000000ff, 0,  // Right hand side value.
834);
835
836#[rustfmt::skip]
837instr!(
838    Opcode::GreaterEqualText, "CMPGES",
839    make_greater_equal_text, parse_greater_equal_text, format_greater_equal_text,
840    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
841    Register, 0x000000ff, 8,  // Left hand side value.
842    Register, 0x000000ff, 0,  // Right hand side value.
843);
844
845#[rustfmt::skip]
846instr!(
847    Opcode::GreaterInteger, "CMPGTI",
848    make_greater_integer, parse_greater_integer, format_greater_integer,
849    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
850    Register, 0x000000ff, 8,  // Left hand side value.
851    Register, 0x000000ff, 0,  // Right hand side value.
852);
853
854#[rustfmt::skip]
855instr!(
856    Opcode::GreaterText, "CMPGTS",
857    make_greater_text, parse_greater_text, format_greater_text,
858    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
859    Register, 0x000000ff, 8,  // Left hand side value.
860    Register, 0x000000ff, 0,  // Right hand side value.
861);
862
863#[rustfmt::skip]
864instr!(
865    Opcode::IntegerToDouble, "ITOD",
866    make_integer_to_double, parse_integer_to_double, format_integer_to_double,
867    Register, 0x000000ff, 0,  // Register with the value to convert.
868);
869
870#[rustfmt::skip]
871instr!(
872    Opcode::Jump, "JUMP",
873    make_jump, parse_jump, format_jump,
874    u16, 0x0000ffff, 0,  // Target address.
875);
876
877#[rustfmt::skip]
878instr!(
879    Opcode::JumpIfFalse, "JMPF",
880    make_jump_if_false, parse_jump_if_false, format_jump_if_false,
881    Register, 0x000000ff, 16,  // Condition register; if 0 (false), jump to target.
882    u16, 0x0000ffff, 0,  // Target address.
883);
884
885#[rustfmt::skip]
886instr!(
887    Opcode::LessDouble, "CMPLTD",
888    make_less_double, parse_less_double, format_less_double,
889    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
890    Register, 0x000000ff, 8,  // Left hand side value.
891    Register, 0x000000ff, 0,  // Right hand side value.
892);
893
894#[rustfmt::skip]
895instr!(
896    Opcode::LessEqualDouble, "CMPLED",
897    make_less_equal_double, parse_less_equal_double, format_less_equal_double,
898    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
899    Register, 0x000000ff, 8,  // Left hand side value.
900    Register, 0x000000ff, 0,  // Right hand side value.
901);
902
903#[rustfmt::skip]
904instr!(
905    Opcode::LessEqualInteger, "CMPLEI",
906    make_less_equal_integer, parse_less_equal_integer, format_less_equal_integer,
907    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
908    Register, 0x000000ff, 8,  // Left hand side value.
909    Register, 0x000000ff, 0,  // Right hand side value.
910);
911
912#[rustfmt::skip]
913instr!(
914    Opcode::LessEqualText, "CMPLES",
915    make_less_equal_text, parse_less_equal_text, format_less_equal_text,
916    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
917    Register, 0x000000ff, 8,  // Left hand side value.
918    Register, 0x000000ff, 0,  // Right hand side value.
919);
920
921#[rustfmt::skip]
922instr!(
923    Opcode::LessInteger, "CMPLTI",
924    make_less_integer, parse_less_integer, format_less_integer,
925    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
926    Register, 0x000000ff, 8,  // Left hand side value.
927    Register, 0x000000ff, 0,  // Right hand side value.
928);
929
930#[rustfmt::skip]
931instr!(
932    Opcode::LessText, "CMPLTS",
933    make_less_text, parse_less_text, format_less_text,
934    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
935    Register, 0x000000ff, 8,  // Left hand side value.
936    Register, 0x000000ff, 0,  // Right hand side value.
937);
938
939#[rustfmt::skip]
940instr!(
941    Opcode::LoadArray, "LOADA",
942    make_load_array, parse_load_array, format_load_array,
943    Register, 0x000000ff, 16,  // Destination register for the loaded value.
944    Register, 0x000000ff, 8,  // Register containing the array pointer.
945    Register, 0x000000ff, 0,  // First register containing subscript values.
946);
947
948#[rustfmt::skip]
949instr!(
950    Opcode::LoadConstant, "LOADC",
951    make_load_constant, parse_load_constant, format_load_constant,
952    Register, 0x000000ff, 16,  // Destination register to load the constant into.
953    u16, 0x0000ffff, 0,  // Index of the constant to load.
954);
955
956#[rustfmt::skip]
957instr!(
958    Opcode::LoadInteger, "LOADI",
959    make_load_integer, parse_load_integer, format_load_integer,
960    Register, 0x000000ff, 16,  // Destination register to load the immediate into.
961    u16, 0x0000ffff, 0,  // Immediate value.
962);
963
964#[rustfmt::skip]
965instr!(
966    Opcode::LoadRegisterPointer, "LOADRP",
967    make_load_register_ptr, parse_load_register_ptr, format_load_register_ptr,
968    Register, 0x000000ff, 16,  // Destination register to load the immediate into.
969    ExprType, 0x000000ff, 8,  // Type of the value pointed to.
970    Register, 0x000000ff, 0,  // Register to load.
971);
972
973#[rustfmt::skip]
974instr!(
975    Opcode::ModuloDouble, "MODD",
976    make_modulo_double, parse_modulo_double, format_modulo_double,
977    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
978    Register, 0x000000ff, 8,  // Left hand side value.
979    Register, 0x000000ff, 0,  // Right hand side value.
980);
981
982#[rustfmt::skip]
983instr!(
984    Opcode::ModuloInteger, "MODI",
985    make_modulo_integer, parse_modulo_integer, format_modulo_integer,
986    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
987    Register, 0x000000ff, 8,  // Left hand side value.
988    Register, 0x000000ff, 0,  // Right hand side value.
989);
990
991#[rustfmt::skip]
992instr!(
993    Opcode::Move, "MOVE",
994    make_move, parse_move, format_move,
995    Register, 0x000000ff, 8,  // Destination register.
996    Register, 0x000000ff, 0,  // Source register.
997);
998
999#[rustfmt::skip]
1000instr!(
1001    Opcode::MultiplyDouble, "MULD",
1002    make_multiply_double, parse_multiply_double, format_multiply_double,
1003    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
1004    Register, 0x000000ff, 8,  // Left hand side value.
1005    Register, 0x000000ff, 0,  // Right hand side value.
1006);
1007
1008#[rustfmt::skip]
1009instr!(
1010    Opcode::MultiplyInteger, "MULI",
1011    make_multiply_integer, parse_multiply_integer, format_multiply_integer,
1012    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
1013    Register, 0x000000ff, 8,  // Left hand side value.
1014    Register, 0x000000ff, 0,  // Right hand side value.
1015);
1016
1017#[rustfmt::skip]
1018instr!(
1019    Opcode::NegateDouble,
1020    "NEGD",
1021    make_negate_double,
1022    parse_negate_double,
1023    format_negate_double,
1024    Register,
1025    0x000000ff,
1026    0, // Register with the value to negate in place.
1027);
1028
1029#[rustfmt::skip]
1030instr!(
1031    Opcode::NegateInteger, "NEGI",
1032    make_negate_integer, parse_negate_integer, format_negate_integer,
1033    Register, 0x000000ff, 0,  // Register with the value to negate in place.
1034);
1035
1036#[rustfmt::skip]
1037instr!(
1038    Opcode::NotEqualBoolean, "CMPNEB",
1039    make_not_equal_boolean, parse_not_equal_boolean, format_not_equal_boolean,
1040    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
1041    Register, 0x000000ff, 8,  // Left hand side value.
1042    Register, 0x000000ff, 0,  // Right hand side value.
1043);
1044
1045#[rustfmt::skip]
1046instr!(
1047    Opcode::NotEqualDouble, "CMPNED",
1048    make_not_equal_double, parse_not_equal_double, format_not_equal_double,
1049    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
1050    Register, 0x000000ff, 8,  // Left hand side value.
1051    Register, 0x000000ff, 0,  // Right hand side value.
1052);
1053
1054#[rustfmt::skip]
1055instr!(
1056    Opcode::NotEqualInteger, "CMPNEI",
1057    make_not_equal_integer, parse_not_equal_integer, format_not_equal_integer,
1058    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
1059    Register, 0x000000ff, 8,  // Left hand side value.
1060    Register, 0x000000ff, 0,  // Right hand side value.
1061);
1062
1063#[rustfmt::skip]
1064instr!(
1065    Opcode::NotEqualText, "CMPNES",
1066    make_not_equal_text, parse_not_equal_text, format_not_equal_text,
1067    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
1068    Register, 0x000000ff, 8,  // Left hand side value.
1069    Register, 0x000000ff, 0,  // Right hand side value.
1070);
1071
1072#[rustfmt::skip]
1073instr!(
1074    Opcode::Nop, "NOP",
1075    make_nop, parse_nop, format_nop,
1076);
1077
1078#[rustfmt::skip]
1079instr!(
1080    Opcode::PowerDouble, "POWD",
1081    make_power_double, parse_power_double, format_power_double,
1082    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
1083    Register, 0x000000ff, 8,  // Left hand side value.
1084    Register, 0x000000ff, 0,  // Right hand side value.
1085);
1086
1087#[rustfmt::skip]
1088instr!(
1089    Opcode::PowerInteger, "POWI",
1090    make_power_integer, parse_power_integer, format_power_integer,
1091    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
1092    Register, 0x000000ff, 8,  // Left hand side value.
1093    Register, 0x000000ff, 0,  // Right hand side value.
1094);
1095
1096#[rustfmt::skip]
1097instr!(
1098    Opcode::Return, "RETURN",
1099    make_return, parse_return, format_return,
1100);
1101
1102#[rustfmt::skip]
1103instr!(
1104    Opcode::SetErrorHandler, "SETEH",
1105    make_set_error_handler, parse_set_error_handler, format_set_error_handler,
1106    ErrorHandlerMode, 0x000000ff, 16,  // Error handler mode.
1107    u16, 0x0000ffff, 0,  // Target address for Jump mode.
1108);
1109
1110#[rustfmt::skip]
1111instr!(
1112    Opcode::ShiftLeft, "SHL",
1113    make_shift_left, parse_shift_left, format_shift_left,
1114    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
1115    Register, 0x000000ff, 8,  // Value to shift.
1116    Register, 0x000000ff, 0,  // Number of bits to shift by.
1117);
1118
1119#[rustfmt::skip]
1120instr!(
1121    Opcode::ShiftRight, "SHR",
1122    make_shift_right, parse_shift_right, format_shift_right,
1123    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
1124    Register, 0x000000ff, 8,  // Value to shift.
1125    Register, 0x000000ff, 0,  // Number of bits to shift by.
1126);
1127
1128#[rustfmt::skip]
1129instr!(
1130    Opcode::StoreArray, "STOREA",
1131    make_store_array, parse_store_array, format_store_array,
1132    Register, 0x000000ff, 16,  // Register containing the array pointer.
1133    Register, 0x000000ff, 8,  // Register containing the value to store.
1134    Register, 0x000000ff, 0,  // First register containing subscript values.
1135);
1136
1137#[rustfmt::skip]
1138instr!(
1139    Opcode::SubtractDouble, "SUBD",
1140    make_subtract_double, parse_subtract_double, format_subtract_double,
1141    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
1142    Register, 0x000000ff, 8,  // Left hand side value.
1143    Register, 0x000000ff, 0,  // Right hand side value.
1144);
1145
1146#[rustfmt::skip]
1147instr!(
1148    Opcode::SubtractInteger, "SUBI",
1149    make_subtract_integer, parse_subtract_integer, format_subtract_integer,
1150    Register, 0x000000ff, 16,  // Destination register to store the result of the operation.
1151    Register, 0x000000ff, 8,  // Left hand side value.
1152    Register, 0x000000ff, 0,  // Right hand side value.
1153);
1154
1155#[rustfmt::skip]
1156instr!(
1157    Opcode::Upcall, "UPCALL",
1158    make_upcall, parse_upcall, format_upcall,
1159    u16, 0x0000ffff, 8,  // Index of the upcall to execute.
1160    Register, 0x000000ff, 0,  // First register with arguments.
1161);
1162
1163#[rustfmt::skip]
1164instr!(
1165    Opcode::UpcallAsync, "UPCALLA",
1166    make_upcall_async, parse_upcall_async, format_upcall_async,
1167    u16, 0x0000ffff, 8,  // Index of the upcall to execute.
1168    Register, 0x000000ff, 0,  // First register with arguments.
1169);
1170
1171/// Returns the opcode of an instruction.
1172pub(crate) fn opcode_of(instr: u32) -> Opcode {
1173    #[allow(unsafe_code)]
1174    unsafe {
1175        let num = unchecked_u32_as_u8(instr >> 24);
1176        debug_assert!(num <= Opcode::Eof as u8);
1177        std::mem::transmute::<u8, Opcode>(num)
1178    }
1179}
1180
1181/// Tags used as integer register values to identify the type stored in another register at
1182/// runtime.
1183///
1184/// This is used in function and command calls that receive variadic arguments (such as `PRINT`)
1185/// to identify the types of the arguments (which can be missing) and separators.
1186#[derive(Clone, Copy, Debug, PartialEq)]
1187#[repr(u8)]
1188pub enum VarArgTag {
1189    /// The argument is missing.  This is only possible for command invocations.
1190    Missing(ArgSep) = 0,
1191
1192    /// The argument is an immediate of the given type.
1193    Immediate(ArgSep, ExprType) = 1,
1194
1195    /// The argument is a pointer.
1196    Pointer(ArgSep) = 2,
1197}
1198
1199impl VarArgTag {
1200    /// Parses a register `value` into a variadic argument tag.
1201    // This is not `TryFrom` because that makes this interface public and forces us to make the
1202    // result type public as well, but we don't need it to be.
1203    pub(crate) fn parse_u64(value: u64) -> ParseResult<Self> {
1204        if value & !(0x0fff) != 0 {
1205            return Err(ParseError::InvalidTypeTag(value));
1206        };
1207
1208        let key_u8 = ((value & 0x0f00) >> 8) as u8;
1209        let sep_u8 = ((value & 0x00f0) >> 4) as u8;
1210        let other_u8 = (value & 0x000f) as u8;
1211
1212        let Ok(sep) = ArgSep::try_from(sep_u8) else {
1213            return Err(ParseError::InvalidTypeTag(value));
1214        };
1215
1216        match key_u8 {
1217            0 => {
1218                if other_u8 == 0 {
1219                    Ok(Self::Missing(sep))
1220                } else {
1221                    Err(ParseError::InvalidTypeTag(value))
1222                }
1223            }
1224            1 => match ExprType::try_from(other_u8) {
1225                Ok(etype) => Ok(Self::Immediate(sep, etype)),
1226                Err(_) => Err(ParseError::InvalidTypeTag(value)),
1227            },
1228            2 => {
1229                if other_u8 == 0 {
1230                    Ok(Self::Pointer(sep))
1231                } else {
1232                    Err(ParseError::InvalidTypeTag(value))
1233                }
1234            }
1235            _ => Err(ParseError::InvalidTypeTag(value)),
1236        }
1237    }
1238
1239    /// Makes a new tag for the type of a variadic argument.
1240    pub(crate) fn make_u16(self) -> u16 {
1241        let (key_u8, sep, other_u8): (u8, ArgSep, u8) = match self {
1242            Self::Missing(sep) => (0, sep, 0),
1243            Self::Immediate(sep, etype) => (1, sep, etype as u8),
1244            Self::Pointer(sep) => (2, sep, 0),
1245        };
1246        u16::from(key_u8) << 8 | u16::from(sep as u8) << 4 | u16::from(other_u8)
1247    }
1248}
1249
1250#[cfg(test)]
1251mod tests {
1252    use super::*;
1253
1254    macro_rules! test_instr {
1255        ( $name:ident, $make:ident, $parse:ident ) => {
1256            #[test]
1257            fn $name() {
1258                let instr = $make();
1259                $parse(instr);
1260            }
1261        };
1262
1263        ( $name:ident, $make:ident, $parse:ident, $v1:expr ) => {
1264            #[test]
1265            fn $name() {
1266                let instr = $make($v1);
1267                assert_eq!($v1, $parse(instr));
1268            }
1269        };
1270
1271        ( $name:ident, $make:ident, $parse:ident, $v1:expr, $v2:expr ) => {
1272            #[test]
1273            fn $name() {
1274                let instr = $make($v1, $v2);
1275                assert_eq!(($v1, $v2), $parse(instr));
1276            }
1277        };
1278
1279        ( $name:ident, $make:ident, $parse:ident, $v1:expr, $v2:expr, $v3:expr ) => {
1280            #[test]
1281            fn $name() {
1282                let instr = $make($v1, $v2, $v3);
1283                assert_eq!(($v1, $v2, $v3), $parse(instr));
1284            }
1285        };
1286    }
1287
1288    test_instr!(
1289        test_add_double,
1290        make_add_double,
1291        parse_add_double,
1292        Register::local(1).unwrap(),
1293        Register::local(2).unwrap(),
1294        Register::local(3).unwrap()
1295    );
1296
1297    test_instr!(
1298        test_add_integer,
1299        make_add_integer,
1300        parse_add_integer,
1301        Register::local(1).unwrap(),
1302        Register::local(2).unwrap(),
1303        Register::local(3).unwrap()
1304    );
1305
1306    test_instr!(
1307        test_alloc,
1308        make_alloc,
1309        parse_alloc,
1310        Register::local(1).unwrap(),
1311        ExprType::Integer
1312    );
1313
1314    test_instr!(
1315        test_alloc_array,
1316        make_alloc_array,
1317        parse_alloc_array,
1318        Register::local(1).unwrap(),
1319        PackedArrayType::new(ExprType::Integer, 3).unwrap(),
1320        Register::local(2).unwrap()
1321    );
1322
1323    test_instr!(
1324        test_bitwise_and,
1325        make_bitwise_and,
1326        parse_bitwise_and,
1327        Register::local(1).unwrap(),
1328        Register::local(2).unwrap(),
1329        Register::local(3).unwrap()
1330    );
1331
1332    test_instr!(test_bitwise_not, make_bitwise_not, parse_bitwise_not, Register::local(1).unwrap());
1333
1334    test_instr!(
1335        test_bitwise_or,
1336        make_bitwise_or,
1337        parse_bitwise_or,
1338        Register::local(1).unwrap(),
1339        Register::local(2).unwrap(),
1340        Register::local(3).unwrap()
1341    );
1342
1343    test_instr!(
1344        test_bitwise_xor,
1345        make_bitwise_xor,
1346        parse_bitwise_xor,
1347        Register::local(1).unwrap(),
1348        Register::local(2).unwrap(),
1349        Register::local(3).unwrap()
1350    );
1351
1352    test_instr!(test_call, make_call, parse_call, Register::local(3).unwrap(), 12345);
1353
1354    test_instr!(
1355        test_concat,
1356        make_concat,
1357        parse_concat,
1358        Register::local(1).unwrap(),
1359        Register::local(2).unwrap(),
1360        Register::local(3).unwrap()
1361    );
1362
1363    test_instr!(
1364        test_divide_double,
1365        make_divide_double,
1366        parse_divide_double,
1367        Register::local(1).unwrap(),
1368        Register::local(2).unwrap(),
1369        Register::local(3).unwrap()
1370    );
1371
1372    test_instr!(
1373        test_divide_integer,
1374        make_divide_integer,
1375        parse_divide_integer,
1376        Register::local(1).unwrap(),
1377        Register::local(2).unwrap(),
1378        Register::local(3).unwrap()
1379    );
1380
1381    test_instr!(
1382        test_double_to_integer,
1383        make_double_to_integer,
1384        parse_double_to_integer,
1385        Register::local(1).unwrap()
1386    );
1387
1388    test_instr!(
1389        test_equal_boolean,
1390        make_equal_boolean,
1391        parse_equal_boolean,
1392        Register::local(1).unwrap(),
1393        Register::local(2).unwrap(),
1394        Register::local(3).unwrap()
1395    );
1396
1397    test_instr!(
1398        test_equal_double,
1399        make_equal_double,
1400        parse_equal_double,
1401        Register::local(1).unwrap(),
1402        Register::local(2).unwrap(),
1403        Register::local(3).unwrap()
1404    );
1405
1406    test_instr!(
1407        test_equal_integer,
1408        make_equal_integer,
1409        parse_equal_integer,
1410        Register::local(1).unwrap(),
1411        Register::local(2).unwrap(),
1412        Register::local(3).unwrap()
1413    );
1414
1415    test_instr!(
1416        test_equal_text,
1417        make_equal_text,
1418        parse_equal_text,
1419        Register::local(1).unwrap(),
1420        Register::local(2).unwrap(),
1421        Register::local(3).unwrap()
1422    );
1423
1424    test_instr!(test_end, make_end, parse_end, Register::local(1).unwrap());
1425
1426    test_instr!(test_eof, make_eof, parse_eof);
1427
1428    test_instr!(test_gosub, make_gosub, parse_gosub, 12345);
1429
1430    test_instr!(
1431        test_greater_double,
1432        make_greater_double,
1433        parse_greater_double,
1434        Register::local(1).unwrap(),
1435        Register::local(2).unwrap(),
1436        Register::local(3).unwrap()
1437    );
1438
1439    test_instr!(
1440        test_greater_equal_double,
1441        make_greater_equal_double,
1442        parse_greater_equal_double,
1443        Register::local(1).unwrap(),
1444        Register::local(2).unwrap(),
1445        Register::local(3).unwrap()
1446    );
1447
1448    test_instr!(
1449        test_greater_equal_integer,
1450        make_greater_equal_integer,
1451        parse_greater_equal_integer,
1452        Register::local(1).unwrap(),
1453        Register::local(2).unwrap(),
1454        Register::local(3).unwrap()
1455    );
1456
1457    test_instr!(
1458        test_greater_equal_text,
1459        make_greater_equal_text,
1460        parse_greater_equal_text,
1461        Register::local(1).unwrap(),
1462        Register::local(2).unwrap(),
1463        Register::local(3).unwrap()
1464    );
1465
1466    test_instr!(
1467        test_greater_integer,
1468        make_greater_integer,
1469        parse_greater_integer,
1470        Register::local(1).unwrap(),
1471        Register::local(2).unwrap(),
1472        Register::local(3).unwrap()
1473    );
1474
1475    test_instr!(
1476        test_greater_text,
1477        make_greater_text,
1478        parse_greater_text,
1479        Register::local(1).unwrap(),
1480        Register::local(2).unwrap(),
1481        Register::local(3).unwrap()
1482    );
1483
1484    test_instr!(
1485        test_integer_to_double,
1486        make_integer_to_double,
1487        parse_integer_to_double,
1488        Register::local(1).unwrap()
1489    );
1490
1491    test_instr!(test_jump, make_jump, parse_jump, 12345);
1492
1493    test_instr!(
1494        test_jump_if_false,
1495        make_jump_if_false,
1496        parse_jump_if_false,
1497        Register::local(1).unwrap(),
1498        12345
1499    );
1500
1501    test_instr!(
1502        test_less_double,
1503        make_less_double,
1504        parse_less_double,
1505        Register::local(1).unwrap(),
1506        Register::local(2).unwrap(),
1507        Register::local(3).unwrap()
1508    );
1509
1510    test_instr!(
1511        test_less_equal_double,
1512        make_less_equal_double,
1513        parse_less_equal_double,
1514        Register::local(1).unwrap(),
1515        Register::local(2).unwrap(),
1516        Register::local(3).unwrap()
1517    );
1518
1519    test_instr!(
1520        test_less_equal_integer,
1521        make_less_equal_integer,
1522        parse_less_equal_integer,
1523        Register::local(1).unwrap(),
1524        Register::local(2).unwrap(),
1525        Register::local(3).unwrap()
1526    );
1527
1528    test_instr!(
1529        test_less_equal_text,
1530        make_less_equal_text,
1531        parse_less_equal_text,
1532        Register::local(1).unwrap(),
1533        Register::local(2).unwrap(),
1534        Register::local(3).unwrap()
1535    );
1536
1537    test_instr!(
1538        test_less_integer,
1539        make_less_integer,
1540        parse_less_integer,
1541        Register::local(1).unwrap(),
1542        Register::local(2).unwrap(),
1543        Register::local(3).unwrap()
1544    );
1545
1546    test_instr!(
1547        test_less_text,
1548        make_less_text,
1549        parse_less_text,
1550        Register::local(1).unwrap(),
1551        Register::local(2).unwrap(),
1552        Register::local(3).unwrap()
1553    );
1554
1555    test_instr!(
1556        test_load_array,
1557        make_load_array,
1558        parse_load_array,
1559        Register::local(1).unwrap(),
1560        Register::local(2).unwrap(),
1561        Register::local(3).unwrap()
1562    );
1563
1564    test_instr!(
1565        test_load_constant,
1566        make_load_constant,
1567        parse_load_constant,
1568        Register::local(1).unwrap(),
1569        12345
1570    );
1571
1572    test_instr!(
1573        test_load_integer,
1574        make_load_integer,
1575        parse_load_integer,
1576        Register::local(1).unwrap(),
1577        12345
1578    );
1579
1580    test_instr!(
1581        test_load_register_ptr,
1582        make_load_register_ptr,
1583        parse_load_register_ptr,
1584        Register::local(1).unwrap(),
1585        ExprType::Double,
1586        Register::local(2).unwrap()
1587    );
1588
1589    test_instr!(
1590        test_modulo_double,
1591        make_modulo_double,
1592        parse_modulo_double,
1593        Register::local(1).unwrap(),
1594        Register::local(2).unwrap(),
1595        Register::local(3).unwrap()
1596    );
1597
1598    test_instr!(
1599        test_modulo_integer,
1600        make_modulo_integer,
1601        parse_modulo_integer,
1602        Register::local(1).unwrap(),
1603        Register::local(2).unwrap(),
1604        Register::local(3).unwrap()
1605    );
1606
1607    test_instr!(
1608        test_move,
1609        make_move,
1610        parse_move,
1611        Register::local(1).unwrap(),
1612        Register::local(2).unwrap()
1613    );
1614
1615    test_instr!(
1616        test_multiply_double,
1617        make_multiply_double,
1618        parse_multiply_double,
1619        Register::local(1).unwrap(),
1620        Register::local(2).unwrap(),
1621        Register::local(3).unwrap()
1622    );
1623
1624    test_instr!(
1625        test_multiply_integer,
1626        make_multiply_integer,
1627        parse_multiply_integer,
1628        Register::local(1).unwrap(),
1629        Register::local(2).unwrap(),
1630        Register::local(3).unwrap()
1631    );
1632
1633    test_instr!(
1634        test_negate_double,
1635        make_negate_double,
1636        parse_negate_double,
1637        Register::local(1).unwrap()
1638    );
1639
1640    test_instr!(
1641        test_negate_integer,
1642        make_negate_integer,
1643        parse_negate_integer,
1644        Register::local(1).unwrap()
1645    );
1646
1647    test_instr!(
1648        test_not_equal_boolean,
1649        make_not_equal_boolean,
1650        parse_not_equal_boolean,
1651        Register::local(1).unwrap(),
1652        Register::local(2).unwrap(),
1653        Register::local(3).unwrap()
1654    );
1655
1656    test_instr!(
1657        test_not_equal_double,
1658        make_not_equal_double,
1659        parse_not_equal_double,
1660        Register::local(1).unwrap(),
1661        Register::local(2).unwrap(),
1662        Register::local(3).unwrap()
1663    );
1664
1665    test_instr!(
1666        test_not_equal_integer,
1667        make_not_equal_integer,
1668        parse_not_equal_integer,
1669        Register::local(1).unwrap(),
1670        Register::local(2).unwrap(),
1671        Register::local(3).unwrap()
1672    );
1673
1674    test_instr!(
1675        test_not_equal_text,
1676        make_not_equal_text,
1677        parse_not_equal_text,
1678        Register::local(1).unwrap(),
1679        Register::local(2).unwrap(),
1680        Register::local(3).unwrap()
1681    );
1682
1683    test_instr!(test_nop, make_nop, parse_nop);
1684
1685    test_instr!(
1686        test_power_double,
1687        make_power_double,
1688        parse_power_double,
1689        Register::local(1).unwrap(),
1690        Register::local(2).unwrap(),
1691        Register::local(3).unwrap()
1692    );
1693
1694    test_instr!(
1695        test_power_integer,
1696        make_power_integer,
1697        parse_power_integer,
1698        Register::local(1).unwrap(),
1699        Register::local(2).unwrap(),
1700        Register::local(3).unwrap()
1701    );
1702
1703    test_instr!(test_return, make_return, parse_return);
1704
1705    test_instr!(
1706        test_shift_left,
1707        make_shift_left,
1708        parse_shift_left,
1709        Register::local(1).unwrap(),
1710        Register::local(2).unwrap(),
1711        Register::local(3).unwrap()
1712    );
1713
1714    test_instr!(
1715        test_shift_right,
1716        make_shift_right,
1717        parse_shift_right,
1718        Register::local(1).unwrap(),
1719        Register::local(2).unwrap(),
1720        Register::local(3).unwrap()
1721    );
1722
1723    test_instr!(
1724        test_store_array,
1725        make_store_array,
1726        parse_store_array,
1727        Register::local(1).unwrap(),
1728        Register::local(2).unwrap(),
1729        Register::local(3).unwrap()
1730    );
1731
1732    test_instr!(
1733        test_subtract_double,
1734        make_subtract_double,
1735        parse_subtract_double,
1736        Register::local(1).unwrap(),
1737        Register::local(2).unwrap(),
1738        Register::local(3).unwrap()
1739    );
1740
1741    test_instr!(
1742        test_subtract_integer,
1743        make_subtract_integer,
1744        parse_subtract_integer,
1745        Register::local(1).unwrap(),
1746        Register::local(2).unwrap(),
1747        Register::local(3).unwrap()
1748    );
1749
1750    test_instr!(test_upcall, make_upcall, parse_upcall, 12345, Register::local(3).unwrap());
1751
1752    test_instr!(
1753        test_upcall_async,
1754        make_upcall_async,
1755        parse_upcall_async,
1756        12345,
1757        Register::local(3).unwrap()
1758    );
1759
1760    #[test]
1761    fn test_exit_code_try_ok() {
1762        assert_eq!(ExitCode(0), ExitCode::try_from(0).unwrap());
1763        assert_eq!(ExitCode(127), ExitCode::try_from(127).unwrap());
1764        assert!(ExitCode::try_from(0).unwrap().is_success());
1765        assert!(!ExitCode::try_from(127).unwrap().is_success());
1766    }
1767
1768    #[test]
1769    fn test_exit_code_try_errors() {
1770        assert!(ExitCode::try_from(-1).is_err());
1771        assert!(ExitCode::try_from(128).is_err());
1772    }
1773
1774    #[test]
1775    fn test_packed_array_type_round_trip() {
1776        for subtype in [ExprType::Boolean, ExprType::Double, ExprType::Integer, ExprType::Text] {
1777            for ndims in [1, 2, 5, 15] {
1778                let packed = PackedArrayType::new(subtype, ndims).unwrap();
1779                assert_eq!(subtype, packed.subtype());
1780                assert_eq!(ndims, usize::from(packed.ndims()));
1781            }
1782        }
1783    }
1784
1785    #[test]
1786    fn test_packed_array_type_display() {
1787        let p = PackedArrayType::new(ExprType::Integer, 2).unwrap();
1788        assert_eq!("[2]%", format!("{}", p));
1789        let p = PackedArrayType::new(ExprType::Text, 1).unwrap();
1790        assert_eq!("[1]$", format!("{}", p));
1791    }
1792
1793    #[test]
1794    fn test_var_arg_tag_ok() {
1795        for sep in [ArgSep::As, ArgSep::End, ArgSep::Long, ArgSep::Short] {
1796            for vat in [
1797                VarArgTag::Missing(sep),
1798                VarArgTag::Pointer(sep),
1799                VarArgTag::Immediate(sep, ExprType::Boolean),
1800                VarArgTag::Immediate(sep, ExprType::Double),
1801                VarArgTag::Immediate(sep, ExprType::Integer),
1802                VarArgTag::Immediate(sep, ExprType::Text),
1803            ] {
1804                assert_eq!(vat, VarArgTag::parse_u64(u64::from(VarArgTag::make_u16(vat))).unwrap());
1805            }
1806        }
1807    }
1808
1809    #[test]
1810    fn test_var_arg_tag_errors() {
1811        // Larger than 12 bits.
1812        VarArgTag::parse_u64(1 << 12).unwrap_err();
1813
1814        // Invalid tag type.
1815        VarArgTag::parse_u64(0x00000500).unwrap_err();
1816
1817        // Missing tag with invalid payload.
1818        VarArgTag::parse_u64(0x00000001).unwrap_err();
1819
1820        // Missing tag with invalid separator.
1821        VarArgTag::parse_u64(0x00000040).unwrap_err();
1822
1823        // ExprType tag with invalid payload.
1824        VarArgTag::parse_u64(0x00000104).unwrap_err();
1825
1826        // ExprType tag with invalid separator.
1827        VarArgTag::parse_u64(0x00000140).unwrap_err();
1828
1829        // Pointer tag with invalid payload.
1830        VarArgTag::parse_u64(0x00000201).unwrap_err();
1831
1832        // Pointer tag with invalid separator.
1833        VarArgTag::parse_u64(0x00000240).unwrap_err();
1834    }
1835}