flow.rsannotatedflow.rssource166 lines · 5.2 KB · raw
1use crate::sm83::bus::BusInterface;
2use crate::sm83::{Flags, Sm83};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5enum JumpCondition {
6    Zero,
7    NotZero,
8    Carry,
9    NoCarry,
10}
11
12impl JumpCondition {
13    fn from_opcode(opcode: u8) -> Self {
14        match (opcode >> 3) & 0x3 {
15            0x0 => Self::NotZero,
16            0x1 => Self::Zero,
17            0x2 => Self::NoCarry,
18            0x3 => Self::Carry,
19            _ => unreachable!("value & 0x3 is always <= 0x3"),
20        }
21    }
22
23    fn check(self, flags: Flags) -> bool {
24        match self {
25            Self::Zero => flags.zero,
26            Self::NotZero => !flags.zero,
27            Self::Carry => flags.carry,
28            Self::NoCarry => !flags.carry,
29        }
30    }
31}
32
33impl Sm83 {
34    // JP u16: Unconditional absolute jump
35    pub(super) fn jp_nn<B: BusInterface>(&mut self, bus: &mut B) {
36        self.registers.pc = self.fetch_operand_u16(bus);
37
38        // JP nn takes 4 M-cycles: opcode read + 16-bit operand read + idle cycle
39        bus.idle();
40    }
41
42    // JP HL: Unconditional absolute jump
43    pub(super) fn jp_hl(&mut self) {
44        self.registers.pc = self.registers.hl();
45    }
46
47    // JP cc, u16: Conditional absolute jump
48    pub(super) fn jp_cc_nn<B: BusInterface>(&mut self, bus: &mut B, opcode: u8) {
49        // Target address is always read
50        let address = self.fetch_operand_u16(bus);
51
52        let condition = JumpCondition::from_opcode(opcode);
53        if condition.check(self.registers.f) {
54            self.registers.pc = address;
55            bus.idle();
56        }
57    }
58
59    // JR i8: Unconditional relative jump
60    pub(super) fn jr_e<B: BusInterface>(&mut self, bus: &mut B) {
61        let operand = self.fetch_operand(bus) as i8;
62        self.registers.pc = self.registers.pc.wrapping_add(operand as u16);
63
64        // JR e takes 3 M-cycles: opcode read + operand read + idle cycle
65        bus.idle();
66    }
67
68    // JR cc, i8: Conditional relative jump
69    pub(super) fn jr_cc_e<B: BusInterface>(&mut self, bus: &mut B, opcode: u8) {
70        let operand = self.fetch_operand(bus) as i8;
71
72        let condition = JumpCondition::from_opcode(opcode);
73        if condition.check(self.registers.f) {
74            self.registers.pc = self.registers.pc.wrapping_add(operand as u16);
75            bus.idle();
76        }
77    }
78
79    // CALL u16: Unconditional call
80    pub(super) fn call_nn<B: BusInterface>(&mut self, bus: &mut B) {
81        let address = self.fetch_operand_u16(bus);
82
83        // Idle cycle in between address read and stack push
84        bus.idle();
85
86        self.push_stack_u16(bus, self.registers.pc);
87        self.registers.pc = address;
88    }
89
90    // CALL cc, u16: Conditional call
91    pub(super) fn call_cc_nn<B: BusInterface>(&mut self, bus: &mut B, opcode: u8) {
92        let address = self.fetch_operand_u16(bus);
93
94        let condition = JumpCondition::from_opcode(opcode);
95        if !condition.check(self.registers.f) {
96            return;
97        }
98
99        // Idle cycle in between address read and stack push
100        bus.idle();
101
102        self.push_stack_u16(bus, self.registers.pc);
103        self.registers.pc = address;
104    }
105
106    // RET: Unconditional return
107    pub(super) fn ret<B: BusInterface>(&mut self, bus: &mut B) {
108        self.registers.pc = self.pop_stack_u16(bus);
109        bus.idle();
110    }
111
112    // RET cc: Conditional return
113    pub(super) fn ret_cc<B: BusInterface>(&mut self, bus: &mut B, opcode: u8) {
114        bus.idle();
115
116        let condition = JumpCondition::from_opcode(opcode);
117        if !condition.check(self.registers.f) {
118            return;
119        }
120
121        self.registers.pc = self.pop_stack_u16(bus);
122        bus.idle();
123    }
124
125    // RETI: Return from interrupt handler
126    pub(super) fn reti<B: BusInterface>(&mut self, bus: &mut B) {
127        self.registers.pc = self.pop_stack_u16(bus);
128        self.registers.ime = true;
129        bus.idle();
130    }
131
132    // RST: Restart
133    pub(super) fn rst<B: BusInterface>(&mut self, bus: &mut B, opcode: u8) {
134        bus.idle();
135
136        self.push_stack_u16(bus, self.registers.pc);
137        self.registers.pc = (opcode & 0x38).into();
138    }
139
140    // HALT: Halt the CPU until an interrupt occurs
141    pub(super) fn halt<B: BusInterface>(&mut self, bus: &mut B) {
142        self.state.halted = true;
143
144        // HALT bug: if a HALT instruction is executed while IME=0 and an interrupt is pending, PC does not increment
145        // after the next opcode fetch
146        self.state.halt_bug_triggered = !self.registers.ime && bus.interrupt_pending();
147    }
148
149    // STOP: Perform a CGB speed switch if KEY1 bit 0 is set, otherwise enters an extreme low-power state
150    pub(super) fn stop<B: BusInterface>(&mut self, bus: &mut B) {
151        // STOP always reads the following opcode and just doesn't do anything with it
152        self.fetch_operand(bus);
153
154        if bus.speed_switch_armed() {
155            bus.perform_speed_switch();
156        } else {
157            // TODO properly implement the (very buggy) STOP instruction:
158            // https://gbdev.io/pandocs/Reducing_Power_Consumption.html#using-the-stop-instruction
159            log::warn!(
160                "STOP instruction executed at PC={:04X}; this is probably a bug",
161                self.registers.pc.wrapping_sub(2)
162            );
163            self.state.halted = true;
164        }
165    }
166}