Skip to main content

rysk_tools/
instruction.rs

1#![allow(clippy::unreadable_literal)]
2use rysk_tools_macro::base_instructions;
3
4#[derive(Debug, Clone)]
5pub enum MachineInstruction {
6    Normal([u8; 4]),
7    Compressed([u8; 2])
8}
9
10#[derive(Copy, Clone, Debug)]
11pub enum Instruction {
12    BaseInstruction(BaseInstruction),
13    PseudoInstruction(PseudoInstruction)
14}
15impl Instruction {
16    // Should be in-place in the future
17    pub fn make_pseudo(instructions: &mut [Self]) -> (Self, &mut [Self]) {
18        (instructions[0], &mut instructions[1..])
19    }
20}
21
22/// Extract the destination register index from an instruction
23macro_rules! destination {
24    ($instruction:ident) => {
25        ((($instruction[0] & 0x80) >> 7) | (($instruction[1] & 0x0F) << 1)) as _
26    };
27}
28/// Extract the first source register index from an instruction
29macro_rules! source1 {
30    ($instruction:ident) => {
31        ((($instruction[1] & 0x80) >> 7) | (($instruction[2] & 0x0F) << 1)) as _
32    };
33}
34/// Extract the second source register index from an instruction
35macro_rules! source2 {
36    ($instruction:ident) => {
37        ((($instruction[2] & 0xF0) >> 4) | (($instruction[3] & 0x01) << 4)) as _
38    };
39}
40
41macro_rules! immediate_i {
42    ($instruction:ident) => {
43        {
44            let extra = if $instruction[3] & 0x80 != 0 { 0xFF } else { 0 };
45            i32::from_le_bytes([(($instruction[2] & 0xF0) >> 4) | (($instruction[3] & 0x0F) << 4), (($instruction[3] & 0xF0) >> 4) | (extra & 0xF0), extra, extra])
46        }
47    };
48}
49macro_rules! immediate_s {
50    ($instruction:ident) => {
51        {
52            let extra = if $instruction[3] & 0x80 != 0 { 0xFF } else { 0 };
53            i32::from_le_bytes([(($instruction[0] & 0x80) >> 7) | (($instruction[1] & 0x0F) << 1) | (($instruction[3] & 0x0E) << 4), (($instruction[3] & 0xF0) >> 4) | (extra & 0xF0), extra, extra])
54        }
55    };
56}
57macro_rules! immediate_b {
58    ($instruction:ident) => {
59        {
60            let extra = if $instruction[3] & 0x80 != 0 { 0xFF } else { 0 };
61            i32::from_le_bytes([
62                (($instruction[1] & 0xF) << 1) | (($instruction[3] & 0x0E) << 4),
63                (($instruction[3] & 0x70) >> 4) | (($instruction[0] & 0x80) >> 4) | (($instruction[3] & 0x80) >> 3) | (extra & 0xE0),
64                extra,
65                extra
66            ])
67        }
68    };
69}
70macro_rules! immediate_u {
71    ($instruction:ident) => {
72        i32::from_le_bytes([0, $instruction[1] & 0xF0, $instruction[2], $instruction[3]])
73    };
74}
75macro_rules! immediate_j {
76    ($instruction:ident) => {
77        {
78            let signed = $instruction[3] & 0x80 != 0;
79            i32::from_le_bytes([
80                (($instruction[2] & 0xE0) >> 4) // 1-3
81                    | (($instruction[3] & 0x0F) << 4), // 4-7
82                (($instruction[3] & 0x70) >> 4) // 8-10
83                    | (($instruction[2] & 0x10) >> 1) // 11
84                    | ($instruction[1] & 0xF0), // 12-15
85                ($instruction[2] & 0x0F) // 16-19
86                    | (($instruction[3] & 0x80) >> 3) // 20
87                    | if signed {0xE0} else {0},
88                    if signed {0xFF} else {0}
89            ])
90        }
91    };
92}
93
94// The heavy lifting is done by a proc macro as there is a lot of very repetitive code otherwise
95base_instructions!{
96    LUI -> U(0b0110111) { "Load Upper Immediate" },
97    AUIPC -> U(0b0010111) { "Add Upper Immediate to Program Counter" },
98
99    JAL -> J(0b1101111) { "Jump and Link" },
100    JALR -> I(0b1100111, 0b000) { "Jump and Link Register" },
101    BEQ -> B(0b1100011, 0b000) { "Branch if Equal" },
102    BNE -> B(0b1100011, 0b001) { "Branch if Not Equal" },
103    BLT -> B(0b1100011, 0b100) { "Branch if Less Than" },
104    BGE -> B(0b1100011, 0b101) { "Branch if Greater Than or Equal" },
105    BLTU -> B(0b1100011, 0b110) { "Branch if Less Than Unsigned" },
106    BGEU -> B(0b1100011, 0b111) { "Branch if Greater Than or Equal Unsigned" },
107
108    LB -> I(0b0000011, 0b000) { "Load Byte" },
109    LH -> I(0b0000011, 0b001) { "Load Half" },
110    LW -> I(0b0000011, 0b010) { "Load Word" },
111    LBU -> I(0b0000011, 0b100) { "Load Byte Unsigned" },
112    LHU -> I(0b0000011, 0b101) { "Load Half Unsigned" },
113    SB -> S(0b0100011, 0b000) { "Store Byte" },
114    SH -> S(0b0100011, 0b001) { "Store Half" },
115    SW -> S(0b0100011, 0b010) { "Store Word" },
116
117    ADDI -> I(0b0010011, 0b000) { "Add Immediate" },
118    SLTI -> I(0b0010011, 0b010) { "Set if Less Than Immediate" },
119    SLTIU -> I(0b0010011, 0b011) { "Set if Less Than Immediate Unsigned" },
120    XORI -> I(0b0010011, 0b100) { "Exclusive Or Immediate" },
121    ORI -> I(0b0010011, 0b110) { "Or Immediate" },
122    ANDI -> I(0b0010011, 0b111) { "And Immediate" },
123
124    SLLI -> SH(0b0010011, 0b001, 0b00000) { "Shift Left Logical Immediate" },
125    SRLI -> SH(0b0010011, 0b101, 0b00000) { "Shift Right Logical Immediate" },
126    SRAI -> SH(0b0010011, 0b101, 0b01000) { "Shift Right Arithmetic Immediate" },
127
128    ADD -> R(0b0110011, 0b000, 0b0000000) { "Add" },
129    SUB -> R(0b0110011, 0b000, 0b0100000) { "Subtract" },
130    SLL -> R(0b0110011, 0b001, 0b0000000) { "Shift Left Logical" },
131    SLT -> R(0b0110011, 0b010, 0b0000000) { "Set if Less Than" },
132    SLTU -> R(0b0110011, 0b011, 0b0000000) { "Set if Less Than Unsigned" },
133    XOR -> R(0b0110011, 0b100, 0b0000000) { "Exclusive Or" },
134    SRL -> R(0b0110011, 0b101, 0b0100000) { "Shift Right Logical" },
135    SRA -> R(0b0110011, 0b101, 0b0100000) { "Shift Right Arithmetic" },
136    OR -> R(0b0110011, 0b110, 0b0000000) { "Or" },
137    AND -> R(0b0110011, 0b111, 0b0000000) { "And" },
138
139    FENCE -> I(0b1110011, 0b000) { "Fence" },
140}
141
142#[derive(Copy, Clone, Debug)]
143pub enum PseudoInstruction {
144
145}