ppu can trigger NMIs
This commit is contained in:
@@ -11,7 +11,7 @@ Usage:
|
|||||||
```
|
```
|
||||||
Make sure to compile with `--release` for 60 fps.
|
Make sure to compile with `--release` for 60 fps.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### what works
|
### what works
|
||||||
* CPU
|
* CPU
|
||||||
|
|||||||
13
src/main.rs
13
src/main.rs
@@ -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),
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -221,7 +226,7 @@ impl PPU {
|
|||||||
// palette memory
|
// palette memory
|
||||||
let data = self.data_buffer;
|
let data = self.data_buffer;
|
||||||
self.data_buffer = mem.readb_ppu(addr);
|
self.data_buffer = mem.readb_ppu(addr);
|
||||||
|
|
||||||
if addr > 0x3F00 { // everything above 0x3F00 is palette
|
if addr > 0x3F00 { // everything above 0x3F00 is palette
|
||||||
self.data_buffer
|
self.data_buffer
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user