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}