Compare commits
No commits in common. "9b3ab73bb11c8aece7479f05ca18a1101cffa596" and "999f661e6bf93ce908cb7f6014270b8759f826bd" have entirely different histories.
9b3ab73bb1
...
999f661e6b
|
@ -58,15 +58,6 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ansi_term"
|
|
||||||
version = "0.12.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
|
||||||
dependencies = [
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.44"
|
version = "1.0.44"
|
||||||
|
@ -245,18 +236,6 @@ version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "chrono"
|
|
||||||
version = "0.4.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"num-integer",
|
|
||||||
"num-traits",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clang-sys"
|
name = "clang-sys"
|
||||||
version = "1.2.2"
|
version = "1.2.2"
|
||||||
|
@ -274,7 +253,7 @@ version = "2.33.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term 0.11.0",
|
"ansi_term",
|
||||||
"atty",
|
"atty",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"strsim 0.8.0",
|
"strsim 0.8.0",
|
||||||
|
@ -732,8 +711,6 @@ dependencies = [
|
||||||
"pixels",
|
"pixels",
|
||||||
"rodio",
|
"rodio",
|
||||||
"rtrb",
|
"rtrb",
|
||||||
"tracing",
|
|
||||||
"tracing-subscriber",
|
|
||||||
"winit",
|
"winit",
|
||||||
"winit_input_helper",
|
"winit_input_helper",
|
||||||
]
|
]
|
||||||
|
@ -1076,15 +1053,6 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "matchers"
|
|
||||||
version = "0.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1"
|
|
||||||
dependencies = [
|
|
||||||
"regex-automata",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.4.1"
|
version = "2.4.1"
|
||||||
|
@ -1338,16 +1306,6 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-integer"
|
|
||||||
version = "0.1.44"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.14"
|
version = "0.2.14"
|
||||||
|
@ -1482,12 +1440,6 @@ version = "2.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-project-lite"
|
|
||||||
version = "0.2.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pixels"
|
name = "pixels"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
|
@ -1600,15 +1552,6 @@ dependencies = [
|
||||||
"regex-syntax",
|
"regex-syntax",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-automata"
|
|
||||||
version = "0.1.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
|
||||||
dependencies = [
|
|
||||||
"regex-syntax",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.6.25"
|
version = "0.6.25"
|
||||||
|
@ -1764,15 +1707,6 @@ version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
|
checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sharded-slab"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
|
|
||||||
dependencies = [
|
|
||||||
"lazy_static",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
@ -1952,15 +1886,6 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "thread_local"
|
|
||||||
version = "1.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd"
|
|
||||||
dependencies = [
|
|
||||||
"once_cell",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tinyvec"
|
name = "tinyvec"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
@ -1985,81 +1910,6 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing"
|
|
||||||
version = "0.1.29"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if 1.0.0",
|
|
||||||
"pin-project-lite",
|
|
||||||
"tracing-attributes",
|
|
||||||
"tracing-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing-attributes"
|
|
||||||
version = "0.1.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing-core"
|
|
||||||
version = "0.1.21"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4"
|
|
||||||
dependencies = [
|
|
||||||
"lazy_static",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing-log"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3"
|
|
||||||
dependencies = [
|
|
||||||
"lazy_static",
|
|
||||||
"log",
|
|
||||||
"tracing-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing-serde"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
"tracing-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing-subscriber"
|
|
||||||
version = "0.2.25"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71"
|
|
||||||
dependencies = [
|
|
||||||
"ansi_term 0.12.1",
|
|
||||||
"chrono",
|
|
||||||
"lazy_static",
|
|
||||||
"matchers",
|
|
||||||
"regex",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"sharded-slab",
|
|
||||||
"smallvec",
|
|
||||||
"thread_local",
|
|
||||||
"tracing",
|
|
||||||
"tracing-core",
|
|
||||||
"tracing-log",
|
|
||||||
"tracing-serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ttf-parser"
|
name = "ttf-parser"
|
||||||
version = "0.6.2"
|
version = "0.6.2"
|
||||||
|
|
|
@ -18,8 +18,6 @@ winit_input_helper = "0.10"
|
||||||
rodio = "0.14"
|
rodio = "0.14"
|
||||||
rtrb = "0.2"
|
rtrb = "0.2"
|
||||||
directories-next = "2.0"
|
directories-next = "2.0"
|
||||||
tracing = "0.1.29"
|
|
||||||
tracing-subscriber = "0.2.25"
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = true
|
debug = true
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::bus::BusIo;
|
use crate::bus::BusIo;
|
||||||
use crate::emu::SM83_CLOCK_SPEED;
|
use crate::emu::SM83_CLOCK_SPEED;
|
||||||
use gen::SampleProducer;
|
use gen::SampleProducer;
|
||||||
use tracing::warn;
|
|
||||||
use types::ch1::{Sweep, SweepDirection};
|
use types::ch1::{Sweep, SweepDirection};
|
||||||
use types::ch3::Volume as Ch3Volume;
|
use types::ch3::Volume as Ch3Volume;
|
||||||
use types::ch4::{CounterWidth, Frequency as Ch4Frequency, PolynomialCounter};
|
use types::ch4::{CounterWidth, Frequency as Ch4Frequency, PolynomialCounter};
|
||||||
|
@ -55,7 +54,7 @@ impl BusIo for Apu {
|
||||||
0x26 => self.ctrl.status(self),
|
0x26 => self.ctrl.status(self),
|
||||||
0x30..=0x3F => self.ch3.read_byte(addr),
|
0x30..=0x3F => self.ch3.read_byte(addr),
|
||||||
_ => {
|
_ => {
|
||||||
warn!("Attempted read from {:#06X}", addr);
|
eprintln!("Read 0xFF from unused IO register {:#06X} [APU]", addr);
|
||||||
0xFF
|
0xFF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +85,10 @@ impl BusIo for Apu {
|
||||||
0x26 => self.set_status(byte),
|
0x26 => self.set_status(byte),
|
||||||
0x30..=0x3F => self.ch3.write_byte(addr, byte),
|
0x30..=0x3F => self.ch3.write_byte(addr, byte),
|
||||||
_ if !self.ctrl.enabled => {}
|
_ if !self.ctrl.enabled => {}
|
||||||
_ => warn!("Attempted write of {:#04X} to {:#06X}", byte, addr),
|
_ => eprintln!(
|
||||||
|
"Wrote {:#04X} to unused IO register {:#06X} [APU]",
|
||||||
|
byte, addr
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
13
src/bus.rs
13
src/bus.rs
|
@ -1,5 +1,3 @@
|
||||||
use tracing::warn;
|
|
||||||
|
|
||||||
use crate::apu::Apu;
|
use crate::apu::Apu;
|
||||||
use crate::cartridge::Cartridge;
|
use crate::cartridge::Cartridge;
|
||||||
use crate::high_ram::HighRam;
|
use crate::high_ram::HighRam;
|
||||||
|
@ -16,7 +14,7 @@ pub(crate) const BOOT_SIZE: usize = 0x100;
|
||||||
pub struct Bus {
|
pub struct Bus {
|
||||||
boot: Option<[u8; BOOT_SIZE]>, // Boot ROM is 256b long
|
boot: Option<[u8; BOOT_SIZE]>, // Boot ROM is 256b long
|
||||||
cart: Option<Cartridge>,
|
cart: Option<Cartridge>,
|
||||||
ppu: Ppu,
|
pub(crate) ppu: Ppu,
|
||||||
work_ram: WorkRam,
|
work_ram: WorkRam,
|
||||||
var_ram: VariableWorkRam,
|
var_ram: VariableWorkRam,
|
||||||
pub(crate) timer: Timer,
|
pub(crate) timer: Timer,
|
||||||
|
@ -102,11 +100,6 @@ impl Bus {
|
||||||
pub(crate) fn cart_mut(&mut self) -> Option<&mut Cartridge> {
|
pub(crate) fn cart_mut(&mut self) -> Option<&mut Cartridge> {
|
||||||
self.cart.as_mut()
|
self.cart.as_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn ppu(&self) -> &Ppu {
|
|
||||||
&self.ppu
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bus {
|
impl Bus {
|
||||||
|
@ -258,7 +251,7 @@ impl BusIo for Bus {
|
||||||
0x4A => self.ppu.pos.window_y,
|
0x4A => self.ppu.pos.window_y,
|
||||||
0x4B => self.ppu.pos.window_x,
|
0x4B => self.ppu.pos.window_x,
|
||||||
_ => {
|
_ => {
|
||||||
warn!("Attempted read from {:#06X} on IO", addr);
|
eprintln!("Read 0xFF from unused IO register {:#06X}.", addr);
|
||||||
0xFF
|
0xFF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -375,7 +368,7 @@ impl BusIo for Bus {
|
||||||
self.boot = None;
|
self.boot = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => warn!("Attempted write of {:#04X} to {:#06X} on IO", byte, addr),
|
_ => eprintln!("Wrote {:#04X} to unused IO register {:#06X}.", byte, addr),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
0xFF80..=0xFFFE => {
|
0xFF80..=0xFFFE => {
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use tracing::{info, warn};
|
|
||||||
|
|
||||||
use crate::bus::BusIo;
|
use crate::bus::BusIo;
|
||||||
|
|
||||||
const RAM_SIZE_ADDRESS: usize = 0x0149;
|
const RAM_SIZE_ADDRESS: usize = 0x0149;
|
||||||
|
@ -17,7 +15,7 @@ pub(crate) struct Cartridge {
|
||||||
impl Cartridge {
|
impl Cartridge {
|
||||||
pub(crate) fn new(memory: Vec<u8>) -> Self {
|
pub(crate) fn new(memory: Vec<u8>) -> Self {
|
||||||
let title = Self::find_title(&memory);
|
let title = Self::find_title(&memory);
|
||||||
info!("Title: {:?}", title);
|
eprintln!("Cartridge Title: {:?}", title);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
mbc: Self::detect_mbc(&memory),
|
mbc: Self::detect_mbc(&memory),
|
||||||
|
@ -41,20 +39,20 @@ impl Cartridge {
|
||||||
let ram_cap = ram_size.capacity();
|
let ram_cap = ram_size.capacity();
|
||||||
let rom_cap = rom_size.capacity();
|
let rom_cap = rom_size.capacity();
|
||||||
|
|
||||||
info!("RAM size: {} bytes", ram_cap);
|
eprintln!("Cartridge Ram Size: {} bytes", ram_cap);
|
||||||
info!("ROM size: {} bytes", rom_size.capacity());
|
eprintln!("Cartridge ROM Size: {} bytes", rom_size.capacity());
|
||||||
info!("MBC kind: {:?}", mbc_kind);
|
eprintln!("MBC Type: {:?}", mbc_kind);
|
||||||
|
|
||||||
match mbc_kind {
|
match mbc_kind {
|
||||||
MBCKind::None => Box::new(NoMBC),
|
MBCKind::None => Box::new(NoMBC),
|
||||||
MBCKind::MBC1 => Box::new(MBC1::new(ram_size, rom_size)),
|
MBCKind::MBC1 => Box::new(MBC1::new(ram_size, rom_size)),
|
||||||
MBCKind::MBC1WithBattery => Box::new(MBC1::with_battery(ram_size, rom_size)),
|
MBCKind::MBC1WithBattery => Box::new(MBC1::with_battery(ram_size, rom_size)), // TODO: Implement Saving
|
||||||
MBCKind::MBC2 => Box::new(MBC2::new(rom_cap)),
|
MBCKind::MBC2 => Box::new(MBC2::new(rom_cap)),
|
||||||
MBCKind::MBC2WithBattery => Box::new(MBC2::with_battery(rom_cap)),
|
MBCKind::MBC2WithBattery => Box::new(MBC2::with_battery(rom_cap)), // TODO: Implement Saving
|
||||||
MBCKind::MBC3 => Box::new(MBC3::new(ram_cap)),
|
MBCKind::MBC3 => Box::new(MBC3::new(ram_cap)),
|
||||||
MBCKind::MBC3WithBattery => Box::new(MBC3::with_battery(ram_cap)),
|
MBCKind::MBC3WithBattery => Box::new(MBC3::with_battery(ram_cap)), // TODO: Implement Saving
|
||||||
MBCKind::MBC5 => Box::new(MBC5::new(ram_cap, rom_cap)),
|
MBCKind::MBC5 => Box::new(MBC5::new(ram_cap, rom_cap)),
|
||||||
MBCKind::MBC5WithBattery => Box::new(MBC5::with_battery(ram_cap, rom_cap)),
|
MBCKind::MBC5WithBattery => Box::new(MBC5::with_battery(ram_cap, rom_cap)), // TDO: Implement Saving
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -598,8 +596,8 @@ impl MBCIo for NoMBC {
|
||||||
MBCResult::Address(addr as usize)
|
MBCResult::Address(addr as usize)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_write(&mut self, _: u16, byte: u8) {
|
fn handle_write(&mut self, _addr: u16, _byte: u8) {
|
||||||
warn!("Attempted write of {:#04X} to cartridge w/out MBC", byte);
|
// eprintln!("Tried to write {:#04X} to a read-only cartridge", byte);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
17
src/cpu.rs
17
src/cpu.rs
|
@ -7,7 +7,7 @@ use std::fmt::{Display, Formatter, Result as FmtResult};
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Cpu {
|
pub struct Cpu {
|
||||||
pub(crate) bus: Bus,
|
pub bus: Bus,
|
||||||
reg: Registers,
|
reg: Registers,
|
||||||
flags: Flags,
|
flags: Flags,
|
||||||
ime: ImeState,
|
ime: ImeState,
|
||||||
|
@ -118,10 +118,13 @@ impl Cpu {
|
||||||
use HaltKind::*;
|
use HaltKind::*;
|
||||||
|
|
||||||
self.bus.clock();
|
self.bus.clock();
|
||||||
return match kind {
|
|
||||||
|
let elapsed = match kind {
|
||||||
ImeEnabled | NonePending => 4,
|
ImeEnabled | NonePending => 4,
|
||||||
SomePending => todo!("Implement HALT bug"),
|
SomePending => todo!("Implement HALT bug"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return elapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
let opcode = self.fetch();
|
let opcode = self.fetch();
|
||||||
|
@ -130,11 +133,11 @@ impl Cpu {
|
||||||
self.handle_ei();
|
self.handle_ei();
|
||||||
|
|
||||||
// For use in Blargg's Test ROMs
|
// For use in Blargg's Test ROMs
|
||||||
// if self.read_byte(0xFF02) == 0x81 {
|
if self.read_byte(0xFF02) == 0x81 {
|
||||||
// let c = self.read_byte(0xFF01) as char;
|
let c = self.read_byte(0xFF01) as char;
|
||||||
// self.write_byte(0xFF02, 0x00);
|
self.write_byte(0xFF02, 0x00);
|
||||||
// eprint!("{}", c);
|
eprint!("{}", c);
|
||||||
// }
|
}
|
||||||
|
|
||||||
elapsed
|
elapsed
|
||||||
}
|
}
|
||||||
|
|
14
src/emu.rs
14
src/emu.rs
|
@ -31,7 +31,7 @@ pub fn run_frame(emu: &mut Emulator, gamepad: &mut Gilrs, key: &WinitInputHelper
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_frame(emu: &Emulator, buf: &mut [u8; GB_HEIGHT * GB_WIDTH * 4]) {
|
pub fn draw_frame(emu: &Emulator, buf: &mut [u8; GB_HEIGHT * GB_WIDTH * 4]) {
|
||||||
buf.copy_from_slice(emu.cpu.bus().ppu().frame_buf());
|
buf.copy_from_slice(emu.cpu.bus().ppu.frame_buf());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Emulator {
|
pub struct Emulator {
|
||||||
|
@ -121,8 +121,6 @@ pub mod build {
|
||||||
use std::io::{Read, Result};
|
use std::io::{Read, Result};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use tracing::info;
|
|
||||||
|
|
||||||
use crate::bus::BOOT_SIZE;
|
use crate::bus::BOOT_SIZE;
|
||||||
use crate::cpu::Cpu;
|
use crate::cpu::Cpu;
|
||||||
|
|
||||||
|
@ -161,14 +159,8 @@ pub mod build {
|
||||||
|
|
||||||
pub fn finish(mut self) -> Emulator {
|
pub fn finish(mut self) -> Emulator {
|
||||||
let mut emu = Emulator::new(match self.boot {
|
let mut emu = Emulator::new(match self.boot {
|
||||||
Some(rom) => {
|
Some(rom) => Cpu::with_boot(rom),
|
||||||
info!("User-provided Boot ROM");
|
None => Cpu::with_boot(*include_bytes!("../bin/bootix_dmg.bin")),
|
||||||
Cpu::with_boot(rom)
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
info!("Built-in Boot ROM");
|
|
||||||
Cpu::with_boot(*include_bytes!("../bin/bootix_dmg.bin"))
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(rom) = self.cart.take() {
|
if let Some(rom) = self.cart.take() {
|
||||||
|
|
|
@ -116,7 +116,7 @@ impl std::fmt::Debug for Instruction {
|
||||||
impl Instruction {
|
impl Instruction {
|
||||||
pub(crate) fn execute(cpu: &mut Cpu, instruction: Self) -> Cycle {
|
pub(crate) fn execute(cpu: &mut Cpu, instruction: Self) -> Cycle {
|
||||||
match instruction {
|
match instruction {
|
||||||
Instruction::NOP => 4,
|
Instruction::NOP => (4),
|
||||||
Instruction::LD(target, src) => match (target, src) {
|
Instruction::LD(target, src) => match (target, src) {
|
||||||
(LDTarget::IndirectImmediateWord, LDSource::SP) => {
|
(LDTarget::IndirectImmediateWord, LDSource::SP) => {
|
||||||
// LD (u16), SP | Store stack pointer in byte at 16-bit register
|
// LD (u16), SP | Store stack pointer in byte at 16-bit register
|
||||||
|
@ -361,12 +361,12 @@ impl Instruction {
|
||||||
let (cycles, sum) = match reg {
|
let (cycles, sum) = match reg {
|
||||||
B | C | D | E | H | L | A => {
|
B | C | D | E | H | L | A => {
|
||||||
let right = cpu.register(reg.cpu_register());
|
let right = cpu.register(reg.cpu_register());
|
||||||
(4, Self::add(left, right, &mut flags))
|
((4), Self::add(left, right, &mut flags))
|
||||||
}
|
}
|
||||||
IndirectHL => {
|
IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
let right = Self::read_byte(&mut cpu.bus, addr);
|
let right = Self::read_byte(&mut cpu.bus, addr);
|
||||||
(8, Self::add(left, right, &mut flags))
|
((8), Self::add(left, right, &mut flags))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -610,13 +610,13 @@ impl Instruction {
|
||||||
B | C | D | E | H | L | A => {
|
B | C | D | E | H | L | A => {
|
||||||
let right = cpu.register(reg.cpu_register());
|
let right = cpu.register(reg.cpu_register());
|
||||||
let sum = Self::add_with_carry_bit(left, right, flags.c(), &mut flags);
|
let sum = Self::add_with_carry_bit(left, right, flags.c(), &mut flags);
|
||||||
(4, sum)
|
((4), sum)
|
||||||
}
|
}
|
||||||
IndirectHL => {
|
IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
let right = Self::read_byte(&mut cpu.bus, addr);
|
let right = Self::read_byte(&mut cpu.bus, addr);
|
||||||
let sum = Self::add_with_carry_bit(left, right, flags.c(), &mut flags);
|
let sum = Self::add_with_carry_bit(left, right, flags.c(), &mut flags);
|
||||||
(8, sum)
|
((8), sum)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
cpu.set_register(CpuRegister::A, sum);
|
cpu.set_register(CpuRegister::A, sum);
|
||||||
|
@ -646,12 +646,12 @@ impl Instruction {
|
||||||
let (cycles, diff) = match reg {
|
let (cycles, diff) = match reg {
|
||||||
B | C | D | E | H | L | A => {
|
B | C | D | E | H | L | A => {
|
||||||
let right = cpu.register(reg.cpu_register());
|
let right = cpu.register(reg.cpu_register());
|
||||||
(4, Self::sub(left, right, &mut flags))
|
((4), Self::sub(left, right, &mut flags))
|
||||||
}
|
}
|
||||||
IndirectHL => {
|
IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
let right = Self::read_byte(&mut cpu.bus, addr);
|
let right = Self::read_byte(&mut cpu.bus, addr);
|
||||||
(8, Self::sub(left, right, &mut flags))
|
((8), Self::sub(left, right, &mut flags))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
cpu.set_register(CpuRegister::A, diff);
|
cpu.set_register(CpuRegister::A, diff);
|
||||||
|
@ -681,13 +681,13 @@ impl Instruction {
|
||||||
B | C | D | E | H | L | A => {
|
B | C | D | E | H | L | A => {
|
||||||
let right = cpu.register(reg.cpu_register());
|
let right = cpu.register(reg.cpu_register());
|
||||||
let diff = Self::sub_with_carry(left, right, flags.c(), &mut flags);
|
let diff = Self::sub_with_carry(left, right, flags.c(), &mut flags);
|
||||||
(4, diff)
|
((4), diff)
|
||||||
}
|
}
|
||||||
IndirectHL => {
|
IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
let right = Self::read_byte(&mut cpu.bus, addr);
|
let right = Self::read_byte(&mut cpu.bus, addr);
|
||||||
let diff = Self::sub_with_carry(left, right, flags.c(), &mut flags);
|
let diff = Self::sub_with_carry(left, right, flags.c(), &mut flags);
|
||||||
(8, diff)
|
((8), diff)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
cpu.set_register(CpuRegister::A, diff);
|
cpu.set_register(CpuRegister::A, diff);
|
||||||
|
@ -717,7 +717,7 @@ impl Instruction {
|
||||||
IndirectHL => {
|
IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
let right = Self::read_byte(&mut cpu.bus, addr);
|
let right = Self::read_byte(&mut cpu.bus, addr);
|
||||||
(8, left & right)
|
((8), left & right)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
cpu.set_register(CpuRegister::A, acc);
|
cpu.set_register(CpuRegister::A, acc);
|
||||||
|
@ -743,7 +743,7 @@ impl Instruction {
|
||||||
IndirectHL => {
|
IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
let right = Self::read_byte(&mut cpu.bus, addr);
|
let right = Self::read_byte(&mut cpu.bus, addr);
|
||||||
(8, left ^ right)
|
((8), left ^ right)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
cpu.set_register(CpuRegister::A, acc);
|
cpu.set_register(CpuRegister::A, acc);
|
||||||
|
@ -769,7 +769,7 @@ impl Instruction {
|
||||||
IndirectHL => {
|
IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
let right = Self::read_byte(&mut cpu.bus, addr);
|
let right = Self::read_byte(&mut cpu.bus, addr);
|
||||||
(8, left | right)
|
((8), left | right)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
cpu.set_register(CpuRegister::A, acc);
|
cpu.set_register(CpuRegister::A, acc);
|
||||||
|
@ -1053,14 +1053,14 @@ impl Instruction {
|
||||||
let byte = cpu.register(reg);
|
let byte = cpu.register(reg);
|
||||||
let rotated = byte.rotate_left(1);
|
let rotated = byte.rotate_left(1);
|
||||||
cpu.set_register(reg, rotated);
|
cpu.set_register(reg, rotated);
|
||||||
(8, byte >> 7, rotated)
|
((8), byte >> 7, rotated)
|
||||||
}
|
}
|
||||||
IndirectHL => {
|
IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
let byte = Self::read_byte(&mut cpu.bus, addr);
|
let byte = Self::read_byte(&mut cpu.bus, addr);
|
||||||
let rotated = byte.rotate_left(1);
|
let rotated = byte.rotate_left(1);
|
||||||
Self::write_byte(&mut cpu.bus, addr, rotated);
|
Self::write_byte(&mut cpu.bus, addr, rotated);
|
||||||
(16, byte >> 7, rotated)
|
((16), byte >> 7, rotated)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
cpu.update_flags(rotated == 0, false, false, most_sgfnt == 0x01);
|
cpu.update_flags(rotated == 0, false, false, most_sgfnt == 0x01);
|
||||||
|
@ -1076,14 +1076,14 @@ impl Instruction {
|
||||||
let byte = cpu.register(reg);
|
let byte = cpu.register(reg);
|
||||||
let rotated = byte.rotate_right(1);
|
let rotated = byte.rotate_right(1);
|
||||||
cpu.set_register(reg, rotated);
|
cpu.set_register(reg, rotated);
|
||||||
(8, byte & 0x01, rotated)
|
((8), byte & 0x01, rotated)
|
||||||
}
|
}
|
||||||
IndirectHL => {
|
IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
let byte = Self::read_byte(&mut cpu.bus, addr);
|
let byte = Self::read_byte(&mut cpu.bus, addr);
|
||||||
let rotated = byte.rotate_right(1);
|
let rotated = byte.rotate_right(1);
|
||||||
Self::write_byte(&mut cpu.bus, addr, rotated);
|
Self::write_byte(&mut cpu.bus, addr, rotated);
|
||||||
(16, byte & 0x01, rotated)
|
((16), byte & 0x01, rotated)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
cpu.update_flags(rotated == 0, false, false, least_sgfnt == 0x01);
|
cpu.update_flags(rotated == 0, false, false, least_sgfnt == 0x01);
|
||||||
|
@ -1101,14 +1101,14 @@ impl Instruction {
|
||||||
let byte = cpu.register(reg);
|
let byte = cpu.register(reg);
|
||||||
let (rotated, carry) = Self::rl_thru_carry(byte, flags.c());
|
let (rotated, carry) = Self::rl_thru_carry(byte, flags.c());
|
||||||
cpu.set_register(reg, rotated);
|
cpu.set_register(reg, rotated);
|
||||||
(8, rotated, carry)
|
((8), rotated, carry)
|
||||||
}
|
}
|
||||||
IndirectHL => {
|
IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
let byte = Self::read_byte(&mut cpu.bus, addr);
|
let byte = Self::read_byte(&mut cpu.bus, addr);
|
||||||
let (rotated, carry) = Self::rl_thru_carry(byte, flags.c());
|
let (rotated, carry) = Self::rl_thru_carry(byte, flags.c());
|
||||||
Self::write_byte(&mut cpu.bus, addr, rotated);
|
Self::write_byte(&mut cpu.bus, addr, rotated);
|
||||||
(16, rotated, carry)
|
((16), rotated, carry)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
cpu.update_flags(rotated == 0, false, false, carry);
|
cpu.update_flags(rotated == 0, false, false, carry);
|
||||||
|
@ -1126,14 +1126,14 @@ impl Instruction {
|
||||||
let byte = cpu.register(reg);
|
let byte = cpu.register(reg);
|
||||||
let (rotated, carry) = Self::rr_thru_carry(byte, flags.c());
|
let (rotated, carry) = Self::rr_thru_carry(byte, flags.c());
|
||||||
cpu.set_register(reg, rotated);
|
cpu.set_register(reg, rotated);
|
||||||
(8, rotated, carry)
|
((8), rotated, carry)
|
||||||
}
|
}
|
||||||
IndirectHL => {
|
IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
let byte = Self::read_byte(&mut cpu.bus, addr);
|
let byte = Self::read_byte(&mut cpu.bus, addr);
|
||||||
let (rotated, carry) = Self::rr_thru_carry(byte, flags.c());
|
let (rotated, carry) = Self::rr_thru_carry(byte, flags.c());
|
||||||
Self::write_byte(&mut cpu.bus, addr, rotated);
|
Self::write_byte(&mut cpu.bus, addr, rotated);
|
||||||
(16, rotated, carry)
|
((16), rotated, carry)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
cpu.update_flags(rotated == 0, false, false, carry);
|
cpu.update_flags(rotated == 0, false, false, carry);
|
||||||
|
@ -1149,14 +1149,14 @@ impl Instruction {
|
||||||
let byte = cpu.register(reg);
|
let byte = cpu.register(reg);
|
||||||
let shifted = byte << 1;
|
let shifted = byte << 1;
|
||||||
cpu.set_register(reg, shifted);
|
cpu.set_register(reg, shifted);
|
||||||
(8, (byte >> 7) & 0x01, shifted)
|
((8), (byte >> 7) & 0x01, shifted)
|
||||||
}
|
}
|
||||||
IndirectHL => {
|
IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
let byte = Self::read_byte(&mut cpu.bus, addr);
|
let byte = Self::read_byte(&mut cpu.bus, addr);
|
||||||
let shifted = byte << 1;
|
let shifted = byte << 1;
|
||||||
Self::write_byte(&mut cpu.bus, addr, shifted);
|
Self::write_byte(&mut cpu.bus, addr, shifted);
|
||||||
(16, (byte >> 7) & 0x01, shifted)
|
((16), (byte >> 7) & 0x01, shifted)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
cpu.update_flags(shifted == 0, false, false, most_sgfnt == 0x01);
|
cpu.update_flags(shifted == 0, false, false, most_sgfnt == 0x01);
|
||||||
|
@ -1172,14 +1172,14 @@ impl Instruction {
|
||||||
let byte = cpu.register(reg);
|
let byte = cpu.register(reg);
|
||||||
let shifted = ((byte >> 7) & 0x01) << 7 | byte >> 1;
|
let shifted = ((byte >> 7) & 0x01) << 7 | byte >> 1;
|
||||||
cpu.set_register(reg, shifted);
|
cpu.set_register(reg, shifted);
|
||||||
(8, byte & 0x01, shifted)
|
((8), byte & 0x01, shifted)
|
||||||
}
|
}
|
||||||
IndirectHL => {
|
IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
let byte = Self::read_byte(&mut cpu.bus, addr);
|
let byte = Self::read_byte(&mut cpu.bus, addr);
|
||||||
let shifted = ((byte >> 7) & 0x01) << 7 | byte >> 1;
|
let shifted = ((byte >> 7) & 0x01) << 7 | byte >> 1;
|
||||||
Self::write_byte(&mut cpu.bus, addr, shifted);
|
Self::write_byte(&mut cpu.bus, addr, shifted);
|
||||||
(16, byte & 0x01, shifted)
|
((16), byte & 0x01, shifted)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
cpu.update_flags(shifted == 0, false, false, least_sgfnt == 0x01);
|
cpu.update_flags(shifted == 0, false, false, least_sgfnt == 0x01);
|
||||||
|
@ -1194,14 +1194,14 @@ impl Instruction {
|
||||||
let reg = reg.cpu_register();
|
let reg = reg.cpu_register();
|
||||||
let swapped = Self::swap_bits(cpu.register(reg));
|
let swapped = Self::swap_bits(cpu.register(reg));
|
||||||
cpu.set_register(reg, swapped);
|
cpu.set_register(reg, swapped);
|
||||||
(8, swapped)
|
((8), swapped)
|
||||||
}
|
}
|
||||||
|
|
||||||
IndirectHL => {
|
IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
let swapped = Self::swap_bits(Self::read_byte(&mut cpu.bus, addr));
|
let swapped = Self::swap_bits(Self::read_byte(&mut cpu.bus, addr));
|
||||||
Self::write_byte(&mut cpu.bus, addr, swapped);
|
Self::write_byte(&mut cpu.bus, addr, swapped);
|
||||||
(16, swapped)
|
((16), swapped)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
cpu.update_flags(swapped == 0, false, false, false);
|
cpu.update_flags(swapped == 0, false, false, false);
|
||||||
|
@ -1217,14 +1217,14 @@ impl Instruction {
|
||||||
let byte = cpu.register(reg);
|
let byte = cpu.register(reg);
|
||||||
let shifted = byte >> 1;
|
let shifted = byte >> 1;
|
||||||
cpu.set_register(reg, shifted);
|
cpu.set_register(reg, shifted);
|
||||||
(8, byte & 0x01, shifted)
|
((8), byte & 0x01, shifted)
|
||||||
}
|
}
|
||||||
IndirectHL => {
|
IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
let byte = Self::read_byte(&mut cpu.bus, addr);
|
let byte = Self::read_byte(&mut cpu.bus, addr);
|
||||||
let shifted = byte >> 1;
|
let shifted = byte >> 1;
|
||||||
Self::write_byte(&mut cpu.bus, addr, shifted);
|
Self::write_byte(&mut cpu.bus, addr, shifted);
|
||||||
(16, byte & 0x01, shifted)
|
((16), byte & 0x01, shifted)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
cpu.update_flags(shift_reg == 0, false, false, least_sgfnt == 0x01);
|
cpu.update_flags(shift_reg == 0, false, false, least_sgfnt == 0x01);
|
||||||
|
@ -1239,12 +1239,12 @@ impl Instruction {
|
||||||
B | C | D | E | H | L | A => {
|
B | C | D | E | H | L | A => {
|
||||||
let reg = reg.cpu_register();
|
let reg = reg.cpu_register();
|
||||||
let byte = cpu.register(reg);
|
let byte = cpu.register(reg);
|
||||||
(8, ((byte >> bit) & 0x01) == 0x01)
|
((8), ((byte >> bit) & 0x01) == 0x01)
|
||||||
}
|
}
|
||||||
IndirectHL => {
|
IndirectHL => {
|
||||||
let addr = cpu.register_pair(RegisterPair::HL);
|
let addr = cpu.register_pair(RegisterPair::HL);
|
||||||
let byte = Self::read_byte(&mut cpu.bus, addr);
|
let byte = Self::read_byte(&mut cpu.bus, addr);
|
||||||
(12, ((byte >> bit) & 0x01) == 0x01)
|
((12), ((byte >> bit) & 0x01) == 0x01)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
flags.set_z(!is_set);
|
flags.set_z(!is_set);
|
||||||
|
|
27
src/main.rs
27
src/main.rs
|
@ -1,15 +1,12 @@
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::{anyhow, Result};
|
||||||
use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg};
|
use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg};
|
||||||
use gb::emu::build::EmulatorBuilder;
|
use gb::emu::build::EmulatorBuilder;
|
||||||
use gb::emu::CYCLES_IN_FRAME;
|
|
||||||
use gb::{Cycle, GB_HEIGHT, GB_WIDTH};
|
use gb::{Cycle, GB_HEIGHT, GB_WIDTH};
|
||||||
use gilrs::Gilrs;
|
use gilrs::Gilrs;
|
||||||
use pixels::{PixelsBuilder, SurfaceTexture};
|
use pixels::{PixelsBuilder, SurfaceTexture};
|
||||||
use rodio::{OutputStream, Sink};
|
use rodio::{OutputStream, Sink};
|
||||||
use tracing::info;
|
|
||||||
use tracing_subscriber::EnvFilter;
|
|
||||||
use winit::dpi::{LogicalSize, PhysicalSize};
|
use winit::dpi::{LogicalSize, PhysicalSize};
|
||||||
use winit::event::{Event, VirtualKeyCode};
|
use winit::event::{Event, VirtualKeyCode};
|
||||||
use winit::event_loop::{ControlFlow, EventLoop};
|
use winit::event_loop::{ControlFlow, EventLoop};
|
||||||
|
@ -44,15 +41,6 @@ fn main() -> Result<()> {
|
||||||
)
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
// Set up subscriber
|
|
||||||
if std::env::var("RUST_LOG").is_err() {
|
|
||||||
std::env::set_var("RUST_LOG", "gb=info");
|
|
||||||
}
|
|
||||||
|
|
||||||
tracing_subscriber::fmt::fmt()
|
|
||||||
.with_env_filter(EnvFilter::from_default_env())
|
|
||||||
.init();
|
|
||||||
|
|
||||||
let mut emu_build =
|
let mut emu_build =
|
||||||
EmulatorBuilder::new().with_cart(m.value_of("rom").expect("ROM path provided"))?;
|
EmulatorBuilder::new().with_cart(m.value_of("rom").expect("ROM path provided"))?;
|
||||||
|
|
||||||
|
@ -63,11 +51,9 @@ fn main() -> Result<()> {
|
||||||
let mut emu = emu_build.finish();
|
let mut emu = emu_build.finish();
|
||||||
|
|
||||||
// Load Save file if it exists
|
// Load Save file if it exists
|
||||||
info!("Attempt to load .sav");
|
|
||||||
emu.try_load_sav().expect("Load save if exists");
|
emu.try_load_sav().expect("Load save if exists");
|
||||||
let rom_title = emu.title();
|
let rom_title = emu.title();
|
||||||
|
|
||||||
info!("Initialize Gamepad");
|
|
||||||
let mut gamepad = Gilrs::new().expect("Initialize Controller Support");
|
let mut gamepad = Gilrs::new().expect("Initialize Controller Support");
|
||||||
|
|
||||||
// Initialize GUI
|
// Initialize GUI
|
||||||
|
@ -98,7 +84,6 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
emu.set_prod(prod);
|
emu.set_prod(prod);
|
||||||
|
|
||||||
info!("Spawn Audio Thread");
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
sink.sleep_until_end();
|
sink.sleep_until_end();
|
||||||
});
|
});
|
||||||
|
@ -108,7 +93,11 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
event_loop.run(move |event, _, control_flow| {
|
event_loop.run(move |event, _, control_flow| {
|
||||||
if let Event::RedrawRequested(_) = event {
|
if let Event::RedrawRequested(_) = event {
|
||||||
if pixels.render().is_err() {
|
if pixels
|
||||||
|
.render()
|
||||||
|
.map_err(|e| anyhow!("pixels.render() failed: {}", e))
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
emu.try_write_sav().expect("Write game save if need be");
|
emu.try_write_sav().expect("Write game save if need be");
|
||||||
|
|
||||||
*control_flow = ControlFlow::Exit;
|
*control_flow = ControlFlow::Exit;
|
||||||
|
@ -130,8 +119,8 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
cycle_count += gb::emu::run_frame(&mut emu, &mut gamepad, &input);
|
cycle_count += gb::emu::run_frame(&mut emu, &mut gamepad, &input);
|
||||||
|
|
||||||
if cycle_count >= CYCLES_IN_FRAME {
|
if cycle_count >= gb::emu::CYCLES_IN_FRAME {
|
||||||
cycle_count %= CYCLES_IN_FRAME;
|
cycle_count %= gb::emu::CYCLES_IN_FRAME;
|
||||||
|
|
||||||
let buf: &mut [u8; GB_WIDTH * GB_HEIGHT * 4] = pixels
|
let buf: &mut [u8; GB_WIDTH * GB_HEIGHT * 4] = pixels
|
||||||
.get_frame()
|
.get_frame()
|
||||||
|
|
294
src/ppu.rs
294
src/ppu.rs
|
@ -45,18 +45,18 @@ pub struct Ppu {
|
||||||
vram: Box<[u8; VRAM_SIZE]>,
|
vram: Box<[u8; VRAM_SIZE]>,
|
||||||
pub(crate) oam: ObjectAttributeTable,
|
pub(crate) oam: ObjectAttributeTable,
|
||||||
pub(crate) dma: DirectMemoryAccess,
|
pub(crate) dma: DirectMemoryAccess,
|
||||||
scan_dot: Cycle,
|
scan_state: OamScanState,
|
||||||
fetch: PixelFetcher,
|
fetch: PixelFetcher,
|
||||||
fifo: PixelFifo,
|
fifo: PixelFifo,
|
||||||
obj_buffer: ObjectBuffer,
|
obj_buffer: ObjectBuffer,
|
||||||
frame_buf: Box<[u8; GB_WIDTH * GB_HEIGHT * 4]>,
|
frame_buf: Box<[u8; GB_WIDTH * GB_HEIGHT * 4]>,
|
||||||
win_stat: WindowStatus,
|
window_stat: WindowStatus,
|
||||||
|
|
||||||
scanline_start: bool,
|
scanline_start: bool,
|
||||||
to_discard: u8,
|
to_discard: u8,
|
||||||
|
|
||||||
x_pos: u8,
|
x_pos: u8,
|
||||||
dot: Cycle,
|
cycle: Cycle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BusIo for Ppu {
|
impl BusIo for Ppu {
|
||||||
|
@ -71,7 +71,7 @@ impl BusIo for Ppu {
|
||||||
|
|
||||||
impl Ppu {
|
impl Ppu {
|
||||||
pub(crate) fn tick(&mut self) {
|
pub(crate) fn tick(&mut self) {
|
||||||
self.dot += 1;
|
self.cycle += 1;
|
||||||
|
|
||||||
if !self.ctrl.lcd_enabled() {
|
if !self.ctrl.lcd_enabled() {
|
||||||
return;
|
return;
|
||||||
|
@ -79,15 +79,7 @@ impl Ppu {
|
||||||
|
|
||||||
match self.stat.mode() {
|
match self.stat.mode() {
|
||||||
PpuMode::OamScan => {
|
PpuMode::OamScan => {
|
||||||
// Cycles 1 -> 80
|
if self.cycle >= 80 {
|
||||||
if self.dot >= 80 {
|
|
||||||
self.x_pos = 0;
|
|
||||||
self.scanline_start = true;
|
|
||||||
self.fetch.back.scanline_first = true;
|
|
||||||
self.to_discard = 0;
|
|
||||||
self.fifo.back.clear();
|
|
||||||
self.fifo.obj.clear();
|
|
||||||
|
|
||||||
self.stat.set_mode(PpuMode::Drawing);
|
self.stat.set_mode(PpuMode::Drawing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +88,7 @@ impl Ppu {
|
||||||
PpuMode::Drawing => {
|
PpuMode::Drawing => {
|
||||||
if self.ctrl.lcd_enabled() {
|
if self.ctrl.lcd_enabled() {
|
||||||
// Only Draw when the LCD Is Enabled
|
// Only Draw when the LCD Is Enabled
|
||||||
self.draw();
|
self.draw(self.cycle);
|
||||||
} else {
|
} else {
|
||||||
self.reset();
|
self.reset();
|
||||||
}
|
}
|
||||||
|
@ -112,22 +104,29 @@ impl Ppu {
|
||||||
|
|
||||||
// Increment Window line counter if scanline had any window pixels on it
|
// Increment Window line counter if scanline had any window pixels on it
|
||||||
// only increment once per scanline though
|
// only increment once per scanline though
|
||||||
if self.win_stat.enabled {
|
if self.window_stat.should_draw() {
|
||||||
self.fetch.back.wl_count += 1;
|
self.fetch.back.window_line.increment();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.fetch.hblank_reset();
|
self.x_pos = 0;
|
||||||
self.win_stat.enabled = false;
|
self.scanline_start = true;
|
||||||
|
self.to_discard = 0;
|
||||||
|
|
||||||
|
self.fetch.hblank_reset();
|
||||||
|
self.window_stat.hblank_reset();
|
||||||
self.obj_buffer.clear();
|
self.obj_buffer.clear();
|
||||||
|
|
||||||
|
self.fifo.back.clear();
|
||||||
|
self.fifo.obj.clear();
|
||||||
|
|
||||||
self.stat.set_mode(PpuMode::HBlank);
|
self.stat.set_mode(PpuMode::HBlank);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PpuMode::HBlank => {
|
PpuMode::HBlank => {
|
||||||
// This mode will always end at 456 cycles
|
// This mode will always end at 456 cycles
|
||||||
|
|
||||||
if self.dot >= 456 {
|
if self.cycle >= 456 {
|
||||||
self.dot %= 456;
|
self.cycle %= 456;
|
||||||
self.pos.line_y += 1;
|
self.pos.line_y += 1;
|
||||||
|
|
||||||
// Update LY==LYC bit
|
// Update LY==LYC bit
|
||||||
|
@ -144,10 +143,9 @@ impl Ppu {
|
||||||
self.int.set_vblank(true);
|
self.int.set_vblank(true);
|
||||||
|
|
||||||
// Reset Window Line Counter in Fetcher
|
// Reset Window Line Counter in Fetcher
|
||||||
self.fetch.back.wl_count = 0;
|
self.fetch.vblank_reset();
|
||||||
|
|
||||||
// Reset WY=LY coincidence flag
|
// Reset WY=LY coincidence flag
|
||||||
self.win_stat.coincidence = false;
|
self.window_stat.vblank_reset();
|
||||||
|
|
||||||
if self.stat.vblank_int() {
|
if self.stat.vblank_int() {
|
||||||
// Enable Vblank LCDStat Interrupt
|
// Enable Vblank LCDStat Interrupt
|
||||||
|
@ -161,7 +159,7 @@ impl Ppu {
|
||||||
self.int.set_lcd_stat(true);
|
self.int.set_lcd_stat(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.scan_dot = Default::default();
|
self.scan_state.reset();
|
||||||
PpuMode::OamScan
|
PpuMode::OamScan
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -169,8 +167,8 @@ impl Ppu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PpuMode::VBlank => {
|
PpuMode::VBlank => {
|
||||||
if self.dot >= 456 {
|
if self.cycle > 456 {
|
||||||
self.dot %= 456;
|
self.cycle %= 456;
|
||||||
self.pos.line_y += 1;
|
self.pos.line_y += 1;
|
||||||
|
|
||||||
// Update LY==LYC bit
|
// Update LY==LYC bit
|
||||||
|
@ -190,7 +188,8 @@ impl Ppu {
|
||||||
self.int.set_lcd_stat(true);
|
self.int.set_lcd_stat(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.scan_dot = Default::default();
|
self.scan_state.reset();
|
||||||
|
|
||||||
self.stat.set_mode(PpuMode::OamScan);
|
self.stat.set_mode(PpuMode::OamScan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,31 +198,37 @@ impl Ppu {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scan_oam(&mut self) {
|
fn scan_oam(&mut self) {
|
||||||
if self.scan_dot % 2 == 0 {
|
match self.scan_state.mode() {
|
||||||
if self.dma.is_active() {
|
OamScanMode::Scan if !self.dma.is_active() => {
|
||||||
return;
|
if !self.window_stat.coincidence() && self.scan_state.count() == 0 {
|
||||||
}
|
// Determine whether we should draw the window next frame
|
||||||
|
self.window_stat
|
||||||
|
.set_coincidence(self.pos.line_y == self.pos.window_y);
|
||||||
|
}
|
||||||
|
|
||||||
if !self.win_stat.coincidence && self.scan_dot == 0 {
|
let sprite_height = self.ctrl.obj_size().as_u8();
|
||||||
self.win_stat.coincidence = self.pos.line_y == self.pos.window_y;
|
let index = self.scan_state.count();
|
||||||
}
|
|
||||||
|
|
||||||
let obj_height = self.ctrl.obj_size().size();
|
let attr = self.oam.attribute(index as usize);
|
||||||
let attr = self.oam.attribute(self.scan_dot as usize / 2);
|
let line_y = self.pos.line_y + 16;
|
||||||
let line_y = self.pos.line_y + 16;
|
|
||||||
|
|
||||||
if attr.x > 0
|
if attr.x > 0
|
||||||
&& line_y >= attr.y
|
&& line_y >= attr.y
|
||||||
&& line_y < (attr.y + obj_height)
|
&& line_y < (attr.y + sprite_height)
|
||||||
&& !self.obj_buffer.is_full()
|
&& !self.obj_buffer.is_full()
|
||||||
{
|
{
|
||||||
self.obj_buffer.add(attr);
|
self.obj_buffer.add(attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.scan_state.increase();
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
self.scan_dot += 1;
|
|
||||||
|
self.scan_state.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&mut self) {
|
fn draw(&mut self, _cycle: Cycle) {
|
||||||
use FetcherState::*;
|
use FetcherState::*;
|
||||||
|
|
||||||
let mut iter = self.obj_buffer.iter_mut();
|
let mut iter = self.obj_buffer.iter_mut();
|
||||||
|
@ -317,49 +322,60 @@ impl Ppu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.ctrl.window_enabled()
|
||||||
|
&& !self.window_stat.should_draw()
|
||||||
|
&& self.window_stat.coincidence()
|
||||||
|
&& self.x_pos as i16 >= self.pos.window_x as i16 - 7
|
||||||
|
{
|
||||||
|
self.window_stat.set_should_draw(true);
|
||||||
|
self.fetch.back.reset();
|
||||||
|
self.fetch.x_pos = 0;
|
||||||
|
self.fifo.back.clear();
|
||||||
|
}
|
||||||
|
|
||||||
if self.fetch.back.is_enabled() {
|
if self.fetch.back.is_enabled() {
|
||||||
match self.fetch.back.state {
|
match self.fetch.back.state {
|
||||||
SleepOne => self.fetch.back.next(TileNumber),
|
|
||||||
TileNumber => {
|
TileNumber => {
|
||||||
let x_pos = self.fetch.x_pos;
|
let x_pos = self.fetch.x_pos;
|
||||||
|
|
||||||
self.fetch.back.should_render_window(self.win_stat.enabled);
|
self.fetch
|
||||||
|
.back
|
||||||
|
.should_render_window(self.window_stat.should_draw());
|
||||||
|
|
||||||
let addr = self.fetch.bg_tile_num_addr(&self.ctrl, &self.pos, x_pos);
|
let addr = self.fetch.bg_tile_num_addr(&self.ctrl, &self.pos, x_pos);
|
||||||
|
|
||||||
let id = self.read_byte(addr);
|
let id = self.read_byte(addr);
|
||||||
self.fetch.back.tile.with_id(id);
|
self.fetch.back.tile.with_id(id);
|
||||||
|
|
||||||
self.fetch.back.next(TileLow);
|
// Move on to the Next state in 2 T-cycles
|
||||||
|
self.fetch.back.next(SleepOne);
|
||||||
}
|
}
|
||||||
SleepTwo => self.fetch.back.next(TileLow),
|
SleepOne => self.fetch.back.next(TileLow),
|
||||||
TileLow => {
|
TileLow => {
|
||||||
let addr = self.fetch.bg_byte_addr(&self.ctrl, &self.pos);
|
let addr = self.fetch.bg_byte_addr(&self.ctrl, &self.pos);
|
||||||
|
|
||||||
let low = self.read_byte(addr);
|
let low = self.read_byte(addr);
|
||||||
self.fetch.back.tile.with_low_byte(low);
|
self.fetch.back.tile.with_low_byte(low);
|
||||||
|
|
||||||
self.fetch.back.next(SleepThree);
|
self.fetch.back.next(SleepTwo);
|
||||||
}
|
}
|
||||||
SleepThree => self.fetch.back.next(TileHigh),
|
SleepTwo => self.fetch.back.next(TileHigh),
|
||||||
TileHigh => {
|
TileHigh => {
|
||||||
let addr = self.fetch.bg_byte_addr(&self.ctrl, &self.pos);
|
let addr = self.fetch.bg_byte_addr(&self.ctrl, &self.pos);
|
||||||
|
|
||||||
let high = self.read_byte(addr + 1);
|
let high = self.read_byte(addr + 1);
|
||||||
self.fetch.back.tile.with_high_byte(high);
|
self.fetch.back.tile.with_high_byte(high);
|
||||||
|
|
||||||
if self.fetch.back.scanline_first {
|
self.fetch.back.next(SleepThree);
|
||||||
self.fetch.back.reset();
|
|
||||||
|
|
||||||
self.fetch.back.scanline_first = false;
|
|
||||||
} else {
|
|
||||||
self.fetch.back.next(ToFifoOne);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ToFifoOne | ToFifoTwo => {
|
SleepThree => self.fetch.back.next(ToFifoOne),
|
||||||
|
ToFifoOne => {
|
||||||
|
self.fetch.back.next(ToFifoTwo);
|
||||||
|
}
|
||||||
|
ToFifoTwo => {
|
||||||
if let Ok(()) = self.fetch.send_to_fifo(&mut self.fifo) {
|
if let Ok(()) = self.fetch.send_to_fifo(&mut self.fifo) {
|
||||||
self.fetch.x_pos += 1;
|
self.fetch.x_pos += 1;
|
||||||
self.fetch.back.next(SleepOne);
|
self.fetch.back.next(TileNumber);
|
||||||
self.fetch.back.tile = Default::default();
|
self.fetch.back.tile = Default::default();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -372,7 +388,7 @@ impl Ppu {
|
||||||
self.scanline_start = false;
|
self.scanline_start = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.win_stat.enabled && self.to_discard > 0 && !self.fifo.back.is_empty() {
|
if self.to_discard > 0 && !self.fifo.back.is_empty() {
|
||||||
let _ = self.fifo.back.pop_front();
|
let _ = self.fifo.back.pop_front();
|
||||||
self.to_discard -= 1;
|
self.to_discard -= 1;
|
||||||
|
|
||||||
|
@ -390,17 +406,6 @@ impl Ppu {
|
||||||
|
|
||||||
self.x_pos += 1;
|
self.x_pos += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.ctrl.window_enabled()
|
|
||||||
&& !self.win_stat.enabled
|
|
||||||
&& self.win_stat.coincidence
|
|
||||||
&& self.x_pos as i16 >= self.pos.window_x as i16 - 7
|
|
||||||
{
|
|
||||||
self.win_stat.enabled = true;
|
|
||||||
self.fetch.back.reset();
|
|
||||||
self.fetch.x_pos = 0;
|
|
||||||
self.fifo.back.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,6 +413,7 @@ impl Ppu {
|
||||||
self.pos.line_y = 0;
|
self.pos.line_y = 0;
|
||||||
self.stat.set_mode(PpuMode::HBlank);
|
self.stat.set_mode(PpuMode::HBlank);
|
||||||
|
|
||||||
|
// TODO: Is this an unnecessary performance hit?
|
||||||
let mut blank = WHITE.repeat(self.frame_buf.len() / 4);
|
let mut blank = WHITE.repeat(self.frame_buf.len() / 4);
|
||||||
self.frame_buf.swap_with_slice(&mut blank);
|
self.frame_buf.swap_with_slice(&mut blank);
|
||||||
}
|
}
|
||||||
|
@ -465,7 +471,7 @@ impl Default for Ppu {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
vram: Box::new([0u8; VRAM_SIZE]),
|
vram: Box::new([0u8; VRAM_SIZE]),
|
||||||
dot: Default::default(),
|
cycle: Default::default(),
|
||||||
frame_buf: Box::new([0; GB_WIDTH * GB_HEIGHT * 4]),
|
frame_buf: Box::new([0; GB_WIDTH * GB_HEIGHT * 4]),
|
||||||
int: Default::default(),
|
int: Default::default(),
|
||||||
ctrl: Default::default(),
|
ctrl: Default::default(),
|
||||||
|
@ -473,11 +479,11 @@ impl Default for Ppu {
|
||||||
pos: Default::default(),
|
pos: Default::default(),
|
||||||
stat: Default::default(),
|
stat: Default::default(),
|
||||||
oam: Default::default(),
|
oam: Default::default(),
|
||||||
scan_dot: Default::default(),
|
scan_state: Default::default(),
|
||||||
fetch: Default::default(),
|
fetch: Default::default(),
|
||||||
fifo: Default::default(),
|
fifo: Default::default(),
|
||||||
obj_buffer: Default::default(),
|
obj_buffer: Default::default(),
|
||||||
win_stat: Default::default(),
|
window_stat: Default::default(),
|
||||||
dma: Default::default(),
|
dma: Default::default(),
|
||||||
x_pos: 0,
|
x_pos: 0,
|
||||||
scanline_start: true,
|
scanline_start: true,
|
||||||
|
@ -653,6 +659,10 @@ impl PixelFetcher {
|
||||||
self.x_pos = 0;
|
self.x_pos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn vblank_reset(&mut self) {
|
||||||
|
self.back.vblank_reset();
|
||||||
|
}
|
||||||
|
|
||||||
fn bg_tile_num_addr(&self, control: &LCDControl, pos: &ScreenPosition, x_pos: u8) -> u16 {
|
fn bg_tile_num_addr(&self, control: &LCDControl, pos: &ScreenPosition, x_pos: u8) -> u16 {
|
||||||
let line_y = pos.line_y;
|
let line_y = pos.line_y;
|
||||||
let scroll_y = pos.scroll_y;
|
let scroll_y = pos.scroll_y;
|
||||||
|
@ -672,7 +682,7 @@ impl PixelFetcher {
|
||||||
|
|
||||||
let scx_offset = if is_window { 0 } else { scroll_x / 8 };
|
let scx_offset = if is_window { 0 } else { scroll_x / 8 };
|
||||||
let y_offset = if is_window {
|
let y_offset = if is_window {
|
||||||
self.back.wl_count as u16 / 8
|
self.back.window_line.count() as u16 / 8
|
||||||
} else {
|
} else {
|
||||||
((line_y as u16 + scroll_y as u16) & 0xFF) / 8
|
((line_y as u16 + scroll_y as u16) & 0xFF) / 8
|
||||||
};
|
};
|
||||||
|
@ -696,7 +706,7 @@ impl PixelFetcher {
|
||||||
};
|
};
|
||||||
|
|
||||||
let offset = if is_window {
|
let offset = if is_window {
|
||||||
self.back.wl_count as u16 % 8
|
self.back.window_line.count() as u16 % 8
|
||||||
} else {
|
} else {
|
||||||
(line_y as u16 + scroll_y as u16) % 8
|
(line_y as u16 + scroll_y as u16) % 8
|
||||||
};
|
};
|
||||||
|
@ -758,10 +768,9 @@ trait Fetcher {
|
||||||
struct BackgroundFetcher {
|
struct BackgroundFetcher {
|
||||||
state: FetcherState,
|
state: FetcherState,
|
||||||
tile: TileBuilder,
|
tile: TileBuilder,
|
||||||
wl_count: u8,
|
window_line: WindowLineCounter,
|
||||||
is_window_tile: bool,
|
is_window_tile: bool,
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
scanline_first: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BackgroundFetcher {
|
impl BackgroundFetcher {
|
||||||
|
@ -784,6 +793,10 @@ impl BackgroundFetcher {
|
||||||
fn is_enabled(&self) -> bool {
|
fn is_enabled(&self) -> bool {
|
||||||
self.enabled
|
self.enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn vblank_reset(&mut self) {
|
||||||
|
self.window_line.vblank_reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Fetcher for BackgroundFetcher {
|
impl Fetcher for BackgroundFetcher {
|
||||||
|
@ -792,7 +805,7 @@ impl Fetcher for BackgroundFetcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&mut self) {
|
fn reset(&mut self) {
|
||||||
self.state = FetcherState::SleepOne;
|
self.state = Default::default();
|
||||||
self.tile = Default::default();
|
self.tile = Default::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -808,43 +821,53 @@ impl Fetcher for BackgroundFetcher {
|
||||||
impl Default for BackgroundFetcher {
|
impl Default for BackgroundFetcher {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
state: FetcherState::SleepOne,
|
state: Default::default(),
|
||||||
tile: Default::default(),
|
tile: Default::default(),
|
||||||
is_window_tile: Default::default(),
|
is_window_tile: Default::default(),
|
||||||
wl_count: Default::default(),
|
window_line: Default::default(),
|
||||||
enabled: true,
|
enabled: true,
|
||||||
scanline_first: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default)]
|
||||||
struct ObjectFetcher {
|
struct ObjectFetcher {
|
||||||
state: FetcherState,
|
state: FetcherState,
|
||||||
tile: TileBuilder,
|
tile: TileBuilder,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ObjectFetcher {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
state: FetcherState::TileNumber,
|
|
||||||
tile: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Fetcher for ObjectFetcher {
|
impl Fetcher for ObjectFetcher {
|
||||||
fn next(&mut self, state: FetcherState) {
|
fn next(&mut self, state: FetcherState) {
|
||||||
self.state = state
|
self.state = state
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset(&mut self) {
|
fn reset(&mut self) {
|
||||||
self.state = FetcherState::TileNumber;
|
self.state = Default::default();
|
||||||
self.tile = Default::default();
|
self.tile = Default::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hblank_reset(&mut self) {
|
fn hblank_reset(&mut self) {
|
||||||
self.reset()
|
self.state = Default::default();
|
||||||
|
self.tile = Default::default();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct WindowLineCounter {
|
||||||
|
count: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowLineCounter {
|
||||||
|
fn increment(&mut self) {
|
||||||
|
self.count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vblank_reset(&mut self) {
|
||||||
|
self.count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn count(&self) -> u8 {
|
||||||
|
self.count
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -860,6 +883,12 @@ enum FetcherState {
|
||||||
ToFifoTwo,
|
ToFifoTwo,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for FetcherState {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::TileNumber
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct BgPixelProperty {
|
struct BgPixelProperty {
|
||||||
shade_id: u8,
|
shade_id: u8,
|
||||||
|
@ -930,11 +959,84 @@ impl TileBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct OamScanState {
|
||||||
|
count: u8,
|
||||||
|
mode: OamScanMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OamScanState {
|
||||||
|
fn increase(&mut self) {
|
||||||
|
self.count += 1;
|
||||||
|
self.count %= 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
self.count = Default::default();
|
||||||
|
self.mode = Default::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn count(&self) -> u8 {
|
||||||
|
self.count
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mode(&self) -> &OamScanMode {
|
||||||
|
&self.mode
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&mut self) {
|
||||||
|
use OamScanMode::*;
|
||||||
|
|
||||||
|
self.mode = match self.mode {
|
||||||
|
Scan => Sleep,
|
||||||
|
Sleep => Scan,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
enum OamScanMode {
|
||||||
|
Scan,
|
||||||
|
Sleep,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for OamScanMode {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Scan
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct WindowStatus {
|
struct WindowStatus {
|
||||||
/// This will be true if WY == LY at any point in the frame thus far
|
/// This will be true if WY == LY at any point in the frame thus far
|
||||||
coincidence: bool,
|
coincidence: bool,
|
||||||
/// This will be true if the conditions which tell the PPU to start
|
/// This will be true if the conditions which tell the PPU to start
|
||||||
/// drawing from the window tile map is true
|
/// drawing from the window tile map is true
|
||||||
enabled: bool,
|
should_draw: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowStatus {
|
||||||
|
fn should_draw(&self) -> bool {
|
||||||
|
self.should_draw
|
||||||
|
}
|
||||||
|
|
||||||
|
fn coincidence(&self) -> bool {
|
||||||
|
self.coincidence
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_should_draw(&mut self, value: bool) {
|
||||||
|
self.should_draw = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_coincidence(&mut self, value: bool) {
|
||||||
|
self.coincidence = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hblank_reset(&mut self) {
|
||||||
|
self.should_draw = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vblank_reset(&mut self) {
|
||||||
|
self.coincidence = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,7 +183,7 @@ pub enum ObjectSize {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectSize {
|
impl ObjectSize {
|
||||||
pub(crate) fn size(&self) -> u8 {
|
pub(crate) fn as_u8(&self) -> u8 {
|
||||||
use ObjectSize::*;
|
use ObjectSize::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
|
|
Loading…
Reference in New Issue