Skip to content

Commit 20ab894

Browse files
authored
Merge pull request #2 from derzz/bus-rom
Support for ROMs and added memory bus
2 parents 6d93e6e + 68aea66 commit 20ab894

9 files changed

Lines changed: 216 additions & 48479 deletions

File tree

output.txt

Lines changed: 0 additions & 48312 deletions
This file was deleted.

snake.nes

32 KB
Binary file not shown.

src/bus.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
use core::panic;
2+
3+
use crate::cpu::Mem;
4+
use crate::rom::Rom;
5+
6+
pub struct Bus {
7+
cpu_vram: [u8; 2048],
8+
rom: Rom,
9+
}
10+
11+
impl Bus {
12+
pub fn new(rom: Rom) -> Self {
13+
Bus {
14+
cpu_vram: [0; 2048],
15+
rom: rom,
16+
}
17+
}
18+
fn read_prg_rom(&self, mut addr: u16) -> u8 {
19+
addr -= 0x8000;
20+
if self.rom.prg_rom.len() == 0x4000 && addr >= 0x4000 {
21+
//mirror 16 kb for addressible space
22+
addr = addr % 0x4000;
23+
}
24+
self.rom.prg_rom[addr as usize]
25+
}
26+
}
27+
28+
const RAM: u16 = 0x0000;
29+
const RAM_MIRRORS_END: u16 = 0x1FFF;
30+
const PPU_REGISTERS: u16 = 0x2000;
31+
const PPU_REGISTERS_MIRRORS_END: u16 = 0x3FFF;
32+
const PROGRAM_RAM: u16 = 0x8000;
33+
const PROGRAM_RAM_END: u16 = 0xFFFF;
34+
35+
impl Mem for Bus {
36+
fn mem_read(&self, addr: u16) -> u8 {
37+
match addr {
38+
RAM..=RAM_MIRRORS_END => {
39+
let mirror_down_addr = addr & 0b00000111_11111111;
40+
self.cpu_vram[mirror_down_addr as usize]
41+
}
42+
PROGRAM_RAM..=PROGRAM_RAM_END => {
43+
self.read_prg_rom(addr)
44+
}
45+
PPU_REGISTERS..=PPU_REGISTERS_MIRRORS_END => {
46+
let _mirror_down_addr = addr & 0b00100000_00000111;
47+
todo!("PPU is not supported yet")
48+
}
49+
_ => {
50+
println!("Ignoring mem access at {}", addr);
51+
0
52+
}
53+
}
54+
}
55+
56+
fn mem_write(&mut self, addr: u16, data: u8) {
57+
match addr {
58+
RAM..=RAM_MIRRORS_END => {
59+
let mirror_down_addr = addr & 0b11111111111;
60+
self.cpu_vram[mirror_down_addr as usize] = data;
61+
}
62+
PROGRAM_RAM..=PROGRAM_RAM_END => {
63+
panic!("Attempt to write to program RAM space!");
64+
}
65+
PPU_REGISTERS..=PPU_REGISTERS_MIRRORS_END => {
66+
let _mirror_down_addr = addr & 0b00100000_00000111;
67+
todo!("PPU is not supported yet");
68+
}
69+
_ => {
70+
println!("Ignoring mem write-access at {}", addr);
71+
}
72+
}
73+
}
74+
}

src/cpu.rs

Lines changed: 68 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
use super::print_title;
1+
use crate::bus::Bus;
22
use bitflags::bitflags;
33
use std::fmt;
4-
use std::time::Duration;
5-
6-
mod branch_test;
7-
mod group1_test;
8-
mod group2_test;
9-
mod group3_test;
10-
mod op;
11-
mod other_test;
12-
mod sb1_test;
13-
mod sb2_test;
14-
mod test_fn;
4+
use nes::print_title;
5+
6+
// Testing files, not needed now due to BUS and ROM implemenation messing up the tests
7+
// mod branch_test;
8+
// mod group1_test;
9+
// mod group2_test;
10+
// mod group3_test;
11+
// mod op;
12+
// mod other_test;
13+
// mod sb1_test;
14+
// mod sb2_test;
15+
// mod test_fn;
1516

1617
type Byte = u8;
1718

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

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

8181
const STACK_RESET: u8 = 0xFD;
8282
const STACK: u16 = 0x0100;
83-
const PROGRAM_START: usize = 0x0600;
84-
85-
impl CPU {
86-
pub fn new() -> Self {
87-
CPU {
88-
pc: 0,
89-
a: 0,
90-
x: 0,
91-
y: 0,
92-
sp: STACK_RESET,
93-
flags: CpuFlags::from_bits_truncate(0b0010_0100),
94-
memory: [0; 0xFFFF],
95-
clock_time: Duration::from_millis(1), // Example value
96-
}
97-
}
9883

84+
pub trait Mem {
85+
fn mem_read(&self, addr: u16) -> Byte;
86+
fn mem_write(&mut self, addr: u16, data: u8);
9987
// Used to read address in little endian
10088
fn mem_read_u16(&mut self, pos: u16) -> u16 {
10189
// If interrupt request is enabled, stop program exectuion
102-
if pos == 0xFFFE && self.flags.contains(CpuFlags::INTERRUPT_DISABLE) {
103-
// BUG Used for irq handler, mitigating for now
104-
println!("mem_read_u16: Detected break. Reading from IRQ handler...");
105-
return 0xFFFF;
106-
}
90+
// if pos == 0xFFFE && self.flags.contains(CpuFlags::INTERRUPT_DISABLE) {
91+
// // BUG Used for irq handler, mitigating for now
92+
// println!("mem_read_u16: Detected break. Reading from IRQ handler...");
93+
// return 0xFFFF;
94+
// }
10795
let lo = self.mem_read(pos) as u16;
10896
let hi = self.mem_read(pos + 1) as u16;
10997
(hi << 8) | (lo as u16)
@@ -116,21 +104,53 @@ impl CPU {
116104
self.mem_write(pos, lo);
117105
self.mem_write(pos + 1, hi);
118106
}
107+
}
119108

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

128-
fn fn_reset(&mut self){
129-
for i in PROGRAM_START as usize.. 0xFFFF {
130-
self.memory[i] = 0;
114+
fn mem_write(&mut self, addr: u16, data: u8) {
115+
self.bus.mem_write(addr, data);
116+
}
117+
118+
fn mem_read_u16(&mut self, pos: u16) -> u16 {
119+
self.bus.mem_read_u16(pos)
120+
}
121+
122+
fn mem_write_u16(&mut self, pos: u16, data: u16) {
123+
self.bus.mem_write_u16(pos, data);
124+
}
125+
}
126+
127+
impl CPU {
128+
pub fn new(bus: Bus) -> Self {
129+
CPU {
130+
pc: 0,
131+
a: 0,
132+
x: 0,
133+
y: 0,
134+
sp: STACK_RESET,
135+
flags: CpuFlags::from_bits_truncate(0b0010_0100),
136+
bus: bus,
131137
}
132138
}
133139

140+
// // Resets RAM from $0000 to $07FF
141+
// // If program_start neds to be changed(eg as in snake, we subtract 1)
142+
// fn ram_reset(&mut self) {
143+
// for i in 0x0..PROGRAM_START as usize {
144+
// self.memory[i] = 0;
145+
// }
146+
// }
147+
148+
// fn fn_reset(&mut self) {
149+
// for i in PROGRAM_START as usize..0xFFFF {
150+
// self.memory[i] = 0;
151+
// }
152+
// }
153+
134154
// Restores registers and initalizes PC to the 2 byte value at 0xFFFC
135155
pub fn reset(&mut self) {
136156
println!("reset: Initalized");
@@ -140,96 +160,27 @@ impl CPU {
140160
self.flags = CpuFlags::from_bits_truncate(0b00100100);
141161
self.sp = STACK_RESET;
142162
self.pc = self.mem_read_u16(0xFFFC);
143-
self.ram_reset();
144163
}
145164

146165
pub fn load(&mut self, program: Vec<u8>) {
147-
println!("load: Initalized");
148-
self.memory[PROGRAM_START as usize..(PROGRAM_START as usize + program.len())]
149-
.copy_from_slice(&program[..]);
150-
self.mem_write_u16(0xFFFC, PROGRAM_START as u16); // Save reference to program in 0xFFFC
151-
println!("load: Finished!");
166+
for i in 0..(program.len() as u16) {
167+
self.mem_write(0x0000 + i, program[i as usize]);
168+
}
169+
self.mem_write_u16(0xFFFC, 0x0000);
152170
}
153171

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

161-
pub fn instruction_print(&self, program: Vec<u8>) {
162-
let program_len = program.len();
163-
println!(
164-
"Memory dump ({} bytes from 0x{:04X}):",
165-
program_len, PROGRAM_START
166-
);
167-
println!("Addr | Hex | ASCII");
168-
println!("--------+------------------------------------------+------------------");
169-
170-
for i in 0..program_len {
171-
let addr = PROGRAM_START + i;
172-
173-
// Print address at start of each line
174-
if i % 16 == 0 {
175-
if i > 0 {
176-
print!(" | ");
177-
// Print ASCII representation for previous line
178-
for j in i - 16..i {
179-
let byte = self.memory[PROGRAM_START + j];
180-
if byte >= 32 && byte <= 126 {
181-
print!("{}", byte as char);
182-
} else {
183-
print!(".");
184-
}
185-
}
186-
println!();
187-
}
188-
print!("{:04X} | ", addr);
189-
}
190-
191-
// Print byte value
192-
print!("{:02X} ", self.memory[addr]);
193-
194-
// Add extra space after 8 bytes
195-
if i % 16 == 7 {
196-
print!(" ");
197-
}
198-
}
199-
200-
// Print ASCII for the last line
201-
let remaining = program_len % 16;
202-
if remaining > 0 {
203-
// Pad for alignment
204-
for i in remaining..16 {
205-
// Use 'i' instead of '_'
206-
print!(" ");
207-
if remaining <= 8 && i == 7 {
208-
print!(" ");
209-
}
210-
}
211-
}
212-
213-
print!(" | ");
214-
let start_idx = program_len - (if remaining > 0 { remaining } else { 16 });
215-
for j in start_idx..program_len {
216-
let byte = self.memory[PROGRAM_START + j];
217-
if byte >= 32 && byte <= 126 {
218-
print!("{}", byte as char);
219-
} else {
220-
print!(".");
221-
}
222-
}
223-
println!("\n");
224-
}
225-
226178
pub fn load_and_run(&mut self, program: Vec<u8>) {
227179
println!("load_and_run: Initalized");
228180
self.load(program.clone());
229181
self.reset();
230182
// USED FOR TESTING
231183
println!("Printing out what's in instructions");
232-
self.instruction_print(program);
233184
self.run();
234185
}
235186

@@ -252,18 +203,6 @@ impl CPU {
252203
}
253204
}
254205

255-
pub fn mem_read(&self, addr: u16) -> Byte {
256-
let ret = self.memory[addr as usize];
257-
// self.pc = self.pc.wrapping_add(1);
258-
ret
259-
}
260-
261-
pub fn mem_write(&mut self, addr: u16, data: u8) {
262-
let ret = self.memory[addr as usize] = data;
263-
// self.pc = self.pc.wrapping_add(1);
264-
ret
265-
}
266-
267206
pub fn run(&mut self) {
268207
self.run_with_callback(|_| {});
269208
}
@@ -299,6 +238,7 @@ impl CPU {
299238
// Top is hard coding remaining instructions
300239
if op == 0x0 {
301240
self.brk();
241+
return; // NOTE: Break will return without PC needing to jump anywhere
302242
} else if op == 0x20 {
303243
self.jsr();
304244
} else if op == 0x40 {
@@ -466,10 +406,6 @@ impl CPU {
466406
fn pha(&mut self) {
467407
println!("pha: Initalized");
468408
self.stack_push(self.a);
469-
println!(
470-
"pha: Pushed {}",
471-
self.memory[(0x0100 + self.sp.wrapping_add(1) as u16) as usize]
472-
);
473409
}
474410

475411
fn pla(&mut self) {

src/cpu/group1_test.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,6 @@ mod group1_test {
590590
test_cmp_helper(&mut cpu, 0x10, 0x20, false, false, true); // A < M
591591
}
592592

593-
594593
#[test]
595594
fn test_sbc() {
596595
let mut cpu = CPU::new();
@@ -599,15 +598,15 @@ mod group1_test {
599598
for i in 0..2 {
600599
let carry = if i == 0 { false } else { true };
601600
let c = carry as u8;
602-
601+
603602
// Basic subtraction: 0x05 - 0x02 = 0x03
604603
gen_test(&mut cpu, fh, sh, 0x05, 0x02, 0x03 - !carry as u8, carry);
605604
test_adc_flag_check(&cpu, true, false, false, false, "Basic subtraction");
606-
605+
607606
// Subtraction with borrow: 0x05 - 0x08 = 0xFD (with carry)
608607
gen_test(&mut cpu, fh, sh, 0x05, 0x08, 0xFD - !carry as u8, carry);
609608
test_adc_flag_check(&cpu, false, false, false, true, "Subtraction with borrow");
610-
609+
611610
// Negative result: 0x05 - 0x08 = 0xFD (with carry)
612611
gen_test(&mut cpu, fh, sh, 0x05, 0x08, 0xFD - !carry as u8, carry);
613612
test_adc_flag_check(&cpu, false, false, false, true, "Negative result");

src/cpu/other_test.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ mod other_test {
1313
fn test_jsr_rts() {
1414
let mut cpu = CPU::new();
1515
let target_addr = PROGRAM_START + 5; // Address of INX
16-
let lil_end = (target_addr & 0xFF) as u8;
17-
let big_end = ((target_addr >> 8) & 0xFF) as u8; // Correct high byte
16+
let lil_end = (target_addr & 0xFF) as u8;
17+
let big_end = ((target_addr >> 8) & 0xFF) as u8; // Correct high byte
1818
cpu.load_and_run(vec![
1919
other_op::JSR,
2020
lil_end as u8,

src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
pub mod cpu;
21
#[macro_export]
32
macro_rules! print_title {
43
($title:expr) => {

0 commit comments

Comments
 (0)