Set initial PC from ROM
This commit is contained in:
parent
d4715bbf62
commit
ea6a4e1680
@ -1,6 +1,7 @@
|
|||||||
use crate::cpu::op::AddressingMode::*;
|
use crate::cpu::op::AddressingMode::*;
|
||||||
use crate::cpu::op::{AddressingMode, Instruction};
|
use crate::cpu::op::{AddressingMode, Instruction};
|
||||||
use crate::cpu::Cpu;
|
use crate::cpu::Cpu;
|
||||||
|
use simplelog::debug;
|
||||||
|
|
||||||
impl Cpu {
|
impl Cpu {
|
||||||
pub fn disassemble_next_instr(&self) {
|
pub fn disassemble_next_instr(&self) {
|
||||||
@ -11,8 +12,8 @@ impl Cpu {
|
|||||||
let operation = instr.op().to_string();
|
let operation = instr.op().to_string();
|
||||||
let addr_mode = self.disassemble_addr_mode(instr.addr_mode(), registers.pc);
|
let addr_mode = self.disassemble_addr_mode(instr.addr_mode(), registers.pc);
|
||||||
|
|
||||||
dbg!(
|
debug!(
|
||||||
"DIS {:#04x} - {:#02x} {:?} {:?} - A {:#02x}, X {:#02x}, Y {:#02x}, SP: {:#02x}, F: {:#02x}",
|
"{:#04X} - ${:04X} {} {}\tA {:#04X}, X {:#04X}, Y {:#04X}, SP: {:#04X}, F: {:#04X}",
|
||||||
op_code,
|
op_code,
|
||||||
registers.pc,
|
registers.pc,
|
||||||
operation,
|
operation,
|
||||||
@ -30,19 +31,19 @@ impl Cpu {
|
|||||||
let byte_peek = (word_peek & 0xFF) as u8;
|
let byte_peek = (word_peek & 0xFF) as u8;
|
||||||
|
|
||||||
match addr_mode {
|
match addr_mode {
|
||||||
ZP0 => format!("${:#02x}", byte_peek),
|
ZP0 => format!("${:04X}", byte_peek),
|
||||||
ZPX => format!("${:#02x},x", byte_peek),
|
ZPX => format!("${:04X},x", byte_peek),
|
||||||
ZPY => format!("${:#02x},x", byte_peek),
|
ZPY => format!("${:04X},x", byte_peek),
|
||||||
ABS => format!("${:#04x}", word_peek),
|
ABS => format!("${:04X}", word_peek),
|
||||||
ABX => format!("${:#04x},x", word_peek),
|
ABX => format!("${:04X},x", word_peek),
|
||||||
ABY => format!("${:#04x},y", word_peek),
|
ABY => format!("${:04X},y", word_peek),
|
||||||
IND => format!("(${:#04x})", word_peek),
|
IND => format!("(${:04X})", word_peek),
|
||||||
IDX => format!("(${:#02x},x)", word_peek),
|
IDX => format!("(${:04X},x)", word_peek),
|
||||||
IDY => format!("(${:#02x}),y", word_peek),
|
IDY => format!("(${:04X}),y", word_peek),
|
||||||
REL => format!("#${:#04x}", word_peek),
|
REL => format!("#${:04X}", word_peek),
|
||||||
ACC => String::from('A'),
|
ACC => String::from('A'),
|
||||||
IMP => String::from(""),
|
IMP => String::from("I"),
|
||||||
IMM => format!("{:#02x}", byte_peek),
|
IMM => format!("{:04X}", byte_peek),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,15 @@ mod disassembler;
|
|||||||
mod op;
|
mod op;
|
||||||
mod operations;
|
mod operations;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::rc::Rc;
|
|
||||||
use crate::memory::MemoryBus;
|
use crate::memory::MemoryBus;
|
||||||
use crate::Clock;
|
use crate::Clock;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
use simplelog::debug;
|
||||||
|
|
||||||
|
const INITIAL_PC_ADDRESS: u16 = 0xFFFC;
|
||||||
|
const NMI_HANDLER_ADDRESS: u16 = 0xFFFA;
|
||||||
const STACK_ADDR: u16 = 0x0100;
|
const STACK_ADDR: u16 = 0x0100;
|
||||||
|
|
||||||
/// Represents a 6502 CPU
|
/// Represents a 6502 CPU
|
||||||
@ -151,6 +154,25 @@ impl Cpu {
|
|||||||
nmi_requested: false,
|
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 {
|
impl CpuInternals for Cpu {
|
||||||
@ -259,6 +281,12 @@ impl Clock for Cpu {
|
|||||||
self.busy_cycle_count = instr.cycles();
|
self.busy_cycle_count = instr.cycles();
|
||||||
self.exec_instruction(instr);
|
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()
|
Self::operand_decode_acc()
|
||||||
} else if addr_mode == IMM {
|
} else if addr_mode == IMM {
|
||||||
self.operand_decode_immediate()
|
self.operand_decode_immediate()
|
||||||
|
} else if addr_mode == IMP {
|
||||||
|
self.operand_decode_implicit()
|
||||||
} else {
|
} else {
|
||||||
// Other modes are addresses
|
// Other modes are addresses
|
||||||
let mut page_crossed = false;
|
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
|
/// d - byte % 256
|
||||||
fn operand_addr_zp(&mut self) -> u16 {
|
fn operand_addr_zp(&mut self) -> u16 {
|
||||||
self.program_get_next_byte() as u16
|
self.program_get_next_byte() as u16
|
||||||
|
@ -3,6 +3,7 @@ mod mappers;
|
|||||||
mod memory;
|
mod memory;
|
||||||
mod rom;
|
mod rom;
|
||||||
pub mod system;
|
pub mod system;
|
||||||
|
mod ppu;
|
||||||
|
|
||||||
pub trait Clock {
|
pub trait Clock {
|
||||||
/// Run a clock cycle
|
/// Run a clock cycle
|
||||||
|
@ -37,9 +37,11 @@ impl MemoryBus {
|
|||||||
} else if addr < PPU_MAX_ADDR {
|
} else if addr < PPU_MAX_ADDR {
|
||||||
let relative_addr = addr - RAM_MAX_ADDR;
|
let relative_addr = addr - RAM_MAX_ADDR;
|
||||||
let ppu_register = relative_addr % 8;
|
let ppu_register = relative_addr % 8;
|
||||||
todo!()
|
// todo!()
|
||||||
|
0
|
||||||
} else if addr < APU_MAX_ADDR {
|
} else if addr < APU_MAX_ADDR {
|
||||||
todo!()
|
// todo!()
|
||||||
|
0
|
||||||
} else if addr < UNMAPPED_MAX_ADDR {
|
} else if addr < UNMAPPED_MAX_ADDR {
|
||||||
assert!(false);
|
assert!(false);
|
||||||
0
|
0
|
||||||
@ -63,7 +65,10 @@ impl MemoryBus {
|
|||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `addr` - The address of the word
|
/// * `addr` - The address of the word
|
||||||
pub fn get_word(&self, addr: u16) -> u16 {
|
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
|
/// Sets a byte in memory
|
||||||
@ -72,6 +77,22 @@ impl MemoryBus {
|
|||||||
/// * `addr` - The address of the byte
|
/// * `addr` - The address of the byte
|
||||||
/// * `value` - The value to set
|
/// * `value` - The value to set
|
||||||
pub fn set_byte(&mut self, addr: u16, value: u8) {
|
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::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use simplelog::info;
|
|
||||||
use crate::cpu::Cpu;
|
use crate::cpu::Cpu;
|
||||||
use crate::mappers::get_mapper;
|
use crate::mappers::get_mapper;
|
||||||
use crate::memory::MemoryBus;
|
use crate::memory::MemoryBus;
|
||||||
use crate::rom::{Rom, RomReadError};
|
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 {
|
pub struct System {
|
||||||
cpu: Cpu,
|
cpu: Cpu,
|
||||||
memory_bus: Rc<RefCell<MemoryBus>>,
|
memory_bus: Rc<RefCell<MemoryBus>>,
|
||||||
|
ppu: Ppu
|
||||||
}
|
}
|
||||||
|
|
||||||
impl System {
|
impl System {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let memory_bus = Rc::new(RefCell::new(MemoryBus::new()));
|
let memory_bus = Rc::new(RefCell::new(MemoryBus::new()));
|
||||||
let cpu = Cpu::new(Rc::clone(&memory_bus));
|
let cpu = Cpu::new(Rc::clone(&memory_bus));
|
||||||
|
let ppu = Ppu::new(Rc::clone(&memory_bus));
|
||||||
|
|
||||||
Self {
|
Self { cpu, memory_bus, ppu }
|
||||||
cpu,
|
|
||||||
memory_bus
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 rom = Rom::read(path)?;
|
||||||
let mapper = get_mapper(rom);
|
let mapper = get_mapper(rom);
|
||||||
|
|
||||||
self.memory_bus.borrow_mut().register_mapper(mapper);
|
self.memory_bus.borrow_mut().register_mapper(mapper);
|
||||||
|
self.cpu.initialize();
|
||||||
|
|
||||||
info!("Successfully inserted ROM into system");
|
info!("Successfully inserted ROM into system");
|
||||||
Ok(())
|
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::system::System;
|
||||||
|
use core::Clock;
|
||||||
use simplelog::*;
|
use simplelog::*;
|
||||||
|
|
||||||
const ROM_PATH: &'static str = "./roms/dk.nes";
|
const ROM_PATH: &'static str = "./roms/dk.nes";
|
||||||
@ -15,5 +18,10 @@ fn main() {
|
|||||||
let mut system = System::new();
|
let mut system = System::new();
|
||||||
system.insert_rom(ROM_PATH).expect("Failed to insert ROM");
|
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