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.
This commit is contained in:
2025-05-09 13:47:26 +01:00
parent 9be93f4aa9
commit e9a40bd9f7
8 changed files with 459 additions and 156 deletions

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "sm83"]
path = sm83
url = https://github.com/SingleStepTests/sm83.git

View File

@@ -7,6 +7,7 @@
<w>addsp</w> <w>addsp</w>
<w>dechl</w> <w>dechl</w>
<w>inchl</w> <w>inchl</w>
<w>incu</w>
<w>instrs</w> <w>instrs</w>
<w>jpcc</w> <w>jpcc</w>
<w>jphl</w> <w>jphl</w>

1
.idea/vcs.xml generated
View File

@@ -2,5 +2,6 @@
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" /> <mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/sm83" vcs="Git" />
</component> </component>
</project> </project>

95
Cargo.lock generated
View File

@@ -2,6 +2,101 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 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]] [[package]]
name = "untitled" name = "untitled"
version = "0.1.0" version = "0.1.0"
dependencies = [
"glob",
"serde_json",
]

View File

@@ -6,3 +6,5 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
serde_json = "1.0.140"
glob = "0.3.2"

1
sm83 Submodule

Submodule sm83 added at f9c3021024

View File

@@ -1,8 +1,10 @@
mod registers; mod registers;
use std::ops::Index; use glob::glob;
use serde_json::Value;
use crate::registers::FlagsRegister; use crate::registers::FlagsRegister;
use crate::registers::Registers; use crate::registers::Registers;
use crate::Target::Immediate;
#[derive(Debug)] #[derive(Debug)]
struct GameRom { struct GameRom {
@@ -114,12 +116,14 @@ impl GameRom {
} }
} }
#[derive(Debug)]
struct CPU { struct CPU {
registers: Registers, registers: Registers,
pc: u16, pc: u16,
bus: MemoryBus, bus: MemoryBus,
sp: u16, sp: u16,
} }
const BOOT_BEGIN: usize = 0x0000; const BOOT_BEGIN: usize = 0x0000;
const BOOT_END: usize = 0x00FF; const BOOT_END: usize = 0x00FF;
const CART_BEGIN: usize = 0x0100; const CART_BEGIN: usize = 0x0100;
@@ -127,8 +131,9 @@ const CART_END: usize = 0x7FFF;
const VRAM_BEGIN: usize = 0x8000; const VRAM_BEGIN: usize = 0x8000;
const VRAM_END: usize = 0x9FFF; const VRAM_END: usize = 0x9FFF;
#[derive(Debug)]
struct GPU { vram: Vec<u8>, tile_set: [[[TilePixelValue; 8]; 8]; 384] } struct GPU { vram: Vec<u8>, tile_set: [[[TilePixelValue; 8]; 8]; 384] }
#[derive(Copy, Clone)] #[derive(Copy, Clone, Debug)]
enum TilePixelValue { Three, Two, One, Zero } enum TilePixelValue { Three, Two, One, Zero }
impl GPU { impl GPU {
@@ -136,7 +141,7 @@ impl GPU {
self.vram[address] self.vram[address]
} }
fn write_vram(&mut self, index: usize, value: u8) { 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 // If our index is greater than 0x1800, we're not writing to the tile set storage
// so we can just return. // so we can just return.
if index >= 0x1800 { return } if index >= 0x1800 { return }
@@ -197,15 +202,21 @@ impl GPU {
} }
} }
#[derive(Debug)]
struct MemoryBus { struct MemoryBus {
memory: [u8; 0xFFFF], memory: [u8; 0xFFFF+1],
gpu: GPU, gpu: GPU,
rom: GameRom, rom: GameRom,
boot: Vec<u8>, boot: Vec<u8>,
flat_ram: bool,
} }
impl MemoryBus { impl MemoryBus {
fn read_byte(&self, address: u16) -> u8 { fn read_byte(&self, address: u16) -> u8 {
let address = address as usize; let address = address as usize;
if self.flat_ram {
return self.memory[address]
}
match address { match address {
VRAM_BEGIN ..= VRAM_END => { VRAM_BEGIN ..= VRAM_END => {
self.gpu.read_vram(address - VRAM_BEGIN) self.gpu.read_vram(address - VRAM_BEGIN)
@@ -217,16 +228,25 @@ impl MemoryBus {
self.rom.read_byte(address) 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] _ => self.memory[address]
} }
} }
fn write_byte(&mut self, address: u16, value: u8) { 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; let address = address as usize;
match address { match address {
VRAM_BEGIN ..= VRAM_END => { VRAM_BEGIN ..= VRAM_END => {
self.gpu.write_vram(address - VRAM_BEGIN, value) self.gpu.write_vram(address - VRAM_BEGIN, value)
} }
CART_BEGIN ..= CART_END => {
self.rom.write_byte(address, value)
}
_ => self.memory[address as usize] = value _ => self.memory[address as usize] = value
} }
} }
@@ -292,7 +312,7 @@ enum Instruction {
ADC(Target), ADC(Target),
ADD(Target), ADD(Target),
ADDHL(TargetU16Register), ADDHL(TargetU16Register),
ADDSP(u8), ADDSP(i8),
AND(Target), AND(Target),
BIT(u8, Target), BIT(u8, Target),
CALL(Condition, u16), CALL(Condition, u16),
@@ -301,12 +321,12 @@ enum Instruction {
CPL, CPL,
DAA, DAA,
DEC(Target), DEC(Target),
DECHL(TargetU16Register), DECU16(TargetU16Register),
DI, DI,
EI, EI,
HALT, HALT,
INC(Target), INC(Target),
INCHL(TargetU16Register), INCU16(TargetU16Register),
JP(Condition, u16), JP(Condition, u16),
JPHL, JPHL,
JR(Condition, i8), JR(Condition, i8),
@@ -346,6 +366,82 @@ impl CPU {
println!("CPU init"); println!("CPU init");
self.bus.write_byte(0xFF50, 0x00); 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 { fn check_condition(&self, condition: Condition) -> bool {
match condition { match condition {
Condition::NZ => !self.registers.f.zero, Condition::NZ => !self.registers.f.zero,
@@ -406,8 +502,8 @@ impl CPU {
self.sp = self.sp.wrapping_sub(2); self.sp = self.sp.wrapping_sub(2);
} }
fn pop_stack(&mut self) -> u16 { fn pop_stack(&mut self) -> u16 {
let low = (self.bus.read_byte(self.sp) as u16) << 8; let low = self.bus.read_byte(self.sp) as u16;
let high = self.bus.read_byte(self.sp + 1) as u16; let high = (self.bus.read_byte(self.sp + 1) as u16) << 8;
self.sp = self.sp.wrapping_add(2); self.sp = self.sp.wrapping_add(2);
high | low high | low
} }
@@ -441,7 +537,7 @@ impl CPU {
0x00 => { Instruction::NOP } 0x00 => { Instruction::NOP }
0x01 => { Instruction::LD(LoadTarget::CopyR16N16(TargetU16Register::BC, self.bus.read_u16(self.pc.wrapping_add(1)))) } 0x01 => { Instruction::LD(LoadTarget::CopyR16N16(TargetU16Register::BC, self.bus.read_u16(self.pc.wrapping_add(1)))) }
0x02 => { Instruction::LD(LoadTarget::CopyR16A(TargetU16Register::BC)) } 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)) } 0x04 => { Instruction::INC(Target::U8Register(TargetRegister::B)) }
0x05 => { Instruction::DEC(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)))) } 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)))) } 0x08 => { Instruction::LD(LoadTarget::CopyN16SP(self.bus.read_u16(self.pc.wrapping_add(1)))) }
0x09 => { Instruction::ADDHL(TargetU16Register::BC) } 0x09 => { Instruction::ADDHL(TargetU16Register::BC) }
0x0A => { Instruction::LD(LoadTarget::CopyAR16(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)) } 0x0C => { Instruction::INC(Target::U8Register(TargetRegister::C)) }
0x0D => { Instruction::DEC(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)))) } 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))) } 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)))) } 0x11 => { Instruction::LD(LoadTarget::CopyR16N16(TargetU16Register::DE, self.bus.read_u16(self.pc.wrapping_add(1)))) }
0x12 => { Instruction::LD(LoadTarget::CopyR16A(TargetU16Register::DE)) } 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)) } 0x14 => { Instruction::INC(Target::U8Register(TargetRegister::D)) }
0x15 => { Instruction::DEC(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)))) } 0x16 => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::D, self.bus.read_byte(self.pc.wrapping_add(1)))) }
0x17 => { Instruction::RL(Target::U8Register(TargetRegister::A)) } 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) } 0x19 => { Instruction::ADDHL(TargetU16Register::DE) }
0x1A => { Instruction::LD(LoadTarget::CopyAR16(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)) } 0x1C => { Instruction::INC(Target::U8Register(TargetRegister::E)) }
0x1D => { Instruction::DEC(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)))) } 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) } 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)))) } 0x21 => { Instruction::LD(LoadTarget::CopyR16N16(TargetU16Register::HL, self.bus.read_u16(self.pc.wrapping_add(1)))) }
0x22 => { Instruction::LD(LoadTarget::CopyHLIA) } 0x22 => { Instruction::LD(LoadTarget::CopyHLIA) }
0x23 => { Instruction::INC(Target::U16Register(TargetU16Register::HL)) } 0x23 => { Instruction::INCU16(TargetU16Register::HL) }
0x24 => { Instruction::INC(Target::U8Register(TargetRegister::H)) } 0x24 => { Instruction::INC(Target::U8Register(TargetRegister::H)) }
0x25 => { Instruction::DEC(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)))) } 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) } 0x28 => { Instruction::JR(Condition::Z, self.bus.read_byte(self.pc.wrapping_add(1)) as i8) }
0x29 => { Instruction::ADDHL(TargetU16Register::HL) } 0x29 => { Instruction::ADDHL(TargetU16Register::HL) }
0x2A => { Instruction::LD(LoadTarget::CopyAHLI) } 0x2A => { Instruction::LD(LoadTarget::CopyAHLI) }
0x2B => { Instruction::DEC(Target::U16Register(TargetU16Register::HL)) } 0x2B => { Instruction::DECU16(TargetU16Register::HL) }
0x2C => { Instruction::INC(Target::U8Register(TargetRegister::L)) } 0x2C => { Instruction::INC(Target::U8Register(TargetRegister::L)) }
0x2D => { Instruction::DEC(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)))) } 0x2E => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::L, self.bus.read_byte(self.pc.wrapping_add(1)))) }
0x2F => { Instruction::CPL } 0x2F => { Instruction::CPL }
0x30 => { Instruction::JR(Condition::NC, self.bus.read_byte(self.pc.wrapping_add(1)) as i8) } 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) } 0x32 => { Instruction::LD(LoadTarget::CopyHLDA) }
0x33 => { Instruction::INC(Target::U16Register(TargetU16Register::SP)) } 0x33 => { Instruction::INCU16(TargetU16Register::SP) }
0x34 => { Instruction::INC(Target::Address(self.get_u16_reg_value(TargetU16Register::HL))) } 0x34 => { Instruction::INC(Target::U16Register(TargetU16Register::HL)) }
0x35 => { Instruction::DEC(Target::Address(self.get_u16_reg_value(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)))) } 0x36 => { Instruction::LD(LoadTarget::CopyHLN8(self.bus.read_byte(self.pc.wrapping_add(1)))) }
0x37 => { Instruction::SCF } 0x37 => { Instruction::SCF }
0x38 => { Instruction::JR(Condition::C, self.bus.read_byte(self.pc.wrapping_add(1)) as i8) } 0x38 => { Instruction::JR(Condition::C, self.bus.read_byte(self.pc.wrapping_add(1)) as i8) }
0x39 => { Instruction::ADDHL(TargetU16Register::SP) } 0x39 => { Instruction::ADDHL(TargetU16Register::SP) }
0x3A => { Instruction::LD(LoadTarget::CopyAHLD)} 0x3A => { Instruction::LD(LoadTarget::CopyAHLD)}
0x3B => { Instruction::DEC(Target::U16Register(TargetU16Register::SP)) } 0x3B => { Instruction::DECU16(TargetU16Register::SP) }
0x3C => { Instruction::INC(Target::U8Register(TargetRegister::A)) } 0x3C => { Instruction::INC(Target::U8Register(TargetRegister::A)) }
0x3D => { Instruction::DEC(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)))) } 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)) } 0x7B => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::E)) }
0x7C => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::H)) } 0x7C => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::H)) }
0x7D => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::L)) } 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)) } 0x7F => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::A)) }
0x80 => { Instruction::ADD(Target::U8Register(TargetRegister::B)) } 0x80 => { Instruction::ADD(Target::U8Register(TargetRegister::B)) }
0x81 => { Instruction::ADD(Target::U8Register(TargetRegister::C)) } 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))) } 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))) } 0xC4 => { Instruction::CALL(Condition::NZ, self.bus.read_u16(self.pc.wrapping_add(1))) }
0xC5 => { Instruction::PUSH(TargetU16Register::BC) } 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) } 0xC7 => { Instruction::RST(0x00) }
0xC8 => { Instruction::RET(Condition::Z) } 0xC8 => { Instruction::RET(Condition::Z) }
0xC9 => { Instruction::RET(Condition::None) } 0xC9 => { Instruction::RET(Condition::None) }
@@ -665,23 +761,23 @@ impl CPU {
0xE5 => { Instruction::PUSH(TargetU16Register::HL) } 0xE5 => { Instruction::PUSH(TargetU16Register::HL) }
0xE6 => { Instruction::AND(Target::Immediate(self.bus.read_byte(self.pc.wrapping_add(1)))) } 0xE6 => { Instruction::AND(Target::Immediate(self.bus.read_byte(self.pc.wrapping_add(1)))) }
0xE7 => { Instruction::RST(0x04) } 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 } 0xE9 => { Instruction::JPHL }
0xEA => { Instruction::LD(LoadTarget::CopyN16A(self.bus.read_u16(self.pc.wrapping_add(1)))) } 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) } 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) } 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 } 0xF3 => { Instruction::DI }
0xF5 => { Instruction::PUSH(TargetU16Register::AF) } 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) } 0xF7 => { Instruction::RST(0x06) }
0xF8 => { Instruction::LD(LoadTarget::CopyHLSPE8(self.bus.read_byte(self.pc.wrapping_add(1)) as i8)) } 0xF8 => { Instruction::LD(LoadTarget::CopyHLSPE8(self.bus.read_byte(self.pc.wrapping_add(1)) as i8)) }
0xF9 => { Instruction::LD(LoadTarget::CopySPHL) } 0xF9 => { Instruction::LD(LoadTarget::CopySPHL) }
0xFA => { Instruction::LD(LoadTarget::CopyAN16(self.bus.read_u16(self.pc.wrapping_add(1)))) } 0xFA => { Instruction::LD(LoadTarget::CopyAN16(self.bus.read_u16(self.pc.wrapping_add(1)))) }
0xFB => { Instruction::EI } 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) } 0xFF => { Instruction::RST(0x07) }
_ => { panic!("Invalid u8 opcode: {:02X}", opcode); } _ => { panic!("Invalid u8 opcode: {:02X}", opcode); }
} }
@@ -948,24 +1044,26 @@ impl CPU {
} }
} }
fn execute_next_instruction(&mut self) { fn execute_next_instruction(&mut self) {
if self.pc >= 0x100 { // if self.pc >= 0x100 {
println!("Boot Complete"); // println!("Boot Complete");
} // }
let inst = self.next_instruction(); let inst = self.next_instruction();
println!("{} {:?}", self.pc, inst); println!("{:x} {:?} {:?}", self.pc, inst, self.registers.f);
self.execute(inst); self.execute(inst);
} }
fn execute(&mut self, instruction: Instruction) { fn execute(&mut self, instruction: Instruction) {
match instruction { match instruction {
Instruction::ADC(target) => { Instruction::ADC(target) => {
let value = self.get_target_value(target); let value = self.get_target_value(target);
let (mut new_value, did_overflow) = self.registers.a.overflowing_add(value); let carry = self.registers.f.carry as u8;
new_value += did_overflow as u8; let (value_with_carry, carry_overflow) = value.overflowing_add(carry);
self.registers.a = new_value; let (new_value, did_overflow) = self.registers.a.overflowing_add(value_with_carry);
self.registers.f.zero = new_value == 0; self.registers.f.zero = new_value == 0;
self.registers.f.subtract = false; self.registers.f.subtract = false;
self.registers.f.carry = did_overflow; self.registers.f.half_carry = (self.registers.a & 0xF) + (value & 0xF) + carry > 0xF;
self.registers.f.half_carry = (self.registers.a & 0xF) + (value & 0xF) > 0xF; self.registers.f.carry = did_overflow | carry_overflow;
self.registers.a = new_value;
self.pc += match target { self.pc += match target {
Target::Immediate(_) => {2} Target::Immediate(_) => {2}
_ => {1} _ => {1}
@@ -974,34 +1072,37 @@ impl CPU {
Instruction::ADD(target) => { Instruction::ADD(target) => {
let value = self.get_target_value(target); let value = self.get_target_value(target);
let (new_value, did_overflow) = self.registers.a.overflowing_add(value); 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.zero = new_value == 0;
self.registers.f.subtract = false; self.registers.f.subtract = false;
self.registers.f.carry = did_overflow; 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) > 0xF;
self.registers.a = new_value;
self.pc += match target { self.pc += match target {
Target::Immediate(_) => {2} Target::Immediate(_) => {2}
_ => {1} _ => {1}
}; };
} }
Instruction::ADDHL(target) => { Instruction::ADDHL(target) => {
let value = self.get_u16_reg_value(target); let value_source = self.get_u16_reg_value(target);
let (new_value, did_overflow) = self.registers.get_hl().overflowing_add(value); 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.set_hl(new_value);
self.registers.f.zero = new_value == 0;
self.registers.f.subtract = false; self.registers.f.subtract = false;
self.registers.f.carry = did_overflow; 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; self.pc += 1;
} }
Instruction::ADDSP(value) => { Instruction::ADDSP(value) => {
let offset = (value as i16) as u16; let new_value = self.sp.wrapping_add_signed(value as i16);
let (new_value, did_overflow) = self.sp.overflowing_add(offset); // 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.sp = new_value;
self.registers.f.zero = false; self.registers.f.zero = false;
self.registers.f.subtract = 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; self.pc += 2;
} }
Instruction::AND(target) => { Instruction::AND(target) => {
@@ -1021,7 +1122,7 @@ impl CPU {
self.registers.f.zero = value >> bit & 0x1 == 0; self.registers.f.zero = value >> bit & 0x1 == 0;
self.registers.f.subtract = false; self.registers.f.subtract = false;
self.registers.f.half_carry = true; self.registers.f.half_carry = true;
self.pc += 2; self.pc = self.pc.wrapping_add(2);
} }
Instruction::CALL(condition, address) => { Instruction::CALL(condition, address) => {
if self.check_condition(condition) { 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 + 1, ((pc >> 8) & 0xFF) as u8);
self.bus.write_byte(self.sp, (pc & 0xFF) as u8); self.bus.write_byte(self.sp, (pc & 0xFF) as u8);
self.pc = address; self.pc = address;
} else {
self.pc = self.pc.wrapping_add(3);
} }
} }
Instruction::CCF => { Instruction::CCF => {
@@ -1044,7 +1147,8 @@ impl CPU {
self.registers.f.zero = new_value == 0; self.registers.f.zero = new_value == 0;
self.registers.f.subtract = true; self.registers.f.subtract = true;
self.registers.f.carry = did_overflow; 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 { self.pc += match target {
Target::Immediate(_) => {2} Target::Immediate(_) => {2}
_ => {1} _ => {1}
@@ -1067,51 +1171,45 @@ impl CPU {
if self.registers.f.carry { if self.registers.f.carry {
adjustment += 0x60; 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 { } else {
if self.registers.f.half_carry || self.registers.a & 0xF > 0x9 { if self.registers.f.half_carry || self.registers.a & 0xF > 0x9 {
adjustment += 0x6; adjustment += 0x6;
} }
if self.registers.f.carry || self.registers.a > 0x99 { if self.registers.f.carry || self.registers.a > 0x99 {
adjustment += 0x60; 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.a = new_value;
self.registers.f.carry = did_overflow;
self.registers.f.zero = new_value == 0; self.registers.f.zero = new_value == 0;
self.registers.f.half_carry = false; self.registers.f.half_carry = false;
self.registers.f.carry = did_overflow;
self.pc += 1; self.pc += 1;
} }
Instruction::DEC(target) => { 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 value = self.get_target_value(target);
let new_value = value.wrapping_sub(1); let new_value = value.wrapping_sub(1);
self.set_target_value(target, new_value); self.set_target_value(target, new_value);
self.registers.f.zero = new_value == 0; self.registers.f.zero = new_value == 0;
self.registers.f.subtract = true; self.registers.f.subtract = true;
self.registers.f.half_carry = (new_value & 0xF) + (value & 0xF) > 0xF; let (_, half_carry) = (value & 0xF).overflowing_sub(1);
self.pc += 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 => { Instruction::DI => {
self.pc += 1; self.pc += 1;
@@ -1124,38 +1222,25 @@ impl CPU {
} }
Instruction::INC(target) => { 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 value = self.get_target_value(target);
let new_value = value.wrapping_add(1); let new_value = value.wrapping_add(1);
self.set_target_value(target, new_value); self.set_target_value(target, new_value);
self.registers.f.zero = new_value == 0; self.registers.f.zero = new_value == 0;
self.registers.f.subtract = true; self.registers.f.subtract = false;
self.registers.f.half_carry = (new_value & 0xF) + (value & 0xF) > 0xF; 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; self.pc += 1;
} }
Instruction::JP(condition, address) => { Instruction::JP(condition, address) => {
if self.check_condition(condition) { if self.check_condition(condition) {
self.pc = address; self.pc = address;
} else {
self.pc = self.pc.wrapping_add(3);
} }
} }
Instruction::JPHL => { Instruction::JPHL => {
@@ -1164,10 +1249,14 @@ impl CPU {
Instruction::JR(condition, offset) => { Instruction::JR(condition, offset) => {
if self.check_condition(condition) { if self.check_condition(condition) {
self.pc = if offset.is_negative() { 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 { } 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) => { Instruction::LD(target) => {
@@ -1175,7 +1264,7 @@ impl CPU {
LoadTarget::CopyR8R8(dest_register, source_register) => { LoadTarget::CopyR8R8(dest_register, source_register) => {
let value = self.get_u8_reg_value(source_register); let value = self.get_u8_reg_value(source_register);
self.set_u8_reg_value(dest_register, value); self.set_u8_reg_value(dest_register, value);
self.pc += 1; self.pc = self.pc.wrapping_add(1);
} }
LoadTarget::CopyR8N8(dest_register, value) => { LoadTarget::CopyR8N8(dest_register, value) => {
self.set_u8_reg_value(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 value = self.get_u8_reg_value(source_register);
let address = self.registers.get_hl(); let address = self.registers.get_hl();
self.bus.write_byte(address, value); self.bus.write_byte(address, value);
self.pc += 1; self.pc = self.pc.wrapping_add(1);
} }
LoadTarget::CopyHLN8(value) => { LoadTarget::CopyHLN8(value) => {
let address = self.registers.get_hl(); let address = self.registers.get_hl();
@@ -1215,13 +1304,19 @@ impl CPU {
let offset = self.get_target_value(target); let offset = self.get_target_value(target);
let address = 0xFF00 | offset as u16; let address = 0xFF00 | offset as u16;
self.bus.write_byte(address, self.registers.a); 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) => { LoadTarget::CopyAPort(target) => {
let offset = self.get_target_value(target); let offset = self.get_target_value(target);
let address = 0xFF00 | offset as u16; let address = 0xFF00 | offset as u16;
self.registers.a = self.bus.read_byte(address); 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) => { LoadTarget::CopyHN16A(address) => {
if address >= 0xFF00 { if address >= 0xFF00 {
@@ -1279,24 +1374,21 @@ impl CPU {
} }
LoadTarget::CopySPN16(value) => { LoadTarget::CopySPN16(value) => {
self.sp = value; self.sp = value;
self.pc += 3; self.pc = self.pc.wrapping_add(3);
} }
LoadTarget::CopyN16SP(address) => { LoadTarget::CopyN16SP(address) => {
self.bus.write_byte(address, (0xF & self.sp) as u8); self.bus.write_u16(address, self.sp);
self.bus.write_byte(address + 1, (self.sp >> 8) as u8);
self.pc += 3; self.pc += 3;
} }
LoadTarget::CopyHLSPE8(value) => { LoadTarget::CopyHLSPE8(value) => {
let new_value = self.sp.wrapping_add_signed(value as i16);
let new_value = match value.is_negative() { let carry = (self.sp as i32 ^ value as i32 ^ new_value as i32) & 0x100 != 0;
true => { self.sp.wrapping_sub(value.abs() as u16) } let half_carry = (self.sp as i32 ^ value as i32 ^ new_value as i32) & 0x10 != 0;
false => { self.sp.wrapping_add(value as u16) }
};
self.registers.set_hl(new_value); self.registers.set_hl(new_value);
self.registers.f.zero = false; self.registers.f.zero = false;
self.registers.f.subtract = false; self.registers.f.subtract = false;
self.registers.f.carry = (new_value & 0xFF) + (self.sp & 0xFF) > 0xFF; self.registers.f.carry = carry;
self.registers.f.half_carry = (new_value & 0xF) + (self.sp & 0xF) > 0xF; self.registers.f.half_carry = half_carry;
self.pc += 2; self.pc += 2;
} }
LoadTarget::CopySPHL => { LoadTarget::CopySPHL => {
@@ -1334,7 +1426,7 @@ impl CPU {
let mut value = self.get_target_value(target); let mut value = self.get_target_value(target);
value &= !(1 << bit); value &= !(1 << bit);
self.set_target_value(target, value); self.set_target_value(target, value);
self.pc += 2; self.pc = self.pc.wrapping_add(2);
} }
Instruction::RET(condition) => { Instruction::RET(condition) => {
if self.check_condition(condition) { if self.check_condition(condition) {
@@ -1350,70 +1442,92 @@ impl CPU {
let old_value = self.get_target_value(target); let old_value = self.get_target_value(target);
let old_carry = self.registers.f.carry; let old_carry = self.registers.f.carry;
self.registers.f.carry = old_value & 0x80 == 0x80; 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.set_target_value(target, new_value);
self.registers.f.zero = false;
self.registers.f.subtract = false; self.registers.f.subtract = false;
self.registers.f.half_carry = false; self.registers.f.half_carry = false;
self.pc += match self.bus.read_byte(self.pc) { match self.bus.read_byte(self.pc) {
0x17 => {1} 0x17 => {
_ => {2} 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) => { Instruction::RLC(target) => {
let old_value = self.get_target_value(target); let old_value = self.get_target_value(target);
self.registers.f.carry = old_value & 0x80 == 0x80; 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.set_target_value(target, new_value);
self.registers.f.zero = false;
self.registers.f.subtract = false; self.registers.f.subtract = false;
self.registers.f.half_carry = false; self.registers.f.half_carry = false;
self.pc += match self.bus.read_byte(self.pc) { self.pc += match self.bus.read_byte(self.pc) {
0x07 => {1} 0x07 => {
_ => {2} self.registers.f.zero = false;
1
}
_ => {
self.registers.f.zero = new_value == 0;
2
}
} }
} }
Instruction::RR(target) => { Instruction::RR(target) => {
let old_value = self.get_target_value(target); let old_value = self.get_target_value(target);
let old_carry = self.registers.f.carry; let old_carry = self.registers.f.carry;
self.registers.f.carry = old_value & 0x1 == 0x1; 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.set_target_value(target, new_value);
self.registers.f.zero = false;
self.registers.f.subtract = false; self.registers.f.subtract = false;
self.registers.f.half_carry = false; self.registers.f.half_carry = false;
self.pc += match self.bus.read_byte(self.pc) { self.pc += match self.bus.read_byte(self.pc) {
0x1F => {1} 0x1F => {
_ => {2} self.registers.f.zero = false;
1
}
_ => {
self.registers.f.zero = new_value == 0;
2
}
} }
} }
Instruction::RRC(target) => { Instruction::RRC(target) => {
let old_value = self.get_target_value(target); let old_value = self.get_target_value(target);
self.registers.f.carry = old_value & 0x1 == 0x1; 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.set_target_value(target, new_value);
self.registers.f.zero = false;
self.registers.f.subtract = false; self.registers.f.subtract = false;
self.registers.f.half_carry = false; self.registers.f.half_carry = false;
self.pc += match self.bus.read_byte(self.pc) { self.pc += match self.bus.read_byte(self.pc) {
0x0F => {1} 0x0F => {
_ => {2} self.registers.f.zero = false;
1
}
_ => {
self.registers.f.zero = new_value == 0;
2
}
} }
} }
Instruction::RST(idx) => { Instruction::RST(idx) => {
self.push_stack(self.pc); self.push_stack(self.pc.wrapping_add(1));
let address = self.bus.read_byte((idx as u16) * 8) as u16; self.pc = idx as u16 * 8;
self.pc = address;
} }
Instruction::SBC(target) => { Instruction::SBC(target) => {
let value = self.get_target_value(target); let value = self.get_target_value(target);
let (mut new_value, did_overflow) = self.registers.a.overflowing_sub(value); let carry = self.registers.f.carry as u8;
new_value -= did_overflow as u8; let (new_value_with_carry, carry_overflow) = self.registers.a.overflowing_sub(carry);
self.registers.a = new_value; let (new_value, did_overflow) = new_value_with_carry.overflowing_sub(value);
self.registers.f.zero = new_value == 0; self.registers.f.zero = new_value == 0;
self.registers.f.subtract = true; self.registers.f.subtract = true;
self.registers.f.carry = did_overflow; self.registers.f.carry = did_overflow | carry_overflow;
self.registers.f.half_carry = (self.registers.a & 0xF) + (value & 0xF) > 0xF; 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 { self.pc += match target {
Target::Immediate(_) => {2} Target::Immediate(_) => {2}
_ => {1} _ => {1}
@@ -1429,7 +1543,7 @@ impl CPU {
let mut value = self.get_target_value(target); let mut value = self.get_target_value(target);
value |= 1 << bit; value |= 1 << bit;
self.set_target_value(target, value); self.set_target_value(target, value);
self.pc += 2; self.pc = self.pc.wrapping_add(2);
} }
Instruction::SLA(target) => { Instruction::SLA(target) => {
let value = self.get_target_value(target); let value = self.get_target_value(target);
@@ -1439,7 +1553,7 @@ impl CPU {
self.registers.f.subtract = false; self.registers.f.subtract = false;
self.registers.f.carry = value & 0x80 == 0x80; self.registers.f.carry = value & 0x80 == 0x80;
self.registers.f.half_carry = false; self.registers.f.half_carry = false;
self.pc += 2; self.pc = self.pc.wrapping_add(2);
} }
Instruction::SRA(target) => { Instruction::SRA(target) => {
let value = self.get_target_value(target); let value = self.get_target_value(target);
@@ -1447,7 +1561,7 @@ impl CPU {
self.set_target_value(target, new_value); self.set_target_value(target, new_value);
self.registers.f.zero = new_value == 0; self.registers.f.zero = new_value == 0;
self.registers.f.subtract = false; 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.registers.f.half_carry = false;
self.pc += 2; self.pc += 2;
} }
@@ -1459,10 +1573,10 @@ impl CPU {
self.registers.f.subtract = false; self.registers.f.subtract = false;
self.registers.f.carry = value & 0x1 == 0x1; self.registers.f.carry = value & 0x1 == 0x1;
self.registers.f.half_carry = false; self.registers.f.half_carry = false;
self.pc += 2; self.pc = self.pc.wrapping_add(2);
} }
Instruction::STOP(_) => { Instruction::STOP(_) => {
self.pc += 2; self.pc += 1;
} }
Instruction::SUB(target) => { Instruction::SUB(target) => {
let value = self.get_target_value(target); let value = self.get_target_value(target);
@@ -1485,7 +1599,7 @@ impl CPU {
self.registers.f.subtract = false; self.registers.f.subtract = false;
self.registers.f.carry = false; self.registers.f.carry = false;
self.registers.f.half_carry = false; self.registers.f.half_carry = false;
self.pc += 2; self.pc = self.pc.wrapping_add(2);
} }
Instruction::XOR(target) => { Instruction::XOR(target) => {
let value = self.get_target_value(target); let value = self.get_target_value(target);
@@ -1505,6 +1619,84 @@ impl CPU {
fn main() { 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 boot_rom = std::fs::read("boot/dmg.bin").unwrap();
let game_rom= GameRom::load("cpu_instrs/cpu_instrs.gb"); let game_rom= GameRom::load("cpu_instrs/cpu_instrs.gb");
@@ -1514,15 +1706,21 @@ fn main() {
registers:Registers{a:0,b:0,c:0,d:0,e:0,f:FlagsRegister::from(0),h:0,l:0}, registers:Registers{a:0,b:0,c:0,d:0,e:0,f:FlagsRegister::from(0),h:0,l:0},
pc:0, pc:0,
bus:MemoryBus{ bus:MemoryBus{
memory: [0xFFu8; 0xFFFF], memory: [0xFFu8; 0xFFFF+1],
gpu:GPU{ vram: vec![0xFFu8;VRAM_END-VRAM_BEGIN+1], tile_set: [[[TilePixelValue::Zero; 8]; 8]; 384] }, gpu:GPU{ vram: vec![0xFFu8;VRAM_END-VRAM_BEGIN+1], tile_set: [[[TilePixelValue::Zero; 8]; 8]; 384] },
rom: game_rom, rom: game_rom,
boot: boot_rom, boot: boot_rom,
flat_ram: false
}, },
sp: 0, sp: 0,
}; };
gameboy.init(); gameboy.init();
let mut count =0;
loop { loop {
count += 1;
if count % 1000 == 0 {
println!("PC: {:04X}, {count}", gameboy.pc);
}
gameboy.execute_next_instruction() gameboy.execute_next_instruction()
} }
} }

View File

@@ -1,5 +1,6 @@
#[derive(Clone, Copy)] #[derive(Clone, Copy, Debug)]
#[derive(PartialEq)]
pub(crate) struct FlagsRegister { pub(crate) struct FlagsRegister {
pub(crate) zero: bool, pub(crate) zero: bool,
pub(crate) subtract: bool, pub(crate) subtract: bool,
@@ -36,6 +37,7 @@ impl From<u8> for FlagsRegister {
} }
} }
} }
#[derive(Debug)]
pub(crate) struct Registers { pub(crate) struct Registers {
pub(crate) a: u8, pub(crate) a: u8,
pub(crate) b: u8, pub(crate) b: u8,