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>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
1
.idea/vcs.xml
generated
@@ -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
95
Cargo.lock
generated
@@ -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",
|
||||||
|
]
|
||||||
|
|||||||
@@ -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
1
sm83
Submodule
Submodule sm83 added at f9c3021024
492
src/main.rs
492
src/main.rs
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user