ppu can trigger NMIs

This commit is contained in:
Daniel Bauer
2020-01-20 10:20:18 +01:00
parent acd014991d
commit b1a996f009
5 changed files with 61 additions and 8 deletions

View File

@@ -11,7 +11,7 @@ Usage:
``` ```
Make sure to compile with `--release` for 60 fps. Make sure to compile with `--release` for 60 fps.
![Screenshot](https://i.imgur.com/1TMHZrs.png) ![Screenshot](https://i.imgur.com/4s4cDWHl.png)
### what works ### what works
* CPU * CPU

View File

@@ -110,7 +110,7 @@ fn main() -> Result<(), Error> {
}).collect(); }).collect();
// Main loop // Main loop
let mut run = true; let mut run = false;
while let Some(event) = events.next(&mut window) { while let Some(event) = events.next(&mut window) {
if let Some(_) = event.render_args() { if let Some(_) = event.render_args() {
// Run enough clocks to render the next frame // Run enough clocks to render the next frame
@@ -121,8 +121,8 @@ fn main() -> Result<(), Error> {
let mut ppu = nes.ppu.borrow_mut(); let mut ppu = nes.ppu.borrow_mut();
let ppu_bus = nes.ppu_bus.borrow(); let ppu_bus = nes.ppu_bus.borrow();
// main_texture.update(&mut texture_ctx, &ppu.canvas_main).unwrap(); // main_texture.update(&mut texture_ctx, &ppu.canvas_main).unwrap();
pattern_table_textures[0].update(&mut texture_ctx, &ppu.get_pattern_table(&*ppu_bus, 0, 1)).unwrap(); pattern_table_textures[0].update(&mut texture_ctx, &ppu.get_pattern_table(&*ppu_bus, 0, 0)).unwrap();
pattern_table_textures[1].update(&mut texture_ctx, &ppu.get_pattern_table(&*ppu_bus, 1, 1)).unwrap(); pattern_table_textures[1].update(&mut texture_ctx, &ppu.get_pattern_table(&*ppu_bus, 1, 0)).unwrap();
palette_textures[0].update(&mut texture_ctx, &ppu.get_palette(&*ppu_bus, 0)).unwrap(); palette_textures[0].update(&mut texture_ctx, &ppu.get_palette(&*ppu_bus, 0)).unwrap();
palette_textures[1].update(&mut texture_ctx, &ppu.get_palette(&*ppu_bus, 1)).unwrap(); palette_textures[1].update(&mut texture_ctx, &ppu.get_palette(&*ppu_bus, 1)).unwrap();
palette_textures[2].update(&mut texture_ctx, &ppu.get_palette(&*ppu_bus, 2)).unwrap(); palette_textures[2].update(&mut texture_ctx, &ppu.get_palette(&*ppu_bus, 2)).unwrap();
@@ -249,6 +249,13 @@ fn render_cpu(glyphs: &mut GlyphBrush<Resources, Factory>, cpu: &CPU, offset: [f
cpu.is_flag_set(Flags::CARRY) cpu.is_flag_set(Flags::CARRY)
], ],
offset); offset);
glyphs.queue(Section {
text: &format!("({:#04x})", cpu.regs.flags.bits()),
scale: *FT_SCALE,
screen_position: (offset[0]+200.0, offset[1]+FT_SIZE_PX) ,
color: FT_COLOR_WHITE,
..Section::default()
});
let cpu_register_texts = [ let cpu_register_texts = [
&format!("A: {0:#x} ({0})", cpu.regs.a), &format!("A: {0:#x} ({0})", cpu.regs.a),

View File

@@ -74,7 +74,13 @@ impl NES {
if self.clock_count % 3 == 0 { if self.clock_count % 3 == 0 {
self.cpu.clock(&mut self.bus); self.cpu.clock(&mut self.bus);
} }
self.ppu.borrow_mut().clock(&mut *self.ppu_bus.borrow_mut()); let mut ppu = self.ppu.borrow_mut();
ppu.clock(&mut *self.ppu_bus.borrow_mut());
if ppu.nmi {
ppu.nmi = false;
self.cpu.nmi(&mut self.bus);
debug!("NMI triggered by PPU.")
}
if self.clock_count % 100000 == 0 { if self.clock_count % 100000 == 0 {
info!("clock {}", self.clock_count); info!("clock {}", self.clock_count);
} }

View File

@@ -123,6 +123,41 @@ impl CPU {
self.curr_op = 0x00; self.curr_op = 0x00;
} }
fn interrupt<T: Memory>(&mut self, mem: &mut T, pc_addr: Addr) {
// push pc to stack
self.pushb_sp(mem, (self.regs.pc >> 8) as Byte);
self.pushb_sp(mem, self.regs.pc as Byte);
// push status reg with set interrupt flags to stack
self.set_flag(Flags::BREAK, false);
self.set_flag(Flags::UNUSED, true);
self.set_flag(Flags::IRQ, true);
self.pushb_sp(mem, self.regs.flags.bits());
// read new pc
let lo = mem.readb(pc_addr);
let hi = mem.readb(pc_addr + 1);
self.regs.pc = ((hi as Addr) << 8) | lo as Addr;
}
// Non maskable interrupt
pub fn nmi<T: Memory>(&mut self, mem: &mut T) {
let new_pc_addr = 0xFFFA;
self.interrupt(mem, new_pc_addr);
self.cycles_ahead = 8;
}
// Interrupt request: Same as nmi, but only takes place if interrupts are
// allowed
pub fn irq<T: Memory>(&mut self, mem: &mut T) {
if self.is_flag_set(Flags::IRQ) {
let new_pc_addr = 0xFFFE;
self.interrupt(mem, new_pc_addr);
self.cycles_ahead = 7;
};
}
// True if the operation is not finished yet // True if the operation is not finished yet
pub fn is_ahead(&self) -> bool { pub fn is_ahead(&self) -> bool {
return self.cycles_ahead > 0; return self.cycles_ahead > 0;

View File

@@ -98,10 +98,11 @@ pub struct PPU {
pub regs: Registers, pub regs: Registers,
pub cycle: u16, pub cycle: u16,
pub scanline: u16, pub scanline: u16,
pub frame_ready: bool,
pub nmi: bool,
pub canvas_main: Sprite, pub canvas_main: Sprite,
pub pattern_tables: [Sprite; 2], pub pattern_tables: [Sprite; 2],
pub palettes: [Sprite; 8], pub palettes: [Sprite; 8],
pub frame_ready: bool,
addr_latch_set: bool, addr_latch_set: bool,
data_buffer: Byte, data_buffer: Byte,
} }
@@ -112,6 +113,8 @@ impl PPU {
regs: Registers::new(), regs: Registers::new(),
cycle: 0, cycle: 0,
scanline: 0, scanline: 0,
frame_ready: false,
nmi: false,
canvas_main: ImageBuffer::from_pixel(256, 240, PALETTE[&0x00]), canvas_main: ImageBuffer::from_pixel(256, 240, PALETTE[&0x00]),
pattern_tables: [ pattern_tables: [
ImageBuffer::from_pixel(128, 128, PALETTE[&0x00]), ImageBuffer::from_pixel(128, 128, PALETTE[&0x00]),
@@ -127,7 +130,6 @@ impl PPU {
ImageBuffer::from_pixel(4, 1, PALETTE[&0x00]), ImageBuffer::from_pixel(4, 1, PALETTE[&0x00]),
ImageBuffer::from_pixel(4, 1, PALETTE[&0x00]), ImageBuffer::from_pixel(4, 1, PALETTE[&0x00]),
], ],
frame_ready: false,
addr_latch_set: false, addr_latch_set: false,
data_buffer: 0 data_buffer: 0
} }
@@ -190,6 +192,9 @@ impl PPU {
// set/clear vblank flag // set/clear vblank flag
if self.scanline == 241 && self.cycle == 1 { if self.scanline == 241 && self.cycle == 1 {
self.set_status(Status::VERTICAL_BLANK, true); self.set_status(Status::VERTICAL_BLANK, true);
if self.get_control(Control::ENABLE_NMI) {
self.nmi = true;
}
} else if self.scanline == 261 && self.cycle == 1 { } else if self.scanline == 261 && self.cycle == 1 {
self.set_status(Status::VERTICAL_BLANK, false); self.set_status(Status::VERTICAL_BLANK, false);