Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48,312 changes: 0 additions & 48,312 deletions output.txt

This file was deleted.

Binary file added snake.nes
Binary file not shown.
74 changes: 74 additions & 0 deletions src/bus.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use core::panic;

use crate::cpu::Mem;
use crate::rom::Rom;

pub struct Bus {
cpu_vram: [u8; 2048],
rom: Rom,
}

impl Bus {
pub fn new(rom: Rom) -> Self {
Bus {
cpu_vram: [0; 2048],
rom: rom,
}
}
fn read_prg_rom(&self, mut addr: u16) -> u8 {
addr -= 0x8000;
if self.rom.prg_rom.len() == 0x4000 && addr >= 0x4000 {
//mirror 16 kb for addressible space
addr = addr % 0x4000;
}
self.rom.prg_rom[addr as usize]
}
}

const RAM: u16 = 0x0000;
const RAM_MIRRORS_END: u16 = 0x1FFF;
const PPU_REGISTERS: u16 = 0x2000;
const PPU_REGISTERS_MIRRORS_END: u16 = 0x3FFF;
const PROGRAM_RAM: u16 = 0x8000;
const PROGRAM_RAM_END: u16 = 0xFFFF;

impl Mem for Bus {
fn mem_read(&self, addr: u16) -> u8 {
match addr {
RAM..=RAM_MIRRORS_END => {
let mirror_down_addr = addr & 0b00000111_11111111;
self.cpu_vram[mirror_down_addr as usize]
}
PROGRAM_RAM..=PROGRAM_RAM_END => {
self.read_prg_rom(addr)
}
PPU_REGISTERS..=PPU_REGISTERS_MIRRORS_END => {
let _mirror_down_addr = addr & 0b00100000_00000111;
todo!("PPU is not supported yet")
}
_ => {
println!("Ignoring mem access at {}", addr);
0
}
}
}

fn mem_write(&mut self, addr: u16, data: u8) {
match addr {
RAM..=RAM_MIRRORS_END => {
let mirror_down_addr = addr & 0b11111111111;
self.cpu_vram[mirror_down_addr as usize] = data;
}
PROGRAM_RAM..=PROGRAM_RAM_END => {
panic!("Attempt to write to program RAM space!");
}
PPU_REGISTERS..=PPU_REGISTERS_MIRRORS_END => {
let _mirror_down_addr = addr & 0b00100000_00000111;
todo!("PPU is not supported yet");
}
_ => {
println!("Ignoring mem write-access at {}", addr);
}
}
}
}
200 changes: 68 additions & 132 deletions src/cpu.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
use super::print_title;
use crate::bus::Bus;
use bitflags::bitflags;
use std::fmt;
use std::time::Duration;

mod branch_test;
mod group1_test;
mod group2_test;
mod group3_test;
mod op;
mod other_test;
mod sb1_test;
mod sb2_test;
mod test_fn;
use nes::print_title;

// Testing files, not needed now due to BUS and ROM implemenation messing up the tests
// mod branch_test;
// mod group1_test;
// mod group2_test;
// mod group3_test;
// mod op;
// mod other_test;
// mod sb1_test;
// mod sb2_test;
// mod test_fn;

type Byte = u8;

Expand All @@ -38,8 +39,7 @@ pub struct CPU {
pub sp: Byte,
pub flags: CpuFlags,
// [0x8000... 0xFFFF] is reserved for program ROM
pub memory: [u8; 0xFFFF],
pub clock_time: Duration,
pub bus: Bus,
}

#[derive(Debug)]
Expand Down Expand Up @@ -80,30 +80,18 @@ impl fmt::Display for AddressingMode {

const STACK_RESET: u8 = 0xFD;
const STACK: u16 = 0x0100;
const PROGRAM_START: usize = 0x0600;

impl CPU {
pub fn new() -> Self {
CPU {
pc: 0,
a: 0,
x: 0,
y: 0,
sp: STACK_RESET,
flags: CpuFlags::from_bits_truncate(0b0010_0100),
memory: [0; 0xFFFF],
clock_time: Duration::from_millis(1), // Example value
}
}

pub trait Mem {
fn mem_read(&self, addr: u16) -> Byte;
fn mem_write(&mut self, addr: u16, data: u8);
// Used to read address in little endian
fn mem_read_u16(&mut self, pos: u16) -> u16 {
// If interrupt request is enabled, stop program exectuion
if pos == 0xFFFE && self.flags.contains(CpuFlags::INTERRUPT_DISABLE) {
// BUG Used for irq handler, mitigating for now
println!("mem_read_u16: Detected break. Reading from IRQ handler...");
return 0xFFFF;
}
// if pos == 0xFFFE && self.flags.contains(CpuFlags::INTERRUPT_DISABLE) {
// // BUG Used for irq handler, mitigating for now
// println!("mem_read_u16: Detected break. Reading from IRQ handler...");
// return 0xFFFF;
// }
let lo = self.mem_read(pos) as u16;
let hi = self.mem_read(pos + 1) as u16;
(hi << 8) | (lo as u16)
Expand All @@ -116,21 +104,53 @@ impl CPU {
self.mem_write(pos, lo);
self.mem_write(pos + 1, hi);
}
}

// Resets RAM from $0000 to $07FF
// If program_start neds to be changed(eg as in snake, we subtract 1)
fn ram_reset(&mut self) {
for i in 0x0..PROGRAM_START as usize {
self.memory[i] = 0;
}
impl Mem for CPU {
fn mem_read(&self, addr: u16) -> u8 {
self.bus.mem_read(addr)
}

fn fn_reset(&mut self){
for i in PROGRAM_START as usize.. 0xFFFF {
self.memory[i] = 0;
fn mem_write(&mut self, addr: u16, data: u8) {
self.bus.mem_write(addr, data);
}

fn mem_read_u16(&mut self, pos: u16) -> u16 {
self.bus.mem_read_u16(pos)
}

fn mem_write_u16(&mut self, pos: u16, data: u16) {
self.bus.mem_write_u16(pos, data);
}
}

impl CPU {
pub fn new(bus: Bus) -> Self {
CPU {
pc: 0,
a: 0,
x: 0,
y: 0,
sp: STACK_RESET,
flags: CpuFlags::from_bits_truncate(0b0010_0100),
bus: bus,
}
}

// // Resets RAM from $0000 to $07FF
// // If program_start neds to be changed(eg as in snake, we subtract 1)
// fn ram_reset(&mut self) {
// for i in 0x0..PROGRAM_START as usize {
// self.memory[i] = 0;
// }
// }

// fn fn_reset(&mut self) {
// for i in PROGRAM_START as usize..0xFFFF {
// self.memory[i] = 0;
// }
// }

// Restores registers and initalizes PC to the 2 byte value at 0xFFFC
pub fn reset(&mut self) {
println!("reset: Initalized");
Expand All @@ -140,96 +160,27 @@ impl CPU {
self.flags = CpuFlags::from_bits_truncate(0b00100100);
self.sp = STACK_RESET;
self.pc = self.mem_read_u16(0xFFFC);
self.ram_reset();
}

pub fn load(&mut self, program: Vec<u8>) {
println!("load: Initalized");
self.memory[PROGRAM_START as usize..(PROGRAM_START as usize + program.len())]
.copy_from_slice(&program[..]);
self.mem_write_u16(0xFFFC, PROGRAM_START as u16); // Save reference to program in 0xFFFC
println!("load: Finished!");
for i in 0..(program.len() as u16) {
self.mem_write(0x0000 + i, program[i as usize]);
}
self.mem_write_u16(0xFFFC, 0x0000);
}

// This function is meant for testing, where the test can insert their own values afterwards
pub fn load_and_reset(&mut self, program: Vec<u8>) {
self.fn_reset();
self.load(program);
self.reset();
}

pub fn instruction_print(&self, program: Vec<u8>) {
let program_len = program.len();
println!(
"Memory dump ({} bytes from 0x{:04X}):",
program_len, PROGRAM_START
);
println!("Addr | Hex | ASCII");
println!("--------+------------------------------------------+------------------");

for i in 0..program_len {
let addr = PROGRAM_START + i;

// Print address at start of each line
if i % 16 == 0 {
if i > 0 {
print!(" | ");
// Print ASCII representation for previous line
for j in i - 16..i {
let byte = self.memory[PROGRAM_START + j];
if byte >= 32 && byte <= 126 {
print!("{}", byte as char);
} else {
print!(".");
}
}
println!();
}
print!("{:04X} | ", addr);
}

// Print byte value
print!("{:02X} ", self.memory[addr]);

// Add extra space after 8 bytes
if i % 16 == 7 {
print!(" ");
}
}

// Print ASCII for the last line
let remaining = program_len % 16;
if remaining > 0 {
// Pad for alignment
for i in remaining..16 {
// Use 'i' instead of '_'
print!(" ");
if remaining <= 8 && i == 7 {
print!(" ");
}
}
}

print!(" | ");
let start_idx = program_len - (if remaining > 0 { remaining } else { 16 });
for j in start_idx..program_len {
let byte = self.memory[PROGRAM_START + j];
if byte >= 32 && byte <= 126 {
print!("{}", byte as char);
} else {
print!(".");
}
}
println!("\n");
}

pub fn load_and_run(&mut self, program: Vec<u8>) {
println!("load_and_run: Initalized");
self.load(program.clone());
self.reset();
// USED FOR TESTING
println!("Printing out what's in instructions");
self.instruction_print(program);
self.run();
}

Expand All @@ -252,18 +203,6 @@ impl CPU {
}
}

pub fn mem_read(&self, addr: u16) -> Byte {
let ret = self.memory[addr as usize];
// self.pc = self.pc.wrapping_add(1);
ret
}

pub fn mem_write(&mut self, addr: u16, data: u8) {
let ret = self.memory[addr as usize] = data;
// self.pc = self.pc.wrapping_add(1);
ret
}

pub fn run(&mut self) {
self.run_with_callback(|_| {});
}
Expand Down Expand Up @@ -299,6 +238,7 @@ impl CPU {
// Top is hard coding remaining instructions
if op == 0x0 {
self.brk();
return; // NOTE: Break will return without PC needing to jump anywhere
} else if op == 0x20 {
self.jsr();
} else if op == 0x40 {
Expand Down Expand Up @@ -466,10 +406,6 @@ impl CPU {
fn pha(&mut self) {
println!("pha: Initalized");
self.stack_push(self.a);
println!(
"pha: Pushed {}",
self.memory[(0x0100 + self.sp.wrapping_add(1) as u16) as usize]
);
}

fn pla(&mut self) {
Expand Down
7 changes: 3 additions & 4 deletions src/cpu/group1_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,6 @@ mod group1_test {
test_cmp_helper(&mut cpu, 0x10, 0x20, false, false, true); // A < M
}


#[test]
fn test_sbc() {
let mut cpu = CPU::new();
Expand All @@ -599,15 +598,15 @@ mod group1_test {
for i in 0..2 {
let carry = if i == 0 { false } else { true };
let c = carry as u8;

// Basic subtraction: 0x05 - 0x02 = 0x03
gen_test(&mut cpu, fh, sh, 0x05, 0x02, 0x03 - !carry as u8, carry);
test_adc_flag_check(&cpu, true, false, false, false, "Basic subtraction");

// Subtraction with borrow: 0x05 - 0x08 = 0xFD (with carry)
gen_test(&mut cpu, fh, sh, 0x05, 0x08, 0xFD - !carry as u8, carry);
test_adc_flag_check(&cpu, false, false, false, true, "Subtraction with borrow");

// Negative result: 0x05 - 0x08 = 0xFD (with carry)
gen_test(&mut cpu, fh, sh, 0x05, 0x08, 0xFD - !carry as u8, carry);
test_adc_flag_check(&cpu, false, false, false, true, "Negative result");
Expand Down
4 changes: 2 additions & 2 deletions src/cpu/other_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ mod other_test {
fn test_jsr_rts() {
let mut cpu = CPU::new();
let target_addr = PROGRAM_START + 5; // Address of INX
let lil_end = (target_addr & 0xFF) as u8;
let big_end = ((target_addr >> 8) & 0xFF) as u8; // Correct high byte
let lil_end = (target_addr & 0xFF) as u8;
let big_end = ((target_addr >> 8) & 0xFF) as u8; // Correct high byte
cpu.load_and_run(vec![
other_op::JSR,
lil_end as u8,
Expand Down
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
pub mod cpu;
#[macro_export]
macro_rules! print_title {
($title:expr) => {
Expand Down
Loading
Loading