lc3_zkvm/
opcode.rs

1//! LC3 Opcode Module
2//!
3//! This module defines the opcodes for the LC3 (Little Computer 3) Zero-Knowledge Virtual Machine.
4//! It includes enumerations for the opcodes and their associated functions.
5
6/// Represents the LC3 opcodes
7#[allow(non_camel_case_types)]
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum Opcode {
10    /// Branch
11    /// Conditional branch based on condition codes (N, Z, P)
12    /// Format: 0000 NZP PCoffset9
13    OP_BR = 0x0,
14
15    /// Add
16    /// Add two values and store the result
17    /// Format: 0001 DR SR1 000 SR2 (register mode)
18    ///         0001 DR SR1 1 imm5 (immediate mode)
19    OP_ADD = 0x1,
20
21    /// Load
22    /// Load a value from memory into a register
23    /// Format: 0010 DR PCoffset9
24    OP_LD = 0x2,
25
26    /// Store
27    /// Store a value from a register into memory
28    /// Format: 0011 SR PCoffset9
29    OP_ST = 0x3,
30
31    /// Jump to Subroutine
32    /// Jump to a subroutine and save the return address
33    /// Format: 0100 1 PCoffset11 (JSR)
34    ///         0100 000 BaseR 000000 (JSRR)
35    OP_JSR = 0x4,
36
37    /// Bitwise AND
38    /// Perform bitwise AND operation
39    /// Format: 0101 DR SR1 000 SR2 (register mode)
40    ///         0101 DR SR1 1 imm5 (immediate mode)
41    OP_AND = 0x5,
42
43    /// Load Register
44    /// Load a value from memory using a base register and offset
45    /// Format: 0110 DR BaseR offset6
46    OP_LDR = 0x6,
47
48    /// Store Register
49    /// Store a value to memory using a base register and offset
50    /// Format: 0111 SR BaseR offset6
51    OP_STR = 0x7,
52
53    /// Return from Interrupt
54    /// Unused in LC3
55    /// Format: 1000 000000000000
56    OP_RTI = 0x8,
57
58    /// Bitwise NOT
59    /// Perform bitwise NOT operation
60    /// Format: 1001 DR SR 111111
61    OP_NOT = 0x9,
62
63    /// Load Indirect
64    /// Load a value from memory using an address stored in memory
65    /// Format: 1010 DR PCoffset9
66    OP_LDI = 0xA,
67
68    /// Store Indirect
69    /// Store a value to memory using an address stored in memory
70    /// Format: 1011 SR PCoffset9
71    OP_STI = 0xB,
72
73    /// Jump
74    /// Jump to an address specified by a register
75    /// Format: 1100 000 BaseR 000000
76    OP_JMP = 0xC,
77
78    /// Reserved
79    /// Unused opcode
80    /// Format: 1101 000000000000
81    OP_RES = 0xD,
82
83    /// Load Effective Address
84    /// Load a memory address into a register
85    /// Format: 1110 DR PCoffset9
86    OP_LEA = 0xE,
87
88    /// Execute Trap
89    /// Execute a system call
90    /// Format: 1111 0000 trapvect8
91    OP_TRAP = 0xF,
92}
93
94impl Opcode {
95    /// Convert a u16 to an Opcode
96    pub fn from_u16(value: u16) -> Option<Self> {
97        match value {
98            0 => Some(Opcode::OP_BR),
99            1 => Some(Opcode::OP_ADD),
100            2 => Some(Opcode::OP_LD),
101            3 => Some(Opcode::OP_ST),
102            4 => Some(Opcode::OP_JSR),
103            5 => Some(Opcode::OP_AND),
104            6 => Some(Opcode::OP_LDR),
105            7 => Some(Opcode::OP_STR),
106            8 => Some(Opcode::OP_RTI),
107            9 => Some(Opcode::OP_NOT),
108            10 => Some(Opcode::OP_LDI),
109            11 => Some(Opcode::OP_STI),
110            12 => Some(Opcode::OP_JMP),
111            13 => Some(Opcode::OP_RES),
112            14 => Some(Opcode::OP_LEA),
113            15 => Some(Opcode::OP_TRAP),
114            _ => None,
115        }
116    }
117
118    /// Convert an Opcode to a u16
119    pub fn to_u16(self) -> u16 {
120        self as u16
121    }
122}
123
124/// Extract the opcode from a 16-bit instruction
125pub fn extract_opcode(instruction: u16) -> Option<Opcode> {
126    Opcode::from_u16(instruction >> 12)
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132
133    #[test]
134    fn test_opcode_conversion() {
135        assert_eq!(Opcode::from_u16(0), Some(Opcode::OP_BR));
136        assert_eq!(Opcode::from_u16(15), Some(Opcode::OP_TRAP));
137        assert_eq!(Opcode::from_u16(16), None);
138
139        assert_eq!(Opcode::OP_BR.to_u16(), 0);
140        assert_eq!(Opcode::OP_TRAP.to_u16(), 15);
141    }
142
143    #[test]
144    fn test_extract_opcode() {
145        assert_eq!(extract_opcode(0b0001_000_000_000_000), Some(Opcode::OP_ADD));
146        assert_eq!(
147            extract_opcode(0b1111_000_000_000_000),
148            Some(Opcode::OP_TRAP)
149        );
150        assert_eq!(
151            extract_opcode(0b1111_111_111_111_111),
152            Some(Opcode::OP_TRAP)
153        );
154    }
155}