Skip to content

Commit

Permalink
cpu: finished opcodes
Browse files Browse the repository at this point in the history
  • Loading branch information
choffmann committed May 18, 2024
1 parent 31af5a1 commit 7974729
Show file tree
Hide file tree
Showing 7 changed files with 279 additions and 21 deletions.
53 changes: 53 additions & 0 deletions gameboy-lib/src/cpu/command/bit_commands.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use crate::cpu::{instructions::BitInstruction, registers::Register, Cpu};

use super::Command;

pub struct BitCommand<'a> {
instruction: &'a BitInstruction,
cpu: &'a mut Cpu,
}

impl<'a> BitCommand<'a> {
pub fn new(instruction: &'a BitInstruction, cpu: &'a mut Cpu) -> BitCommand<'a> {
BitCommand { instruction, cpu }
}

fn bit(&mut self, bit: &u8, from: &Register) -> u16 {
let value = self.cpu.registers.get(from);
let result = value & (1 << bit);

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

self.cpu.pc.wrapping_add(2)
}

fn res(&mut self, bit: &u8, from: &Register) -> u16 {
let value = self.cpu.registers.get(from);
let result = value & !(1 << bit);

self.cpu.registers.set(from, result);

self.cpu.pc.wrapping_add(2)
}

fn set(&mut self, bit: &u8, from: &Register) -> u16 {
let value = self.cpu.registers.get(from);
let result = value | (1 << bit);

self.cpu.registers.set(from, result);

self.cpu.pc.wrapping_add(2)
}
}

impl Command for BitCommand<'_> {
fn execute(&mut self) -> u16 {
match &self.instruction {
BitInstruction::Bit(bit, from) => self.bit(bit, from),
BitInstruction::Res(bit, from) => self.res(bit, from),
BitInstruction::Set(bit, from) => self.set(bit, from),
}
}
}
49 changes: 49 additions & 0 deletions gameboy-lib/src/cpu/command/call_commands.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use crate::cpu::{instructions::{CallInstruction, FlagCondition}, Cpu};

use super::Command;


pub struct CallCommand<'a> {
instruction: &'a CallInstruction,
cpu: &'a mut Cpu,
}

impl<'a> CallCommand<'a> {
pub fn new(instruction: &'a CallInstruction, cpu: &'a mut Cpu) -> CallCommand<'a> {
CallCommand { instruction, cpu }
}

fn call(&mut self) -> u16 {
let address = self.cpu.memory.read_16(self.cpu.pc + 1);
let next_pc = self.cpu.pc.wrapping_add(3);
let next_sp = self.cpu.registers.sp.get().wrapping_sub(2);
self.cpu.registers.sp.set(next_sp);
self.cpu.memory.write_16(self.cpu.registers.sp.get(), next_pc);

address
}

fn call_conditional(&mut self, condition: &FlagCondition) -> u16 {
let address = self.cpu.memory.read_16(self.cpu.pc + 1);
let next_pc = self.cpu.pc.wrapping_add(3);

if self.cpu.resolve_flag_condition(&condition) {
let next_sp = self.cpu.registers.sp.get().wrapping_sub(2);
self.cpu.registers.sp.set(next_sp);
self.cpu.memory.write_16(self.cpu.registers.sp.get(), next_pc);

self.cpu.pc = address;
}

next_pc
}
}

impl Command for CallCommand<'_> {
fn execute(&mut self) -> u16 {
match &self.instruction {
CallInstruction::Call => self.call(),
CallInstruction::CallCond(condition) => self.call_conditional(condition),
}
}
}
61 changes: 61 additions & 0 deletions gameboy-lib/src/cpu/command/jump_commands.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use crate::cpu::{
instructions::{FlagCondition, JumpInstruction},
registers::Register,
Cpu,
};

use super::Command;

pub struct JumpCommand<'a> {
instruction: &'a JumpInstruction,
cpu: &'a mut Cpu,
}

impl<'a> JumpCommand<'a> {
pub fn new(instruction: &'a JumpInstruction, cpu: &'a mut Cpu) -> JumpCommand<'a> {
JumpCommand { instruction, cpu }
}

fn jp(&mut self) -> u16 {
self.cpu.memory.read_16(self.cpu.pc + 1)
}

fn jp_cc(&mut self, condition: &FlagCondition) -> u16 {
let address = self.cpu.memory.read_16(self.cpu.pc + 1);
if self.cpu.resolve_flag_condition(condition) {
address
} else {
self.cpu.pc.wrapping_add(3)
}
}

fn jp_hl(&mut self) -> u16 {
self.cpu.registers.get_16(&Register::HL)
}

fn jr(&mut self) -> u16 {
let offset = self.cpu.memory.read(self.cpu.pc + 1) as u16;
self.cpu.pc.wrapping_add(offset)
}

fn jr_cc(&mut self, condition: &FlagCondition) -> u16 {
let offset = self.cpu.memory.read(self.cpu.pc + 1) as u16;
if self.cpu.resolve_flag_condition(condition) {
self.cpu.pc.wrapping_add(offset)
} else {
self.cpu.pc.wrapping_add(2)
}
}
}

impl Command for JumpCommand<'_> {
fn execute(&mut self) -> u16 {
match &self.instruction {
JumpInstruction::Jp => self.jp(),
JumpInstruction::JpCond(condition) => self.jp_cc(condition),
JumpInstruction::JpHL => self.jp_hl(),
JumpInstruction::Jr => self.jr(),
JumpInstruction::JrCond(condition) => self.jr_cc(condition),
}
}
}
14 changes: 13 additions & 1 deletion gameboy-lib/src/cpu/command/misc_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ impl<'a> MiscCommand<'a> {
}

fn nop(&mut self) -> u16 {
self.cpu.pc.wrapping_add(1)
self.cpu.pc.wrapping_add(0)
}

fn swap(&mut self, from: &Register) -> u16 {
Expand Down Expand Up @@ -53,6 +53,16 @@ impl<'a> MiscCommand<'a> {

self.cpu.pc.wrapping_add(1)
}

fn ei(&mut self) -> u16 {
self.cpu.interrupts_enabled = true;
self.cpu.pc.wrapping_add(1)
}

fn di(&mut self) -> u16 {
self.cpu.interrupts_enabled = false;
self.cpu.pc.wrapping_add(1)
}
}

impl Command for MiscCommand<'_> {
Expand All @@ -62,6 +72,8 @@ impl Command for MiscCommand<'_> {
MiscInstruction::Swap(from) => self.swap(from),
MiscInstruction::CCF => self.ccf(),
MiscInstruction::SCF => self.scf(),
MiscInstruction::EI => self.ei(),
MiscInstruction::DI => self.di(),
_ => unimplemented!(),
}
}
Expand Down
43 changes: 26 additions & 17 deletions gameboy-lib/src/cpu/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ pub mod alu_commands;
pub mod load_commands;
pub mod misc_commands;
pub mod rotate_commands;
pub mod bit_commands;
pub mod jump_commands;
pub mod call_commands;
pub mod return_commands;

pub trait Command {
fn execute(&mut self) -> u16;
Expand All @@ -20,29 +24,34 @@ impl<'a> CommandFactory<'a> {
CommandFactory { cpu }
}

pub fn create_command(&'a mut self, instruction: &'a Instruction, prefixed: bool) -> Box<dyn Command + 'a> {
if prefixed {
self.prefix_command(instruction)
} else {
self.command(instruction)
}
}

fn command(&'a mut self, instruction: &'a Instruction) -> Box<dyn Command + 'a> {
pub fn create_command(&'a mut self, instruction: &'a Instruction) -> Box<dyn Command + 'a> {
match instruction {
Instruction::Load(load_command) => Box::new(LoadCommand::new(load_command, self.cpu)),
Instruction::Arithmetic(alu_command) => Box::new(ArithmeticCommand::new(alu_command, self.cpu)),
Instruction::Misc(misc_command) => Box::new(misc_commands::MiscCommand::new(&misc_command, self.cpu)),
Instruction::Rotate(rotate_command) => Box::new(rotate_commands::RotateCommand::new(rotate_command, self.cpu)),
_ => unimplemented!(),
Instruction::Bit(bit_command) => Box::new(bit_commands::BitCommand::new(bit_command, self.cpu)),
Instruction::Jump(jump_command) => Box::new(jump_commands::JumpCommand::new(jump_command, self.cpu)),
Instruction::Call(call_command) => Box::new(call_commands::CallCommand::new(call_command, self.cpu)),
Instruction::Return(return_command) => Box::new(return_commands::ReturnCommand::new(return_command, self.cpu)),
}
}

fn prefix_command(&'a mut self, instruction: &'a Instruction) -> Box<dyn Command + 'a> {
match instruction {
Instruction::Misc(misc_command) => Box::new(misc_commands::MiscCommand::new(&misc_command, self.cpu)),
Instruction::Rotate(rotate_command) => Box::new(rotate_commands::RotateCommand::new(rotate_command, self.cpu)),
_ => unimplemented!(),
}
}
// fn command(&'a mut self, instruction: &'a Instruction) -> Box<dyn Command + 'a> {
// match instruction {
// Instruction::Load(load_command) => Box::new(LoadCommand::new(load_command, self.cpu)),
// Instruction::Arithmetic(alu_command) => Box::new(ArithmeticCommand::new(alu_command, self.cpu)),
// Instruction::Misc(misc_command) => Box::new(misc_commands::MiscCommand::new(&misc_command, self.cpu)),
// Instruction::Rotate(rotate_command) => Box::new(rotate_commands::RotateCommand::new(rotate_command, self.cpu)),
// _ => unimplemented!(),
// }
// }

// fn prefix_command(&'a mut self, instruction: &'a Instruction) -> Box<dyn Command + 'a> {
// match instruction {
// Instruction::Misc(misc_command) => Box::new(misc_commands::MiscCommand::new(&misc_command, self.cpu)),
// Instruction::Rotate(rotate_command) => Box::new(rotate_commands::RotateCommand::new(rotate_command, self.cpu)),
// _ => unimplemented!(),
// }
// }
}
62 changes: 62 additions & 0 deletions gameboy-lib/src/cpu/command/return_commands.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use crate::cpu::{
instructions::{FlagCondition, ReturnInstruction},
Cpu,
};

use super::Command;

pub struct ReturnCommand<'a> {
instruction: &'a ReturnInstruction,
cpu: &'a mut Cpu,
}

impl<'a> ReturnCommand<'a> {
pub fn new(instruction: &'a ReturnInstruction, cpu: &'a mut Cpu) -> ReturnCommand<'a> {
ReturnCommand { instruction, cpu }
}

fn rst(&mut self, address: &u8) -> u16 {
let current_address = self.cpu.memory.read_16(self.cpu.pc);
let next_sp = self.cpu.registers.sp.get().wrapping_sub(2);
self.cpu.registers.sp.set(next_sp);
self.cpu
.memory
.write_16(self.cpu.registers.sp.get(), current_address);

0x0000 + (*address as u16)
}

fn ret(&mut self) -> u16 {
let address = self.cpu.memory.read_16(self.cpu.registers.sp.get());
self.cpu
.registers
.sp
.set(self.cpu.registers.sp.get().wrapping_add(2));

address
}

fn ret_conditional(&mut self, condition: &FlagCondition) -> u16 {
if self.cpu.resolve_flag_condition(&condition) {
let address = self.cpu.memory.read_16(self.cpu.registers.sp.get());
self.cpu
.registers
.sp
.set(self.cpu.registers.sp.get().wrapping_add(2));
address
} else {
self.cpu.pc.wrapping_add(1)
}
}
}

impl Command for ReturnCommand<'_> {
fn execute(&mut self) -> u16 {
match &self.instruction {
ReturnInstruction::Rst(address) => self.rst(&address),
ReturnInstruction::Ret => self.ret(),
ReturnInstruction::RetCond(condition) => self.ret_conditional(condition),
_ => unimplemented!(),
}
}
}
18 changes: 15 additions & 3 deletions gameboy-lib/src/cpu/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{cpu::instructions::Instruction, memory::Memory};

use self::{command::CommandFactory, registers::Register};
use self::{command::CommandFactory, instructions::FlagCondition, registers::Register};

pub mod command;
pub mod instructions;
Expand All @@ -14,6 +14,7 @@ enum FlagUpdate {
}

pub struct Cpu {
pub interrupts_enabled: bool,
pub registers: registers::Registers,
pub pc: u16,
pub memory: Memory,
Expand All @@ -22,6 +23,7 @@ pub struct Cpu {
impl Cpu {
pub fn new() -> Cpu {
Cpu {
interrupts_enabled: false,
registers: registers::Registers::new(),
pc: 0,
memory: Memory::new(),
Expand Down Expand Up @@ -79,6 +81,15 @@ impl Cpu {
}
}

fn resolve_flag_condition(&mut self, condition: &FlagCondition) -> bool {
match condition {
FlagCondition::NZ => !self.registers.f.zero,
FlagCondition::Z => self.registers.f.zero,
FlagCondition::NC => !self.registers.f.carry,
FlagCondition::C => self.registers.f.carry,
}
}

fn update_flag(&mut self, flag: FlagUpdate) {
match flag {
FlagUpdate::Zero(value) => self.registers.f.zero = value,
Expand All @@ -88,9 +99,10 @@ impl Cpu {
}
}

fn execute(&mut self, instruction: Instruction, prefixed: bool) -> u16 {
fn execute(&mut self, instruction: Instruction, _prefixed: bool) -> u16 {
println!("[CPU] Executing {:?}", instruction);
let mut factory = CommandFactory::new(self);
let mut command = factory.create_command(&instruction, prefixed);
let mut command = factory.create_command(&instruction);
command.execute()
}

Expand Down

0 comments on commit 7974729

Please sign in to comment.