jump.rsannotatedjump.rssource175 lines · 4.7 KB · raw
1use crate::core::instructions::InstructionExecutor;
2use crate::core::{IndexRegister, Register16, Registers};
3use crate::traits::BusInterface;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6enum JumpCondition {
7    NonZero,
8    Zero,
9    NoCarry,
10    Carry,
11    ParityOdd,
12    ParityEven,
13    Positive,
14    Negative,
15}
16
17impl JumpCondition {
18    fn from_opcode(opcode: u8) -> Self {
19        match opcode & 0x38 {
20            0x00 => Self::NonZero,
21            0x08 => Self::Zero,
22            0x10 => Self::NoCarry,
23            0x18 => Self::Carry,
24            0x20 => Self::ParityOdd,
25            0x28 => Self::ParityEven,
26            0x30 => Self::Positive,
27            0x38 => Self::Negative,
28            _ => unreachable!("value & 0x38 is always one of the above 8 values"),
29        }
30    }
31
32    fn check(self, registers: &Registers) -> bool {
33        match self {
34            Self::NonZero => !registers.f.zero,
35            Self::Zero => registers.f.zero,
36            Self::NoCarry => !registers.f.carry,
37            Self::Carry => registers.f.carry,
38            Self::ParityOdd => !registers.f.overflow,
39            Self::ParityEven => registers.f.overflow,
40            Self::Positive => !registers.f.sign,
41            Self::Negative => registers.f.sign,
42        }
43    }
44}
45
46macro_rules! impl_jr_op {
47    ($name:ident) => {
48        pub(super) fn $name(&mut self) -> u32 {
49            let offset = self.fetch_operand() as i8;
50
51            self.cpu.registers.pc = (i32::from(self.cpu.registers.pc) + i32::from(offset)) as u16;
52
53            12
54        }
55    };
56    ($name:ident, $flag:ident == $flag_value:expr) => {
57        pub(super) fn $name(&mut self) -> u32 {
58            let offset = self.fetch_operand() as i8;
59
60            if self.cpu.registers.f.$flag == $flag_value {
61                self.cpu.registers.pc =
62                    (i32::from(self.cpu.registers.pc) + i32::from(offset)) as u16;
63                12
64            } else {
65                7
66            }
67        }
68    };
69}
70
71impl<B: BusInterface> InstructionExecutor<'_, '_, B> {
72    impl_jr_op!(jr_e);
73    impl_jr_op!(jr_c_e, carry == true);
74    impl_jr_op!(jr_nc_e, carry == false);
75    impl_jr_op!(jr_z_e, zero == true);
76    impl_jr_op!(jr_nz_e, zero == false);
77
78    pub(super) fn jp_nn(&mut self) -> u32 {
79        let address = self.fetch_operand_u16();
80        self.cpu.registers.pc = address;
81
82        10
83    }
84
85    pub(super) fn jp_cc_nn(&mut self, opcode: u8) -> u32 {
86        let condition = JumpCondition::from_opcode(opcode);
87        let address = self.fetch_operand_u16();
88
89        if condition.check(&self.cpu.registers) {
90            self.cpu.registers.pc = address;
91        }
92
93        10
94    }
95
96    pub(super) fn jp_hl(&mut self, index: Option<IndexRegister>) -> u32 {
97        let register = index.map_or(Register16::HL, IndexRegister::into);
98        let address = register.read_from(&self.cpu.registers);
99
100        self.cpu.registers.pc = address;
101
102        4
103    }
104
105    pub(super) fn djnz_e(&mut self) -> u32 {
106        let offset = self.fetch_operand() as i8;
107
108        let b = self.cpu.registers.b;
109        self.cpu.registers.b = b.wrapping_sub(1);
110
111        if b != 1 {
112            self.cpu.registers.pc = (i32::from(self.cpu.registers.pc) + i32::from(offset)) as u16;
113            13
114        } else {
115            8
116        }
117    }
118
119    pub(super) fn call_nn(&mut self) -> u32 {
120        let address = self.fetch_operand_u16();
121
122        self.push_stack(self.cpu.registers.pc);
123        self.cpu.registers.pc = address;
124
125        17
126    }
127
128    pub(super) fn call_cc_nn(&mut self, opcode: u8) -> u32 {
129        let condition = JumpCondition::from_opcode(opcode);
130        let address = self.fetch_operand_u16();
131
132        if condition.check(&self.cpu.registers) {
133            self.push_stack(self.cpu.registers.pc);
134            self.cpu.registers.pc = address;
135
136            17
137        } else {
138            10
139        }
140    }
141
142    pub(super) fn ret(&mut self) -> u32 {
143        self.cpu.registers.pc = self.pop_stack();
144
145        10
146    }
147
148    pub(super) fn ret_cc(&mut self, opcode: u8) -> u32 {
149        let condition = JumpCondition::from_opcode(opcode);
150
151        if condition.check(&self.cpu.registers) {
152            self.cpu.registers.pc = self.pop_stack();
153            11
154        } else {
155            5
156        }
157    }
158
159    // On systems that don't use the special RETI hardware line, RETI and RETN behave identically
160    pub(super) fn reti_retn(&mut self) -> u32 {
161        self.cpu.registers.pc = self.pop_stack();
162        self.cpu.registers.iff1 = self.cpu.registers.iff2;
163
164        14
165    }
166
167    pub(super) fn rst_p(&mut self, opcode: u8) -> u32 {
168        let address = opcode & 0x38;
169
170        self.push_stack(self.cpu.registers.pc);
171        self.cpu.registers.pc = address.into();
172
173        11
174    }
175}