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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
use self::super::super::isa::{GeneralPurposeRegisterBank, SpecialPurposeRegister};
use self::super::{MicroOpPerformError, MicroOp};
use self::super::super::vm::{Memory, Ports};


const FLAG_BIT_ZERO: usize = 0; // Zero flag
const FLAG_BIT_CARRY: usize = 1; // Carry flag
const FLAG_BIT_PARITY: usize = 2; // Parity (even number of set bits)
const FLAG_BIT_EQUALS: usize = 3; // Equals flag
const FLAG_BIT_GREATER: usize = 4; // Greater than

const FLAG_MASK_ZERO: u8 = 1u8 << FLAG_BIT_ZERO;
const FLAG_MASK_CARRY: u8 = 1u8 << FLAG_BIT_CARRY;
const FLAG_MASK_PARITY: u8 = 1u8 << FLAG_BIT_PARITY;
const FLAG_MASK_EQUALS: u8 = 1u8 << FLAG_BIT_EQUALS;
const FLAG_MASK_GREATER: u8 = 1u8 << FLAG_BIT_GREATER;

const FLAG_CLEARFLAGS_MASK_ALU_OP: u8 = !(FLAG_MASK_ZERO | FLAG_MASK_CARRY | FLAG_MASK_PARITY);
const FLAG_CLEARFLAGS_MASK_COMP: u8 = !(FLAG_MASK_ZERO | FLAG_MASK_PARITY | FLAG_MASK_EQUALS | FLAG_MASK_GREATER);


static VALID_IS_OK_VALUES: &[u8] = &[0, 1];


impl MicroOp {
    /// Perform this μOp
    ///
    /// The `Ok(..)` return value indicates whether to continue execution (i.e. not halt)
    ///
    /// # Examples
    ///
    /// ```
    /// # use pir_8_emu::isa::{GeneralPurposeRegister, SpecialPurposeRegister};
    /// # use pir_8_emu::vm::{Memory, Ports};
    /// # use pir_8_emu::micro::MicroOp;
    /// # let (mut memory, mut ports, mut registers, mut pc, mut sp, mut adr, mut ins) =
    /// #     (Memory::new(), Ports::new(), GeneralPurposeRegister::defaults(),
    /// #      SpecialPurposeRegister::new("Program Counter", "PC"), SpecialPurposeRegister::new("Stack Pointer", "SP"),
    /// #      SpecialPurposeRegister::new("Memory Address", "ADR"), SpecialPurposeRegister::new("Instruction", "INS"));
    /// memory[0x1A00] = 0x69;
    ///
    /// let mut stack = vec![0x1A, 0x00];
    /// assert_eq!(MicroOp::AdrWrite.perform(&mut stack, &mut memory, &mut ports, &mut registers,
    ///                                      &mut pc, &mut sp, &mut adr, &mut ins),
    ///            Ok(true));
    /// assert_eq!(*adr, 0x1A00);
    /// assert_eq!(stack, &[]);
    ///
    /// assert_eq!(MicroOp::FetchAddress.perform(&mut stack, &mut memory, &mut ports, &mut registers,
    ///                                          &mut pc, &mut sp, &mut adr, &mut ins),
    ///            Ok(true));
    /// assert_eq!(stack, &[0x69]);
    ///
    /// assert_eq!(MicroOp::LoadInstruction.perform(&mut stack, &mut memory, &mut ports, &mut registers,
    ///                                             &mut pc, &mut sp, &mut adr, &mut ins),
    ///            Ok(true));
    /// assert_eq!(stack, &[]);
    /// assert_eq!(*ins, 0x69);
    /// ```
    pub fn perform(self, stack: &mut Vec<u8>, memory: &mut Memory, ports: &mut Ports, registers: &mut GeneralPurposeRegisterBank,
                   pc: &mut SpecialPurposeRegister<u16>, sp: &mut SpecialPurposeRegister<u16>, adr: &mut SpecialPurposeRegister<u16>,
                   ins: &mut SpecialPurposeRegister<u8>)
                   -> Result<bool, MicroOpPerformError> {
        match self {
            MicroOp::Nop => {}
            MicroOp::Halt => return Ok(false),
            MicroOp::LoadInstruction => {
                let byte = stack.pop().ok_or(MicroOpPerformError::MicrostackUnderflow)?;

                **ins = byte;
            }

            MicroOp::AdrWrite => {
                let address = pop_address(stack)?;

                **adr = address;
            }
            MicroOp::AdrRead => {
                push_address(stack, **adr);
            }

            MicroOp::StackPush => {
                let byte = stack.pop().ok_or(MicroOpPerformError::MicrostackUnderflow)?;

                **sp = sp.wrapping_sub(1);
                memory[**sp] = byte;
            }
            MicroOp::StackPop => {
                let byte = memory[**sp];
                **sp = sp.wrapping_add(1);

                stack.push(byte);
            }

            MicroOp::Alu(op) => {
                let flags = stack.pop().ok_or(MicroOpPerformError::MicrostackUnderflow)?;
                let rhs = stack.pop().ok_or(MicroOpPerformError::MicrostackUnderflow)?;
                let lhs = stack.pop().ok_or(MicroOpPerformError::MicrostackUnderflow)?;

                let mut carry = (flags & FLAG_MASK_CARRY) != 0;
                let result = op.perform(lhs, rhs, &mut carry);

                let flags = (flags & FLAG_CLEARFLAGS_MASK_ALU_OP)         |
                            if carry { FLAG_MASK_CARRY } else { 0b00000 } | // forcebreak
                            s_reg_flags(result);

                stack.push(result);
                stack.push(flags);
            }

            MicroOp::PortIn => {
                let port = stack.pop().ok_or(MicroOpPerformError::MicrostackUnderflow)?;

                let data = ports.read(port);

                stack.push(data);
            }
            MicroOp::PortOut => {
                let port = stack.pop().ok_or(MicroOpPerformError::MicrostackUnderflow)?;
                let data = stack.pop().ok_or(MicroOpPerformError::MicrostackUnderflow)?;

                ports.write(port, data);
            }

            MicroOp::Compare => {
                let flags = stack.pop().ok_or(MicroOpPerformError::MicrostackUnderflow)?;
                let rhs = stack.pop().ok_or(MicroOpPerformError::MicrostackUnderflow)?;
                let lhs = stack.pop().ok_or(MicroOpPerformError::MicrostackUnderflow)?;

                let flags = (flags & FLAG_CLEARFLAGS_MASK_COMP)                   |
                            (if lhs == rhs { FLAG_MASK_EQUALS } else { 0b00000 }) |
                            (if lhs > rhs { FLAG_MASK_GREATER } else { 0b00000 }) | // forcebreak
                            s_reg_flags(lhs);

                stack.push(flags);
            }

            MicroOp::MakeImmediate(b) => stack.push(b),
            MicroOp::LoadImmediate => {
                let byte = memory[**pc];
                **pc = pc.wrapping_add(1);

                stack.push(byte);
            }

            MicroOp::FetchAddress => {
                let byte = memory[**adr];

                stack.push(byte);
            }
            MicroOp::WriteAddress => {
                let byte = stack.pop().ok_or(MicroOpPerformError::MicrostackUnderflow)?;

                memory[**adr] = byte;
            }

            MicroOp::CheckJumpCondition(cond) => {
                let flags = stack.pop().ok_or(MicroOpPerformError::MicrostackUnderflow)?;

                let val = if cond.is_satisfied(flags) { 1 } else { 0 };

                stack.push(val);
            }
            MicroOp::Jump => {
                let is_ok = stack.pop().ok_or(MicroOpPerformError::MicrostackUnderflow)?;

                match is_ok {
                    0 => {}
                    1 => **pc = **adr,
                    _ => return Err(MicroOpPerformError::InvalidMicrostackTop(is_ok, VALID_IS_OK_VALUES)),
                }
            }

            MicroOp::ReadRegister(aaa) => {
                let byte = *registers[aaa as usize];

                stack.push(byte);
            }
            MicroOp::WriteRegister(aaa) => {
                let byte = stack.pop().ok_or(MicroOpPerformError::MicrostackUnderflow)?;

                *registers[aaa as usize] = byte;
            }
        }

        Ok(true)
    }
}


fn s_reg_flags(s: u8) -> u8 {
    (if s == 0 { FLAG_MASK_ZERO } else { 0b00000 })                     | // forcebreak
    (if (s.count_ones() % 2) == 0 { FLAG_MASK_PARITY } else { 0b00000 })
}

fn push_address(stack: &mut Vec<u8>, address: u16) {
    let high_byte = (address >> 8) as u8;
    let low_byte = (address & 0xFF) as u8;

    stack.push(high_byte);
    stack.push(low_byte);
}

fn pop_address(stack: &mut Vec<u8>) -> Result<u16, MicroOpPerformError> {
    let low_byte = stack.pop().ok_or(MicroOpPerformError::MicrostackUnderflow)?;
    let high_byte = stack.pop().ok_or(MicroOpPerformError::MicrostackUnderflow)?;

    Ok(((high_byte as u16) << 8) | (low_byte as u16))
}