From 1fc72038221736d53db2e32b6c889ee17d22144c Mon Sep 17 00:00:00 2001 From: Cedrik Hoffmann Date: Thu, 23 Nov 2023 18:58:45 +0100 Subject: [PATCH] Handle relative jumps --- src/cpu/cpu.rs | 92 +++++++++++++---- src/cpu/instructions.rs | 222 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 295 insertions(+), 19 deletions(-) diff --git a/src/cpu/cpu.rs b/src/cpu/cpu.rs index e93a04f..c1d3aff 100644 --- a/src/cpu/cpu.rs +++ b/src/cpu/cpu.rs @@ -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); @@ -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), } @@ -95,7 +132,7 @@ 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, @@ -103,7 +140,7 @@ impl CPU { 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 { @@ -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) @@ -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)); } diff --git a/src/cpu/instructions.rs b/src/cpu/instructions.rs index 85c39d5..e5ddd61 100644 --- a/src/cpu/instructions.rs +++ b/src/cpu/instructions.rs @@ -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)] @@ -145,6 +150,222 @@ impl Instruction { fn from_byte_with_prefix(byte: u8) -> Option { 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 @@ -354,6 +575,7 @@ impl Instruction { 0x32 => Some(Instruction::LDD), + _ => { eprintln!("[INS] Missing byte Instruction 0x{:x}", byte); None