Set initial PC from ROM
This commit is contained in:
parent
d4715bbf62
commit
ea6a4e1680
@ -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"] }
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -3,6 +3,7 @@ mod mappers;
|
||||
mod memory;
|
||||
mod rom;
|
||||
pub mod system;
|
||||
mod ppu;
|
||||
|
||||
pub trait Clock {
|
||||
/// Run a clock cycle
|
||||
|
@ -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
20
core/src/ppu/mod.rs
Normal 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!()
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user