Set initial PC from ROM

This commit is contained in:
william 2025-01-26 22:33:11 -05:00
parent d4715bbf62
commit ea6a4e1680
9 changed files with 142 additions and 31 deletions

View File

@ -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"] }

View File

@ -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),
}
}
}

View File

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

View File

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

View File

@ -3,6 +3,7 @@ mod mappers;
mod memory;
mod rom;
pub mod system;
mod ppu;
pub trait Clock {
/// Run a clock cycle

View File

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

20
core/src/ppu/mod.rs Normal file
View File

@ -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<RefCell<MemoryBus>>,
}
impl Ppu {
pub fn new(memory_bus: Rc<RefCell<MemoryBus>>) -> Self {
Self { memory_bus }
}
}
impl Clock for Ppu {
fn cycle(&mut self) {
// todo!()
}
}

View File

@ -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<RefCell<MemoryBus>>,
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(())
}
}
}
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();
}
}
}
}

View File

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