Skip to content

Commit

Permalink
cpu: implement swap
Browse files Browse the repository at this point in the history
  • Loading branch information
choffmann committed May 16, 2024
1 parent e6c0f10 commit e88b2d2
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 7 deletions.
11 changes: 11 additions & 0 deletions gameboy-lib/src/cpu/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ pub enum Instruction {
Inc16(Register), // Increment register
Dec(Register), // Decrement register
Dec16(Register), // Decrement register

Swap(Register), // Swap upper and lower nibbles of register
}

impl Instruction {
Expand All @@ -40,6 +42,15 @@ impl Instruction {

fn from_byte_with_prefix(byte: u8) -> Option<Instruction> {
match byte {
0x37 => Some(Instruction::Swap(Register::A)),
0x30 => Some(Instruction::Swap(Register::B)),
0x31 => Some(Instruction::Swap(Register::C)),
0x32 => Some(Instruction::Swap(Register::D)),
0x33 => Some(Instruction::Swap(Register::E)),
0x34 => Some(Instruction::Swap(Register::H)),
0x35 => Some(Instruction::Swap(Register::L)),
0x36 => Some(Instruction::Swap(Register::HL)),

_ => None,
}
}
Expand Down
145 changes: 138 additions & 7 deletions gameboy-lib/src/cpu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,15 @@ impl Cpu {
None => panic!("[CPU] Invalid instruction 0x{:x}", instruction),
};

self.pc = next_pc;
if next_pc == self.pc {
panic!("[CPU] Instruction did not increment PC");
}

if prefixed {
self.pc = next_pc + 1;
} else {
self.pc = next_pc;
}
}

fn update_flag(&mut self, flag: FlagUpdate) {
Expand Down Expand Up @@ -117,6 +125,7 @@ impl Cpu {

fn execute_prefixed(&mut self, instruction: Instruction) -> u16 {
match instruction {
Instruction::Swap(from) => self.swap(&from),
_ => panic!("[CPU] Not implementet {:?}", instruction),
}
}
Expand All @@ -132,6 +141,27 @@ impl Cpu {
}
}

fn swap(&mut self, from: &Register) -> u16 {
let (value, pc) = self.extract_operand(from);
let upper = value >> 4;
let lower = value << 4;
let result = upper | lower;

if let Register::HL = from {
self.memory
.write(self.registers.get_16(&Register::HL), result);
} else {
self.registers.set(from, result);
}

self.registers.f.zero = result == 0;
self.registers.f.subtract = false;
self.registers.f.half_carry = false;
self.registers.f.carry = false;

return pc;
}

fn alu_operation16<F>(&mut self, instruction: &Instruction, op: F) -> u16
where
F: Fn(u16, u16) -> (u16, Vec<FlagUpdate>),
Expand Down Expand Up @@ -1773,7 +1803,10 @@ mod tests {
// Add HL, HL
cpu.registers.set_16(&Register::HL, 0x9ABC);
cpu.step();
assert_eq!(cpu.registers.get_16(&Register::HL), 0x09ABC_u16.wrapping_add(0x9ABC));
assert_eq!(
cpu.registers.get_16(&Register::HL),
0x09ABC_u16.wrapping_add(0x9ABC)
);
assert_eq!(cpu.registers.f.subtract, false);
assert_eq!(cpu.registers.f.half_carry, true);
assert_eq!(cpu.registers.f.carry, true);
Expand All @@ -1797,7 +1830,10 @@ mod tests {
// Add SP, 0x00
cpu.registers.set_16(&Register::SP, default_sp.clone());
cpu.step();
assert_eq!(cpu.registers.get_16(&Register::SP), default_sp.wrapping_add(0x00));
assert_eq!(
cpu.registers.get_16(&Register::SP),
default_sp.wrapping_add(0x00)
);
assert_eq!(cpu.registers.f.zero, false);
assert_eq!(cpu.registers.f.subtract, false);
assert_eq!(cpu.registers.f.half_carry, false);
Expand All @@ -1806,7 +1842,10 @@ mod tests {
// Add SP, 0x01
cpu.registers.set_16(&Register::SP, default_sp.clone());
cpu.step();
assert_eq!(cpu.registers.get_16(&Register::SP), default_sp.wrapping_add(0x01));
assert_eq!(
cpu.registers.get_16(&Register::SP),
default_sp.wrapping_add(0x01)
);
assert_eq!(cpu.registers.f.zero, false);
assert_eq!(cpu.registers.f.subtract, false);
assert_eq!(cpu.registers.f.half_carry, false);
Expand All @@ -1815,7 +1854,10 @@ mod tests {
// Add SP, 0x02
cpu.registers.set_16(&Register::SP, default_sp.clone());
cpu.step();
assert_eq!(cpu.registers.get_16(&Register::SP), default_sp.wrapping_add(0x02));
assert_eq!(
cpu.registers.get_16(&Register::SP),
default_sp.wrapping_add(0x02)
);
assert_eq!(cpu.registers.f.zero, false);
assert_eq!(cpu.registers.f.subtract, false);
assert_eq!(cpu.registers.f.half_carry, false);
Expand All @@ -1824,7 +1866,10 @@ mod tests {
// Add SP, 0xFF
cpu.registers.set_16(&Register::SP, default_sp.clone());
cpu.step();
assert_eq!(cpu.registers.get_16(&Register::SP), default_sp.wrapping_add(0xFF));
assert_eq!(
cpu.registers.get_16(&Register::SP),
default_sp.wrapping_add(0xFF)
);
assert_eq!(cpu.registers.f.zero, false);
assert_eq!(cpu.registers.f.subtract, false);
assert_eq!(cpu.registers.f.half_carry, false);
Expand Down Expand Up @@ -1882,6 +1927,92 @@ mod tests {

// Dec SP
cpu.step();
assert_eq!(cpu.registers.get_16(&Register::SP), 0x0000_u16.wrapping_sub(1));
assert_eq!(
cpu.registers.get_16(&Register::SP),
0x0000_u16.wrapping_sub(1)
);
}

#[test]
fn execute_swap() {
let mut cpu = Cpu::new();
cpu.registers.set(&Register::A, 0b10101010);
cpu.registers.set(&Register::B, 0b11001100);
cpu.registers.set(&Register::C, 0b11110000);
cpu.registers.set(&Register::D, 0b00001111);
cpu.registers.set(&Register::E, 0b01100010);
cpu.registers.set(&Register::H, 0b00000000);
cpu.registers.set(&Register::L, 0b11111111);
cpu.memory.write(0x00FF, 0b01001101);

cpu.boot(vec![
0xCB, 0x37, 0xCB, 0x30, 0xCB, 0x31, 0xCB, 0x32, 0xCB, 0x33, 0xCB, 0x34, 0xCB, 0x35,
0xCB, 0x36,
]);

// Swap A
cpu.step();
assert_eq!(cpu.registers.get(&Register::A), 0b10101010);
assert_eq!(cpu.registers.f.zero, false);
assert_eq!(cpu.registers.f.subtract, false);
assert_eq!(cpu.registers.f.half_carry, false);
assert_eq!(cpu.registers.f.carry, false);

// Swap B
cpu.step();
assert_eq!(cpu.registers.get(&Register::B), 0b11001100);
assert_eq!(cpu.registers.f.zero, false);
assert_eq!(cpu.registers.f.subtract, false);
assert_eq!(cpu.registers.f.half_carry, false);
assert_eq!(cpu.registers.f.carry, false);

// Swap C
cpu.step();
assert_eq!(cpu.registers.get(&Register::C), 0b00001111);
assert_eq!(cpu.registers.f.zero, false);
assert_eq!(cpu.registers.f.subtract, false);
assert_eq!(cpu.registers.f.half_carry, false);
assert_eq!(cpu.registers.f.carry, false);

// Swap D
cpu.step();
assert_eq!(cpu.registers.get(&Register::D), 0b11110000);
assert_eq!(cpu.registers.f.zero, false);
assert_eq!(cpu.registers.f.subtract, false);
assert_eq!(cpu.registers.f.half_carry, false);
assert_eq!(cpu.registers.f.carry, false);

// Swap E
cpu.step();
assert_eq!(cpu.registers.get(&Register::E), 0b00100110);
assert_eq!(cpu.registers.f.zero, false);
assert_eq!(cpu.registers.f.subtract, false);
assert_eq!(cpu.registers.f.half_carry, false);
assert_eq!(cpu.registers.f.carry, false);

// Swap H
cpu.step();
assert_eq!(cpu.registers.get(&Register::H), 0b00000000);
assert_eq!(cpu.registers.f.zero, true);
assert_eq!(cpu.registers.f.subtract, false);
assert_eq!(cpu.registers.f.half_carry, false);
assert_eq!(cpu.registers.f.carry, false);

// Swap L
cpu.step();
assert_eq!(cpu.registers.get(&Register::L), 0b11111111);
assert_eq!(cpu.registers.f.zero, false);
assert_eq!(cpu.registers.f.subtract, false);
assert_eq!(cpu.registers.f.half_carry, false);
assert_eq!(cpu.registers.f.carry, false);

// Swap (HL)
cpu.registers.set_16(&Register::HL, 0x00FF);
cpu.step();
assert_eq!(cpu.memory.read(0x00FF), 0b11010100);
assert_eq!(cpu.registers.f.zero, false);
assert_eq!(cpu.registers.f.subtract, false);
assert_eq!(cpu.registers.f.half_carry, false);
assert_eq!(cpu.registers.f.carry, false);
}
}

0 comments on commit e88b2d2

Please sign in to comment.