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 {
|
pub trait MemoryReader {
|
||||||
fn readb<T: Memory>(&self, mem: &T, addr: Addr) -> Byte {
|
fn readb<T: Memory>(&self, mem: &T, addr: Addr) -> Byte {
|
||||||
mem.readb(addr)
|
mem.readb(addr)
|
||||||
@@ -99,23 +108,14 @@ pub trait MemoryReader {
|
|||||||
pub trait PPUMemory {
|
pub trait PPUMemory {
|
||||||
fn readb_ppu(&self, addr: Addr) -> Byte;
|
fn readb_ppu(&self, addr: Addr) -> Byte;
|
||||||
fn writeb_ppu(&mut self, addr: Addr, data: 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 {
|
// pub trait PPUMemoryReader {
|
||||||
fn readb_ppu<T: PPUMemory>(&self, mem: &T, addr: Addr) -> Byte {
|
// fn readb_ppu<T: PPUMemory>(&self, mem: &T, addr: Addr) -> Byte {
|
||||||
mem.readb_ppu(addr)
|
// mem.readb_ppu(addr)
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn writeb_ppu<T: PPUMemory>(&mut self, mem: &mut T, addr: Addr, data: Byte) {
|
// fn writeb_ppu<T: PPUMemory>(&mut self, mem: &mut T, addr: Addr, data: Byte) {
|
||||||
mem.writeb_ppu(addr, data)
|
// mem.writeb_ppu(addr, data)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|||||||
158
src/nes/ppu.rs
158
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 crate::nes::types::*;
|
||||||
use image::{ImageBuffer, Rgba};
|
use image::{ImageBuffer, Rgba};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
@@ -58,7 +58,7 @@ pub struct Registers {
|
|||||||
// 0x2005
|
// 0x2005
|
||||||
pub scroll: Byte,
|
pub scroll: Byte,
|
||||||
// 0x2006
|
// 0x2006
|
||||||
pub addr: Byte,
|
pub addr: Addr,
|
||||||
// 0x2007
|
// 0x2007
|
||||||
pub data: Byte,
|
pub data: Byte,
|
||||||
// 0x2008
|
// 0x2008
|
||||||
@@ -87,10 +87,11 @@ pub struct PPU {
|
|||||||
pub scanline: u16,
|
pub scanline: u16,
|
||||||
pub canvas_main: Sprite,
|
pub canvas_main: Sprite,
|
||||||
pattern_table: [Sprite; 2],
|
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 {
|
impl PPU {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
@@ -101,6 +102,7 @@ impl PPU {
|
|||||||
canvas_main: ImageBuffer::new(256, 240),
|
canvas_main: ImageBuffer::new(256, 240),
|
||||||
pattern_table: [ImageBuffer::new(128, 128), ImageBuffer::new(128, 128)],
|
pattern_table: [ImageBuffer::new(128, 128), ImageBuffer::new(128, 128)],
|
||||||
frame_ready: false,
|
frame_ready: false,
|
||||||
|
addr_latch_set: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,6 +110,7 @@ impl PPU {
|
|||||||
self.regs = Registers::new();
|
self.regs = Registers::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Set a flag with the corresponding mask
|
// Set a flag with the corresponding mask
|
||||||
fn set_flag(&mut self, register: PPURegister, flag: Byte, val: bool) {
|
fn set_flag(&mut self, register: PPURegister, flag: Byte, val: bool) {
|
||||||
// TODO Likely not all of them should be rw and treated as flags
|
// 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::OAMAddr => &mut self.regs.oam_addr,
|
||||||
PPURegister::OAMData => &mut self.regs.oam_data,
|
PPURegister::OAMData => &mut self.regs.oam_data,
|
||||||
PPURegister::Scroll => &mut self.regs.scroll,
|
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::Data => &mut self.regs.data,
|
||||||
PPURegister::DMA => &mut self.regs.dma,
|
PPURegister::DMA => &mut self.regs.dma,
|
||||||
|
_ => { return }
|
||||||
};
|
};
|
||||||
|
|
||||||
if val {
|
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
|
// PPU renders 262 scanlines with 341 clocks per line. One px per clock
|
||||||
// Scanline -1,261: Dummy scanline
|
// Scanline -1,261: Dummy scanline
|
||||||
@@ -162,24 +184,72 @@ impl PPU {
|
|||||||
} else if self.scanline == 261 && self.cycle == 1 {
|
} else if self.scanline == 261 && self.cycle == 1 {
|
||||||
self.set_flag(PPURegister::Status, STATUS_VERTICAL_BLANK, false);
|
self.set_flag(PPURegister::Status, STATUS_VERTICAL_BLANK, false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if (self.cycle < self.canvas_main.width() as u16) &&
|
// CPU can write certain registers of the PPU through the bus
|
||||||
// (self.scanline < self.canvas_main.height() as u16) {
|
pub fn readb(&self, addr: Addr) -> Byte {
|
||||||
// // random noise
|
// Only certain registers of the PPU can actually by read
|
||||||
// let mut rng = rand::thread_rng();
|
// remaining registers and read attemps will return garbage
|
||||||
// let x = self.cycle;
|
match addr {
|
||||||
// let y = self.scanline;
|
// status
|
||||||
|
0x2002 => { self.regs.status },
|
||||||
|
// oam data
|
||||||
|
0x2004 => { unimplemented!() },
|
||||||
|
// ppu data
|
||||||
|
0x2007 => { unimplemented!() },
|
||||||
|
_ => 0x00, // unmapped reads
|
||||||
|
}
|
||||||
|
|
||||||
// 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 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
|
// 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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -310,4 +344,18 @@ mod tests {
|
|||||||
assert_eq!(ppu.regs.mask, 0b0000001,
|
assert_eq!(ppu.regs.mask, 0b0000001,
|
||||||
"register={:#010b}; should be 0b0000001", ppu.regs.status);
|
"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