Compare commits

...

6 Commits

11 changed files with 345 additions and 272 deletions

152
Cargo.lock generated
View File

@ -58,6 +58,15 @@ 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"
@ -236,6 +245,18 @@ 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"
@ -253,7 +274,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", "ansi_term 0.11.0",
"atty", "atty",
"bitflags", "bitflags",
"strsim 0.8.0", "strsim 0.8.0",
@ -711,6 +732,8 @@ dependencies = [
"pixels", "pixels",
"rodio", "rodio",
"rtrb", "rtrb",
"tracing",
"tracing-subscriber",
"winit", "winit",
"winit_input_helper", "winit_input_helper",
] ]
@ -1053,6 +1076,15 @@ 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"
@ -1306,6 +1338,16 @@ 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"
@ -1440,6 +1482,12 @@ 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"
@ -1552,6 +1600,15 @@ 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"
@ -1707,6 +1764,15 @@ 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"
@ -1886,6 +1952,15 @@ 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"
@ -1910,6 +1985,81 @@ 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"

View File

@ -18,6 +18,8 @@ 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

View File

@ -1,6 +1,7 @@
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};
@ -54,7 +55,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),
_ => { _ => {
eprintln!("Read 0xFF from unused IO register {:#06X} [APU]", addr); warn!("Attempted read from {:#06X}", addr);
0xFF 0xFF
} }
} }
@ -85,10 +86,7 @@ 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 => {}
_ => eprintln!( _ => warn!("Attempted write of {:#04X} to {:#06X}", byte, addr),
"Wrote {:#04X} to unused IO register {:#06X} [APU]",
byte, addr
),
} }
} }
} }

View File

@ -1,3 +1,5 @@
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;
@ -14,7 +16,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>,
pub(crate) ppu: Ppu, ppu: Ppu,
work_ram: WorkRam, work_ram: WorkRam,
var_ram: VariableWorkRam, var_ram: VariableWorkRam,
pub(crate) timer: Timer, pub(crate) timer: Timer,
@ -100,6 +102,11 @@ 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 {
@ -251,7 +258,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,
_ => { _ => {
eprintln!("Read 0xFF from unused IO register {:#06X}.", addr); warn!("Attempted read from {:#06X} on IO", addr);
0xFF 0xFF
} }
} }
@ -368,7 +375,7 @@ impl BusIo for Bus {
self.boot = None; self.boot = None;
} }
} }
_ => eprintln!("Wrote {:#04X} to unused IO register {:#06X}.", byte, addr), _ => warn!("Attempted write of {:#04X} to {:#06X} on IO", byte, addr),
}; };
} }
0xFF80..=0xFFFE => { 0xFF80..=0xFFFE => {

View File

@ -1,3 +1,5 @@
use tracing::{info, warn};
use crate::bus::BusIo; use crate::bus::BusIo;
const RAM_SIZE_ADDRESS: usize = 0x0149; const RAM_SIZE_ADDRESS: usize = 0x0149;
@ -15,7 +17,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);
eprintln!("Cartridge Title: {:?}", title); info!("Title: {:?}", title);
Self { Self {
mbc: Self::detect_mbc(&memory), mbc: Self::detect_mbc(&memory),
@ -39,20 +41,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();
eprintln!("Cartridge Ram Size: {} bytes", ram_cap); info!("RAM size: {} bytes", ram_cap);
eprintln!("Cartridge ROM Size: {} bytes", rom_size.capacity()); info!("ROM size: {} bytes", rom_size.capacity());
eprintln!("MBC Type: {:?}", mbc_kind); info!("MBC kind: {:?}", 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)), // TODO: Implement Saving MBCKind::MBC1WithBattery => Box::new(MBC1::with_battery(ram_size, rom_size)),
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)), // TODO: Implement Saving MBCKind::MBC2WithBattery => Box::new(MBC2::with_battery(rom_cap)),
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)), // TODO: Implement Saving MBCKind::MBC3WithBattery => Box::new(MBC3::with_battery(ram_cap)),
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)), // TDO: Implement Saving MBCKind::MBC5WithBattery => Box::new(MBC5::with_battery(ram_cap, rom_cap)),
} }
} }
@ -596,8 +598,8 @@ impl MBCIo for NoMBC {
MBCResult::Address(addr as usize) MBCResult::Address(addr as usize)
} }
fn handle_write(&mut self, _addr: u16, _byte: u8) { fn handle_write(&mut self, _: u16, byte: u8) {
// eprintln!("Tried to write {:#04X} to a read-only cartridge", byte); warn!("Attempted write of {:#04X} to cartridge w/out MBC", byte);
} }
} }

View File

@ -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 bus: Bus, pub(crate) bus: Bus,
reg: Registers, reg: Registers,
flags: Flags, flags: Flags,
ime: ImeState, ime: ImeState,
@ -118,13 +118,10 @@ 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();
@ -133,11 +130,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
} }

View File

@ -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,6 +121,8 @@ 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;
@ -159,8 +161,14 @@ 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) => Cpu::with_boot(rom), Some(rom) => {
None => Cpu::with_boot(*include_bytes!("../bin/bootix_dmg.bin")), info!("User-provided Boot ROM");
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() {

View File

@ -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);

View File

@ -1,12 +1,15 @@
use std::convert::TryInto; use std::convert::TryInto;
use anyhow::{anyhow, Result}; use 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};
@ -41,6 +44,15 @@ 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"))?;
@ -51,9 +63,11 @@ 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
@ -84,6 +98,7 @@ 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();
}); });
@ -93,11 +108,7 @@ 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 if pixels.render().is_err() {
.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;
@ -119,8 +130,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 >= gb::emu::CYCLES_IN_FRAME { if cycle_count >= CYCLES_IN_FRAME {
cycle_count %= gb::emu::CYCLES_IN_FRAME; cycle_count %= 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()

View File

@ -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_state: OamScanState, scan_dot: Cycle,
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]>,
window_stat: WindowStatus, win_stat: WindowStatus,
scanline_start: bool, scanline_start: bool,
to_discard: u8, to_discard: u8,
x_pos: u8, x_pos: u8,
cycle: Cycle, dot: 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.cycle += 1; self.dot += 1;
if !self.ctrl.lcd_enabled() { if !self.ctrl.lcd_enabled() {
return; return;
@ -79,7 +79,15 @@ impl Ppu {
match self.stat.mode() { match self.stat.mode() {
PpuMode::OamScan => { PpuMode::OamScan => {
if self.cycle >= 80 { // Cycles 1 -> 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);
} }
@ -88,7 +96,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.cycle); self.draw();
} else { } else {
self.reset(); self.reset();
} }
@ -104,29 +112,22 @@ 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.window_stat.should_draw() { if self.win_stat.enabled {
self.fetch.back.window_line.increment(); self.fetch.back.wl_count += 1;
} }
self.x_pos = 0;
self.scanline_start = true;
self.to_discard = 0;
self.fetch.hblank_reset(); self.fetch.hblank_reset();
self.window_stat.hblank_reset(); self.win_stat.enabled = false;
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.cycle >= 456 { if self.dot >= 456 {
self.cycle %= 456; self.dot %= 456;
self.pos.line_y += 1; self.pos.line_y += 1;
// Update LY==LYC bit // Update LY==LYC bit
@ -143,9 +144,10 @@ 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.vblank_reset(); self.fetch.back.wl_count = 0;
// Reset WY=LY coincidence flag // Reset WY=LY coincidence flag
self.window_stat.vblank_reset(); self.win_stat.coincidence = false;
if self.stat.vblank_int() { if self.stat.vblank_int() {
// Enable Vblank LCDStat Interrupt // Enable Vblank LCDStat Interrupt
@ -159,7 +161,7 @@ impl Ppu {
self.int.set_lcd_stat(true); self.int.set_lcd_stat(true);
} }
self.scan_state.reset(); self.scan_dot = Default::default();
PpuMode::OamScan PpuMode::OamScan
}; };
@ -167,8 +169,8 @@ impl Ppu {
} }
} }
PpuMode::VBlank => { PpuMode::VBlank => {
if self.cycle > 456 { if self.dot >= 456 {
self.cycle %= 456; self.dot %= 456;
self.pos.line_y += 1; self.pos.line_y += 1;
// Update LY==LYC bit // Update LY==LYC bit
@ -188,8 +190,7 @@ impl Ppu {
self.int.set_lcd_stat(true); self.int.set_lcd_stat(true);
} }
self.scan_state.reset(); self.scan_dot = Default::default();
self.stat.set_mode(PpuMode::OamScan); self.stat.set_mode(PpuMode::OamScan);
} }
} }
@ -198,37 +199,31 @@ impl Ppu {
} }
fn scan_oam(&mut self) { fn scan_oam(&mut self) {
match self.scan_state.mode() { if self.scan_dot % 2 == 0 {
OamScanMode::Scan if !self.dma.is_active() => { if self.dma.is_active() {
if !self.window_stat.coincidence() && self.scan_state.count() == 0 { return;
// Determine whether we should draw the window next frame
self.window_stat
.set_coincidence(self.pos.line_y == self.pos.window_y);
}
let sprite_height = self.ctrl.obj_size().as_u8();
let index = self.scan_state.count();
let attr = self.oam.attribute(index as usize);
let line_y = self.pos.line_y + 16;
if attr.x > 0
&& line_y >= attr.y
&& line_y < (attr.y + sprite_height)
&& !self.obj_buffer.is_full()
{
self.obj_buffer.add(attr);
}
self.scan_state.increase();
} }
_ => {}
}
self.scan_state.next(); if !self.win_stat.coincidence && self.scan_dot == 0 {
self.win_stat.coincidence = self.pos.line_y == self.pos.window_y;
}
let obj_height = self.ctrl.obj_size().size();
let attr = self.oam.attribute(self.scan_dot as usize / 2);
let line_y = self.pos.line_y + 16;
if attr.x > 0
&& line_y >= attr.y
&& line_y < (attr.y + obj_height)
&& !self.obj_buffer.is_full()
{
self.obj_buffer.add(attr);
}
}
self.scan_dot += 1;
} }
fn draw(&mut self, _cycle: Cycle) { fn draw(&mut self) {
use FetcherState::*; use FetcherState::*;
let mut iter = self.obj_buffer.iter_mut(); let mut iter = self.obj_buffer.iter_mut();
@ -322,60 +317,49 @@ 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 self.fetch.back.should_render_window(self.win_stat.enabled);
.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);
// Move on to the Next state in 2 T-cycles self.fetch.back.next(TileLow);
self.fetch.back.next(SleepOne);
} }
SleepOne => self.fetch.back.next(TileLow), SleepTwo => 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(SleepTwo); self.fetch.back.next(SleepThree);
} }
SleepTwo => self.fetch.back.next(TileHigh), SleepThree => 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);
self.fetch.back.next(SleepThree); if self.fetch.back.scanline_first {
self.fetch.back.reset();
self.fetch.back.scanline_first = false;
} else {
self.fetch.back.next(ToFifoOne);
}
} }
SleepThree => self.fetch.back.next(ToFifoOne), ToFifoOne | ToFifoTwo => {
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(TileNumber); self.fetch.back.next(SleepOne);
self.fetch.back.tile = Default::default(); self.fetch.back.tile = Default::default();
} }
} }
@ -388,7 +372,7 @@ impl Ppu {
self.scanline_start = false; self.scanline_start = false;
} }
if self.to_discard > 0 && !self.fifo.back.is_empty() { if !self.win_stat.enabled && 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;
@ -406,6 +390,17 @@ 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();
}
} }
} }
@ -413,7 +408,6 @@ 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);
} }
@ -471,7 +465,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]),
cycle: Default::default(), dot: 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(),
@ -479,11 +473,11 @@ impl Default for Ppu {
pos: Default::default(), pos: Default::default(),
stat: Default::default(), stat: Default::default(),
oam: Default::default(), oam: Default::default(),
scan_state: Default::default(), scan_dot: Default::default(),
fetch: Default::default(), fetch: Default::default(),
fifo: Default::default(), fifo: Default::default(),
obj_buffer: Default::default(), obj_buffer: Default::default(),
window_stat: Default::default(), win_stat: Default::default(),
dma: Default::default(), dma: Default::default(),
x_pos: 0, x_pos: 0,
scanline_start: true, scanline_start: true,
@ -659,10 +653,6 @@ 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;
@ -682,7 +672,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.window_line.count() as u16 / 8 self.back.wl_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
}; };
@ -706,7 +696,7 @@ impl PixelFetcher {
}; };
let offset = if is_window { let offset = if is_window {
self.back.window_line.count() as u16 % 8 self.back.wl_count as u16 % 8
} else { } else {
(line_y as u16 + scroll_y as u16) % 8 (line_y as u16 + scroll_y as u16) % 8
}; };
@ -768,9 +758,10 @@ trait Fetcher {
struct BackgroundFetcher { struct BackgroundFetcher {
state: FetcherState, state: FetcherState,
tile: TileBuilder, tile: TileBuilder,
window_line: WindowLineCounter, wl_count: u8,
is_window_tile: bool, is_window_tile: bool,
enabled: bool, enabled: bool,
scanline_first: bool,
} }
impl BackgroundFetcher { impl BackgroundFetcher {
@ -793,10 +784,6 @@ 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 {
@ -805,7 +792,7 @@ impl Fetcher for BackgroundFetcher {
} }
fn reset(&mut self) { fn reset(&mut self) {
self.state = Default::default(); self.state = FetcherState::SleepOne;
self.tile = Default::default(); self.tile = Default::default();
} }
@ -821,53 +808,43 @@ impl Fetcher for BackgroundFetcher {
impl Default for BackgroundFetcher { impl Default for BackgroundFetcher {
fn default() -> Self { fn default() -> Self {
Self { Self {
state: Default::default(), state: FetcherState::SleepOne,
tile: Default::default(), tile: Default::default(),
is_window_tile: Default::default(), is_window_tile: Default::default(),
window_line: Default::default(), wl_count: Default::default(),
enabled: true, enabled: true,
scanline_first: true,
} }
} }
} }
#[derive(Debug, Default)] #[derive(Debug)]
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 = Default::default(); self.state = FetcherState::TileNumber;
self.tile = Default::default(); self.tile = Default::default();
} }
fn hblank_reset(&mut self) { fn hblank_reset(&mut self) {
self.state = Default::default(); self.reset()
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
} }
} }
@ -883,12 +860,6 @@ 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,
@ -959,84 +930,11 @@ 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
should_draw: bool, enabled: 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;
}
} }

View File

@ -183,7 +183,7 @@ pub enum ObjectSize {
} }
impl ObjectSize { impl ObjectSize {
pub(crate) fn as_u8(&self) -> u8 { pub(crate) fn size(&self) -> u8 {
use ObjectSize::*; use ObjectSize::*;
match self { match self {