summary refs log tree commit diff
path: root/src/instructions.rs
blob: 37ffe921505b948def7e8cd844123ea923f7a2be (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use core::panic;

use crate::interpreter::{MemAddress, StackAddress};
use crate::opcodes::OpCode;

#[derive(Debug)]
pub struct Instruction {
    pub opcode: OpCode,
    pub in_operands: Vec<Operand>,
    pub out_operands: Vec<Operand>,
}

enum Nybble {
    High,
    Low,
}

#[derive(PartialEq, Debug, Clone)]
pub enum Operand {
    Constant(i32),
    PopStack,
    FrameIndirect(StackAddress),
    MemIndirect(MemAddress),
    MemRamIndirect(MemAddress),
}

impl Instruction {
    pub fn read<'a, I>(bytes: &mut I) -> Result<(Instruction, u32), u32>
    where
        I: IntoIterator<Item = &'a u8> + Copy
    {
        let mut bytes = bytes.into_iter().enumerate().peekable();

        let instruction_length = {
            let first_byte = *bytes.peek().unwrap().1;
            match first_byte >> 6 {
                0b00 | 0b01 => 1,
                0b10 => 2,
                0b11 => 4,
                _ => unreachable!()
            }
        };

        let mask = match instruction_length {
            1 => 0,
            2 => 0x8000,
            4 => 0xC0000000,
            _ => panic!("Invalid opcode byte array"),
        };

        let instruction_bytes = (0..4-instruction_length).into_iter().map(|_n| 0).chain(
                (0..instruction_length).into_iter().map(|_n| *bytes.next().unwrap().1)
            ).collect::<Vec<_>>();

        let opcode_num = u32::from_be_bytes(instruction_bytes.clone().try_into().unwrap()) & !mask;
        let opcode = OpCode::get_from_code(opcode_num);
        if let None = opcode {
            return Err(opcode_num);
        }
        let opcode = opcode.unwrap();
        let input_operand_count = OpCode::get_input_arg_count(opcode.clone());
        let output_operand_count = OpCode::get_output_arg_count(opcode.clone());

        let operand_count = input_operand_count + output_operand_count;
        let operand_bytes = (operand_count + 1) / 2;
        let skip_last_half = operand_bytes != operand_count / 2;
        let operand_bytes = (0..operand_bytes).into_iter().map(|_n| *bytes.next().unwrap().1);
        let halfbytes : Vec<_> = operand_bytes.flat_map(|byte| vec![(byte, Nybble::Low), (byte, Nybble::High)]).collect();

        // Just ensure other borrows to the byte array have closed by rebinding it
        // This verifies we're at the operand data position
        let mut data_bytes = bytes;

        let mut operands : Vec<Operand> = halfbytes.iter().map(|(byte, nybble)| {
            let value = match nybble {
                Nybble::High => byte >> 4,
                Nybble::Low => byte & 0x0f,
            };

            match value {
                0 => Operand::Constant(0),
                1 => Operand::Constant(i8::from_be_bytes([*data_bytes.next().unwrap().1]) as i32),
                2 => Operand::Constant(i16::from_be_bytes([*data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1]) as i32),
                3 => Operand::Constant(i32::from_be_bytes([*data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1])),
                4 => panic!("Invalid operand addressing mode: {value:x}"),
                5 => Operand::MemIndirect(u32::from_be_bytes([0, 0, 0, *data_bytes.next().unwrap().1])),
                6 => Operand::MemIndirect(u32::from_be_bytes([0, 0, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1])),
                7 => Operand::MemIndirect(u32::from_be_bytes([*data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1])),
                8 => Operand::PopStack,
                9 => Operand::FrameIndirect(u32::from_be_bytes([0, 0, 0, *data_bytes.next().unwrap().1])),
                0xa => Operand::FrameIndirect(u32::from_be_bytes([0, 0, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1])),
                0xb => Operand::FrameIndirect(u32::from_be_bytes([*data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1])),
                0xc => panic!("Invalid operand addressing mode: {value:x}"),
                0xd => Operand::MemRamIndirect(u32::from_be_bytes([0, 0, 0, *data_bytes.next().unwrap().1])),
                0xe => Operand::MemRamIndirect(u32::from_be_bytes([0, 0, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1])),
                0xf => Operand::MemRamIndirect(u32::from_be_bytes([*data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1])),
                _ => unreachable!()
            }
        }).collect();

        // Should only trim it off if it was all 0
        if skip_last_half && operands.len() > 0 {
            assert!(*operands.last().unwrap() == Operand::Constant(0));
            operands.remove(operands.len() - 1);
        }

        let in_operands = operands[0..input_operand_count].try_into().unwrap();
        let out_operands = operands[input_operand_count..].try_into().unwrap();

        // Return instruction along with the number of bytes we've read
        Ok((Instruction {
            opcode,
            in_operands,
            out_operands,
        }, data_bytes.peek().unwrap().0 as u32))
    }
}