Skip to content

Commit 0dfaeb9

Browse files
committed
implement oam and sprite rendering
1 parent aadb9c4 commit 0dfaeb9

7 files changed

Lines changed: 154 additions & 36 deletions

File tree

src/cpu.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -827,7 +827,7 @@ impl Cpu {
827827
let hi = self.next();
828828
let data = u16::from(lo) | (u16::from(hi) << 8);
829829
if data == instr_addr {
830-
// panic!("Trapped at {:#x}", instr_addr);
830+
panic!("Trapped at {:#x}", instr_addr);
831831
}
832832
self.registers.pc = data;
833833
}
@@ -1322,7 +1322,6 @@ impl Cpu {
13221322
}
13231323

13241324
fn interrupt_nmi(&mut self) {
1325-
println!("NMI");
13261325
self.push_stack((self.registers.pc >> 8) as u8);
13271326
self.push_stack((self.registers.pc & 0xFF) as u8);
13281327

src/cpu/bus.rs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::cpu::{INTERRUPT_VECTOR_RES_HI, INTERRUPT_VECTOR_RES_LO};
33
use crate::memory::{Memory, Ram};
44
use crate::nes_rom::NesRom;
55
use crate::ppu::ppu_memory::PpuMemory;
6-
use crate::ppu::Ppu;
6+
use crate::ppu::{Ppu, OAM_SIZE};
77

88
pub struct Bus {
99
sram: Ram,
@@ -47,26 +47,25 @@ impl Bus {
4747
let register = (a - 0x2000) % 8;
4848
match register {
4949
2 => self.ppu.read_ppu_status(),
50-
4 => unimplemented!(),
50+
4 => self.ppu.read_oam_data(),
5151
7 => self.ppu.read_ppu_data(),
5252
_ => panic!(
5353
"Unexpected PPU register read: {:#X} (Register {})",
5454
a, register
5555
),
5656
}
57-
} else if a == 0x4016 {
57+
} else if a == 0x4016 {
5858
self.controller.read()
5959
} else if a == 0x4017 {
6060
// TODO player 2 controller
6161
0
62-
}else if (0x6000..0x8000).contains(&a) {
62+
} else if (0x6000..0x8000).contains(&a) {
6363
self.prg_ram.read(a - 0x6000)
6464
} else if a >= 0x8000 {
6565
self.rom.prg_rom[(a as usize - 0x8000) % self.rom.prg_rom.len()]
6666
} else {
6767
println!("Tried to read unmapped address: {:#X}", a);
6868
0
69-
// panic!("Tried to read unmapped address: {:#X}", a)
7069
}
7170
}
7271

@@ -78,10 +77,8 @@ impl Bus {
7877
match register {
7978
0 => self.ppu.write_ppu_ctrl(v),
8079
1 => self.ppu.write_ppu_mask(v),
81-
3 => {
82-
// unimplemented!()
83-
}
84-
4 => unimplemented!(),
80+
3 => self.ppu.write_oam_addr(v),
81+
4 => self.ppu.write_oam_data(v),
8582
5 => self.ppu.write_ppu_scroll(v),
8683
6 => self.ppu.write_ppu_addr(v),
8784
7 => self.ppu.write_ppu_data(v),
@@ -91,10 +88,14 @@ impl Bus {
9188
),
9289
};
9390
} else if a == 0x4014 {
94-
// unimplemented!()
91+
for i in 0..OAM_SIZE {
92+
let addr = ((v as u16) << 8) | (i as u16);
93+
let value = self.read(addr);
94+
self.ppu.write_oam_data(value);
95+
}
9596
} else if a == 0x4016 {
9697
self.controller.write(v);
97-
}else if (0x4000..=0x4017).contains(&a) {
98+
} else if (0x4000..=0x4017).contains(&a) {
9899
// TODO APU
99100
} else if (0x6000..0x8000).contains(&a) {
100101
self.prg_ram.write(a - 0x6000, v);
@@ -104,8 +105,12 @@ impl Bus {
104105
}
105106

106107
pub fn reset_vector(&self) -> u16 {
107-
let hi = self.rom.prg_rom[(INTERRUPT_VECTOR_RES_HI - 0x8000) as usize % self.rom.prg_rom.len()] as u16;
108-
let lo = self.rom.prg_rom[(INTERRUPT_VECTOR_RES_LO - 0x8000) as usize % self.rom.prg_rom.len()] as u16;
108+
let hi = self.rom.prg_rom
109+
[(INTERRUPT_VECTOR_RES_HI - 0x8000) as usize % self.rom.prg_rom.len()]
110+
as u16;
111+
let lo = self.rom.prg_rom
112+
[(INTERRUPT_VECTOR_RES_LO - 0x8000) as usize % self.rom.prg_rom.len()]
113+
as u16;
109114
(hi << 8) | lo
110115
}
111116
}

src/main.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ mod render;
2020
async fn main() -> Result<(), anyhow::Error> {
2121
println!("Starting Emulator!");
2222

23-
// let rom = NesRom::read_from_file("./vendor/nestest/nestest.nes")?;
23+
// let rom = NesRom::read_from_file("vendor/nes-test-roms/blargg_litewall/litewall5.nes")?;
2424
let rom = NesRom::read_from_file("./Thwaite.nes")?;
2525
println!("{rom:#?}");
2626

@@ -41,7 +41,6 @@ async fn main() -> Result<(), anyhow::Error> {
4141
} else {
4242
if cpu.poll_new_frame() {
4343
render_frame(&mut cpu).await;
44-
println!("frame");
4544
}
4645
cpu.tick();
4746
handle_keyboard_input(&mut cpu);
@@ -50,8 +49,8 @@ async fn main() -> Result<(), anyhow::Error> {
5049
}
5150

5251
fn handle_keyboard_input(cpu: &mut Cpu) {
53-
cpu.bus.controller.button_states[CONTROLLER_BUTTON_A] = is_key_down(KeyCode::A);
54-
cpu.bus.controller.button_states[CONTROLLER_BUTTON_B] = is_key_down(KeyCode::S);
52+
cpu.bus.controller.button_states[CONTROLLER_BUTTON_A] = is_key_down(KeyCode::S);
53+
cpu.bus.controller.button_states[CONTROLLER_BUTTON_B] = is_key_down(KeyCode::A);
5554
cpu.bus.controller.button_states[CONTROLLER_BUTTON_SELECT] = is_key_down(KeyCode::LeftShift);
5655
cpu.bus.controller.button_states[CONTROLLER_BUTTON_START] = is_key_down(KeyCode::Enter);
5756
cpu.bus.controller.button_states[CONTROLLER_BUTTON_UP] = is_key_down(KeyCode::Up);

src/ppu.rs

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::memory::Memory;
1+
use crate::memory::{Memory, Ram};
22
use crate::nes_rom::NametableMirroring;
33
use crate::ppu::ppu_memory::PpuMemory;
44

@@ -110,12 +110,13 @@ const SCANLINES: u32 = 262;
110110
const VISIBLE_SCANLIENS: u32 = 240;
111111
const SCANLINE_CYCLES: u32 = 341;
112112

113+
pub const OAM_SIZE: usize = 0x100;
114+
113115
pub struct Ppu<M: Memory> {
114116
ctrl: u8,
115117
mask: u8,
116118
status: u8,
117119
oam_addr: u8,
118-
oam_data: u8,
119120
scroll: PpuScroll,
120121
addr: PpuAddr,
121122
data_buffer: u8,
@@ -127,6 +128,7 @@ pub struct Ppu<M: Memory> {
127128
new_frame: bool,
128129

129130
pub memory: M,
131+
pub oam: [u8; OAM_SIZE],
130132
}
131133

132134
impl Ppu<PpuMemory> {
@@ -136,7 +138,6 @@ impl Ppu<PpuMemory> {
136138
mask: 0,
137139
status: 0,
138140
oam_addr: 0,
139-
oam_data: 0,
140141
scroll: PpuScroll::new(),
141142
addr: PpuAddr::new(),
142143
data_buffer: 0,
@@ -146,6 +147,7 @@ impl Ppu<PpuMemory> {
146147
nmi: false,
147148
new_frame: false,
148149
memory: PpuMemory::new(chr_rom, mirroring),
150+
oam: [0; OAM_SIZE],
149151
}
150152
}
151153
}
@@ -202,8 +204,6 @@ impl<M: Memory> Ppu<M> {
202204
self.ctrl >> bit & 1 == 1
203205
}
204206

205-
206-
207207
pub fn write_ppu_mask(&mut self, value: u8) {
208208
self.mask = value;
209209
}
@@ -240,6 +240,19 @@ impl<M: Memory> Ppu<M> {
240240
}
241241
}
242242

243+
pub fn write_oam_addr(&mut self, value: u8) {
244+
self.oam_addr = value;
245+
}
246+
247+
pub fn read_oam_data(&mut self) -> u8 {
248+
self.oam[self.oam_addr as usize]
249+
}
250+
251+
pub fn write_oam_data(&mut self, value: u8) {
252+
self.oam[self.oam_addr as usize] = value;
253+
self.oam_addr = self.oam_addr.wrapping_add(1);
254+
}
255+
243256
pub fn write_ppu_scroll(&mut self, value: u8) {
244257
self.scroll.set(value);
245258
}
@@ -276,6 +289,14 @@ impl<M: Memory> Ppu<M> {
276289
}
277290
}
278291

292+
pub fn sprite_pattern_addr(&self) -> u16 {
293+
if self.get_ctrl_bit(PPU_CTRL_SPRITE_PATTERN_ADDR_BIT) {
294+
0x1000
295+
} else {
296+
0x0
297+
}
298+
}
299+
279300
pub fn background_pattern_addr(&self) -> u16 {
280301
if self.get_ctrl_bit(PPU_CTRL_BACKRGROUND_ADDR_BIT) {
281302
0x1000
@@ -291,11 +312,11 @@ impl<M: Memory> Ppu<M> {
291312

292313
#[cfg(test)]
293314
mod test {
294-
use crate::memory::{Memory};
295-
use crate::ppu::{Ppu, PpuAddr, PpuScroll, PPU_CTRL_VRAM_ADD_INCREMENT_BIT};
315+
use crate::memory::test::DummyMemory;
316+
use crate::memory::{Memory, Ram};
317+
use crate::ppu::{Ppu, PpuAddr, PpuScroll, OAM_SIZE, PPU_CTRL_VRAM_ADD_INCREMENT_BIT};
296318
use std::cell::RefCell;
297319
use std::rc::Rc;
298-
use crate::memory::test::DummyMemory;
299320

300321
impl<M: Memory> Ppu<M> {
301322
fn new_with_memory(memory: M) -> Self {
@@ -304,7 +325,6 @@ mod test {
304325
mask: 0,
305326
status: 0,
306327
oam_addr: 0,
307-
oam_data: 0,
308328
scroll: PpuScroll::new(),
309329
addr: PpuAddr::new(),
310330
data_buffer: 0,
@@ -314,6 +334,7 @@ mod test {
314334
nmi: false,
315335
new_frame: false,
316336
memory,
337+
oam: [0; OAM_SIZE],
317338
}
318339
}
319340

src/ppu/ppu_memory.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ impl Memory for PpuMemory {
5757

5858
fn write(&mut self, addr: u16, data: u8) {
5959
if addr < 0x2000 {
60-
// panic!("Attempted write to CHR ROM at {:#X}", addr);
60+
println!("Attempted write to CHR ROM at {:#X}", addr);
6161
} else if (0x2000..0x3000).contains(&addr) {
6262
let vram_addr = addr - 0x2000;
6363
self.vram.write(self.mirror_vram_addr(vram_addr), data);

src/render.rs

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1+
mod sprite;
2+
13
use crate::cpu::Cpu;
24
use crate::memory::Memory;
35
use crate::nes_rom::NesRom;
46
use crate::ppu::ppu_memory::PpuMemory;
5-
use crate::ppu::Ppu;
7+
use crate::ppu::{Ppu, OAM_SIZE};
8+
use crate::render::sprite::Sprite;
69
use macroquad::color::{Color, BLACK, BLUE, RED, WHITE};
710
use macroquad::prelude::{draw_rectangle, next_frame, request_new_screen_size};
811

12+
const SCREEN_WIDTH: u16 = 256;
13+
const SCREEN_HEIGHT: u16 = 240;
14+
915
const RENDER_SCALE: f32 = 4.;
1016
const TILE_SIZE: f32 = RENDER_SCALE * 8.;
1117

@@ -21,10 +27,10 @@ const SYSTEM_PALLETE: [u32; 64] = [
2127
];
2228

2329
pub fn get_color(idx: u8) -> Color {
24-
Color::from_hex(SYSTEM_PALLETE[idx as usize])
30+
Color::from_hex(SYSTEM_PALLETE[idx as usize % 64])
2531
}
2632

27-
pub fn get_palette(ppu: &Ppu<PpuMemory>, palette_idx: u16) -> [Color; 4] {
33+
pub fn get_bg_palette(ppu: &Ppu<PpuMemory>, palette_idx: u16) -> [Color; 4] {
2834
[
2935
get_color(ppu.memory.palette_table.read(0)),
3036
get_color(ppu.memory.palette_table.read(palette_idx * 4 + 1)),
@@ -33,15 +39,27 @@ pub fn get_palette(ppu: &Ppu<PpuMemory>, palette_idx: u16) -> [Color; 4] {
3339
]
3440
}
3541

42+
pub fn get_sprite_palette(ppu: &Ppu<PpuMemory>, palette_idx: u16) -> [Color; 4] {
43+
get_bg_palette(ppu, palette_idx + 4)
44+
}
45+
3646
pub async fn render_frame(cpu: &mut Cpu) {
3747
let ppu = &mut cpu.bus.ppu;
38-
let bank = ppu.background_pattern_addr();
3948

4049
request_new_screen_size(
4150
RENDER_SCALE * (8 * 32) as f32,
4251
RENDER_SCALE * (8 * 30) as f32,
4352
);
4453

54+
render_background(ppu).await;
55+
render_sprites(ppu).await;
56+
57+
next_frame().await
58+
}
59+
60+
async fn render_background(ppu: &mut Ppu<PpuMemory>) {
61+
let bank = ppu.background_pattern_addr();
62+
4563
fn calc_screen_pos(tile_index: usize, pixel_index: usize) -> (f32, f32) {
4664
let tile_x = (tile_index % 32) as f32;
4765
let tile_y = (tile_index / 32) as f32;
@@ -59,7 +77,7 @@ pub async fn render_frame(cpu: &mut Cpu) {
5977
// which tile are we rendering?
6078
let tile = ppu.memory.read(nametable_addr + tile_index) as u16;
6179
let chr_data = ppu.memory.chr_rom
62-
[(bank + tile * 16) as usize..=(bank + tile * 16 + 15) as usize]
80+
[(bank + tile * 16) as usize..(bank + tile * 16 + 16) as usize]
6381
.to_vec();
6482
let pixels = chr_data_to_pixels(chr_data);
6583

@@ -75,7 +93,7 @@ pub async fn render_frame(cpu: &mut Cpu) {
7593
(1, 1) => (meta_palette >> 6) & 0b11,
7694
_ => panic!("unexpected tile position"),
7795
};
78-
let colors = get_palette(ppu, palette_idx);
96+
let colors = get_bg_palette(ppu, palette_idx);
7997

8098
for i in 0..pixels.len() {
8199
let (screen_x, screen_y) = calc_screen_pos(tile_index as usize, i);
@@ -88,7 +106,48 @@ pub async fn render_frame(cpu: &mut Cpu) {
88106
)
89107
}
90108
}
91-
next_frame().await
109+
}
110+
111+
async fn render_sprites(ppu: &Ppu<PpuMemory>) {
112+
for oam_idx in (0..OAM_SIZE).step_by(4).rev() {
113+
let sprite = Sprite::from_data(&ppu.oam[oam_idx..oam_idx + 4]);
114+
if !sprite.visible {
115+
continue;
116+
}
117+
let bank = ppu.sprite_pattern_addr();
118+
let tile = sprite.tile_index as u16;
119+
let chr_data = ppu.memory.chr_rom
120+
[(bank + tile * 16) as usize..(bank + tile * 16 + 16) as usize]
121+
.to_vec();
122+
let pixels = chr_data_to_pixels(chr_data);
123+
124+
let colors = get_sprite_palette(ppu, sprite.palette as u16);
125+
126+
for x in 0..8 {
127+
for y in 0..8 {
128+
let pixel_idx = (y * 8 + x) as usize;
129+
if pixels[pixel_idx] == 0 {
130+
continue;
131+
}
132+
133+
let x = if sprite.flip_horizontally { 7 - x } else { x };
134+
let y = if sprite.flip_vertically { 7 - y } else { y };
135+
136+
if sprite.x as u16 + x > SCREEN_WIDTH || sprite.y as u16 + y > SCREEN_HEIGHT {
137+
continue;
138+
}
139+
let (screen_x, screen_y) = (sprite.x as u16 + x, sprite.y as u16 + y);
140+
141+
draw_rectangle(
142+
screen_x as f32 * RENDER_SCALE,
143+
screen_y as f32 * RENDER_SCALE,
144+
RENDER_SCALE,
145+
RENDER_SCALE,
146+
colors[pixels[pixel_idx] as usize],
147+
)
148+
}
149+
}
150+
}
92151
}
93152

94153
pub async fn debug_chr_rom(rom: &NesRom) {

0 commit comments

Comments
 (0)