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:
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "sm83"]
|
||||
path = sm83
|
||||
url = https://github.com/SingleStepTests/sm83.git
|
||||
1
.idea/dictionaries/project.xml
generated
1
.idea/dictionaries/project.xml
generated
@@ -7,6 +7,7 @@
|
||||
<w>addsp</w>
|
||||
<w>dechl</w>
|
||||
<w>inchl</w>
|
||||
<w>incu</w>
|
||||
<w>instrs</w>
|
||||
<w>jpcc</w>
|
||||
<w>jphl</w>
|
||||
|
||||
1
.idea/vcs.xml
generated
1
.idea/vcs.xml
generated
@@ -2,5 +2,6 @@
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/sm83" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
95
Cargo.lock
generated
95
Cargo.lock
generated
@@ -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",
|
||||
]
|
||||
|
||||
@@ -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"
|
||||
1
sm83
Submodule
1
sm83
Submodule
Submodule sm83 added at f9c3021024
492
src/main.rs
492
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<u8>, 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<u8>,
|
||||
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),
|
||||
@@ -346,6 +366,82 @@ impl CPU {
|
||||
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,6 +1619,84 @@ 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");
|
||||
|
||||
@@ -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},
|
||||
pc:0,
|
||||
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] },
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -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<u8> for FlagsRegister {
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Registers {
|
||||
pub(crate) a: u8,
|
||||
pub(crate) b: u8,
|
||||
|
||||
Reference in New Issue
Block a user