ppu read/write

This commit is contained in:
Daniel Bauer
2020-01-18 13:32:57 +01:00
parent 5349dbcd53
commit 504cc9db9b
4 changed files with 77 additions and 49 deletions

View File

@@ -37,7 +37,7 @@ lazy_static! {
}
fn main() -> Result<(), Error> {
simple_logger::init_with_level(Level::Debug).unwrap();
simple_logger::init_with_level(Level::Info).unwrap();
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
bail!("No cartridge supplied. Usage: ./jane cartridge.nes");

View File

@@ -53,7 +53,7 @@ impl Registers {
impl Debug for Registers {
fn fmt(&self, f: &mut Formatter) -> Result {
write!(f, "{{ a: {:#x}, x: {:#x}, y: {:#x}, sp: {:#x}, pc: {:#x}, flags: {:#010b} }}",
self.a, self.x, self.y, self.sp, self.pc, self.flags.bits)
self.a, self.x, self.y, self.sp, self.pc, self.flags.bits())
}
}
@@ -169,7 +169,7 @@ impl CPU {
fn set_flag_nz(&mut self, val: Byte) {
self.set_flag(Flags::ZERO, val == 0);
self.set_flag(Flags::NEGATIVE, (val & Flags::NEGATIVE.bits) > 0);
self.set_flag(Flags::NEGATIVE, (val & Flags::NEGATIVE.bits()) > 0);
}
pub fn get_flag(&self, flag: Flags) -> Byte {
@@ -522,8 +522,8 @@ impl CPU {
// the zeroflag is set to the result of operand AND accumulator.
fn op_BIT<T: Memory>(&mut self, mem: &T, addr: Word) -> bool {
let val = self.readb(mem, addr);
self.set_flag(Flags::OVERFLOW, (val & Flags::OVERFLOW.bits) > 1);
self.set_flag(Flags::NEGATIVE, (val & Flags::NEGATIVE.bits) > 1);
self.set_flag(Flags::OVERFLOW, (val & Flags::OVERFLOW.bits()) > 1);
self.set_flag(Flags::NEGATIVE, (val & Flags::NEGATIVE.bits()) > 1);
self.set_flag(Flags::ZERO, (val & self.regs.a) == 0);
false
}
@@ -579,7 +579,7 @@ impl CPU {
// Push flags to stack
self.set_flag(Flags::BREAK, true);
self.pushb_sp(mem, self.regs.flags.bits);
self.pushb_sp(mem, self.regs.flags.bits());
self.set_flag(Flags::BREAK, false);
// set PC to IRQ vector
@@ -875,7 +875,7 @@ impl CPU {
// Pushes a copy of the status flags on to the stack.
fn op_PHP<T: Memory>(&mut self, mem: &mut T) -> bool {
let tmp = self.regs.flags | Flags::BREAK;
self.pushb_sp(mem, tmp.bits);
self.pushb_sp(mem, tmp.bits());
self.set_flag(Flags::BREAK, false);
false
}

View File

@@ -58,7 +58,7 @@ impl Memory for NESMemory {
return self.ram[(addr & RAM_PHYS_RANGE[1]) as usize]
}
if PPU_ADDR_RANGE[0] <= addr && addr <= PPU_ADDR_RANGE[1] {
let ppu = self.ppu.borrow();
let mut ppu = self.ppu.borrow_mut();
return ppu.readb(addr & PPU_PHYS_RANGE[1]);
}
0x0000 // generic response

View File

@@ -102,6 +102,7 @@ pub struct PPU {
pattern_table: [Sprite; 2],
pub frame_ready: bool,
addr_latch_set: bool,
data_buffer: Byte,
}
// impl PPUMemoryReader for PPU {}
@@ -116,6 +117,7 @@ impl PPU {
pattern_table: [ImageBuffer::new(128, 128), ImageBuffer::new(128, 128)],
frame_ready: false,
addr_latch_set: false,
data_buffer: 0
}
}
@@ -183,19 +185,39 @@ impl PPU {
}
// CPU can write certain registers of the PPU through the bus
pub fn readb(&self, addr: Addr) -> Byte {
pub fn readb(&mut 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.bits },
0x2002 => {
let status = self.regs.status.bits();
// Reading the status register also clears VBLANK and the
// address latch
self.set_status(Status::VERTICAL_BLANK, false);
self.addr_latch_set = false;
status
},
// oam data
0x2004 => { unimplemented!() },
0x2004 => { /* TODO */ 0x00 },
// ppu data
0x2007 => { unimplemented!() },
_ => 0x00, // unmapped reads
0x2007 => {
// ppu reads are delayed by one clock. Therefore, this uses
// a buffer variable to return the data from the previous
// read, and then set the new data to the buffer. However,
// because the PPU is weird, this does not apply for the
// palette memory
let data = self.data_buffer;
self.data_buffer = self.readb_ppu(addr);
if addr > 0x3F00 { // everything above 0x3F00 is palette
self.data_buffer
} else {
data
}
},
_ => 0x00, // unmapped reads
}
}
// CPU can write certain registers of the PPU through the bus
@@ -203,19 +225,25 @@ impl PPU {
// Only some of the PPU regs can be written to
match addr {
// Control
0x2000 => { self.regs.ctrl = Control::from_bits(data).unwrap() },
0x2000 => {
self.regs.ctrl = Control::from_bits(data).unwrap()
},
// Mask
0x2001 => { self.regs.mask = Mask::from_bits(data).unwrap() },
0x2001 => {
self.regs.mask = Mask::from_bits(data).unwrap()
},
// OAM address
0x2003 => { unreachable!() },
0x2003 => { /* TODO */ },
// OAM data
0x2004 => { unreachable!() },
0x2004 => { /* TODO */ },
// Scroll
0x2005 => { unimplemented!() },
0x2005 => { /* TODO */ },
// Addr
// To write a 16bit addr to the ppu, two consecutive writes are
// required to set the hi and lo byte, respectively
0x2006 => {
0x2006 => {
// To write a 16bit addr to the ppu, two consecutive writes are
// required to set the hi and lo byte of the address.
// addr_latch_set indicates wether the hi byte is already
// set or not
if !self.addr_latch_set {
self.regs.addr = self.regs.addr & 0x00FF | (data as Word) << 8;
} else {
@@ -226,13 +254,13 @@ impl PPU {
// write data to the ppu addr bus
0x2007 => {
self.writeb_ppu(self.regs.addr, data);
// after write, increment vram addr for further writes.
// after write, increment vram addr for next write.
// The increment value is determined by the vertical mode
// flag of the status reg 0: +1, 1: +32
if self.get_control(Control::INCREMENT_MODE) {
self.regs.addr += 32;
self.regs.addr += if self.get_control(Control::INCREMENT_MODE) {
32
} else {
self.regs.addr += 1;
1
}
},
_ => { } // unwriteable addr, do nothing
@@ -305,40 +333,40 @@ mod tests {
let mut ppu = PPU::new();
// set unset flag
assert_eq!(ppu.regs.status.bits, 0b00000000);
ppu.set_status(VERTICAL_BLANK, true);
assert_eq!(ppu.regs.status.bits, 0b10000000,
"register={:#010b}; should be 0b10000000", ppu.regs.status.bits);
ppu.set_status(VERTICAL_BLANK, false);
assert_eq!(ppu.regs.status.bits, 0b00000000,
"register={:#010b}; should be 0b00000000", ppu.regs.status.bits);
assert_eq!(ppu.regs.status.bits(), 0b00000000);
ppu.set_status(Status::VERTICAL_BLANK, true);
assert_eq!(ppu.regs.status.bits(), 0b10000000,
"register={:#010b}; should be 0b10000000", ppu.regs.status.bits());
ppu.set_status(Status::VERTICAL_BLANK, false);
assert_eq!(ppu.regs.status.bits(), 0b00000000,
"register={:#010b}; should be 0b00000000", ppu.regs.status.bits());
// set same flag twice
ppu.set_status(VERTICAL_BLANK, true);
assert_eq!(ppu.regs.status.bits, 0b10000000,
"register={:#010b}; should be 0b10000000", ppu.regs.status.bits);
ppu.set_status(VERTICAL_BLANK, true);
assert_eq!(ppu.regs.status.bits, 0b10000000,
"register={:#010b}; should be 0b10000000", ppu.regs.status.bits);
ppu.set_status(Status::VERTICAL_BLANK, true);
assert_eq!(ppu.regs.status.bits(), 0b10000000,
"register={:#010b}; should be 0b10000000", ppu.regs.status.bits());
ppu.set_status(Status::VERTICAL_BLANK, true);
assert_eq!(ppu.regs.status.bits(), 0b10000000,
"register={:#010b}; should be 0b10000000", ppu.regs.status.bits());
// set other flag
ppu.set_status(SPRITE_ZERO_HIT, true);
assert_eq!(ppu.regs.status.bits, 0b11000000,
"register={:#010b}; should be 0b11000000", ppu.regs.status.bits);
ppu.set_status(Status::SPRITE_ZERO_HIT, true);
assert_eq!(ppu.regs.status.bits(), 0b11000000,
"register={:#010b}; should be 0b11000000", ppu.regs.status.bits());
ppu.set_status(SPRITE_ZERO_HIT, false);
assert_eq!(ppu.regs.status.bits, 0b10000000,
"register={:#010b}; should be 0b10000000", ppu.regs.status.bits);
ppu.set_status(Status::SPRITE_ZERO_HIT, false);
assert_eq!(ppu.regs.status.bits(), 0b10000000,
"register={:#010b}; should be 0b10000000", ppu.regs.status.bits());
// set other register
ppu.set_control(Control::INCREMENT_MODE, true);
assert_eq!(ppu.regs.status.bits, 0b10000000,
"register={:#010b}; should be 0b10000000", ppu.regs.status.bits);
assert_eq!(ppu.regs.control.bits, 0b0000100,
"register={:#010b}; should be 0b0000001", ppu.regs.control.bits);
assert_eq!(ppu.regs.status.bits(), 0b10000000,
"register={:#010b}; should be 0b10000000", ppu.regs.status.bits());
assert_eq!(ppu.regs.ctrl.bits(), 0b0000100,
"register={:#010b}; should be 0b0000001", ppu.regs.ctrl.bits());
}
#[test]