ppu read/write
This commit is contained in:
@@ -37,7 +37,7 @@ lazy_static! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Error> {
|
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();
|
let args: Vec<String> = env::args().collect();
|
||||||
if args.len() < 2 {
|
if args.len() < 2 {
|
||||||
bail!("No cartridge supplied. Usage: ./jane cartridge.nes");
|
bail!("No cartridge supplied. Usage: ./jane cartridge.nes");
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ impl Registers {
|
|||||||
impl Debug for Registers {
|
impl Debug for Registers {
|
||||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||||
write!(f, "{{ a: {:#x}, x: {:#x}, y: {:#x}, sp: {:#x}, pc: {:#x}, flags: {:#010b} }}",
|
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) {
|
fn set_flag_nz(&mut self, val: Byte) {
|
||||||
self.set_flag(Flags::ZERO, val == 0);
|
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 {
|
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.
|
// the zeroflag is set to the result of operand AND accumulator.
|
||||||
fn op_BIT<T: Memory>(&mut self, mem: &T, addr: Word) -> bool {
|
fn op_BIT<T: Memory>(&mut self, mem: &T, addr: Word) -> bool {
|
||||||
let val = self.readb(mem, addr);
|
let val = self.readb(mem, addr);
|
||||||
self.set_flag(Flags::OVERFLOW, (val & Flags::OVERFLOW.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::NEGATIVE, (val & Flags::NEGATIVE.bits()) > 1);
|
||||||
self.set_flag(Flags::ZERO, (val & self.regs.a) == 0);
|
self.set_flag(Flags::ZERO, (val & self.regs.a) == 0);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
@@ -579,7 +579,7 @@ impl CPU {
|
|||||||
|
|
||||||
// Push flags to stack
|
// Push flags to stack
|
||||||
self.set_flag(Flags::BREAK, true);
|
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);
|
self.set_flag(Flags::BREAK, false);
|
||||||
|
|
||||||
// set PC to IRQ vector
|
// set PC to IRQ vector
|
||||||
@@ -875,7 +875,7 @@ impl CPU {
|
|||||||
// Pushes a copy of the status flags on to the stack.
|
// Pushes a copy of the status flags on to the stack.
|
||||||
fn op_PHP<T: Memory>(&mut self, mem: &mut T) -> bool {
|
fn op_PHP<T: Memory>(&mut self, mem: &mut T) -> bool {
|
||||||
let tmp = self.regs.flags | Flags::BREAK;
|
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);
|
self.set_flag(Flags::BREAK, false);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ impl Memory for NESMemory {
|
|||||||
return self.ram[(addr & RAM_PHYS_RANGE[1]) as usize]
|
return self.ram[(addr & RAM_PHYS_RANGE[1]) as usize]
|
||||||
}
|
}
|
||||||
if PPU_ADDR_RANGE[0] <= addr && addr <= PPU_ADDR_RANGE[1] {
|
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]);
|
return ppu.readb(addr & PPU_PHYS_RANGE[1]);
|
||||||
}
|
}
|
||||||
0x0000 // generic response
|
0x0000 // generic response
|
||||||
|
|||||||
106
src/nes/ppu.rs
106
src/nes/ppu.rs
@@ -102,6 +102,7 @@ pub struct PPU {
|
|||||||
pattern_table: [Sprite; 2],
|
pattern_table: [Sprite; 2],
|
||||||
pub frame_ready: bool,
|
pub frame_ready: bool,
|
||||||
addr_latch_set: bool,
|
addr_latch_set: bool,
|
||||||
|
data_buffer: Byte,
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl PPUMemoryReader for PPU {}
|
// impl PPUMemoryReader for PPU {}
|
||||||
@@ -116,6 +117,7 @@ impl PPU {
|
|||||||
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,
|
addr_latch_set: false,
|
||||||
|
data_buffer: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,19 +185,39 @@ impl PPU {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CPU can write certain registers of the PPU through the bus
|
// 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
|
// Only certain registers of the PPU can actually by read
|
||||||
// remaining registers and read attemps will return garbage
|
// remaining registers and read attemps will return garbage
|
||||||
match addr {
|
match addr {
|
||||||
// status
|
// 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
|
// oam data
|
||||||
0x2004 => { unimplemented!() },
|
0x2004 => { /* TODO */ 0x00 },
|
||||||
// ppu data
|
// ppu data
|
||||||
0x2007 => { unimplemented!() },
|
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
|
_ => 0x00, // unmapped reads
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// CPU can write certain registers of the PPU through the bus
|
// 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
|
// Only some of the PPU regs can be written to
|
||||||
match addr {
|
match addr {
|
||||||
// Control
|
// Control
|
||||||
0x2000 => { self.regs.ctrl = Control::from_bits(data).unwrap() },
|
0x2000 => {
|
||||||
|
self.regs.ctrl = Control::from_bits(data).unwrap()
|
||||||
|
},
|
||||||
// Mask
|
// Mask
|
||||||
0x2001 => { self.regs.mask = Mask::from_bits(data).unwrap() },
|
0x2001 => {
|
||||||
|
self.regs.mask = Mask::from_bits(data).unwrap()
|
||||||
|
},
|
||||||
// OAM address
|
// OAM address
|
||||||
0x2003 => { unreachable!() },
|
0x2003 => { /* TODO */ },
|
||||||
// OAM data
|
// OAM data
|
||||||
0x2004 => { unreachable!() },
|
0x2004 => { /* TODO */ },
|
||||||
// Scroll
|
// Scroll
|
||||||
0x2005 => { unimplemented!() },
|
0x2005 => { /* TODO */ },
|
||||||
// Addr
|
// 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 {
|
if !self.addr_latch_set {
|
||||||
self.regs.addr = self.regs.addr & 0x00FF | (data as Word) << 8;
|
self.regs.addr = self.regs.addr & 0x00FF | (data as Word) << 8;
|
||||||
} else {
|
} else {
|
||||||
@@ -226,13 +254,13 @@ impl PPU {
|
|||||||
// write data to the ppu addr bus
|
// write data to the ppu addr bus
|
||||||
0x2007 => {
|
0x2007 => {
|
||||||
self.writeb_ppu(self.regs.addr, data);
|
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
|
// The increment value is determined by the vertical mode
|
||||||
// flag of the status reg 0: +1, 1: +32
|
// flag of the status reg 0: +1, 1: +32
|
||||||
if self.get_control(Control::INCREMENT_MODE) {
|
self.regs.addr += if self.get_control(Control::INCREMENT_MODE) {
|
||||||
self.regs.addr += 32;
|
32
|
||||||
} else {
|
} else {
|
||||||
self.regs.addr += 1;
|
1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => { } // unwriteable addr, do nothing
|
_ => { } // unwriteable addr, do nothing
|
||||||
@@ -305,40 +333,40 @@ mod tests {
|
|||||||
let mut ppu = PPU::new();
|
let mut ppu = PPU::new();
|
||||||
|
|
||||||
// set unset flag
|
// set unset flag
|
||||||
assert_eq!(ppu.regs.status.bits, 0b00000000);
|
assert_eq!(ppu.regs.status.bits(), 0b00000000);
|
||||||
ppu.set_status(VERTICAL_BLANK, true);
|
ppu.set_status(Status::VERTICAL_BLANK, true);
|
||||||
assert_eq!(ppu.regs.status.bits, 0b10000000,
|
assert_eq!(ppu.regs.status.bits(), 0b10000000,
|
||||||
"register={:#010b}; should be 0b10000000", ppu.regs.status.bits);
|
"register={:#010b}; should be 0b10000000", ppu.regs.status.bits());
|
||||||
ppu.set_status(VERTICAL_BLANK, false);
|
ppu.set_status(Status::VERTICAL_BLANK, false);
|
||||||
assert_eq!(ppu.regs.status.bits, 0b00000000,
|
assert_eq!(ppu.regs.status.bits(), 0b00000000,
|
||||||
"register={:#010b}; should be 0b00000000", ppu.regs.status.bits);
|
"register={:#010b}; should be 0b00000000", ppu.regs.status.bits());
|
||||||
|
|
||||||
|
|
||||||
// set same flag twice
|
// set same flag twice
|
||||||
ppu.set_status(VERTICAL_BLANK, true);
|
ppu.set_status(Status::VERTICAL_BLANK, true);
|
||||||
assert_eq!(ppu.regs.status.bits, 0b10000000,
|
assert_eq!(ppu.regs.status.bits(), 0b10000000,
|
||||||
"register={:#010b}; should be 0b10000000", ppu.regs.status.bits);
|
"register={:#010b}; should be 0b10000000", ppu.regs.status.bits());
|
||||||
ppu.set_status(VERTICAL_BLANK, true);
|
ppu.set_status(Status::VERTICAL_BLANK, true);
|
||||||
assert_eq!(ppu.regs.status.bits, 0b10000000,
|
assert_eq!(ppu.regs.status.bits(), 0b10000000,
|
||||||
"register={:#010b}; should be 0b10000000", ppu.regs.status.bits);
|
"register={:#010b}; should be 0b10000000", ppu.regs.status.bits());
|
||||||
|
|
||||||
|
|
||||||
// set other flag
|
// set other flag
|
||||||
ppu.set_status(SPRITE_ZERO_HIT, true);
|
ppu.set_status(Status::SPRITE_ZERO_HIT, true);
|
||||||
assert_eq!(ppu.regs.status.bits, 0b11000000,
|
assert_eq!(ppu.regs.status.bits(), 0b11000000,
|
||||||
"register={:#010b}; should be 0b11000000", ppu.regs.status.bits);
|
"register={:#010b}; should be 0b11000000", ppu.regs.status.bits());
|
||||||
|
|
||||||
ppu.set_status(SPRITE_ZERO_HIT, false);
|
ppu.set_status(Status::SPRITE_ZERO_HIT, false);
|
||||||
assert_eq!(ppu.regs.status.bits, 0b10000000,
|
assert_eq!(ppu.regs.status.bits(), 0b10000000,
|
||||||
"register={:#010b}; should be 0b10000000", ppu.regs.status.bits);
|
"register={:#010b}; should be 0b10000000", ppu.regs.status.bits());
|
||||||
|
|
||||||
|
|
||||||
// set other register
|
// set other register
|
||||||
ppu.set_control(Control::INCREMENT_MODE, true);
|
ppu.set_control(Control::INCREMENT_MODE, true);
|
||||||
assert_eq!(ppu.regs.status.bits, 0b10000000,
|
assert_eq!(ppu.regs.status.bits(), 0b10000000,
|
||||||
"register={:#010b}; should be 0b10000000", ppu.regs.status.bits);
|
"register={:#010b}; should be 0b10000000", ppu.regs.status.bits());
|
||||||
assert_eq!(ppu.regs.control.bits, 0b0000100,
|
assert_eq!(ppu.regs.ctrl.bits(), 0b0000100,
|
||||||
"register={:#010b}; should be 0b0000001", ppu.regs.control.bits);
|
"register={:#010b}; should be 0b0000001", ppu.regs.ctrl.bits());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user