Skip to content

Commit 6d4dc4f

Browse files
committed
implement ppu addr and data registers
1 parent 687efa2 commit 6d4dc4f

6 files changed

Lines changed: 199 additions & 34 deletions

File tree

src/cpu/cpu_memory.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::nes_rom::NesRom;
2-
use crate::ram::Ram;
2+
use crate::memory::{Memory, Ram};
33

44
pub struct CpuMemory {
55
sram: Ram,

src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
mod cpu;
22
mod nes_rom;
33
mod ppu;
4-
mod ram;
4+
mod memory;
55

66
use crate::nes_rom::NesRom;
77
use crate::ppu::Ppu;
@@ -17,7 +17,7 @@ const COLORS: [Color; 4] = [BLACK, DARKGRAY, LIGHTGRAY, WHITE];
1717
async fn main() -> Result<(), anyhow::Error> {
1818
println!("Starting Emulator!");
1919

20-
let rom = NesRom::read_from_file("./tetris.nes")?;
20+
let rom = NesRom::read_from_file("./dk.nes")?;
2121
println!("{rom:#?}");
2222

2323
let memory_map = CpuMemory::new(rom.clone());

src/memory.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
use std::cell::RefCell;
2+
use std::rc::Rc;
3+
4+
pub trait Memory {
5+
fn read(&self, addr: u16) -> u8;
6+
fn write(&mut self, addr: u16, data: u8);
7+
}
8+
9+
pub struct Ram(Vec<u8>);
10+
11+
impl Ram {
12+
pub fn new(size: usize) -> Self {
13+
Self(vec![0; size])
14+
}
15+
}
16+
17+
impl Memory for Ram {
18+
#[inline]
19+
fn read(&self, a: u16) -> u8 {
20+
self.0[a as usize]
21+
}
22+
23+
#[inline]
24+
fn write(&mut self, a: u16, v: u8) {
25+
self.0[a as usize] = v;
26+
}
27+
}
28+
29+
pub struct DummyMemory {
30+
last_read_addr: RefCell<u16>,
31+
last_write_addr: u16,
32+
last_write_value: u8
33+
}
34+
35+
impl DummyMemory {
36+
pub fn new() -> Self {
37+
DummyMemory {
38+
last_read_addr: RefCell::new(0),
39+
last_write_addr: 0,
40+
last_write_value: 0
41+
}
42+
}
43+
44+
pub fn last_read_addr(&self) -> u16 {
45+
*self.last_read_addr.borrow()
46+
}
47+
48+
pub fn last_write_addr(&self) -> u16 {
49+
self.last_write_addr
50+
}
51+
52+
pub fn last_write_value(&self) -> u8 {
53+
self.last_write_value
54+
}
55+
}
56+
57+
impl Memory for DummyMemory {
58+
fn read(&self, addr: u16) -> u8 {
59+
*self.last_read_addr.borrow_mut() = addr;
60+
(addr & 0xFF) as u8
61+
}
62+
63+
fn write(&mut self, addr: u16, data: u8) {
64+
self.last_write_addr = addr;
65+
self.last_write_value = data;
66+
}
67+
}
68+
69+
impl<T: Memory> Memory for Rc<RefCell<T>> {
70+
fn read(&self, addr: u16) -> u8 {
71+
self.as_ref().borrow().read(addr)
72+
}
73+
74+
fn write(&mut self, addr: u16, data: u8) {
75+
self.as_ref().borrow_mut().write(addr, data);
76+
}
77+
}

src/ppu.rs

Lines changed: 116 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::memory::Memory;
12
use crate::nes_rom::NametableMirroring;
23
use crate::ppu::ppu_memory::PpuMemory;
34

@@ -33,8 +34,10 @@ impl PpuAddr {
3334
((self.hi as u16) << 8 | (self.lo as u16)) & PPU_ADDR_MASK
3435
}
3536

36-
fn increment_addr(&mut self, amount: u16) {
37-
//TODO
37+
fn increment_addr(&mut self, amount: u8) {
38+
let new_addr = self.get_addr().wrapping_add(amount as u16);
39+
self.hi = (new_addr >> 8) as u8;
40+
self.lo = (new_addr & 0xFF) as u8;
3841
}
3942
}
4043

@@ -46,7 +49,7 @@ const PPU_SPRITE_SIZE_BIT: u8 = 5;
4649
const PPU_MASTER_SLAVE_SELECT_BIT: u8 = 6;
4750
const PPU_VBLANK_NMI_BIT: u8 = 7;
4851

49-
struct PpuRegisters {
52+
pub struct Ppu<M: Memory> {
5053
ctrl: u8,
5154
mask: u8,
5255
status: u8,
@@ -56,10 +59,12 @@ struct PpuRegisters {
5659
addr: PpuAddr,
5760
data_buffer: u8,
5861
oam_dma: u8,
62+
63+
memory: M,
5964
}
6065

61-
impl PpuRegisters {
62-
pub fn new() -> Self {
66+
impl Ppu<PpuMemory> {
67+
pub fn new(chr_rom: Vec<u8>, mirroring: NametableMirroring) -> Self {
6368
Self {
6469
ctrl: 0,
6570
mask: 0,
@@ -70,20 +75,117 @@ impl PpuRegisters {
7075
addr: PpuAddr::new(),
7176
data_buffer: 0,
7277
oam_dma: 0,
78+
memory: PpuMemory::new(chr_rom, mirroring),
7379
}
7480
}
7581
}
7682

77-
pub struct Ppu {
78-
registers: PpuRegisters,
79-
memory: PpuMemory,
80-
}
81-
82-
impl Ppu {
83-
pub fn new(chr_rom: Vec<u8>, mirroring: NametableMirroring) -> Self {
83+
impl<M: Memory> Ppu<M> {
84+
fn new_with_memory(memory: M) -> Self {
8485
Self {
85-
registers: PpuRegisters::new(),
86-
memory: PpuMemory::new(chr_rom, mirroring),
86+
ctrl: 0,
87+
mask: 0,
88+
status: 0,
89+
oam_addr: 0,
90+
oam_data: 0,
91+
scroll: 0,
92+
addr: PpuAddr::new(),
93+
data_buffer: 0,
94+
oam_dma: 0,
95+
memory,
8796
}
8897
}
98+
99+
pub fn write_ppu_addr(&mut self, value: u8) {
100+
self.addr.set(value)
101+
}
102+
103+
pub fn read_ppu_data(&mut self) -> u8 {
104+
let addr = self.addr.get_addr();
105+
let result = if (0x3F00..=0x3FFF).contains(&addr) {
106+
self.memory.read(addr)
107+
} else {
108+
let value = self.data_buffer;
109+
self.data_buffer = self.memory.read(addr);
110+
value
111+
};
112+
113+
self.addr.increment_addr(self.addr_increment_amount());
114+
result
115+
}
116+
117+
pub fn write_ppu_data(&mut self, value: u8) {
118+
self.memory.write(self.addr.get_addr(), value);
119+
120+
self.addr.increment_addr(self.addr_increment_amount());
121+
}
122+
123+
pub fn get_ctrl_bit(&self, bit: u8) -> bool {
124+
self.ctrl >> bit & 1 == 1
125+
}
126+
127+
pub fn set_ctrl_bit(&mut self, bit: u8, value: bool) {
128+
if value {
129+
self.ctrl |= 1 << bit;
130+
} else {
131+
self.ctrl &= !(1 << bit);
132+
}
133+
}
134+
135+
fn addr_increment_amount(&self) -> u8 {
136+
if !self.get_ctrl_bit(PPU_VRAM_ADD_INCREMENT_BIT) {
137+
1 // across
138+
} else {
139+
32 // down
140+
}
141+
}
142+
}
143+
144+
#[cfg(test)]
145+
mod test {
146+
use crate::memory::DummyMemory;
147+
use crate::ppu::{Ppu, PPU_VRAM_ADD_INCREMENT_BIT};
148+
use std::cell::RefCell;
149+
use std::rc::Rc;
150+
151+
#[test]
152+
pub fn test_ppu_addr() {
153+
let memory = Rc::new(RefCell::new(DummyMemory::new()));
154+
let mut ppu = Ppu::new_with_memory(memory.clone());
155+
156+
ppu.set_ctrl_bit(PPU_VRAM_ADD_INCREMENT_BIT, false);
157+
158+
ppu.write_ppu_addr(0x34);
159+
ppu.write_ppu_addr(0x56);
160+
assert_eq!(ppu.addr.get_addr(), 0x3456);
161+
162+
ppu.read_ppu_data();
163+
assert_eq!(memory.borrow().last_read_addr(), 0x3456);
164+
assert_eq!(ppu.addr.get_addr(), 0x3457);
165+
166+
assert_eq!(ppu.read_ppu_data(), 0x56); // read on dummy memory returns low byte of address
167+
assert_eq!(memory.borrow().last_read_addr(), 0x3457);
168+
assert_eq!(ppu.addr.get_addr(), 0x3458);
169+
170+
ppu.write_ppu_data(0xCA);
171+
assert_eq!(memory.borrow().last_read_addr(), 0x3457);
172+
assert_eq!(memory.borrow().last_write_addr(), 0x3458);
173+
assert_eq!(memory.borrow().last_write_value(), 0xCA);
174+
assert_eq!(ppu.addr.get_addr(), 0x3459);
175+
176+
ppu.set_ctrl_bit(PPU_VRAM_ADD_INCREMENT_BIT, true);
177+
ppu.read_ppu_data();
178+
assert_eq!(ppu.addr.get_addr(), 0x3479); // now it increments by 0x20 because of the changed ctrl bit
179+
ppu.set_ctrl_bit(PPU_VRAM_ADD_INCREMENT_BIT, false);
180+
181+
ppu.addr.set(0x3f);
182+
ppu.addr.set(0xBD);
183+
assert_eq!(ppu.addr.get_addr(), 0x3fBD);
184+
assert_eq!(ppu.read_ppu_data(), 0xBD); // read in palette range returns value immediately
185+
186+
ppu.addr.set(0x3f);
187+
ppu.addr.set(0xff);
188+
ppu.read_ppu_data();
189+
assert_eq!(ppu.addr.get_addr(), 0x0000); // wraparound after 0x3fff
190+
}
89191
}

src/ppu/ppu_memory.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::memory::Memory;
12
use crate::nes_rom::NametableMirroring;
23

34
pub struct PpuMemory {
@@ -9,7 +10,9 @@ impl PpuMemory {
910
pub fn new(chr_rom: Vec<u8>, mirroring: NametableMirroring) -> Self {
1011
Self { chr_rom, mirroring }
1112
}
13+
}
1214

15+
impl Memory for PpuMemory {
1316
fn read(&self, addr: u16) -> u8 {
1417
if addr < 0x2000 {
1518
self.chr_rom[addr as usize]

src/ram.rs

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

0 commit comments

Comments
 (0)