more work on ppu
This commit is contained in:
@@ -81,6 +81,15 @@ impl Memory for NESMemory {
|
||||
}
|
||||
}
|
||||
|
||||
// impl PPUMemory for NESMemory {
|
||||
// fn readb_ppu(&self, addr: Addr) -> Byte {
|
||||
|
||||
// }
|
||||
// fn writeb_ppu(&mut self, addr: Addr, data: Byte) {
|
||||
|
||||
// }
|
||||
// }
|
||||
|
||||
pub trait MemoryReader {
|
||||
fn readb<T: Memory>(&self, mem: &T, addr: Addr) -> Byte {
|
||||
mem.readb(addr)
|
||||
@@ -99,23 +108,14 @@ pub trait MemoryReader {
|
||||
pub trait PPUMemory {
|
||||
fn readb_ppu(&self, addr: Addr) -> Byte;
|
||||
fn writeb_ppu(&mut self, addr: Addr, data: Byte);
|
||||
fn readw_ppu(&self, addr: Addr) -> Word {
|
||||
let lo = self.readb_ppu(addr);
|
||||
let hi = self.readb_ppu(addr+1);
|
||||
(hi as Word) << 8 | lo as Word
|
||||
}
|
||||
fn writew_ppu(&mut self, addr: Addr, data: Word) {
|
||||
self.writeb_ppu(addr, data as Byte);
|
||||
self.writeb_ppu(addr + 1, (data >> 8) as Byte);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PPUMemoryReader {
|
||||
fn readb_ppu<T: PPUMemory>(&self, mem: &T, addr: Addr) -> Byte {
|
||||
mem.readb_ppu(addr)
|
||||
}
|
||||
// pub trait PPUMemoryReader {
|
||||
// fn readb_ppu<T: PPUMemory>(&self, mem: &T, addr: Addr) -> Byte {
|
||||
// mem.readb_ppu(addr)
|
||||
// }
|
||||
|
||||
fn writeb_ppu<T: PPUMemory>(&mut self, mem: &mut T, addr: Addr, data: Byte) {
|
||||
mem.writeb_ppu(addr, data)
|
||||
}
|
||||
}
|
||||
// fn writeb_ppu<T: PPUMemory>(&mut self, mem: &mut T, addr: Addr, data: Byte) {
|
||||
// mem.writeb_ppu(addr, data)
|
||||
// }
|
||||
// }
|
||||
|
||||
162
src/nes/ppu.rs
162
src/nes/ppu.rs
@@ -1,4 +1,4 @@
|
||||
use crate::nes::memory::{Memory,PPUMemoryReader,PPUMemory};
|
||||
use crate::nes::memory::{Memory,PPUMemory};
|
||||
use crate::nes::types::*;
|
||||
use image::{ImageBuffer, Rgba};
|
||||
use rand::Rng;
|
||||
@@ -58,7 +58,7 @@ pub struct Registers {
|
||||
// 0x2005
|
||||
pub scroll: Byte,
|
||||
// 0x2006
|
||||
pub addr: Byte,
|
||||
pub addr: Addr,
|
||||
// 0x2007
|
||||
pub data: Byte,
|
||||
// 0x2008
|
||||
@@ -87,10 +87,11 @@ pub struct PPU {
|
||||
pub scanline: u16,
|
||||
pub canvas_main: Sprite,
|
||||
pattern_table: [Sprite; 2],
|
||||
pub frame_ready: bool
|
||||
pub frame_ready: bool,
|
||||
addr_latch_set: bool,
|
||||
}
|
||||
|
||||
impl PPUMemoryReader for PPU {}
|
||||
// impl PPUMemoryReader for PPU {}
|
||||
|
||||
impl PPU {
|
||||
pub fn new() -> Self {
|
||||
@@ -101,6 +102,7 @@ impl PPU {
|
||||
canvas_main: ImageBuffer::new(256, 240),
|
||||
pattern_table: [ImageBuffer::new(128, 128), ImageBuffer::new(128, 128)],
|
||||
frame_ready: false,
|
||||
addr_latch_set: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,6 +110,7 @@ impl PPU {
|
||||
self.regs = Registers::new();
|
||||
}
|
||||
|
||||
|
||||
// Set a flag with the corresponding mask
|
||||
fn set_flag(&mut self, register: PPURegister, flag: Byte, val: bool) {
|
||||
// TODO Likely not all of them should be rw and treated as flags
|
||||
@@ -118,9 +121,10 @@ impl PPU {
|
||||
PPURegister::OAMAddr => &mut self.regs.oam_addr,
|
||||
PPURegister::OAMData => &mut self.regs.oam_data,
|
||||
PPURegister::Scroll => &mut self.regs.scroll,
|
||||
PPURegister::Addr => &mut self.regs.addr,
|
||||
// PPURegister::Addr => &mut self.regs.addr,
|
||||
PPURegister::Data => &mut self.regs.data,
|
||||
PPURegister::DMA => &mut self.regs.dma,
|
||||
_ => { return }
|
||||
};
|
||||
|
||||
if val {
|
||||
@@ -130,6 +134,24 @@ impl PPU {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_flag(&self, register: PPURegister, flag: Byte) -> bool {
|
||||
// TODO Likely not all of them should be rw and treated as flags
|
||||
let reg = match register {
|
||||
PPURegister::Control => &self.regs.ctrl,
|
||||
PPURegister::Mask => &self.regs.mask,
|
||||
PPURegister::Status => &self.regs.status,
|
||||
PPURegister::OAMAddr => &self.regs.oam_addr,
|
||||
PPURegister::OAMData => &self.regs.oam_data,
|
||||
PPURegister::Scroll => &self.regs.scroll,
|
||||
// PPURegister::Addr => &mut self.regs.addr,
|
||||
PPURegister::Data => &self.regs.data,
|
||||
PPURegister::DMA => &self.regs.dma,
|
||||
_ => { unreachable!() }
|
||||
};
|
||||
|
||||
reg & flag > 0
|
||||
}
|
||||
|
||||
|
||||
// PPU renders 262 scanlines with 341 clocks per line. One px per clock
|
||||
// Scanline -1,261: Dummy scanline
|
||||
@@ -162,24 +184,72 @@ impl PPU {
|
||||
} else if self.scanline == 261 && self.cycle == 1 {
|
||||
self.set_flag(PPURegister::Status, STATUS_VERTICAL_BLANK, false);
|
||||
}
|
||||
|
||||
// if (self.cycle < self.canvas_main.width() as u16) &&
|
||||
// (self.scanline < self.canvas_main.height() as u16) {
|
||||
// // random noise
|
||||
// let mut rng = rand::thread_rng();
|
||||
// let x = self.cycle;
|
||||
// let y = self.scanline;
|
||||
|
||||
// let color = rng.gen::<bool>();
|
||||
// let px = if color {
|
||||
// PALETTE[&0x0f]
|
||||
// } else {
|
||||
// PALETTE[&0x20]
|
||||
// };
|
||||
}
|
||||
|
||||
// self.canvas_main.put_pixel(x as u32, y as u32, px);
|
||||
// }
|
||||
// CPU can write certain registers of the PPU through the bus
|
||||
pub fn readb(&self, addr: Addr) -> Byte {
|
||||
// Only certain registers of the PPU can actually by read
|
||||
// remaining registers and read attemps will return garbage
|
||||
match addr {
|
||||
// status
|
||||
0x2002 => { self.regs.status },
|
||||
// oam data
|
||||
0x2004 => { unimplemented!() },
|
||||
// ppu data
|
||||
0x2007 => { unimplemented!() },
|
||||
_ => 0x00, // unmapped reads
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// CPU can write certain registers of the PPU through the bus
|
||||
pub fn writeb(&mut self, addr: Addr, data: Byte) {
|
||||
// Only some of the PPU regs can be written to
|
||||
match addr {
|
||||
// Control
|
||||
0x2000 => { self.regs.ctrl = data },
|
||||
// Mask
|
||||
0x2001 => { self.regs.mask = data },
|
||||
// OAM address
|
||||
0x2003 => { unreachable!() },
|
||||
// OAM data
|
||||
0x2004 => { unreachable!() },
|
||||
// Scroll
|
||||
0x2005 => { unimplemented!() },
|
||||
// Addr
|
||||
// To write a 16bit addr to the ppu, two consecutive writes are
|
||||
// required to set the hi and lo byte, respectively
|
||||
0x2006 => {
|
||||
if !self.addr_latch_set {
|
||||
self.regs.addr = self.regs.addr & 0x00FF | (data as Word) << 8;
|
||||
} else {
|
||||
self.regs.addr = self.regs.addr & 0xFF00 | data as Word;
|
||||
}
|
||||
self.addr_latch_set = !self.addr_latch_set;
|
||||
}
|
||||
// write data to the ppu addr bus
|
||||
0x2007 => {
|
||||
self.writeb_ppu(self.regs.addr, data);
|
||||
// after write, increment vram addr for further writes.
|
||||
// The increment value is determined by the vertical mode
|
||||
// flag of the status reg 0: +1, 1: +32
|
||||
if self.get_flag(PPURegister::Control, CTRL_INCR_MODE) {
|
||||
self.regs.addr += 32;
|
||||
} else {
|
||||
self.regs.addr += 1;
|
||||
}
|
||||
},
|
||||
_ => { } // unwriteable addr, do nothing
|
||||
}
|
||||
}
|
||||
|
||||
// Some read/writes are nt
|
||||
fn writeb_ppu(&mut self, addr: Addr, data: Byte) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
fn readb_ppu(&self, addr: Addr) -> Byte {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
// Get the correct color for the pixel from the given palette
|
||||
@@ -230,42 +300,6 @@ impl PPU {
|
||||
}
|
||||
}
|
||||
|
||||
impl Memory for PPU {
|
||||
fn readb(&self, addr: Addr) -> Byte {
|
||||
// Only some of the PPU regs can actually by read
|
||||
match addr {
|
||||
// PPU status
|
||||
0x2002 => { return self.regs.status },
|
||||
// OAM data (object attribute memory = list of 64 sprites)
|
||||
0x2004 => { unimplemented!() },
|
||||
// ppu data
|
||||
0x2007 => { unimplemented!() },
|
||||
_ => 0x00
|
||||
}
|
||||
|
||||
}
|
||||
fn writeb(&mut self, addr: Addr, data: Byte) {
|
||||
// Only some of the PPU regs can be written to
|
||||
match addr {
|
||||
// Control
|
||||
0x2000 => { self.regs.ctrl = data },
|
||||
// Mask
|
||||
0x2001 => { self.regs.mask = data },
|
||||
// OAM address
|
||||
0x2003 => { unimplemented!() },
|
||||
// OAM data
|
||||
0x2004 => { unimplemented!() },
|
||||
// Scroll
|
||||
0x2005 => { self.regs.scroll = data },
|
||||
// Address
|
||||
0x2006 => { self.regs.addr = data },
|
||||
// ppu data
|
||||
0x2007 => { self.regs.data = data },
|
||||
_ => { unreachable!() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -310,4 +344,18 @@ mod tests {
|
||||
assert_eq!(ppu.regs.mask, 0b0000001,
|
||||
"register={:#010b}; should be 0b0000001", ppu.regs.status);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_addr() {
|
||||
let mut ppu = PPU::new();
|
||||
// TODO unit test
|
||||
assert_eq!(ppu.regs.addr, 0x0000);
|
||||
|
||||
ppu.writeb(0x2006, 0x12);
|
||||
assert_eq!(ppu.regs.addr, 0x1200);
|
||||
ppu.writeb(0x2006, 0x34);
|
||||
assert_eq!(ppu.regs.addr, 0x1234);
|
||||
ppu.writeb(0x2006, 0x56);
|
||||
assert_eq!(ppu.regs.addr, 0x5634);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user