From ea6a4e16803d50b7b710b3f265a5c87452c39dff Mon Sep 17 00:00:00 2001 From: william Date: Sun, 26 Jan 2025 22:33:11 -0500 Subject: [PATCH] Set initial PC from ROM --- core/Cargo.toml | 2 +- core/src/cpu/disassembler.rs | 29 ++++++++++++++------------- core/src/cpu/mod.rs | 34 +++++++++++++++++++++++++++++--- core/src/cpu/op.rs | 10 ++++++++++ core/src/lib.rs | 1 + core/src/memory.rs | 29 +++++++++++++++++++++++---- core/src/ppu/mod.rs | 20 +++++++++++++++++++ core/src/system.rs | 38 ++++++++++++++++++++++++++++-------- ui/src/main.rs | 10 +++++++++- 9 files changed, 142 insertions(+), 31 deletions(-) create mode 100644 core/src/ppu/mod.rs diff --git a/core/Cargo.toml b/core/Cargo.toml index 50b736e..cfcde8e 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,4 +8,4 @@ edition = "2021" [dependencies] bitflags = "2.7.0" simplelog = { version = "^0.12.0", features = ["paris"] } -strum = { version = "0.26", features = ["derive"] } +strum = { version = "0.26", features = ["derive"] } \ No newline at end of file diff --git a/core/src/cpu/disassembler.rs b/core/src/cpu/disassembler.rs index ede8a22..aef3cca 100644 --- a/core/src/cpu/disassembler.rs +++ b/core/src/cpu/disassembler.rs @@ -1,6 +1,7 @@ use crate::cpu::op::AddressingMode::*; use crate::cpu::op::{AddressingMode, Instruction}; use crate::cpu::Cpu; +use simplelog::debug; impl Cpu { pub fn disassemble_next_instr(&self) { @@ -11,8 +12,8 @@ impl Cpu { let operation = instr.op().to_string(); let addr_mode = self.disassemble_addr_mode(instr.addr_mode(), registers.pc); - dbg!( - "DIS {:#04x} - {:#02x} {:?} {:?} - A {:#02x}, X {:#02x}, Y {:#02x}, SP: {:#02x}, F: {:#02x}", + debug!( + "{:#04X} - ${:04X} {} {}\tA {:#04X}, X {:#04X}, Y {:#04X}, SP: {:#04X}, F: {:#04X}", op_code, registers.pc, operation, @@ -30,19 +31,19 @@ impl Cpu { let byte_peek = (word_peek & 0xFF) as u8; match addr_mode { - ZP0 => format!("${:#02x}", byte_peek), - ZPX => format!("${:#02x},x", byte_peek), - ZPY => format!("${:#02x},x", byte_peek), - ABS => format!("${:#04x}", word_peek), - ABX => format!("${:#04x},x", word_peek), - ABY => format!("${:#04x},y", word_peek), - IND => format!("(${:#04x})", word_peek), - IDX => format!("(${:#02x},x)", word_peek), - IDY => format!("(${:#02x}),y", word_peek), - REL => format!("#${:#04x}", word_peek), + ZP0 => format!("${:04X}", byte_peek), + ZPX => format!("${:04X},x", byte_peek), + ZPY => format!("${:04X},x", byte_peek), + ABS => format!("${:04X}", word_peek), + ABX => format!("${:04X},x", word_peek), + ABY => format!("${:04X},y", word_peek), + IND => format!("(${:04X})", word_peek), + IDX => format!("(${:04X},x)", word_peek), + IDY => format!("(${:04X}),y", word_peek), + REL => format!("#${:04X}", word_peek), ACC => String::from('A'), - IMP => String::from(""), - IMM => format!("{:#02x}", byte_peek), + IMP => String::from("I"), + IMM => format!("{:04X}", byte_peek), } } } diff --git a/core/src/cpu/mod.rs b/core/src/cpu/mod.rs index 1acb21a..b958f48 100644 --- a/core/src/cpu/mod.rs +++ b/core/src/cpu/mod.rs @@ -2,12 +2,15 @@ mod disassembler; mod op; mod operations; -use std::cell::RefCell; -use std::rc::Rc; use crate::memory::MemoryBus; use crate::Clock; +use std::cell::RefCell; +use std::rc::Rc; use bitflags::bitflags; +use simplelog::debug; +const INITIAL_PC_ADDRESS: u16 = 0xFFFC; +const NMI_HANDLER_ADDRESS: u16 = 0xFFFA; const STACK_ADDR: u16 = 0x0100; /// Represents a 6502 CPU @@ -151,6 +154,25 @@ impl Cpu { nmi_requested: false, } } + + pub fn initialize(&mut self) { + let init_pc = self.memory_bus.borrow().get_word(INITIAL_PC_ADDRESS); + self.registers.pc = init_pc; + debug!("Initialized CPU to PC ${:#04x}", init_pc); + } + + fn process_nmi(&mut self) { + let handler_addr = self.memory_bus.borrow().get_word(NMI_HANDLER_ADDRESS); + debug!("NMI, PC -> {:#04x}", handler_addr); + + self.stack_push_context(); + self.nmi_requested = false; + self.registers.pc = handler_addr; + } + + fn process_oam(&mut self) { + todo!(); + } } impl CpuInternals for Cpu { @@ -259,6 +281,12 @@ impl Clock for Cpu { self.busy_cycle_count = instr.cycles(); self.exec_instruction(instr); - // TODO: NMI, OAM DMA + if self.nmi_requested { + self.process_nmi(); + } + + if self.oam_dma_triggered { + self.process_oam(); + } } } diff --git a/core/src/cpu/op.rs b/core/src/cpu/op.rs index 5c6d0a9..9e51654 100644 --- a/core/src/cpu/op.rs +++ b/core/src/cpu/op.rs @@ -125,6 +125,8 @@ impl Cpu { Self::operand_decode_acc() } else if addr_mode == IMM { self.operand_decode_immediate() + } else if addr_mode == IMP { + self.operand_decode_implicit() } else { // Other modes are addresses let mut page_crossed = false; @@ -168,6 +170,14 @@ impl Cpu { } } + fn operand_decode_implicit(&self) -> Operand { + Operand { + operand_type: OperandType::Immediate, + value: 0, + is_page_crossing: false, + } + } + /// d - byte % 256 fn operand_addr_zp(&mut self) -> u16 { self.program_get_next_byte() as u16 diff --git a/core/src/lib.rs b/core/src/lib.rs index c8b619c..28753dc 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -3,6 +3,7 @@ mod mappers; mod memory; mod rom; pub mod system; +mod ppu; pub trait Clock { /// Run a clock cycle diff --git a/core/src/memory.rs b/core/src/memory.rs index a3a938d..436f626 100644 --- a/core/src/memory.rs +++ b/core/src/memory.rs @@ -37,9 +37,11 @@ impl MemoryBus { } else if addr < PPU_MAX_ADDR { let relative_addr = addr - RAM_MAX_ADDR; let ppu_register = relative_addr % 8; - todo!() + // todo!() + 0 } else if addr < APU_MAX_ADDR { - todo!() + // todo!() + 0 } else if addr < UNMAPPED_MAX_ADDR { assert!(false); 0 @@ -63,7 +65,10 @@ impl MemoryBus { /// # Arguments /// * `addr` - The address of the word pub fn get_word(&self, addr: u16) -> u16 { - unimplemented!("MemoryBus::get_word"); + let low = self.get_byte(addr) as u16; + let high = self.get_byte(addr + 1) as u16; + + (high << 8) | low } /// Sets a byte in memory @@ -72,6 +77,22 @@ impl MemoryBus { /// * `addr` - The address of the byte /// * `value` - The value to set pub fn set_byte(&mut self, addr: u16, value: u8) { - unimplemented!("MemoryBus::set_byte"); + assert!(addr < MEMORY_MAX_ADDR); + if addr < RAM_MAX_ADDR { + let ram_addr = (addr as usize) % RAM_SIZE; + self.ram[ram_addr] = value; + } else if addr < PPU_MAX_ADDR { + let relative_addr = addr - RAM_MAX_ADDR; + let ppu_register = relative_addr % 8; + // todo!() + } else if addr < APU_MAX_ADDR { + } else if addr < UNMAPPED_MAX_ADDR { + assert!(false); + } else if addr < CARTRIDGE_RAM_MAX_ADDR { + assert!(false); + } else { + // Cannot write to ROM + assert!(false); + } } } diff --git a/core/src/ppu/mod.rs b/core/src/ppu/mod.rs new file mode 100644 index 0000000..283cbac --- /dev/null +++ b/core/src/ppu/mod.rs @@ -0,0 +1,20 @@ +use crate::memory::MemoryBus; +use crate::Clock; +use std::cell::RefCell; +use std::rc::Rc; + +pub struct Ppu { + memory_bus: Rc>, +} + +impl Ppu { + pub fn new(memory_bus: Rc>) -> Self { + Self { memory_bus } + } +} + +impl Clock for Ppu { + fn cycle(&mut self) { + // todo!() + } +} diff --git a/core/src/system.rs b/core/src/system.rs index f027792..15a64d8 100644 --- a/core/src/system.rs +++ b/core/src/system.rs @@ -1,34 +1,56 @@ +use simplelog::info; use std::cell::RefCell; use std::rc::Rc; -use simplelog::info; use crate::cpu::Cpu; use crate::mappers::get_mapper; use crate::memory::MemoryBus; use crate::rom::{Rom, RomReadError}; +use crate::Clock; +use crate::ppu::Ppu; + +const CPU_CLOCK_DIVISOR: usize = 12; +const CPU_CYCLE_PER_REFRESH: usize = MASTER_CYCLE_PER_REFRESH / CPU_CLOCK_DIVISOR; +const MASTER_CLOCK: usize = 21477272; // NTSC NES Master Clock (~21.47 MHz) +const MASTER_CYCLE_PER_REFRESH: usize = MASTER_CLOCK / REFRESH_RATE; +const PPU_CLOCK_DIVISOR: usize = 4; +const PPU_CYCLE_PER_REFRESH: usize = CPU_CLOCK_DIVISOR / PPU_CLOCK_DIVISOR; +const REFRESH_RATE: usize = 60; // 60 Hz pub struct System { cpu: Cpu, memory_bus: Rc>, + ppu: Ppu } impl System { pub fn new() -> Self { let memory_bus = Rc::new(RefCell::new(MemoryBus::new())); let cpu = Cpu::new(Rc::clone(&memory_bus)); - - Self { - cpu, - memory_bus - } + let ppu = Ppu::new(Rc::clone(&memory_bus)); + + Self { cpu, memory_bus, ppu } } - pub fn insert_rom(&self, path: &str) -> Result<(), RomReadError> { + pub fn insert_rom(&mut self, path: &str) -> Result<(), RomReadError> { let rom = Rom::read(path)?; let mapper = get_mapper(rom); self.memory_bus.borrow_mut().register_mapper(mapper); + self.cpu.initialize(); info!("Successfully inserted ROM into system"); Ok(()) } -} \ No newline at end of file +} + +impl Clock for System { + fn cycle(&mut self) { + for _cpu_cycle in 0usize..CPU_CYCLE_PER_REFRESH { + self.cpu.cycle(); + + for _ppu_cycle in 0usize..PPU_CYCLE_PER_REFRESH { + self.ppu.cycle(); + } + } + } +} diff --git a/ui/src/main.rs b/ui/src/main.rs index 8f14a65..35a0560 100644 --- a/ui/src/main.rs +++ b/ui/src/main.rs @@ -1,4 +1,7 @@ +use std::thread::sleep; +use std::time::Duration; use core::system::System; +use core::Clock; use simplelog::*; const ROM_PATH: &'static str = "./roms/dk.nes"; @@ -15,5 +18,10 @@ fn main() { let mut system = System::new(); system.insert_rom(ROM_PATH).expect("Failed to insert ROM"); - print!("Do stuff"); + loop { + system.cycle(); + + // TODO: Inaccurate, ~60hz + sleep(Duration::from_millis(16)); + } }