From e9a40bd9f714a1592d229036daaa528a371ed9f9 Mon Sep 17 00:00:00 2001 From: Ajurna Date: Fri, 9 May 2025 13:47:26 +0100 Subject: [PATCH] Refactor and extend CPU and memory emulation. Added and refactored several instructions and memory operations, improving accuracy and functionality for emulation. Introduced new dependencies (`serde_json`, `glob`) and submodule for test framework integration. Enhanced debugging, flat RAM handling, and opcode parsing while fixing multiple calculation and flag-setting issues. --- .gitmodules | 3 + .idea/dictionaries/project.xml | 1 + .idea/vcs.xml | 1 + Cargo.lock | 95 ++++++ Cargo.toml | 2 + sm83 | 1 + src/main.rs | 508 +++++++++++++++++++++++---------- src/registers.rs | 4 +- 8 files changed, 459 insertions(+), 156 deletions(-) create mode 100644 .gitmodules create mode 160000 sm83 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..0c4b25c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "sm83"] + path = sm83 + url = https://github.com/SingleStepTests/sm83.git diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml index 6c4e357..0b5b1a9 100644 --- a/.idea/dictionaries/project.xml +++ b/.idea/dictionaries/project.xml @@ -7,6 +7,7 @@ addsp dechl inchl + incu instrs jpcc jphl diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..530a1f9 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index ccfeb2c..336c839 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,101 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + [[package]] name = "untitled" version = "0.1.0" +dependencies = [ + "glob", + "serde_json", +] diff --git a/Cargo.toml b/Cargo.toml index 62ea391..87dc666 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +serde_json = "1.0.140" +glob = "0.3.2" \ No newline at end of file diff --git a/sm83 b/sm83 new file mode 160000 index 0000000..f9c3021 --- /dev/null +++ b/sm83 @@ -0,0 +1 @@ +Subproject commit f9c30210245dd691661db39f5ace022c465ecc2f diff --git a/src/main.rs b/src/main.rs index 6b036c9..01c96a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,10 @@ mod registers; -use std::ops::Index; +use glob::glob; +use serde_json::Value; use crate::registers::FlagsRegister; use crate::registers::Registers; +use crate::Target::Immediate; #[derive(Debug)] struct GameRom { @@ -114,12 +116,14 @@ impl GameRom { } } +#[derive(Debug)] struct CPU { registers: Registers, pc: u16, bus: MemoryBus, sp: u16, } + const BOOT_BEGIN: usize = 0x0000; const BOOT_END: usize = 0x00FF; const CART_BEGIN: usize = 0x0100; @@ -127,8 +131,9 @@ const CART_END: usize = 0x7FFF; const VRAM_BEGIN: usize = 0x8000; const VRAM_END: usize = 0x9FFF; +#[derive(Debug)] struct GPU { vram: Vec, tile_set: [[[TilePixelValue; 8]; 8]; 384] } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug)] enum TilePixelValue { Three, Two, One, Zero } impl GPU { @@ -136,7 +141,7 @@ impl GPU { self.vram[address] } fn write_vram(&mut self, index: usize, value: u8) { - // self.vram[index] = value; + self.vram[index] = value; // If our index is greater than 0x1800, we're not writing to the tile set storage // so we can just return. if index >= 0x1800 { return } @@ -197,15 +202,21 @@ impl GPU { } } +#[derive(Debug)] struct MemoryBus { - memory: [u8; 0xFFFF], + memory: [u8; 0xFFFF+1], gpu: GPU, rom: GameRom, boot: Vec, + flat_ram: bool, } impl MemoryBus { fn read_byte(&self, address: u16) -> u8 { let address = address as usize; + if self.flat_ram { + return self.memory[address] + } + match address { VRAM_BEGIN ..= VRAM_END => { self.gpu.read_vram(address - VRAM_BEGIN) @@ -217,16 +228,25 @@ impl MemoryBus { self.rom.read_byte(address) } } - CART_BEGIN ..= CART_END => {self.rom.read_byte(address)} + CART_BEGIN ..= CART_END => { + self.rom.read_byte(address) + } _ => self.memory[address] } } fn write_byte(&mut self, address: u16, value: u8) { + if self.flat_ram { + self.memory[address as usize] = value; + return; + } let address = address as usize; match address { VRAM_BEGIN ..= VRAM_END => { self.gpu.write_vram(address - VRAM_BEGIN, value) } + CART_BEGIN ..= CART_END => { + self.rom.write_byte(address, value) + } _ => self.memory[address as usize] = value } } @@ -292,7 +312,7 @@ enum Instruction { ADC(Target), ADD(Target), ADDHL(TargetU16Register), - ADDSP(u8), + ADDSP(i8), AND(Target), BIT(u8, Target), CALL(Condition, u16), @@ -301,12 +321,12 @@ enum Instruction { CPL, DAA, DEC(Target), - DECHL(TargetU16Register), + DECU16(TargetU16Register), DI, EI, HALT, INC(Target), - INCHL(TargetU16Register), + INCU16(TargetU16Register), JP(Condition, u16), JPHL, JR(Condition, i8), @@ -339,13 +359,89 @@ enum TargetRegister { A, B, C, D, E, H, L, } #[derive(Clone, Copy, Debug)] enum TargetU16Register {AF, BC, DE, HL, SP, PC} impl CPU { - + fn init(&mut self) { let l = self.bus.memory.len(); println!("{l}"); println!("CPU init"); self.bus.write_byte(0xFF50, 0x00); } + + fn load_test(test: &Value) -> CPU { + let mut vram = vec![0xFFu8;VRAM_END-VRAM_BEGIN+1]; + let mut game_rom = vec![0xFFu8;CART_END+1]; + let mut memory = [0xFFu8; 0xFFFF+1]; + for mem in test["ram"].as_array().unwrap() { + let address = mem[0].as_u64().unwrap() as usize; + let value = mem[1].as_u64().unwrap() as u8; + memory[address] = value; + // println!("{:x}, {:x}", address, value); + } + + + CPU{ + registers:Registers{ + a:test["a"].as_u64().unwrap() as u8, + b:test["b"].as_u64().unwrap() as u8, + c:test["c"].as_u64().unwrap() as u8, + d:test["d"].as_u64().unwrap() as u8, + e:test["e"].as_u64().unwrap() as u8, + f:FlagsRegister::from(test["f"].as_u64().unwrap() as u8), + h:test["h"].as_u64().unwrap() as u8, + l:test["l"].as_u64().unwrap() as u8}, + pc:test["pc"].as_u64().unwrap() as u16, + sp: test["sp"].as_u64().unwrap() as u16, + bus:MemoryBus{ + memory, + gpu:GPU{ vram, tile_set: [[[TilePixelValue::Zero; 8]; 8]; 384] }, + rom: GameRom{ + data:game_rom, + title: "test".parse().unwrap(), + cgb_flag: 0, + licensee: [0,0], + sgb_flag: 0, + cartridge_type: CartridgeType::RomOnly, + rom_size: 2, + ram_size: 0, + rom_bank_low: 0, + rom_bank_high: 0, + ram_enable: false, + banking_mode: false, + }, + boot: vec![], + flat_ram: true, + } + } + } + + pub(crate) fn compare_state(&self, state: &Value) { + let a = state["a"].as_u64().unwrap(); + let b = state["b"].as_u64().unwrap(); + let c = state["c"].as_u64().unwrap(); + let d = state["d"].as_u64().unwrap(); + let e = state["e"].as_u64().unwrap(); + let f = state["f"].as_u64().unwrap(); + let h = state["h"].as_u64().unwrap(); + let l = state["l"].as_u64().unwrap(); + let pc = state["pc"].as_u64().unwrap(); + let sp = state["sp"].as_u64().unwrap(); + assert_eq!(self.registers.a, a as u8); + assert_eq!(self.registers.b, b as u8); + assert_eq!(self.registers.c, c as u8); + assert_eq!(self.registers.d, d as u8); + assert_eq!(self.registers.e, e as u8); + assert_eq!(self.registers.f, FlagsRegister::from(f as u8)); + assert_eq!(self.registers.h, h as u8); + assert_eq!(self.registers.l, l as u8); + assert_eq!(self.pc, pc as u16); + assert_eq!(self.sp, sp as u16); + for mem in state["ram"].as_array().unwrap() { + let address = mem[0].as_u64().unwrap() as u16; + let value = mem[1].as_u64().unwrap() as u8; + assert_eq!(self.bus.read_byte(address), value); + } + } + fn check_condition(&self, condition: Condition) -> bool { match condition { Condition::NZ => !self.registers.f.zero, @@ -406,8 +502,8 @@ impl CPU { self.sp = self.sp.wrapping_sub(2); } fn pop_stack(&mut self) -> u16 { - let low = (self.bus.read_byte(self.sp) as u16) << 8; - let high = self.bus.read_byte(self.sp + 1) as u16; + let low = self.bus.read_byte(self.sp) as u16; + let high = (self.bus.read_byte(self.sp + 1) as u16) << 8; self.sp = self.sp.wrapping_add(2); high | low } @@ -441,7 +537,7 @@ impl CPU { 0x00 => { Instruction::NOP } 0x01 => { Instruction::LD(LoadTarget::CopyR16N16(TargetU16Register::BC, self.bus.read_u16(self.pc.wrapping_add(1)))) } 0x02 => { Instruction::LD(LoadTarget::CopyR16A(TargetU16Register::BC)) } - 0x03 => { Instruction::INC(Target::U16Register(TargetU16Register::BC)) } + 0x03 => { Instruction::INCU16(TargetU16Register::BC) } 0x04 => { Instruction::INC(Target::U8Register(TargetRegister::B)) } 0x05 => { Instruction::DEC(Target::U8Register(TargetRegister::B)) } 0x06 => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::B, self.bus.read_byte(self.pc.wrapping_add(1)))) } @@ -449,7 +545,7 @@ impl CPU { 0x08 => { Instruction::LD(LoadTarget::CopyN16SP(self.bus.read_u16(self.pc.wrapping_add(1)))) } 0x09 => { Instruction::ADDHL(TargetU16Register::BC) } 0x0A => { Instruction::LD(LoadTarget::CopyAR16(TargetU16Register::BC)) } - 0x0B => { Instruction::DEC(Target::U16Register(TargetU16Register::BC)) } + 0x0B => { Instruction::DECU16(TargetU16Register::BC) } 0x0C => { Instruction::INC(Target::U8Register(TargetRegister::C)) } 0x0D => { Instruction::DEC(Target::U8Register(TargetRegister::C)) } 0x0E => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::C, self.bus.read_byte(self.pc.wrapping_add(1)))) } @@ -457,15 +553,15 @@ impl CPU { 0x10 => { Instruction::STOP(self.bus.read_byte(self.pc.wrapping_add(1))) } 0x11 => { Instruction::LD(LoadTarget::CopyR16N16(TargetU16Register::DE, self.bus.read_u16(self.pc.wrapping_add(1)))) } 0x12 => { Instruction::LD(LoadTarget::CopyR16A(TargetU16Register::DE)) } - 0x13 => { Instruction::INC(Target::U16Register(TargetU16Register::DE)) } + 0x13 => { Instruction::INCU16(TargetU16Register::DE) } 0x14 => { Instruction::INC(Target::U8Register(TargetRegister::D)) } 0x15 => { Instruction::DEC(Target::U8Register(TargetRegister::D)) } 0x16 => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::D, self.bus.read_byte(self.pc.wrapping_add(1)))) } 0x17 => { Instruction::RL(Target::U8Register(TargetRegister::A)) } - 0x18 => { Instruction::JR(Condition::NZ, self.bus.read_byte(self.pc.wrapping_add(1)) as i8) } + 0x18 => { Instruction::JR(Condition::None, self.bus.read_byte(self.pc.wrapping_add(1)) as i8) } 0x19 => { Instruction::ADDHL(TargetU16Register::DE) } 0x1A => { Instruction::LD(LoadTarget::CopyAR16(TargetU16Register::DE)) } - 0x1B => { Instruction::DEC(Target::U16Register(TargetU16Register::DE)) } + 0x1B => { Instruction::DECU16(TargetU16Register::DE) } 0x1C => { Instruction::INC(Target::U8Register(TargetRegister::E)) } 0x1D => { Instruction::DEC(Target::U8Register(TargetRegister::E)) } 0x1E => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::E, self.bus.read_byte(self.pc.wrapping_add(1)))) } @@ -473,7 +569,7 @@ impl CPU { 0x20 => { Instruction::JR(Condition::NZ, self.bus.read_byte(self.pc.wrapping_add(1)) as i8) } 0x21 => { Instruction::LD(LoadTarget::CopyR16N16(TargetU16Register::HL, self.bus.read_u16(self.pc.wrapping_add(1)))) } 0x22 => { Instruction::LD(LoadTarget::CopyHLIA) } - 0x23 => { Instruction::INC(Target::U16Register(TargetU16Register::HL)) } + 0x23 => { Instruction::INCU16(TargetU16Register::HL) } 0x24 => { Instruction::INC(Target::U8Register(TargetRegister::H)) } 0x25 => { Instruction::DEC(Target::U8Register(TargetRegister::H)) } 0x26 => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::H, self.bus.read_byte(self.pc.wrapping_add(1)))) } @@ -481,23 +577,23 @@ impl CPU { 0x28 => { Instruction::JR(Condition::Z, self.bus.read_byte(self.pc.wrapping_add(1)) as i8) } 0x29 => { Instruction::ADDHL(TargetU16Register::HL) } 0x2A => { Instruction::LD(LoadTarget::CopyAHLI) } - 0x2B => { Instruction::DEC(Target::U16Register(TargetU16Register::HL)) } + 0x2B => { Instruction::DECU16(TargetU16Register::HL) } 0x2C => { Instruction::INC(Target::U8Register(TargetRegister::L)) } 0x2D => { Instruction::DEC(Target::U8Register(TargetRegister::L)) } 0x2E => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::L, self.bus.read_byte(self.pc.wrapping_add(1)))) } 0x2F => { Instruction::CPL } 0x30 => { Instruction::JR(Condition::NC, self.bus.read_byte(self.pc.wrapping_add(1)) as i8) } - 0x31 => { Instruction::LD(LoadTarget::CopyR16N16(TargetU16Register::SP, self.bus.read_u16(self.pc.wrapping_add(1)))) } + 0x31 => { Instruction::LD(LoadTarget::CopySPN16(self.bus.read_u16(self.pc.wrapping_add(1)))) } 0x32 => { Instruction::LD(LoadTarget::CopyHLDA) } - 0x33 => { Instruction::INC(Target::U16Register(TargetU16Register::SP)) } - 0x34 => { Instruction::INC(Target::Address(self.get_u16_reg_value(TargetU16Register::HL))) } + 0x33 => { Instruction::INCU16(TargetU16Register::SP) } + 0x34 => { Instruction::INC(Target::U16Register(TargetU16Register::HL)) } 0x35 => { Instruction::DEC(Target::Address(self.get_u16_reg_value(TargetU16Register::HL))) } 0x36 => { Instruction::LD(LoadTarget::CopyHLN8(self.bus.read_byte(self.pc.wrapping_add(1)))) } 0x37 => { Instruction::SCF } 0x38 => { Instruction::JR(Condition::C, self.bus.read_byte(self.pc.wrapping_add(1)) as i8) } 0x39 => { Instruction::ADDHL(TargetU16Register::SP) } 0x3A => { Instruction::LD(LoadTarget::CopyAHLD)} - 0x3B => { Instruction::DEC(Target::U16Register(TargetU16Register::SP)) } + 0x3B => { Instruction::DECU16(TargetU16Register::SP) } 0x3C => { Instruction::INC(Target::U8Register(TargetRegister::A)) } 0x3D => { Instruction::DEC(Target::U8Register(TargetRegister::A)) } 0x3E => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::A, self.bus.read_byte(self.pc.wrapping_add(1)))) } @@ -564,7 +660,7 @@ impl CPU { 0x7B => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::E)) } 0x7C => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::H)) } 0x7D => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::L)) } - 0x7E => { Instruction::LD(LoadTarget::CopyHLR8(TargetRegister::A)) } + 0x7E => { Instruction::LD(LoadTarget::CopyR8HL(TargetRegister::A)) } 0x7F => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::A)) } 0x80 => { Instruction::ADD(Target::U8Register(TargetRegister::B)) } 0x81 => { Instruction::ADD(Target::U8Register(TargetRegister::C)) } @@ -636,7 +732,7 @@ impl CPU { 0xC3 => { Instruction::JP(Condition::None, self.bus.read_u16(self.pc.wrapping_add(1))) } 0xC4 => { Instruction::CALL(Condition::NZ, self.bus.read_u16(self.pc.wrapping_add(1))) } 0xC5 => { Instruction::PUSH(TargetU16Register::BC) } - 0xC6 => { Instruction::ADD(Target::U8Register(TargetRegister::A)) } + 0xC6 => { Instruction::ADD(Immediate(self.bus.read_byte(self.pc.wrapping_add(1)))) } 0xC7 => { Instruction::RST(0x00) } 0xC8 => { Instruction::RET(Condition::Z) } 0xC9 => { Instruction::RET(Condition::None) } @@ -665,23 +761,23 @@ impl CPU { 0xE5 => { Instruction::PUSH(TargetU16Register::HL) } 0xE6 => { Instruction::AND(Target::Immediate(self.bus.read_byte(self.pc.wrapping_add(1)))) } 0xE7 => { Instruction::RST(0x04) } - 0xE8 => { Instruction::ADDSP(self.bus.read_byte(self.pc.wrapping_add(1))) } + 0xE8 => { Instruction::ADDSP(self.bus.read_byte(self.pc.wrapping_add(1)) as i8) } 0xE9 => { Instruction::JPHL } 0xEA => { Instruction::LD(LoadTarget::CopyN16A(self.bus.read_u16(self.pc.wrapping_add(1)))) } - 0xEE => { Instruction::XOR(Target::Immediate(self.bus.read_byte(self.pc.wrapping_add(1)))) } + 0xEE => { Instruction::XOR(Immediate(self.bus.read_byte(self.pc.wrapping_add(1)))) } 0xEF => { Instruction::RST(0x05) } - 0xF0 => { Instruction::LD(LoadTarget::CopyAPort(Target::Immediate(self.bus.read_byte(self.pc.wrapping_add(1))))) } + 0xF0 => { Instruction::LD(LoadTarget::CopyAPort(Immediate(self.bus.read_byte(self.pc.wrapping_add(1))))) } 0xF1 => { Instruction::POP(TargetU16Register::AF) } - 0xF2 => { Instruction::LD(LoadTarget::CopyPortA(Target::U8Register(TargetRegister::C))) } + 0xF2 => { Instruction::LD(LoadTarget::CopyAPort(Target::U8Register(TargetRegister::C))) } 0xF3 => { Instruction::DI } 0xF5 => { Instruction::PUSH(TargetU16Register::AF) } - 0xF6 => { Instruction::OR(Target::Immediate(self.bus.read_byte(self.pc.wrapping_add(1)))) } + 0xF6 => { Instruction::OR(Immediate(self.bus.read_byte(self.pc.wrapping_add(1)))) } 0xF7 => { Instruction::RST(0x06) } 0xF8 => { Instruction::LD(LoadTarget::CopyHLSPE8(self.bus.read_byte(self.pc.wrapping_add(1)) as i8)) } 0xF9 => { Instruction::LD(LoadTarget::CopySPHL) } 0xFA => { Instruction::LD(LoadTarget::CopyAN16(self.bus.read_u16(self.pc.wrapping_add(1)))) } 0xFB => { Instruction::EI } - 0xFE => { Instruction::CP(Target::Immediate(self.bus.read_byte(self.pc.wrapping_add(1)))) } + 0xFE => { Instruction::CP(Immediate(self.bus.read_byte(self.pc.wrapping_add(1)))) } 0xFF => { Instruction::RST(0x07) } _ => { panic!("Invalid u8 opcode: {:02X}", opcode); } } @@ -948,24 +1044,26 @@ impl CPU { } } fn execute_next_instruction(&mut self) { - if self.pc >= 0x100 { - println!("Boot Complete"); - } + // if self.pc >= 0x100 { + // println!("Boot Complete"); + // } let inst = self.next_instruction(); - println!("{} {:?}", self.pc, inst); + println!("{:x} {:?} {:?}", self.pc, inst, self.registers.f); + self.execute(inst); } fn execute(&mut self, instruction: Instruction) { match instruction { Instruction::ADC(target) => { let value = self.get_target_value(target); - let (mut new_value, did_overflow) = self.registers.a.overflowing_add(value); - new_value += did_overflow as u8; - self.registers.a = new_value; + let carry = self.registers.f.carry as u8; + let (value_with_carry, carry_overflow) = value.overflowing_add(carry); + let (new_value, did_overflow) = self.registers.a.overflowing_add(value_with_carry); self.registers.f.zero = new_value == 0; self.registers.f.subtract = false; - self.registers.f.carry = did_overflow; - self.registers.f.half_carry = (self.registers.a & 0xF) + (value & 0xF) > 0xF; + self.registers.f.half_carry = (self.registers.a & 0xF) + (value & 0xF) + carry > 0xF; + self.registers.f.carry = did_overflow | carry_overflow; + self.registers.a = new_value; self.pc += match target { Target::Immediate(_) => {2} _ => {1} @@ -974,34 +1072,37 @@ impl CPU { Instruction::ADD(target) => { let value = self.get_target_value(target); let (new_value, did_overflow) = self.registers.a.overflowing_add(value); - self.registers.a = new_value; self.registers.f.zero = new_value == 0; self.registers.f.subtract = false; self.registers.f.carry = did_overflow; self.registers.f.half_carry = (self.registers.a & 0xF) + (value & 0xF) > 0xF; + self.registers.a = new_value; self.pc += match target { Target::Immediate(_) => {2} _ => {1} }; } Instruction::ADDHL(target) => { - let value = self.get_u16_reg_value(target); - let (new_value, did_overflow) = self.registers.get_hl().overflowing_add(value); + let value_source = self.get_u16_reg_value(target); + let value_dest = self.registers.get_hl(); + let (new_value, did_overflow) = value_dest.overflowing_add(value_source); self.registers.set_hl(new_value); - self.registers.f.zero = new_value == 0; self.registers.f.subtract = false; self.registers.f.carry = did_overflow; - self.registers.f.half_carry = (new_value & 0xFF) + (value & 0xFF) > 0xFF; + self.registers.f.half_carry = (value_dest & 0xFFF) + (value_source & 0xFFF) > 0xFFF; self.pc += 1; } Instruction::ADDSP(value) => { - let offset = (value as i16) as u16; - let (new_value, did_overflow) = self.sp.overflowing_add(offset); + let new_value = self.sp.wrapping_add_signed(value as i16); + // couldn't get this working, so stole this method from + // https://github.com/simias/gb-rs/blob/master/src/cpu/instructions.rs + let carry = (self.sp as i32 ^ value as i32 ^ new_value as i32) & 0x100 != 0; + let half_carry = (self.sp as i32 ^ value as i32 ^ new_value as i32) & 0x10 != 0; + self.registers.f.carry = carry; + self.registers.f.half_carry = half_carry; self.sp = new_value; self.registers.f.zero = false; self.registers.f.subtract = false; - self.registers.f.carry = did_overflow; - self.registers.f.half_carry = (new_value & 0xFF) + (offset & 0xFF) > 0xFF; self.pc += 2; } Instruction::AND(target) => { @@ -1021,7 +1122,7 @@ impl CPU { self.registers.f.zero = value >> bit & 0x1 == 0; self.registers.f.subtract = false; self.registers.f.half_carry = true; - self.pc += 2; + self.pc = self.pc.wrapping_add(2); } Instruction::CALL(condition, address) => { if self.check_condition(condition) { @@ -1030,6 +1131,8 @@ impl CPU { self.bus.write_byte(self.sp + 1, ((pc >> 8) & 0xFF) as u8); self.bus.write_byte(self.sp, (pc & 0xFF) as u8); self.pc = address; + } else { + self.pc = self.pc.wrapping_add(3); } } Instruction::CCF => { @@ -1044,7 +1147,8 @@ impl CPU { self.registers.f.zero = new_value == 0; self.registers.f.subtract = true; self.registers.f.carry = did_overflow; - self.registers.f.half_carry = (self.registers.a & 0xF) + (value & 0xF) > 0xF; + let (_, half_carry) = (self.registers.a & 0xF).overflowing_sub(value & 0xF); + self.registers.f.half_carry = half_carry; self.pc += match target { Target::Immediate(_) => {2} _ => {1} @@ -1067,51 +1171,45 @@ impl CPU { if self.registers.f.carry { adjustment += 0x60; } - (new_value, did_overflow) = self.registers.a.overflowing_sub(adjustment); + // (new_value, did_overflow) = self.registers.a.overflowing_sub(adjustment); + new_value = self.registers.a.wrapping_sub(adjustment); + did_overflow = self.registers.f.carry; } else { if self.registers.f.half_carry || self.registers.a & 0xF > 0x9 { adjustment += 0x6; } if self.registers.f.carry || self.registers.a > 0x99 { adjustment += 0x60; + did_overflow = true; + new_value = self.registers.a.wrapping_add(adjustment); + }else { + (new_value, did_overflow) = self.registers.a.overflowing_add(adjustment); } - (new_value, did_overflow) = self.registers.a.overflowing_add(adjustment); } self.registers.a = new_value; - self.registers.f.carry = did_overflow; self.registers.f.zero = new_value == 0; self.registers.f.half_carry = false; + self.registers.f.carry = did_overflow; self.pc += 1; } Instruction::DEC(target) => { - match target { - Target::U8Register(target) => { - let value = self.get_u8_reg_value(target); - let (new_value, _) = value.overflowing_sub(1); - self.set_u8_reg_value(target, new_value); - self.registers.f.zero = new_value == 0; - self.registers.f.subtract = true; - self.registers.f.half_carry = (new_value & 0xF) + (value & 0xF) > 0xF; - } - Target::U16Register(target) => { - let value = self.get_u16_reg_value(target); - let new_value = value.wrapping_sub(1); - self.set_u16_reg_value(target, new_value); - } - Target::Address(_) => {} - Target::Immediate(_) => {} - } - self.pc += 1; - } - Instruction::DECHL(register) => { - let target = Target::U16Register(register); let value = self.get_target_value(target); let new_value = value.wrapping_sub(1); self.set_target_value(target, new_value); self.registers.f.zero = new_value == 0; self.registers.f.subtract = true; - self.registers.f.half_carry = (new_value & 0xF) + (value & 0xF) > 0xF; - self.pc += 1; + let (_, half_carry) = (value & 0xF).overflowing_sub(1); + self.registers.f.half_carry = half_carry; + self.pc = match target { + Target::Immediate(_) => {self.pc.wrapping_add(2)} + _ => {self.pc.wrapping_add(1)} + }; + } + Instruction::DECU16(register) => { + let value = self.get_u16_reg_value(register); + let new_value = value.wrapping_sub(1); + self.set_u16_reg_value(register, new_value); + self.pc = self.pc.wrapping_add(1); } Instruction::DI => { self.pc += 1; @@ -1124,38 +1222,25 @@ impl CPU { } Instruction::INC(target) => { - match target { - Target::U8Register(target) => { - let value = self.get_u8_reg_value(target); - let (new_value, _) = value.overflowing_add(1); - self.set_u8_reg_value(target, new_value); - self.registers.f.zero = new_value == 0; - self.registers.f.subtract = false; - self.registers.f.half_carry = (new_value & 0xF) + (value & 0xF) > 0xF; - } - Target::U16Register(target) => { - let value = self.get_u16_reg_value(target); - let new_value = value.wrapping_add(1); - self.set_u16_reg_value(target, new_value); - } - Target::Address(_) => {} - Target::Immediate(_) => {} - } - self.pc += 1; - } - Instruction::INCHL(register) => { - let target = Target::U16Register(register); let value = self.get_target_value(target); let new_value = value.wrapping_add(1); self.set_target_value(target, new_value); self.registers.f.zero = new_value == 0; - self.registers.f.subtract = true; - self.registers.f.half_carry = (new_value & 0xF) + (value & 0xF) > 0xF; + self.registers.f.subtract = false; + self.registers.f.half_carry = (value & 0xF) + 1 > 0xF; + self.pc += 1; + } + Instruction::INCU16(register) => { + let value = self.get_u16_reg_value(register); + let new_value = value.wrapping_add(1); + self.set_u16_reg_value(register, new_value); self.pc += 1; } Instruction::JP(condition, address) => { if self.check_condition(condition) { self.pc = address; + } else { + self.pc = self.pc.wrapping_add(3); } } Instruction::JPHL => { @@ -1164,10 +1249,14 @@ impl CPU { Instruction::JR(condition, offset) => { if self.check_condition(condition) { self.pc = if offset.is_negative() { - self.pc.wrapping_sub(offset.abs() as u16 - 1) + let t = self.pc.wrapping_sub((offset as i16).abs() as u16); + t.wrapping_add(2) } else { - self.pc.wrapping_add(offset as u16) + let t = self.pc.wrapping_add(offset as u16); + t.wrapping_add(2) } + } else { + self.pc += 2; } } Instruction::LD(target) => { @@ -1175,7 +1264,7 @@ impl CPU { LoadTarget::CopyR8R8(dest_register, source_register) => { let value = self.get_u8_reg_value(source_register); self.set_u8_reg_value(dest_register, value); - self.pc += 1; + self.pc = self.pc.wrapping_add(1); } LoadTarget::CopyR8N8(dest_register, value) => { self.set_u8_reg_value(dest_register, value); @@ -1189,7 +1278,7 @@ impl CPU { let value = self.get_u8_reg_value(source_register); let address = self.registers.get_hl(); self.bus.write_byte(address, value); - self.pc += 1; + self.pc = self.pc.wrapping_add(1); } LoadTarget::CopyHLN8(value) => { let address = self.registers.get_hl(); @@ -1215,13 +1304,19 @@ impl CPU { let offset = self.get_target_value(target); let address = 0xFF00 | offset as u16; self.bus.write_byte(address, self.registers.a); - self.pc += 2; + self.pc = match target { + Immediate(_) => {self.pc.wrapping_add(2)}, + _ => {self.pc.wrapping_add(1)} + }; } LoadTarget::CopyAPort(target) => { let offset = self.get_target_value(target); let address = 0xFF00 | offset as u16; self.registers.a = self.bus.read_byte(address); - self.pc += 2; + self.pc = match target { + Immediate(_) => {self.pc.wrapping_add(2)} + _ => {self.pc.wrapping_add(1)} + }; } LoadTarget::CopyHN16A(address) => { if address >= 0xFF00 { @@ -1279,24 +1374,21 @@ impl CPU { } LoadTarget::CopySPN16(value) => { self.sp = value; - self.pc += 3; + self.pc = self.pc.wrapping_add(3); } LoadTarget::CopyN16SP(address) => { - self.bus.write_byte(address, (0xF & self.sp) as u8); - self.bus.write_byte(address + 1, (self.sp >> 8) as u8); + self.bus.write_u16(address, self.sp); self.pc += 3; } LoadTarget::CopyHLSPE8(value) => { - - let new_value = match value.is_negative() { - true => { self.sp.wrapping_sub(value.abs() as u16) } - false => { self.sp.wrapping_add(value as u16) } - }; + let new_value = self.sp.wrapping_add_signed(value as i16); + let carry = (self.sp as i32 ^ value as i32 ^ new_value as i32) & 0x100 != 0; + let half_carry = (self.sp as i32 ^ value as i32 ^ new_value as i32) & 0x10 != 0; self.registers.set_hl(new_value); self.registers.f.zero = false; self.registers.f.subtract = false; - self.registers.f.carry = (new_value & 0xFF) + (self.sp & 0xFF) > 0xFF; - self.registers.f.half_carry = (new_value & 0xF) + (self.sp & 0xF) > 0xF; + self.registers.f.carry = carry; + self.registers.f.half_carry = half_carry; self.pc += 2; } LoadTarget::CopySPHL => { @@ -1334,7 +1426,7 @@ impl CPU { let mut value = self.get_target_value(target); value &= !(1 << bit); self.set_target_value(target, value); - self.pc += 2; + self.pc = self.pc.wrapping_add(2); } Instruction::RET(condition) => { if self.check_condition(condition) { @@ -1350,70 +1442,92 @@ impl CPU { let old_value = self.get_target_value(target); let old_carry = self.registers.f.carry; self.registers.f.carry = old_value & 0x80 == 0x80; - let new_value = (self.registers.a << 1) | (if old_carry { 0x01 } else { 0 }); + let new_value = (old_value << 1) | (if old_carry { 0x01 } else { 0 }); self.set_target_value(target, new_value); - self.registers.f.zero = false; self.registers.f.subtract = false; self.registers.f.half_carry = false; - self.pc += match self.bus.read_byte(self.pc) { - 0x17 => {1} - _ => {2} + match self.bus.read_byte(self.pc) { + 0x17 => { + self.registers.f.zero = false; + self.pc = self.pc.wrapping_add(1); + } + _ => { + self.registers.f.zero = new_value == 0; + self.pc = self.pc.wrapping_add(2); + } } } Instruction::RLC(target) => { let old_value = self.get_target_value(target); self.registers.f.carry = old_value & 0x80 == 0x80; - let new_value = (self.registers.a << 1) | (if self.registers.f.carry { 0x1 } else { 0 }); + let new_value = (old_value << 1) | (if self.registers.f.carry { 0x1 } else { 0 }); self.set_target_value(target, new_value); - self.registers.f.zero = false; self.registers.f.subtract = false; self.registers.f.half_carry = false; self.pc += match self.bus.read_byte(self.pc) { - 0x07 => {1} - _ => {2} + 0x07 => { + self.registers.f.zero = false; + 1 + } + _ => { + self.registers.f.zero = new_value == 0; + 2 + } } } Instruction::RR(target) => { let old_value = self.get_target_value(target); let old_carry = self.registers.f.carry; self.registers.f.carry = old_value & 0x1 == 0x1; - let new_value = (self.registers.a >> 1) | (if old_carry { 0x80 } else { 0 }); + let new_value = (old_value >> 1) | (if old_carry { 0x80 } else { 0 }); self.set_target_value(target, new_value); - self.registers.f.zero = false; self.registers.f.subtract = false; self.registers.f.half_carry = false; self.pc += match self.bus.read_byte(self.pc) { - 0x1F => {1} - _ => {2} + 0x1F => { + self.registers.f.zero = false; + 1 + } + _ => { + self.registers.f.zero = new_value == 0; + 2 + } } } Instruction::RRC(target) => { let old_value = self.get_target_value(target); self.registers.f.carry = old_value & 0x1 == 0x1; - let new_value = (self.registers.a >> 1) | (if self.registers.f.carry { 0x80 } else { 0 }); + let new_value = (old_value >> 1) | (if self.registers.f.carry { 0x80 } else { 0 }); self.set_target_value(target, new_value); - self.registers.f.zero = false; self.registers.f.subtract = false; self.registers.f.half_carry = false; self.pc += match self.bus.read_byte(self.pc) { - 0x0F => {1} - _ => {2} + 0x0F => { + self.registers.f.zero = false; + 1 + } + _ => { + self.registers.f.zero = new_value == 0; + 2 + } } } Instruction::RST(idx) => { - self.push_stack(self.pc); - let address = self.bus.read_byte((idx as u16) * 8) as u16; - self.pc = address; + self.push_stack(self.pc.wrapping_add(1)); + self.pc = idx as u16 * 8; } Instruction::SBC(target) => { let value = self.get_target_value(target); - let (mut new_value, did_overflow) = self.registers.a.overflowing_sub(value); - new_value -= did_overflow as u8; - self.registers.a = new_value; + let carry = self.registers.f.carry as u8; + let (new_value_with_carry, carry_overflow) = self.registers.a.overflowing_sub(carry); + let (new_value, did_overflow) = new_value_with_carry.overflowing_sub(value); self.registers.f.zero = new_value == 0; self.registers.f.subtract = true; - self.registers.f.carry = did_overflow; - self.registers.f.half_carry = (self.registers.a & 0xF) + (value & 0xF) > 0xF; + self.registers.f.carry = did_overflow | carry_overflow; + let (temp, half_carry) = (self.registers.a & 0xF).overflowing_sub(carry); + let (_, half_carry2) = temp.overflowing_sub(value & 0xF); + self.registers.f.half_carry = half_carry | half_carry2; + self.registers.a = new_value; self.pc += match target { Target::Immediate(_) => {2} _ => {1} @@ -1429,7 +1543,7 @@ impl CPU { let mut value = self.get_target_value(target); value |= 1 << bit; self.set_target_value(target, value); - self.pc += 2; + self.pc = self.pc.wrapping_add(2); } Instruction::SLA(target) => { let value = self.get_target_value(target); @@ -1439,7 +1553,7 @@ impl CPU { self.registers.f.subtract = false; self.registers.f.carry = value & 0x80 == 0x80; self.registers.f.half_carry = false; - self.pc += 2; + self.pc = self.pc.wrapping_add(2); } Instruction::SRA(target) => { let value = self.get_target_value(target); @@ -1447,7 +1561,7 @@ impl CPU { self.set_target_value(target, new_value); self.registers.f.zero = new_value == 0; self.registers.f.subtract = false; - self.registers.f.carry = value & 0x80 == 0x80; + self.registers.f.carry = value & 0x1 == 0x1; self.registers.f.half_carry = false; self.pc += 2; } @@ -1459,10 +1573,10 @@ impl CPU { self.registers.f.subtract = false; self.registers.f.carry = value & 0x1 == 0x1; self.registers.f.half_carry = false; - self.pc += 2; + self.pc = self.pc.wrapping_add(2); } Instruction::STOP(_) => { - self.pc += 2; + self.pc += 1; } Instruction::SUB(target) => { let value = self.get_target_value(target); @@ -1485,7 +1599,7 @@ impl CPU { self.registers.f.subtract = false; self.registers.f.carry = false; self.registers.f.half_carry = false; - self.pc += 2; + self.pc = self.pc.wrapping_add(2); } Instruction::XOR(target) => { let value = self.get_target_value(target); @@ -1505,24 +1619,108 @@ impl CPU { fn main() { + + /* + { + "name": "0A 0000", + "initial": { + "pc": 16826, + "sp": 9383, + "a": 64, + "b": 95, + "c": 205, + "d": 147, + "e": 168, + "f": 64, + "h": 98, + "l": 251, + "ime": 0, + "ie": 0, + "ram": [ + [ + 16826, + 10 + ], + [ + 24525, + 204 + ] + ] + }, + "final": { + "a": 204, + "b": 95, + "c": 205, + "d": 147, + "e": 168, + "f": 64, + "h": 98, + "l": 251, + "pc": 16827, + "sp": 9383, + "ime": 0, + "ram": [ + [ + 16826, + 10 + ], + [ + 24525, + 204 + ] + ] + }, + "cycles": [ + [ + 16826, + 10, + "r-m" + ], + [ + 24525, + 204, + "r-m" + ] + ] +} + */ + for entry in glob("sm83/v1/*.json").unwrap() { + let json_file = std::fs::read(entry.unwrap()).unwrap(); + let tests: Value = serde_json::from_slice(json_file.as_slice()).unwrap(); + for test in tests.as_array().unwrap() { + println!("{}", test["name"]); + let mut gameboy = CPU::load_test(&test["initial"]); + gameboy.execute_next_instruction(); + gameboy.compare_state(&test["final"]); + } + } + +} +fn run_gameboy() { let boot_rom = std::fs::read("boot/dmg.bin").unwrap(); let game_rom= GameRom::load("cpu_instrs/cpu_instrs.gb"); - + // let game_rom = std::fs::read("cpu_instrs/cpu_instrs.gb").unwrap(); - + let mut gameboy = CPU{ - registers:Registers{a:0,b:0,c:0,d:0,e:0,f:FlagsRegister::from(0),h:0,l:0}, - pc:0, - bus:MemoryBus{ - memory: [0xFFu8; 0xFFFF], + registers:Registers{a:0,b:0,c:0,d:0,e:0,f:FlagsRegister::from(0),h:0,l:0}, + pc:0, + bus:MemoryBus{ + memory: [0xFFu8; 0xFFFF+1], gpu:GPU{ vram: vec![0xFFu8;VRAM_END-VRAM_BEGIN+1], tile_set: [[[TilePixelValue::Zero; 8]; 8]; 384] }, rom: game_rom, boot: boot_rom, - }, + flat_ram: false + }, sp: 0, }; gameboy.init(); + let mut count =0; loop { + count += 1; + if count % 1000 == 0 { + println!("PC: {:04X}, {count}", gameboy.pc); + } gameboy.execute_next_instruction() } -} +} \ No newline at end of file diff --git a/src/registers.rs b/src/registers.rs index f8b6878..2a97b8a 100644 --- a/src/registers.rs +++ b/src/registers.rs @@ -1,5 +1,6 @@ -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] +#[derive(PartialEq)] pub(crate) struct FlagsRegister { pub(crate) zero: bool, pub(crate) subtract: bool, @@ -36,6 +37,7 @@ impl From for FlagsRegister { } } } +#[derive(Debug)] pub(crate) struct Registers { pub(crate) a: u8, pub(crate) b: u8,