Skip to content

Commit

Permalink
Implemented RLCA, RRCA, SCF and fixed *a lot* of opcodes...
Browse files Browse the repository at this point in the history
also fixed up a few of `json.rs`'s issues
  • Loading branch information
velllu committed Sep 12, 2024
1 parent b996b00 commit b1f49ca
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 35 deletions.
13 changes: 5 additions & 8 deletions examples/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,21 +91,18 @@ fn run_test(schemas: Vec<JsonSchema>) {
gameboy.flags = register_schema_to_flags(&schema.initial);

// Running the test rom
loop {
gameboy.step();

// until we find a NOP instruction
if gameboy.bus[gameboy.registers.pc] == 0 {
break;
}
}
gameboy.step();

let mut were_registers_wrong = false;
let mut were_flags_wrong = false;

let final_registers = register_schema_to_registers(&schema.final_);
let final_flags = register_schema_to_flags(&schema.final_);

// These are not implemented yet
gameboy.flags.substraction = false;
gameboy.flags.half_carry = false;

if gameboy.registers == final_registers {
print!("{}", format!(" ✔ Registers are correct ").green());
} else {
Expand Down
8 changes: 7 additions & 1 deletion src/cpu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,16 @@ pub struct Cpu {
/// IME, standing for "Interrupt Master Enable" is basically a switch on whether
/// interrupts should be handled or not
pub ime: bool,

/// Wheter or not the CPU is halted
pub halt: bool,
}

impl Cpu {
pub(crate) fn new() -> Self {
Self { ime: false }
Self {
ime: false,
halt: false,
}
}
}
101 changes: 80 additions & 21 deletions src/cpu/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use crate::{
bus::Bus,
common::{merge_two_u8s_into_u16, split_u16_into_two_u8s, Bit},
common::{merge_two_u8s_into_u16, split_u16_into_two_u8s},
flags::Flags,
registers::Registers,
};
Expand All @@ -22,8 +22,14 @@ impl Cpu {
regs: &mut Registers,
bus: &mut Bus,
) -> (Bytes, Cycles) {
if self.halt {
self.halt = false;
return (0, 1);
}

// TODO: Fix timing on instruction with register `6`, they should have a clock more
match opcode {
// Instruction `NOP`
0x00 => (1, 0),

// Instruction `LD rr, immediate data` - 00rr0001
Expand Down Expand Up @@ -87,8 +93,13 @@ impl Cpu {
}

// Instruction `RLCA r` - 00000111
// Exactly like the CB instruction `RLC a` which is the same opcode
0x07 => self.interpret_cb_opcode(opcode, flags, regs, bus),
// Exactly like the CB instruction `RLC a` which is the same opcode but resets
// zero flag
0x07 => {
self.interpret_cb_opcode(opcode, flags, regs, bus);
flags.zero = false;
(1, 1)
}

// Instruction `LD (immediate data), SP` - 00001000
// Load the lower part of SP into immediate data address and the higher part
Expand All @@ -108,7 +119,10 @@ impl Cpu {
0x09 | 0x19 | 0x29 | 0x39 => {
let register_r = regs.get_register_couple(opcode >> 4);
let register_hl = merge_two_u8s_into_u16(regs.h, regs.l);
(regs.h, regs.l) = split_u16_into_two_u8s(register_hl.wrapping_add(register_r));
let (result, has_oveflown) = register_hl.overflowing_add(register_r);

flags.carry = has_oveflown;
(regs.h, regs.l) = split_u16_into_two_u8s(result);

(1, 2)
}
Expand All @@ -122,13 +136,21 @@ impl Cpu {
(1, 2)
}

// Instruction `RRCA` - 00001111
// Exactly like the CB instruction `RRC a` which is the same opcode but resets
// zero flag
0x0F => {
self.interpret_cb_opcode(opcode, flags, regs, bus);
flags.zero = false;
(1, 1)
}

// Instruction `RLA` - 00010111
// Shift register A left by one and set lower bit to carry flag
// Exactly like the CB instruction `RL a` which is the same opcode but resets
// zero flag
0x17 => {
let mut new_value = regs.a << 1;
new_value.set_bit(0, flags.carry);
regs.a = new_value;

self.interpret_cb_opcode(opcode, flags, regs, bus);
flags.zero = false;
(1, 1)
}

Expand All @@ -142,11 +164,11 @@ impl Cpu {
}

// Instruction `RRA` - 00011111
// Rotate register A to the right by one and set register 7 to carry flag
// Exactly like the CB instruction `RR a` which is the same opcode but resets
// zero flag
0x1F => {
regs.a = regs.a.rotate_right(1);
regs.a.set_bit(7, flags.carry);

self.interpret_cb_opcode(opcode, flags, regs, bus);
flags.zero = false;
(1, 1)
}

Expand Down Expand Up @@ -177,6 +199,14 @@ impl Cpu {
(1, 1)
}

// Instruction `SCF` - 00110111
// Set carry flag
0x37 => {
flags.carry = true;

(1, 1)
}

// Instruction `CCF` - 00111111
// Flip carry flag
0x3F => {
Expand All @@ -187,7 +217,11 @@ impl Cpu {

// Instruction Halt
// This is in the middle of the ld instructions, TODO: implement this fully
0x76 => (0, 1),
0x76 => {
self.halt = true;

(1, 1)
}

// Instruction `LD x, y` - 01xxxyyy
// Load the value of register y into register x
Expand Down Expand Up @@ -372,7 +406,7 @@ impl Cpu {
self.interpret_opcode(RET, flags, regs, bus);
self.ime = true;

(1, 4)
(0, 4)
}

// Instruction `LD io address, register A` - 11100000
Expand All @@ -397,8 +431,16 @@ impl Cpu {
// Instruction `ADD SP, immediate data` - 11101000
// Add immediate data to register SP
0xE8 => {
let immediate_data = bus.next_one(regs) as i8;
regs.sp = add_i8_to_u16(regs.sp, immediate_data);
let immediate_data = bus.next_one(regs);
let immediate_data_signed = immediate_data as i8;

// In this instruction, the carry flag is set if the lower bits of sp and
// the unsigned byte of immediate data overflows
let p = (regs.sp & 0x00FF) as u8;
flags.carry = p.checked_add(immediate_data).is_none();

flags.zero = false;
regs.sp = add_i8_to_u16(regs.sp, immediate_data_signed);

(2, 4)
}
Expand Down Expand Up @@ -449,8 +491,17 @@ impl Cpu {
// Instruction `LD HL, SP + immediate data` - 11111000
// Add SP to signed immediate data and copy the result to register HL
0xF8 => {
let immediate_data = bus.next_one(regs) as i8;
let new_value = add_i8_to_u16(regs.sp, immediate_data);
let immediate_data = bus.next_one(regs);
let immediate_data_signed = immediate_data as i8;

// In this instruction, the carry flag is set if the lower bits of sp and
// the unsigned byte of immediate data overflows
let p = (regs.sp & 0x00FF) as u8;
flags.carry = p.checked_add(immediate_data).is_none();

flags.zero = false;

let new_value = add_i8_to_u16(regs.sp, immediate_data_signed);
(regs.h, regs.l) = split_u16_into_two_u8s(new_value);

(2, 3)
Expand Down Expand Up @@ -515,9 +566,17 @@ fn add_i8_to_u16(u16: u16, i8: i8) -> u16 {
fn do_operation(operation_num: u8, num1: u8, num2: u8, flags: &Flags) -> (u8, bool) {
match operation_num & 0b00000111 {
0 => num1.overflowing_add(num2),
1 => num1.overflowing_add(num2.wrapping_add(flags.carry as u8)),
1 => {
let (result_c, has_overflown_c) = num2.overflowing_add(flags.carry as u8);
let (result, has_overflown) = num1.overflowing_add(result_c);
(result, has_overflown || has_overflown_c)
}
2 => num1.overflowing_sub(num2),
3 => num1.overflowing_sub(num2.wrapping_sub(flags.carry as u8)),
3 => {
let (result_c, has_overflown_c) = num2.overflowing_add(flags.carry as u8);
let (result, has_overflown) = num1.overflowing_sub(result_c);
(result, has_overflown || has_overflown_c)
}
4 => (num1 & num2, false),
5 => (num1 ^ num2, false),
6 => (num1 | num2, false),
Expand Down
31 changes: 26 additions & 5 deletions src/cpu/opcodes_cb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,32 @@ impl Cpu {
// to register r's bit 7
0x00..=0x07 => {
let register_r = regs.get_register(opcode, bus);
let mut new_value = register_r.rotate_left(1);
let mut new_value = register_r << 1;
let bit_7 = register_r.get_bit(7);

new_value.set_bit(0, bit_7);
flags.zero = new_value == 0;
flags.carry = bit_7;

regs.set_register(opcode, new_value, bus);

(1, 1)
}

// Instruction `RRC r` - 00001rrr
// Rotate the contents of register r to the right and set bit 7 and carry flag
// to register r's bit 0
0x08..=0x0F => {
let register_r = regs.get_register(opcode, bus);
let mut new_value = register_r >> 1;
let bit_0 = register_r.get_bit(0);

new_value.set_bit(0, bit_0);
new_value.set_bit(7, bit_0);
flags.zero = new_value == 0;
flags.carry = bit_0;

regs.set_register(opcode, new_value, bus);

(1, 1)
}

Expand All @@ -33,6 +53,7 @@ impl Cpu {
new_value.set_bit(0, flags.carry);

flags.zero = new_value == 0;
flags.carry = register_r.get_bit(7);
regs.set_register(opcode, new_value, bus);

(1, 1)
Expand All @@ -46,6 +67,7 @@ impl Cpu {
new_value.set_bit(7, flags.carry);

flags.zero = new_value == 0;
flags.carry = register_r.get_bit(0);
regs.set_register(opcode, new_value, bus);

(1, 1)
Expand Down Expand Up @@ -89,6 +111,7 @@ impl Cpu {
let new_value = high | low;

flags.zero = new_value == 0;
flags.carry = false;
regs.set_register(opcode, new_value, bus);

(1, 1)
Expand All @@ -113,7 +136,7 @@ impl Cpu {
let number = (opcode >> 3) & 0b00000111;
let register = regs.get_register(opcode, bus);

flags.zero = register.get_bit(number);
flags.zero = !register.get_bit(number);

(1, 1)
}
Expand Down Expand Up @@ -141,8 +164,6 @@ impl Cpu {

(1, 1)
}

_ => panic!("Unimplemented CB opcode: {:x}", opcode),
}
}
}

0 comments on commit b1f49ca

Please sign in to comment.