diff --git a/src/cpu.rs b/src/cpu.rs index b586bc6..7b67ad1 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -1,7 +1,7 @@ use std::fmt::Write; use tracing::debug; -use crate::{debug::DebugLog, mem::CpuMem}; +use crate::{Break, debug::DebugLog, mem::CpuMem}; bitfield::bitfield! { #[derive(Clone, Copy, PartialEq, Eq)] @@ -634,7 +634,9 @@ impl Cpu { if self.status.carry() { let page = self.jmp_rel(off); log!(" : - JMP ${:04X}", self.pc); - if page { return ExecState::PageCross }; + if page { + return ExecState::PageCross; + }; } }), 0xF0 => inst!("BEQ", self.status.zero(), |off| { @@ -642,7 +644,9 @@ impl Cpu { if self.status.zero() { let page = self.jmp_rel(off); log!(" : - JMP ${:04X}", self.pc); - if page { return ExecState::PageCross }; + if page { + return ExecState::PageCross; + }; } }), 0x30 => inst!("BMI", self.status.negative(), |off| { @@ -650,7 +654,9 @@ impl Cpu { if self.status.negative() { let page = self.jmp_rel(off); log!(" : - JMP ${:04X}", self.pc); - if page { return ExecState::PageCross }; + if page { + return ExecState::PageCross; + }; } }), 0xD0 => inst!("BNE", !self.status.zero(), |off| { @@ -658,7 +664,9 @@ impl Cpu { if !self.status.zero() { let page = self.jmp_rel(off); log!(" : - JMP ${:04X}", self.pc); - if page { return ExecState::PageCross }; + if page { + return ExecState::PageCross; + }; } }), 0x10 => inst!("BPL", !self.status.negative(), |off| { @@ -666,7 +674,9 @@ impl Cpu { if !self.status.negative() { let page = self.jmp_rel(off); log!(" : - JMP ${:04X}", self.pc); - if page { return ExecState::PageCross }; + if page { + return ExecState::PageCross; + }; } }), 0x4C => inst!("JMP abs", 0, |low, high| { @@ -1509,14 +1519,14 @@ impl Cpu { }), 0x24 => inst!("BIT zp", 1, |off| { let val = self.read_abs(mem, off, 0); - self.status.set_zero(val == 0); + self.status.set_zero(val & self.a == 0); self.status.set_overflow(val & 0x40 == 0x40); self.status.set_negative(val & 0x80 == 0x80); log!("{addr:04X}: BIT ${:02X} | {:02X}", off, val); }), 0x2C => inst!("BIT abs", 1, |low, high| { let val = self.read_abs(mem, low, high); - self.status.set_zero(val == 0); + self.status.set_zero(val & self.a == 0); self.status.set_overflow(val & 0x40 == 0x40); self.status.set_negative(val & 0x80 == 0x80); log!("{addr:04X}: BIT ${:02X}{:02X} | {:02X}", high, low, val); @@ -1723,7 +1733,7 @@ impl Cpu { } } - fn clock_cpu(&mut self, mem: &mut CpuMem<'_>, nmi: bool, irq: bool) { + pub fn clock_cpu(&mut self, mem: &mut CpuMem<'_>, nmi: bool, irq: bool, br: &Break) -> bool { self.clock_state = match self.clock_state { ClockState::HoldNmi { cycles } => { if cycles == 0 { @@ -1750,6 +1760,10 @@ impl Cpu { } } ClockState::ReadInstruction => { + if br.cpu_exec { + println!("Returning early from clock_cpu"); + return true; + } let addr = self.pc; let instruction = mem.read(self.pc); if instruction != 0x02 { @@ -1900,59 +1914,13 @@ impl Cpu { } // self.irq_pending = self.ppu.irq_waiting() || self.apu.irq_waiting(); self.irq_pending = irq; + false } - pub fn cpu_cycle( - &mut self, - mem: &mut CpuMem<'_>, - dma: &mut DmaState, - nmi: bool, - irq: bool, - ) { - match *dma { - DmaState::Passive => self.clock_cpu(mem, nmi, irq), - // TODO: Validate that this takes the correct number of cycles (513 or 514 cycles) - DmaState::Running { - cpu_addr, - rem, - read: Some(read), - } => { - mem.write(0x2004, read); - // self.ppu.write_reg(4, read); - debug!("OAM DMA write {:02X}", read); - if rem == 0 { - *dma = DmaState::Passive; - } else { - *dma = DmaState::Running { - cpu_addr: cpu_addr + 1, - rem: rem - 1, - read: None, - }; - } - } - DmaState::Running { - cpu_addr, - rem, - read: None, - } => { - let read = mem.read(cpu_addr); - debug!("OAM DMA read {:04X} {:02X}", cpu_addr, read); - *dma = DmaState::Running { - cpu_addr, - rem, - read: Some(read), - }; - } - } - // if [0x8031, 0x8014].contains(&self.cpu.pc) { - // self.dbg_int = true; - // } - } pub fn cpu_cycle_update(&mut self) { if !self.halted { self.cycle += 1; } - } pub fn executed(&self) -> bool { @@ -1978,6 +1946,7 @@ impl Cpu { #[derive(Debug, Clone, Copy)] pub enum DmaState { Passive, + Idle(u16), Running { cpu_addr: u16, rem: u8, @@ -1995,6 +1964,56 @@ impl DmaState { } } } + + pub fn cycle(self, mem: &mut CpuMem<'_>, cpu: &mut Cpu) -> Option { + match self { + DmaState::Passive => None, + DmaState::Idle(cpu_addr) => Some(DmaState::Running { + cpu_addr, + rem: 0xFF, + read: None, + }), + DmaState::Running { + cpu_addr, + rem, + read: Some(read), + } => { + mem.write(0x2004, read); + // self.ppu.write_reg(4, read); + writeln!(&mut cpu.debug_log, "OAM DMA write {:02X}", read).unwrap(); + if rem == 0 { + Some(DmaState::Passive) + } else { + Some(DmaState::Running { + cpu_addr: cpu_addr + 1, + rem: rem - 1, + read: None, + }) + } + } + DmaState::Running { + cpu_addr, + rem, + read: None, + } => { + if cpu.cycle % 2 != 1 { + return Some(self); + } + let read = mem.read(cpu_addr); + writeln!( + &mut cpu.debug_log, + "OAM DMA read {:04X} {:02X}", + cpu_addr, read + ) + .unwrap(); + Some(DmaState::Running { + cpu_addr, + rem, + read: Some(read), + }) + } + } + } } pub struct CycleResult { diff --git a/src/debugger.rs b/src/debugger.rs index 668b48a..b396c37 100644 --- a/src/debugger.rs +++ b/src/debugger.rs @@ -24,7 +24,7 @@ use iced::{ }, }; -use crate::{CycleResult, NES, PPU, mem::Mapped}; +use crate::{Break, CycleResult, NES, PPU, mem::Mapped}; #[derive(Debug, Clone)] pub struct DebuggerState { @@ -254,23 +254,41 @@ impl DebuggerState { fn run_n_clock_cycles(nes: &mut NES, n: usize) { for _ in 0..n { - if nes.run_one_clock_cycle().dbg_int || nes.halted() { + if nes.run_one_clock_cycle(&Break::default()).dbg_int || nes.halted() { break; } } } - fn run_until(nes: &mut NES, mut f: impl FnMut(CycleResult, &NES) -> bool, mut count: usize) { - loop { - let res = nes.run_one_clock_cycle(); - if res.dbg_int || f(res, nes) { - count -= 1; - if count <= 0 { - break; - } - } - if nes.halted() { - break; - } + fn run_until( + nes: &mut NES, + br: &Break, + mut f: impl FnMut(CycleResult, &NES) -> bool, + // mut count: usize, + ) { + // Always run at least 1 cycle + let mut res = nes.run_one_clock_cycle(&Break::default()); + while !nes.halted() && !res.dbg_int && !f(res, nes) { + // if res.dbg_int || f(res, nes) { + // count -= 1; + // if count <= 0 { + // break; + // } + // } + // if nes.halted() { + // break; + // } + res = nes.run_one_clock_cycle(br); + } + } + fn run_until_n( + nes: &mut NES, + br: &Break, + mut f: impl FnMut(CycleResult, &NES) -> bool, + mut count: usize, + ) { + while count > 0 { + Self::run_until(nes, br, &mut f); + count -= 1; } } @@ -280,23 +298,49 @@ impl DebuggerState { DebuggerMessage::SetCPUCycles(n) => self.cpu_cycles = n, DebuggerMessage::SetInstructions(n) => self.instructions = n, DebuggerMessage::SetScanLines(n) => self.scan_lines = n, - DebuggerMessage::SetToScanLine(n) => self.to_scan_line = n, + DebuggerMessage::SetToScanLine(n) => self.to_scan_line = n.min(261), // Max scanline is 261 DebuggerMessage::SetFrames(n) => self.frames = n, DebuggerMessage::SetBreakpoint(n) => self.breakpoint = n, DebuggerMessage::RunPPUCycles => Self::run_n_clock_cycles(nes, self.ppu_cycles), DebuggerMessage::RunCPUCycles => Self::run_n_clock_cycles(nes, self.cpu_cycles * 3), - DebuggerMessage::RunInstructions => { - Self::run_until(nes, |c, _| c.cpu_exec, self.instructions) - } + // DebuggerMessage::RunInstructions => Self::run_until_n( + // nes, + // &Break { + // cpu_exec: true, + // ..Break::default() + // }, + // |_, _| false, + // self.instructions, + // ), + DebuggerMessage::RunInstructions => Self::run_until_n( + nes, + &Break { + ..Break::default() + }, + |res, _| res.cpu_exec, + self.instructions, + ), DebuggerMessage::RunScanLines => Self::run_n_clock_cycles(nes, self.scan_lines * 341), - DebuggerMessage::RunToScanLine => { - Self::run_until(nes, |_, n| n.ppu.scanline == self.to_scan_line, 1) - } + DebuggerMessage::RunToScanLine => Self::run_until( + nes, + &Break { + ppu_scanline: true, + ..Break::default() + }, + |_, n| n.ppu.scanline == self.to_scan_line, + ), DebuggerMessage::RunFrames => Self::run_n_clock_cycles(nes, self.frames * 341 * 262), - DebuggerMessage::RunBreakpoint => { - Self::run_until(nes, |_, nes| nes.cpu.pc as usize == self.breakpoint, 1) + DebuggerMessage::RunBreakpoint => Self::run_until( + nes, + &Break { + break_points: vec![self.breakpoint as u16], + ..Break::default() + }, + |_, nes| nes.cpu.pc as usize == self.breakpoint, + ), + DebuggerMessage::Run => { + Self::run_until(nes, &Break { ..Break::default() }, |_, nes| nes.halted()) } - DebuggerMessage::Run => Self::run_until(nes, |_, nes| nes.halted(), 1), DebuggerMessage::Pause => todo!(), } } diff --git a/src/hex_view.rs b/src/hex_view.rs index 885245a..63295c9 100644 --- a/src/hex_view.rs +++ b/src/hex_view.rs @@ -7,18 +7,24 @@ use iced::{ widget::{column, lazy, row, text}, }; -use crate::mem::Mapped; +use crate::{mem::Mapped, PPU}; pub trait Memory { - fn peek(&self, val: u16) -> Option; + fn peek(&self, val: usize) -> Option; + fn len(&self) -> usize; fn edit_ver(&self) -> usize; } +#[derive(Debug, Clone, Copy)] pub struct Cpu<'a>(pub &'a Mapped); impl Memory for Cpu<'_> { - fn peek(&self, val: u16) -> Option { - self.0.peek_cpu(val) + fn peek(&self, val: usize) -> Option { + self.0.peek_cpu(val as u16) + } + + fn len(&self) -> usize { + 0x10000 } fn edit_ver(&self) -> usize { @@ -26,11 +32,16 @@ impl Memory for Cpu<'_> { } } +#[derive(Debug, Clone, Copy)] pub struct Ppu<'a>(pub &'a Mapped); impl Memory for Ppu<'_> { - fn peek(&self, val: u16) -> Option { - self.0.peek_ppu(val) + fn peek(&self, val: usize) -> Option { + self.0.peek_ppu(val as u16) + } + + fn len(&self) -> usize { + 0x10000 } fn edit_ver(&self) -> usize { @@ -38,6 +49,23 @@ impl Memory for Ppu<'_> { } } +#[derive(Debug, Clone, Copy)] +pub struct Oam<'a>(pub &'a PPU); + +impl Memory for Oam<'_> { + fn peek(&self, val: usize) -> Option { + Some(self.0.peek_oam(val as u8)) + } + + fn len(&self) -> usize { + 0x100 + } + + fn edit_ver(&self) -> usize { + self.0.oam_edit_ver() + } +} + #[derive(Debug, Clone)] pub enum HexEvent {} @@ -60,29 +88,13 @@ impl HexView { Self {} } - pub fn render<'a>(&self, mem: &'a Mapped, ppu: bool) -> Element<'a, HexEvent> { - struct Row<'a>(u16, &'a Mapped, bool); - impl fmt::Display for Row<'_> { + pub fn render_any<'a, M: Memory + Copy + 'a>(&self, mem: M) -> Element<'a, HexEvent> { + struct Row(usize, M); + impl<'a, M: Memory + 'a> fmt::Display for Row { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - Val(if self.2 { - self.1.peek_ppu(self.0) - } else { - self.1.peek_cpu(self.0) - }) - )?; + write!(f, "{}", Val(self.1.peek(self.0)))?; for i in 1..16 { - write!( - f, - " {}", - Val(if self.2 { - self.1.peek_ppu(self.0 + i) - } else { - self.1.peek_cpu(self.0 + i) - }) - )?; + write!(f, " {}", Val(self.1.peek(self.0 + i)))?; } Ok(()) } @@ -90,11 +102,7 @@ impl HexView { column![ text!("Hex view"), iced::widget::scrollable(lazy( - if ppu { - mem.ppu_edit_ver() - } else { - mem.cpu_edit_ver() - }, + mem.edit_ver(), move |_| column( [ text!(" | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F") @@ -102,8 +110,8 @@ impl HexView { .into() ] .into_iter() - .chain((0..u16::MAX).step_by(16).map(|off| { - text!(" {off:04X} | {}", Row(off, mem, ppu)) + .chain((0..mem.len()).step_by(16).map(|off| { + text!(" {off:04X} | {}", Row(off, mem)) .font(Font::MONOSPACE) .into() })) @@ -114,6 +122,15 @@ impl HexView { .width(Fill) .into() } + pub fn render_cpu<'a>(&self, mem: &'a Mapped) -> Element<'a, HexEvent> { + self.render_any(Cpu(mem)) + } + pub fn render_ppu<'a>(&self, mem: &'a Mapped) -> Element<'a, HexEvent> { + self.render_any(Ppu(mem)) + } + pub fn render_oam<'a>(&self, ppu: &'a PPU) -> Element<'a, HexEvent> { + self.render_any(Oam(ppu)) + } pub fn update(&mut self, ev: HexEvent) -> Task { todo!() diff --git a/src/lib.rs b/src/lib.rs index c34d87f..0173e2d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -163,7 +163,45 @@ impl NES { self.ppu.peek_irq() || self.apu.peek_irq() } - pub fn run_one_clock_cycle(&mut self) -> CycleResult { + pub fn run_cpu_cycle( + &mut self, + // mem: &mut CpuMem<'_>, + // dma: &mut DmaState, + // nmi: bool, + // irq: bool, + br: &Break, + ) -> bool { + if let Some(dma) = self.dma.cycle( + &mut CpuMem::new( + &mut self.mapped, + &mut self.ppu, + &mut self.apu, + &mut self.dma, + &mut self.controller, + ), + &mut self.cpu + ) { + self.dma = dma; + false + } else { + let nmi = self.ppu.nmi_waiting() || self.apu.nmi_waiting(); + let irq = self.ppu.irq_waiting() || self.apu.irq_waiting(); + self.cpu.clock_cpu( + &mut CpuMem::new( + &mut self.mapped, + &mut self.ppu, + &mut self.apu, + &mut self.dma, + &mut self.controller, + ), + nmi, + irq, + br, + ) + } + } + + pub fn run_one_clock_cycle(&mut self, br: &Break) -> CycleResult { if self.cpu.halted() { return CycleResult { cpu_exec: false, @@ -172,31 +210,40 @@ impl NES { dbg_int: true, }; } - self.clock_count += 1; - let cpu_exec = if self.clock_count % 3 == 0 { + let cpu_exec = if self.clock_count % 3 == 0 && self.clock_count > 0 { let nmi = self.ppu.nmi_waiting() || self.apu.nmi_waiting(); let irq = self.ppu.irq_waiting() || self.apu.irq_waiting(); let mut dma = DmaState::Passive; - self.cpu.cpu_cycle( - &mut CpuMem::new( - &mut self.mapped, - &mut self.ppu, - &mut self.apu, - &mut dma, - &mut self.controller, - ), - &mut self.dma, - nmi, - irq, - ); + if self.run_cpu_cycle( + // &mut CpuMem::new( + // &mut self.mapped, + // &mut self.ppu, + // &mut self.apu, + // &mut dma, + // &mut self.controller, + // ), + // &mut self.dma, + // nmi, + // irq, + br, + ) { + println!("Returning early from clock_cycle"); + return CycleResult { + cpu_exec: true, + ppu_frame: false, + dma: false, + dbg_int: true, + }; + } self.dma.merge(dma); + self.cpu.cpu_cycle_update(); self.cpu.executed() + } else if self.clock_count == 0 { + self.cpu.cpu_cycle_update(); + false } else { false }; - if self.clock_count % 3 == 1 { - self.cpu.cpu_cycle_update(); - } // let cpu_exec = self.clock_count % 3 == CPU_CLOCK_OFFSET && self.cpu.executed(); if !self.cpu.halted() { let _apu_exec = self.apu.run_one_clock_cycle(self.clock_count); @@ -211,6 +258,7 @@ impl NES { let dbg_int = self.dbg_int | self.ppu.dbg_int; self.dbg_int = false; self.ppu.dbg_int = false; + self.clock_count += 1; CycleResult { cpu_exec, ppu_frame, @@ -243,7 +291,7 @@ impl NES { self.reset(); while !self.cpu.halted() { // info!("Running clock cycle: {}", self.clock_count); - self.run_one_clock_cycle(); + self.run_one_clock_cycle(&Break::default()); } } @@ -281,7 +329,7 @@ impl NES { let mut cur = 0; while !self.cpu.halted() && cur < max_clock_cycles { // info!("Running clock cycle: {}", self.clock_count); - self.run_one_clock_cycle(); + self.run_one_clock_cycle(&Break::default()); cur += 1; } } @@ -326,3 +374,16 @@ impl NES { self.cpu.cycle } } + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] +pub struct Break { + pub dma: bool, + pub nmi: bool, + pub irq: bool, + pub cpu_exec: bool, + pub ppu_frame: bool, + pub ppu_scanline: bool, + pub break_points: Vec, + // pub dma: bool, + // pub dbg_int: bool, +} diff --git a/src/main.rs b/src/main.rs index 074a0a1..821ab5f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -51,6 +51,7 @@ fn main() -> Result<(), iced::Error> { enum MemoryTy { Cpu, PPU, + OAM, } impl fmt::Display for MemoryTy { @@ -86,6 +87,7 @@ impl fmt::Display for HeaderButton { match self { Self::Open(WindowType::Memory(MemoryTy::Cpu, _)) => write!(f, "Open Memory Viewer"), Self::Open(WindowType::Memory(MemoryTy::PPU, _)) => write!(f, "Open PPU Memory Viewer"), + Self::Open(WindowType::Memory(MemoryTy::OAM, _)) => write!(f, "Open OAM Memory Viewer"), Self::Open(WindowType::TileMap) => write!(f, "Open TileMap Viewer"), Self::Open(WindowType::TileViewer) => write!(f, "Open Tile Viewer"), Self::Open(WindowType::Debugger) => write!(f, "Open Debugger"), @@ -108,11 +110,11 @@ struct Emulator { #[derive(Debug, Clone)] enum Message { OpenRom(NES), - Tick(usize), - Frame, - DMA, - CPU, - DebugInt, + // Tick(usize), + // Frame, + // DMA, + // CPU, + // DebugInt, WindowClosed(Id), WindowOpened(Id), Header(HeaderButton), @@ -170,15 +172,15 @@ impl Emulator { fn update(&mut self, message: Message) -> Task { match message { - Message::Tick(count) => { - for _ in 0..count { - self.nes.run_one_clock_cycle(); - } - } - Message::Frame => while !self.nes.run_one_clock_cycle().ppu_frame {}, - Message::DMA => while !self.nes.run_one_clock_cycle().dma {}, - Message::CPU => while !self.nes.run_one_clock_cycle().cpu_exec {}, - Message::DebugInt => while !self.nes.run_one_clock_cycle().dbg_int {}, + // Message::Tick(count) => { + // for _ in 0..count { + // self.nes.run_one_clock_cycle(); + // } + // } + // Message::Frame => while !self.nes.run_one_clock_cycle().ppu_frame {}, + // Message::DMA => while !self.nes.run_one_clock_cycle().dma {}, + // Message::CPU => while !self.nes.run_one_clock_cycle().cpu_exec {}, + // Message::DebugInt => while !self.nes.run_one_clock_cycle().dbg_int {}, Message::WindowClosed(id) => { if let Some(WindowType::Main) = self.windows.remove(&id) { return iced::exit(); @@ -238,8 +240,13 @@ impl Emulator { } Message::Export(ty) => { let raw: Vec<_> = match ty { - MemoryTy::Cpu => (0..=0xFFFF).map(|i| self.nes.mem().peek_cpu(i).unwrap_or(0)).collect(), - MemoryTy::PPU => (0..=0xFFFF).map(|i| self.nes.mem().peek_ppu(i).unwrap_or(0)).collect(), + MemoryTy::Cpu => (0..=0xFFFF) + .map(|i| self.nes.mem().peek_cpu(i).unwrap_or(0)) + .collect(), + MemoryTy::PPU => (0..=0xFFFF) + .map(|i| self.nes.mem().peek_ppu(i).unwrap_or(0)) + .collect(), + MemoryTy::OAM => (0..=0xFF).map(|i| self.nes.ppu().peek_oam(i)).collect(), }; return Task::future(async move { if let Some(file) = rfd::AsyncFileDialog::new() @@ -290,10 +297,13 @@ impl Emulator { Some(WindowType::Memory(ty, view)) => { let hex = match ty { MemoryTy::Cpu => view - .render(self.nes.mem(), false) + .render_cpu(self.nes.mem()) .map(move |e| Message::Hex(win, e)), MemoryTy::PPU => view - .render(self.nes.mem(), true) + .render_ppu(self.nes.mem()) + .map(move |e| Message::Hex(win, e)), + MemoryTy::OAM => view + .render_oam(self.nes.ppu()) .map(move |e| Message::Hex(win, e)), }; let content = column![row![header_menu("Export", [*ty], Message::Export)], hex] @@ -338,6 +348,7 @@ impl Emulator { HeaderButton::Open(WindowType::Debugger), HeaderButton::Open(WindowType::Memory(MemoryTy::Cpu, HexView {})), HeaderButton::Open(WindowType::Memory(MemoryTy::PPU, HexView {})), + HeaderButton::Open(WindowType::Memory(MemoryTy::OAM, HexView {})), HeaderButton::Open(WindowType::TileMap), HeaderButton::Open(WindowType::TileViewer), HeaderButton::Open(WindowType::Palette), diff --git a/src/mem.rs b/src/mem.rs index 1e60c57..6815408 100644 --- a/src/mem.rs +++ b/src/mem.rs @@ -200,10 +200,8 @@ impl MemoryMap { } None } -} -impl Memory for MemoryMap { - fn peek(&self, addr: u16) -> Option { + fn peek_val(&self, addr: u16) -> Option { for segment in &self.segments { if segment.position <= addr && addr - segment.position < segment.size { return match &segment.mem { @@ -212,7 +210,7 @@ impl Memory for MemoryMap { Data::Reg(_) => None, Data::Mirror(pos) => { let offset = addr - segment.position; - self.peek(pos + offset) + self.peek_val(pos + offset) } Data::Disabled => None, }; @@ -220,6 +218,20 @@ impl Memory for MemoryMap { } None } +} + +impl Memory for MemoryMap { + fn peek(&self, addr: usize) -> Option { + self.peek_val(addr as u16) + } + + fn len(&self) -> usize { + self.segments + .iter() + .map(|s| s.position as usize + s.size as usize) + .max() + .unwrap_or(0) + } fn edit_ver(&self) -> usize { self.edit_ver @@ -269,7 +281,7 @@ impl fmt::Display for SegmentId { } } -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct Mapped { cpu: MemoryMap, ppu: MemoryMap, @@ -444,13 +456,13 @@ impl Mapped { } pub fn peek_ppu(&self, addr: u16) -> Option { - self.ppu.peek(addr) + self.ppu.peek_val(addr) } pub fn ppu_edit_ver(&self) -> usize { self.ppu.edit_ver } pub fn peek_cpu(&self, addr: u16) -> Option { - self.cpu.peek(addr) + self.cpu.peek_val(addr) } pub fn cpu_edit_ver(&self) -> usize { self.cpu.edit_ver @@ -608,11 +620,7 @@ impl<'a> CpuMem<'a> { } Some((CPUMMRegisters::APU, offset, val)) => { if offset == 0x014 { - *self.dma = DmaState::Running { - cpu_addr: (val as u16) << 8, - rem: 0xFF, - read: None, - }; + *self.dma = DmaState::Idle((val as u16) << 8); } else if offset == 0x16 { self.controllers.write_joy_strobe(val); } else { diff --git a/src/ppu.rs b/src/ppu.rs index d0cbaf0..bbb04c6 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -81,6 +81,7 @@ pub enum PPUMMRegisters { pub struct OAM { mem: Vec, addr: u8, + edit_ver: usize, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -565,6 +566,7 @@ impl PPU { oam: OAM { mem: vec![0u8; 256], addr: 0, + edit_ver: 0, }, } } @@ -603,7 +605,7 @@ impl PPU { _ => panic!("No register at {:02X}", offset), } } - pub fn write_reg(&mut self, mem: &mut PpuMem, offset: u16, val: u8) { + pub fn write_reg(&mut self, mem: &mut PpuMem, offset: u16, mut val: u8) { match offset { 0x00 => { self.nmi_enabled = val & 0b1000_0000 != 0; @@ -631,8 +633,12 @@ impl PPU { } 0x03 => self.oam.addr = val, 0x04 => { + if self.oam.addr % 4 == 2 { + val &= 0b11100011; + } self.oam.mem[self.oam.addr as usize] = val; self.oam.addr = self.oam.addr.wrapping_add(1); + self.oam.edit_ver += 1; } 0x05 => { if self.background.w { @@ -865,6 +871,12 @@ impl PPU { pub fn irq_waiting(&mut self) -> bool { false } + pub fn peek_oam(&self, addr: u8) -> u8 { + self.oam.mem[addr as usize] + } + pub fn oam_edit_ver(&self) -> usize { + self.oam.edit_ver + } pub fn render_name_table(&self, mem: &Mapped, frame: &mut Frame) { for y in 0..60 { diff --git a/src/test_roms/alu_bit.s b/src/test_roms/alu_bit.s new file mode 100644 index 0000000..81f95d1 --- /dev/null +++ b/src/test_roms/alu_bit.s @@ -0,0 +1,36 @@ +; Verifies that reset doesn't alter any RAM. + +.include "testing.s" + +zp_res VAL +RAM = $300 + +reset: + sei + cld + ldx #$FF + txs + + lda #$F0 + sta VAL + lda #$0F + bit VAL + php + pla + sta RAM+0 + + lda #$F0 + sta $400 + lda #$0F + bit $400 + php + pla + sta RAM+1 + + stp + +nmi: + stp + +irq: + stp diff --git a/src/test_roms/instr_test_v3.rs b/src/test_roms/instructions.rs similarity index 92% rename from src/test_roms/instr_test_v3.rs rename to src/test_roms/instructions.rs index a7eae5c..d65d94b 100644 --- a/src/test_roms/instr_test_v3.rs +++ b/src/test_roms/instructions.rs @@ -61,3 +61,8 @@ rom_test!(implied_instructions, "implied_instrs.nes", |nes| { assert_eq!(nes.last_instruction(), "0x8026 HLT :2 []"); assert_eq!(nes.cpu.a, 0x00); }); + +rom_test!(bit, "alu_bit.nes", |nes| { + assert_eq!(nes.last_instruction(), "0x8021 HLT :2 []"); + mem_cmp(&nes, 0x300, &[0xF6, 0xF6]); +}); diff --git a/src/test_roms/mod.rs b/src/test_roms/mod.rs index f623860..cd34c71 100644 --- a/src/test_roms/mod.rs +++ b/src/test_roms/mod.rs @@ -1,5 +1,5 @@ mod cpu_reset_ram; -mod instr_test_v3; +mod instructions; mod ppu; mod interrupts;