Skip to content

Commit

Permalink
Handle relative jumps
Browse files Browse the repository at this point in the history
  • Loading branch information
choffmann committed Nov 23, 2023
1 parent 6120688 commit 1fc7203
Show file tree
Hide file tree
Showing 2 changed files with 295 additions and 19 deletions.
92 changes: 73 additions & 19 deletions src/cpu/cpu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,25 @@ impl CPU {
Instruction::OR(target) => self.match_or(target),
Instruction::XOR(target) => self.match_xor(target),
Instruction::CP(target) => self.match_cp(target),
Instruction::JP(jmp_condition) => self.match_jmp_condition(jmp_condition),
Instruction::JR(jmp_condition) => self.match_jmp_condition(jmp_condition),
Instruction::JR(jmp_condition) => self.match_jmp_condition(jmp_condition, true),
Instruction::JP(jmp_condition) => self.match_jmp_condition(jmp_condition, false),
Instruction::JPI => self.jump(true),
Instruction::NOP => self.pc.wrapping_add(1),
Instruction::DEC16(target) => self.match_dec16(target),
Instruction::ADD16(target) => self.match_add16(target),
Instruction::INC16(target) => self.match_inc16(target),
Instruction::BIT(value, target) => self.match_bit(target, value),
Instruction::RES(value, target) => {
println!("[CPU] RES 0x{:x} {:?}", value, target);
return self.pc.wrapping_add(2);
}
Instruction::SET(value, target) => {
println!("[CPU] SET 0x{:x} {:?}", value, target);
return self.pc.wrapping_add(2);
}
Instruction::LD8(target, source) => {
println!("[CPU] LD {:?} to {:?}", source, target);
return self.pc.wrapping_add(1);
return self.pc.wrapping_add(2);
}
Instruction::LD16(target, source) => {
let value = self.match_16bit_load_source(source);
Expand All @@ -59,26 +68,54 @@ impl CPU {
}
}

fn match_8bit_load(&mut self, target: Target8Bit, value: u8) -> u16 {
fn match_bit(&mut self, target: Target8Bit, value: u8) -> u16 {
match target {
Target8Bit::A |
Target8Bit::B |
Target8Bit::C |
Target8Bit::D |
Target8Bit::E |
Target8Bit::H |
Target8Bit::L => {
let register_value = self.register.get_8bit(target.into());
self.register.f.zero = (register_value >> value) & 0xf == 0;
self.register.f.subtract = false;
self.register.f.half_carry = true;
return self.pc.wrapping_add(2);
}
Target8Bit::HLI => {
return self.pc.wrapping_add(3);
}
Target8Bit::D8 => panic!("Should not possible")
}
}

fn match_8bit_load(&mut self, target: Target8Bit, value: u8) -> u16 {
return match target {
Target8Bit::A
| Target8Bit::B
| Target8Bit::C
| Target8Bit::D
| Target8Bit::E
| Target8Bit::H
| Target8Bit::L => self.register.set_8bit(target.into(), value),
Target8Bit::D8 => {}
Target8Bit::HLI => {}
}
return self.pc.wrapping_add(1);
| Target8Bit::L => {
self.register.set_8bit(target.into(), value);
self.pc.wrapping_add(1)
}
Target8Bit::D8 => {
self.pc.wrapping_add(2)
}
Target8Bit::HLI => {
self.pc.wrapping_add(2)
}
};
}

fn match_16bit_load(&mut self, target: Target16Bit, value: u16) -> u16 {
match target {
Target16Bit::BC => self.register.set_16bit(target.into(), value),
Target16Bit::DE => self.register.set_16bit(target.into(), value),
Target16Bit::HL => self.register.set_16bit(target.into(), value),
Target16Bit::BC |
Target16Bit::DE |
Target16Bit::HL |
Target16Bit::SP => self.register.set_16bit(target.into(), value),
}

Expand All @@ -95,15 +132,15 @@ impl CPU {
}
}

fn match_jmp_condition(&mut self, jump_condition: JumpCondition) -> u16 {
fn match_jmp_condition(&mut self, jump_condition: JumpCondition, relative: bool) -> u16 {
let should_jump = match jump_condition {
JumpCondition::NotZero => !self.register.f.zero,
JumpCondition::NotCarry => !self.register.f.carry,
JumpCondition::Zero => self.register.f.zero,
JumpCondition::Carry => self.register.f.carry,
JumpCondition::Always => true,
};
return self.jump(should_jump);
return if relative { self.jump_relative(should_jump) } else { self.jump(should_jump) };
}

fn match_add(&mut self, target: Target8Bit, with_carry: bool) -> u16 {
Expand Down Expand Up @@ -271,6 +308,9 @@ impl CPU {
if prefixed {
instruction_byte = self.memory.read_byte(self.pc + 1);
}
if instruction_byte != 0 {
println!("[CPU] Next instruction 0x{:x}", instruction_byte);
}
let next_pc = if let Some(instruction) = Instruction::from_byte(instruction_byte, prefixed)
{
self.execute(instruction)
Expand All @@ -288,16 +328,30 @@ impl CPU {

fn jump(&self, should_jump: bool) -> u16 {
return if should_jump {
let least_sign_byte = self.memory.read_byte(self.pc + 1) as u16;
let most_sign_byte = self.memory.read_byte(self.pc + 2) as u16;
(most_sign_byte << 8) | least_sign_byte
let jmp_address = self.memory.read_next_word(self.pc);
println!("[CPU] Jump to address 0x{:x}", jmp_address);
return jmp_address;
} else {
// If not jump, need counter to froward by 3
// (1 byte for tag and 2 bytes for jump address)
self.pc.wrapping_add(3)
};
}

fn jump_relative(&self, should_jump: bool) -> u16 {
let next_step = self.pc.wrapping_add(2);
return if should_jump {
let offset = self.memory.read_byte(self.pc + 1) as i8;
let jmp_address = if offset >= 0 {
next_step.wrapping_add(offset as u16)
} else {
next_step.wrapping_sub(offset.abs() as u16)
};
println!("[CPU] Jump to address 0x{:x}", jmp_address);
return jmp_address;
} else {
next_step
};
}

fn read_next_mem(&self) -> u8 {
return self.memory.read_byte(self.pc.wrapping_add(1));
}
Expand Down
222 changes: 222 additions & 0 deletions src/cpu/instructions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ pub enum Instruction {
LD8(Target8Bit, Target8Bit),
LD16(Target16Bit, Source16Bit),
LDD,

// CB Flag
BIT(u8, Target8Bit),
RES(u8, Target8Bit),
SET(u8, Target8Bit),
}

#[derive(Debug)]
Expand Down Expand Up @@ -145,6 +150,222 @@ impl Instruction {

fn from_byte_with_prefix(byte: u8) -> Option<Instruction> {
match byte {
0x40 => Some(Instruction::BIT(0, Target8Bit::B)),
0x41 => Some(Instruction::BIT(0, Target8Bit::C)),
0x42 => Some(Instruction::BIT(0, Target8Bit::D)),
0x43 => Some(Instruction::BIT(0, Target8Bit::E)),
0x44 => Some(Instruction::BIT(0, Target8Bit::H)),
0x45 => Some(Instruction::BIT(0, Target8Bit::L)),
0x46 => Some(Instruction::BIT(0, Target8Bit::HLI)),
0x47 => Some(Instruction::BIT(0, Target8Bit::A)),

0x48 => Some(Instruction::BIT(1, Target8Bit::B)),
0x49 => Some(Instruction::BIT(1, Target8Bit::C)),
0x4a => Some(Instruction::BIT(1, Target8Bit::D)),
0x4b => Some(Instruction::BIT(1, Target8Bit::E)),
0x4c => Some(Instruction::BIT(1, Target8Bit::H)),
0x4d => Some(Instruction::BIT(1, Target8Bit::L)),
0x4e => Some(Instruction::BIT(1, Target8Bit::HLI)),
0x4f => Some(Instruction::BIT(1, Target8Bit::A)),

0x50 => Some(Instruction::BIT(2, Target8Bit::B)),
0x51 => Some(Instruction::BIT(2, Target8Bit::C)),
0x52 => Some(Instruction::BIT(2, Target8Bit::D)),
0x53 => Some(Instruction::BIT(2, Target8Bit::E)),
0x54 => Some(Instruction::BIT(2, Target8Bit::H)),
0x55 => Some(Instruction::BIT(2, Target8Bit::L)),
0x56 => Some(Instruction::BIT(2, Target8Bit::HLI)),
0x57 => Some(Instruction::BIT(2, Target8Bit::A)),

0x58 => Some(Instruction::BIT(3, Target8Bit::B)),
0x59 => Some(Instruction::BIT(3, Target8Bit::C)),
0x5a => Some(Instruction::BIT(3, Target8Bit::D)),
0x5b => Some(Instruction::BIT(3, Target8Bit::E)),
0x5c => Some(Instruction::BIT(3, Target8Bit::H)),
0x5d => Some(Instruction::BIT(3, Target8Bit::L)),
0x5e => Some(Instruction::BIT(3, Target8Bit::HLI)),
0x5f => Some(Instruction::BIT(3, Target8Bit::A)),

0x60 => Some(Instruction::BIT(4, Target8Bit::B)),
0x61 => Some(Instruction::BIT(4, Target8Bit::C)),
0x62 => Some(Instruction::BIT(4, Target8Bit::D)),
0x63 => Some(Instruction::BIT(4, Target8Bit::E)),
0x64 => Some(Instruction::BIT(4, Target8Bit::H)),
0x65 => Some(Instruction::BIT(4, Target8Bit::L)),
0x66 => Some(Instruction::BIT(4, Target8Bit::HLI)),
0x67 => Some(Instruction::BIT(4, Target8Bit::A)),

0x68 => Some(Instruction::BIT(5, Target8Bit::B)),
0x69 => Some(Instruction::BIT(5, Target8Bit::C)),
0x6a => Some(Instruction::BIT(5, Target8Bit::D)),
0x6b => Some(Instruction::BIT(5, Target8Bit::E)),
0x6c => Some(Instruction::BIT(5, Target8Bit::H)),
0x6d => Some(Instruction::BIT(5, Target8Bit::L)),
0x6e => Some(Instruction::BIT(5, Target8Bit::HLI)),
0x6f => Some(Instruction::BIT(5, Target8Bit::A)),

0x70 => Some(Instruction::BIT(6, Target8Bit::B)),
0x71 => Some(Instruction::BIT(6, Target8Bit::C)),
0x72 => Some(Instruction::BIT(6, Target8Bit::D)),
0x73 => Some(Instruction::BIT(6, Target8Bit::E)),
0x74 => Some(Instruction::BIT(6, Target8Bit::H)),
0x75 => Some(Instruction::BIT(6, Target8Bit::L)),
0x76 => Some(Instruction::BIT(6, Target8Bit::HLI)),
0x77 => Some(Instruction::BIT(6, Target8Bit::A)),

0x78 => Some(Instruction::BIT(7, Target8Bit::B)),
0x79 => Some(Instruction::BIT(7, Target8Bit::C)),
0x7a => Some(Instruction::BIT(7, Target8Bit::D)),
0x7b => Some(Instruction::BIT(7, Target8Bit::E)),
0x7c => Some(Instruction::BIT(7, Target8Bit::H)),
0x7d => Some(Instruction::BIT(7, Target8Bit::L)),
0x7e => Some(Instruction::BIT(7, Target8Bit::HLI)),
0x7f => Some(Instruction::BIT(7, Target8Bit::A)),

0x80 => Some(Instruction::RES(0, Target8Bit::B)),
0x81 => Some(Instruction::RES(0, Target8Bit::C)),
0x82 => Some(Instruction::RES(0, Target8Bit::D)),
0x83 => Some(Instruction::RES(0, Target8Bit::E)),
0x84 => Some(Instruction::RES(0, Target8Bit::H)),
0x85 => Some(Instruction::RES(0, Target8Bit::L)),
0x86 => Some(Instruction::RES(0, Target8Bit::HLI)),
0x87 => Some(Instruction::RES(0, Target8Bit::A)),

0x88 => Some(Instruction::RES(1, Target8Bit::B)),
0x89 => Some(Instruction::RES(1, Target8Bit::C)),
0x8a => Some(Instruction::RES(1, Target8Bit::D)),
0x8b => Some(Instruction::RES(1, Target8Bit::E)),
0x8c => Some(Instruction::RES(1, Target8Bit::H)),
0x8d => Some(Instruction::RES(1, Target8Bit::L)),
0x8e => Some(Instruction::RES(1, Target8Bit::HLI)),
0x8f => Some(Instruction::RES(1, Target8Bit::A)),

0x90 => Some(Instruction::RES(2, Target8Bit::B)),
0x91 => Some(Instruction::RES(2, Target8Bit::C)),
0x92 => Some(Instruction::RES(2, Target8Bit::D)),
0x93 => Some(Instruction::RES(2, Target8Bit::E)),
0x94 => Some(Instruction::RES(2, Target8Bit::H)),
0x95 => Some(Instruction::RES(2, Target8Bit::L)),
0x96 => Some(Instruction::RES(2, Target8Bit::HLI)),
0x97 => Some(Instruction::RES(2, Target8Bit::A)),

0x98 => Some(Instruction::RES(3, Target8Bit::B)),
0x99 => Some(Instruction::RES(3, Target8Bit::C)),
0x9a => Some(Instruction::RES(3, Target8Bit::D)),
0x9b => Some(Instruction::RES(3, Target8Bit::E)),
0x9c => Some(Instruction::RES(3, Target8Bit::H)),
0x9d => Some(Instruction::RES(3, Target8Bit::L)),
0x9e => Some(Instruction::RES(3, Target8Bit::HLI)),
0x9f => Some(Instruction::RES(3, Target8Bit::A)),

0xa0 => Some(Instruction::RES(4, Target8Bit::B)),
0xa1 => Some(Instruction::RES(4, Target8Bit::C)),
0xa2 => Some(Instruction::RES(4, Target8Bit::D)),
0xa3 => Some(Instruction::RES(4, Target8Bit::E)),
0xa4 => Some(Instruction::RES(4, Target8Bit::H)),
0xa5 => Some(Instruction::RES(4, Target8Bit::L)),
0xa6 => Some(Instruction::RES(4, Target8Bit::HLI)),
0xa7 => Some(Instruction::RES(4, Target8Bit::A)),

0xa8 => Some(Instruction::RES(5, Target8Bit::B)),
0xa9 => Some(Instruction::RES(5, Target8Bit::C)),
0xaa => Some(Instruction::RES(5, Target8Bit::D)),
0xab => Some(Instruction::RES(5, Target8Bit::E)),
0xac => Some(Instruction::RES(5, Target8Bit::H)),
0xad => Some(Instruction::RES(5, Target8Bit::L)),
0xae => Some(Instruction::RES(5, Target8Bit::HLI)),
0xaf => Some(Instruction::RES(5, Target8Bit::A)),

0xb0 => Some(Instruction::RES(6, Target8Bit::B)),
0xb1 => Some(Instruction::RES(6, Target8Bit::C)),
0xb2 => Some(Instruction::RES(6, Target8Bit::D)),
0xb3 => Some(Instruction::RES(6, Target8Bit::E)),
0xb4 => Some(Instruction::RES(6, Target8Bit::H)),
0xb5 => Some(Instruction::RES(6, Target8Bit::L)),
0xb6 => Some(Instruction::RES(6, Target8Bit::HLI)),
0xb7 => Some(Instruction::RES(6, Target8Bit::A)),

0xb8 => Some(Instruction::RES(7, Target8Bit::B)),
0xb9 => Some(Instruction::RES(7, Target8Bit::C)),
0xba => Some(Instruction::RES(7, Target8Bit::D)),
0xbb => Some(Instruction::RES(7, Target8Bit::E)),
0xbc => Some(Instruction::RES(7, Target8Bit::H)),
0xbd => Some(Instruction::RES(7, Target8Bit::L)),
0xbe => Some(Instruction::RES(7, Target8Bit::HLI)),
0xbf => Some(Instruction::RES(7, Target8Bit::A)),

0xc0 => Some(Instruction::SET(0, Target8Bit::B)),
0xc1 => Some(Instruction::SET(0, Target8Bit::C)),
0xc2 => Some(Instruction::SET(0, Target8Bit::D)),
0xc3 => Some(Instruction::SET(0, Target8Bit::E)),
0xc4 => Some(Instruction::SET(0, Target8Bit::H)),
0xc5 => Some(Instruction::SET(0, Target8Bit::L)),
0xc6 => Some(Instruction::SET(0, Target8Bit::HLI)),
0xc7 => Some(Instruction::SET(0, Target8Bit::A)),

0xc8 => Some(Instruction::SET(1, Target8Bit::B)),
0xc9 => Some(Instruction::SET(1, Target8Bit::C)),
0xca => Some(Instruction::SET(1, Target8Bit::D)),
0xcb => Some(Instruction::SET(1, Target8Bit::E)),
0xcc => Some(Instruction::SET(1, Target8Bit::H)),
0xcd => Some(Instruction::SET(1, Target8Bit::L)),
0xce => Some(Instruction::SET(1, Target8Bit::HLI)),
0xcf => Some(Instruction::SET(1, Target8Bit::A)),

0xd0 => Some(Instruction::SET(2, Target8Bit::B)),
0xd1 => Some(Instruction::SET(2, Target8Bit::C)),
0xd2 => Some(Instruction::SET(2, Target8Bit::D)),
0xd3 => Some(Instruction::SET(2, Target8Bit::E)),
0xd4 => Some(Instruction::SET(2, Target8Bit::H)),
0xd5 => Some(Instruction::SET(2, Target8Bit::L)),
0xd6 => Some(Instruction::SET(2, Target8Bit::HLI)),
0xd7 => Some(Instruction::SET(2, Target8Bit::A)),

0xd8 => Some(Instruction::SET(3, Target8Bit::B)),
0xd9 => Some(Instruction::SET(3, Target8Bit::C)),
0xda => Some(Instruction::SET(3, Target8Bit::D)),
0xdb => Some(Instruction::SET(3, Target8Bit::E)),
0xdc => Some(Instruction::SET(3, Target8Bit::H)),
0xdd => Some(Instruction::SET(3, Target8Bit::L)),
0xde => Some(Instruction::SET(3, Target8Bit::HLI)),
0xdf => Some(Instruction::SET(3, Target8Bit::A)),

0xe0 => Some(Instruction::SET(4, Target8Bit::B)),
0xe1 => Some(Instruction::SET(4, Target8Bit::C)),
0xe2 => Some(Instruction::SET(4, Target8Bit::D)),
0xe3 => Some(Instruction::SET(4, Target8Bit::E)),
0xe4 => Some(Instruction::SET(4, Target8Bit::H)),
0xe5 => Some(Instruction::SET(4, Target8Bit::L)),
0xe6 => Some(Instruction::SET(4, Target8Bit::HLI)),
0xe7 => Some(Instruction::SET(4, Target8Bit::A)),

0xe8 => Some(Instruction::SET(5, Target8Bit::B)),
0xe9 => Some(Instruction::SET(5, Target8Bit::C)),
0xea => Some(Instruction::SET(5, Target8Bit::D)),
0xeb => Some(Instruction::SET(5, Target8Bit::E)),
0xec => Some(Instruction::SET(5, Target8Bit::H)),
0xed => Some(Instruction::SET(5, Target8Bit::L)),
0xee => Some(Instruction::SET(5, Target8Bit::HLI)),
0xef => Some(Instruction::SET(5, Target8Bit::A)),

0xf0 => Some(Instruction::SET(6, Target8Bit::B)),
0xf1 => Some(Instruction::SET(6, Target8Bit::C)),
0xf2 => Some(Instruction::SET(6, Target8Bit::D)),
0xf3 => Some(Instruction::SET(6, Target8Bit::E)),
0xf4 => Some(Instruction::SET(6, Target8Bit::H)),
0xf5 => Some(Instruction::SET(6, Target8Bit::L)),
0xf6 => Some(Instruction::SET(6, Target8Bit::HLI)),
0xf7 => Some(Instruction::SET(6, Target8Bit::A)),

0xf8 => Some(Instruction::SET(7, Target8Bit::B)),
0xf9 => Some(Instruction::SET(7, Target8Bit::C)),
0xfa => Some(Instruction::SET(7, Target8Bit::D)),
0xfb => Some(Instruction::SET(7, Target8Bit::E)),
0xfc => Some(Instruction::SET(7, Target8Bit::H)),
0xfd => Some(Instruction::SET(7, Target8Bit::L)),
0xfe => Some(Instruction::SET(7, Target8Bit::HLI)),
0xff => Some(Instruction::SET(7, Target8Bit::A)),

_ => {
println!("[INS] Missing byte Instruction 0x{:x} with prefix", byte);
None
Expand Down Expand Up @@ -354,6 +575,7 @@ impl Instruction {

0x32 => Some(Instruction::LDD),


_ => {
eprintln!("[INS] Missing byte Instruction 0x{:x}", byte);
None
Expand Down

0 comments on commit 1fc7203

Please sign in to comment.