From 0edff96acccceb102fd97a9200f5504200d5fa50 Mon Sep 17 00:00:00 2001 From: Determinant Date: Sat, 11 Nov 2017 20:05:49 -0500 Subject: ... --- src/cartridge.rs | 40 ++++++++-- src/main.rs | 32 ++++---- src/mapper.rs | 23 +++--- src/memory.rs | 139 ++++++++++++++++++++++++++++---- src/mos6502.rs | 23 ++++-- src/ppu.rs | 240 +++++++++++++++++++++++++++++++++++++++++++++++++------ 6 files changed, 418 insertions(+), 79 deletions(-) diff --git a/src/cartridge.rs b/src/cartridge.rs index 28928c0..f96c234 100644 --- a/src/cartridge.rs +++ b/src/cartridge.rs @@ -1,10 +1,38 @@ +use core::cell::RefCell; + +#[derive(Copy, Clone)] +pub enum MirrorType { + Horizontal = 0, + Vertical = 1, + Single0 = 2, + Single1 = 3, + Four = 4 +} + pub enum BankType { - PRG_ROM, /* program rom */ - CHR_ROM, /* pattern rom */ - SRAM, /* save ram */ + PrgRom, /* program rom */ + ChrRom, /* pattern rom */ + Sram, /* save ram */ +} + +pub struct Cartridge { + chr_rom: [u8; 8192], + prg_rom: RefCell<[u8; 8192]>, + sram: [u8; 8192], + pub mirror_type: MirrorType } -pub trait Cartridge { - fn get_bank_num(&self, kind: BankType) -> usize; - fn get_bank(&self, idx: usize, kind: BankType) -> *mut [u8]; +impl Cartridge { + pub fn get_bank_num(&self, kind: BankType) -> usize {0} + pub fn get_bank(&self, idx: usize, kind: BankType) -> *mut [u8] { + &mut *self.prg_rom.borrow_mut() + } + pub fn new() -> Self { + Cartridge { + chr_rom: [0; 8192], + prg_rom: RefCell::new([0; 8192]), + sram: [0; 8192], + mirror_type: MirrorType::Horizontal + } + } } diff --git a/src/main.rs b/src/main.rs index 4a963b6..447fa85 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,20 +4,24 @@ mod mos6502; mod ppu; mod cartridge; mod mapper; -fn main() { - /* - let code = [0xa9, 0x01, 0x8d, 0x00, 0x02, 0xa9, 0x05, 0x8d, 0x01, 0x02, 0xa9, 0x08, 0x8d, 0x02, 0x02 ]; - let code2 = [0xa9, 0x03, 0x4c, 0x08, 0x06, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x02 ]; - let dasm = mos6502::disasm::Disassembler::new(code2.iter()); - for l in dasm { - println!("{}", l); +use core::ptr::null_mut; + +struct Window { +} + +impl ppu::Screen for Window { + fn put(&mut self, x: u8, y: u8, color: u8) { + println!("put color 0x{:02x} at ({}, {})", color, x, y); + } + fn render(&self) { + println!("a frame has been redrawn"); } - let a = 0x03; - let b = 0x4c; - let c = 0x08; - let d = 0x06; - println!("{}", disasm::parse(code2[0], &[a, b, c, d])); - */ - let mut mem = memory::CPUMemory::new(); +} + +fn main() { + //let mut ppu = ppu::PPU::new( + let mut cart = cartridge::Cartridge::new(); + let mut mapper = mapper::Mapper2::new(&mut cart); + let mut mem = memory::CPUMemory::new(null_mut(), null_mut(), &mut mapper); let cpu = mos6502::CPU::new(&mut mem); } diff --git a/src/mapper.rs b/src/mapper.rs index d83157a..7522454 100644 --- a/src/mapper.rs +++ b/src/mapper.rs @@ -1,7 +1,8 @@ +#![allow(dead_code)] use memory::VMem; use cartridge::{Cartridge, BankType}; -pub struct Mapper2<'a, T: 'a> where T: Cartridge { - cart: &'a T, +pub struct Mapper2<'a> { + cart: &'a Cartridge, prg_bank1: &'a [u8], prg_bank2: &'a [u8], chr_bank: &'a mut [u8], @@ -9,7 +10,7 @@ pub struct Mapper2<'a, T: 'a> where T: Cartridge { bank_num: usize } -impl<'a, T> VMem for Mapper2<'a, T> where T: Cartridge { +impl<'a> VMem for Mapper2<'a> { fn read(&self, addr: u16) -> u8 { let addr = addr as usize; if addr < 0x2000 { @@ -31,7 +32,7 @@ impl<'a, T> VMem for Mapper2<'a, T> where T: Cartridge { self.chr_bank[addr] = data; } else if addr >= 0x8000 { self.prg_bank1 = unsafe {&*self.cart.get_bank( - data as usize % self.bank_num, BankType::PRG_ROM)}; + data as usize % self.bank_num, BankType::PrgRom)}; } else if addr >= 0x6000 { self.sram[addr - 0x6000] = data; } else { @@ -40,15 +41,15 @@ impl<'a, T> VMem for Mapper2<'a, T> where T: Cartridge { } } -impl<'a, T> Mapper2<'a, T> where T: Cartridge { - fn new(cart: &'a mut T) -> Self { - let bank_num = cart.get_bank_num(BankType::PRG_ROM); +impl<'a> Mapper2<'a> { + pub fn new(cart: &'a mut Cartridge) -> Self { + let bank_num = cart.get_bank_num(BankType::PrgRom); unsafe { Mapper2{cart, - prg_bank1: &*cart.get_bank(0, BankType::PRG_ROM), - prg_bank2: &*cart.get_bank(bank_num - 1, BankType::PRG_ROM), - chr_bank: &mut *cart.get_bank(0, BankType::CHR_ROM), - sram: &mut *cart.get_bank(0, BankType::SRAM), + prg_bank1: &*cart.get_bank(0, BankType::PrgRom), + prg_bank2: &*cart.get_bank(bank_num - 1, BankType::PrgRom), + chr_bank: &mut *cart.get_bank(0, BankType::ChrRom), + sram: &mut *cart.get_bank(0, BankType::Sram), bank_num} } } diff --git a/src/memory.rs b/src/memory.rs index 852b837..f506a86 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,39 +1,146 @@ +use ppu::PPU; +use mos6502::CPU; +use cartridge::{MirrorType, Cartridge}; +use core::cell::{RefCell, RefMut}; + pub trait VMem { fn read(&self, addr: u16) -> u8; fn write(&mut self, addr: u16, data: u8); } -pub struct CPUMemory { - internal: [u8; 2048] +pub struct CPUMemory<'a> { + internal: [u8; 2048], + ppu: *mut PPU<'a>, + cpu: *mut CPU<'a>, + mapper: &'a mut VMem +} + +impl<'a> CPUMemory<'a> { + pub fn new(cpu: *mut CPU<'a>, + ppu: *mut PPU<'a>, + mapper: &'a mut VMem) -> Self { + CPUMemory{internal: [0; 2048], + cpu, ppu, + mapper} + } +} + +impl<'a> VMem for CPUMemory<'a> { + fn read(&self, addr: u16) -> u8 { + match addr { + _ => if addr < 0x2000 { + self.internal[(addr & 0x07ff) as usize] + } else if addr < 0x4000 { + let ppu = unsafe {&mut *self.ppu}; + match addr & 0x7 { + 0x2 => ppu.read_status(), + 0x4 => ppu.read_oamdata(), + 0x7 => ppu.read_data(), + _ => panic!("invalid ppu reg read access at 0x{:04x}", addr) + } + } else if addr < 0x4020 { + println!("feeding dummy data for 0x{:04x}", addr); + 0 + } else { + self.mapper.read(addr) + } + } + } + fn write(&mut self, addr: u16, data: u8) { + let ppu = unsafe {&mut *self.ppu}; + let cpu = unsafe {&mut *self.cpu}; + if addr < 0x2000 { + self.internal[(addr & 0x07ff) as usize] = data; + } else if addr < 0x4000 { + match addr & 0x7 { + 0x0 => ppu.write_ctl(data), + 0x1 => ppu.write_mask(data), + 0x3 => ppu.write_oamaddr(data), + 0x4 => ppu.write_oamdata(data), + 0x5 => ppu.write_scroll(data), + 0x6 => ppu.write_addr(data), + 0x7 => ppu.write_data(data), + _ => panic!("invalid ppu reg write access at 0x{:04x}", addr) + } + } else if addr < 0x4020 { + match addr { + 0x4014 => ppu.write_oamdma(data, cpu), + _ => println!("ignore writing for 0x{:04x}", addr) + } + } else { + self.mapper.write(addr, data) + } + } +} + +pub struct PPUMemory<'a> { + pattern_table: [u8; 0x2000], + nametable: [u8; 0x1000], + palette: [u8; 0x20], + cart: &'a Cartridge, + mapper: &'a mut VMem, } -impl CPUMemory { - pub fn new() -> Self { - CPUMemory{internal: [0; 2048]} +impl<'a> PPUMemory<'a> { + pub fn new(mapper: &'a mut VMem, + cart: &'a Cartridge) -> Self { + PPUMemory{ + pattern_table: [0; 0x2000], + nametable: [0; 0x1000], + palette: [0; 0x20], + cart, + mapper} } } -impl<'a> VMem for CPUMemory { +const MIRROR_IDX: [[u8; 4]; 5] = [ + [0, 0, 1, 1], + [0, 1, 0, 1], + [0, 0, 0, 0], + [1, 1, 1, 1], + [0, 1, 2, 3], +]; + +fn get_mirror_addr(kind: MirrorType, mut addr: u16) -> u16 { + addr = (addr - 0x2000) & 0x0fff; + let table = addr >> 10; + let offset = addr & 0x03ff; + 0x2000 + ((MIRROR_IDX[kind as usize][table as usize] as u16) << 10) + offset +} + +fn mirror_palette(addr: u16) -> u16 { + if addr >= 0x10 && addr & 3 == 0 { + addr - 0x10 + } else { addr } +} + +impl<'a> VMem for PPUMemory<'a> { fn read(&self, addr: u16) -> u8 { if addr < 0x2000 { - self.internal[(addr & 0x07ff) as usize] + //self.pattern_table[addr as usize] + self.mapper.read(addr) + } else if addr < 0x3f00 { + let kind = self.cart.mirror_type; + self.nametable[(get_mirror_addr(kind, addr) & 0x07ff) as usize] } else if addr < 0x4000 { - match addr & 0x7 { - _ => 0 - } + self.palette[mirror_palette(addr & 0x1f) as usize] } else { - panic!("invalid memory read access at 0x{:04x}", addr) + panic!("invalid ppu read access at 0x{:04x}", addr) } } + fn write(&mut self, addr: u16, data: u8) { if addr < 0x2000 { - self.internal[(addr & 0x07ff) as usize] = data; + //self.pattern_table[addr as usize] = data + self.mapper.write(addr, data) + } else if addr < 0x3f00 { + let kind = self.cart.mirror_type; + self.nametable[(get_mirror_addr(kind, addr) & 0x07ff) as usize] = data } else if addr < 0x4000 { - match addr & 0x7 { - _ => () - } + self.palette[mirror_palette(addr & 0x1f) as usize] = data } else { - panic!("invalid memory write access at 0x{:04x}", addr) + panic!("invalid ppu read access at 0x{:04x}", addr) } } + } diff --git a/src/mos6502.rs b/src/mos6502.rs index 7af2d33..58ac217 100644 --- a/src/mos6502.rs +++ b/src/mos6502.rs @@ -701,9 +701,9 @@ pub struct CPU<'a> { opr: u16, ea: u16, /* effective address */ imm_val: u8, - cycle: u32, + pub cycle: u32, int: Option, - mem: &'a mut VMem + pub mem: &'a mut VMem } macro_rules! make_int { @@ -729,9 +729,18 @@ impl<'a> CPU<'a> { #[inline(always)] pub fn get_neg(&self) -> u8 { (self.status >> 7) & 1 } pub fn new(mem: &'a mut VMem) -> Self { - CPU{a: 0, x: 0, y: 0, - pc: 0, sp: 0, status: 0, - opr: 0, ea: 0, cycle: 0, imm_val: 0, + let pc = read16!(mem, RESET_VECTOR as u16); + /* nes power up state */ + let a = 0; + let x = 0; + let y = 0; + let sp = 0xfd; + let status = 0x34; + let cycle = 0; + + CPU{a, x, y, + pc, sp, status, cycle, + opr: 0, ea: 0, imm_val: 0, int: None, addr_mode: AddrMode::EffAddr, mem} @@ -773,10 +782,10 @@ impl<'a> CPU<'a> { pub fn reset(&mut self) { self.pc = read16!(self.mem, RESET_VECTOR as u16); - /* nes power up state */ - self.sp.wrapping_sub(3); + self.sp = self.sp.wrapping_sub(3); self.status |= INT_FLAG; self.cycle = 0; + self.int = None; } pub fn trigger_nmi(&mut self) { diff --git a/src/ppu.rs b/src/ppu.rs index fc1c09a..6aea1fc 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -1,4 +1,6 @@ +#![allow(dead_code)] use memory::VMem; +use mos6502::CPU; use core::intrinsics::transmute; pub trait Screen { @@ -15,7 +17,7 @@ struct Sprite { x: u8 } -struct PPU<'a, 'b> { +pub struct PPU<'a> { /* internal srams */ nametable_ram: [u8; 2048], palette_ram: [u8; 32], @@ -24,12 +26,14 @@ struct PPU<'a, 'b> { ppuctl: u8, ppumask: u8, ppustatus: u8, + oamaddr: u8, + + reg: u8, x: u8, /* fine x scroll */ v: u16, /* current vram addr */ t: u16, /* temporary vram addr */ w: bool, /* first/second write toggle */ - vblank: bool, cycle: u16, /* cycle in the current scanline */ /* rendering regs & latches */ /* background registers */ @@ -46,13 +50,130 @@ struct PPU<'a, 'b> { sp_bitmap: [[u8; 2]; 8], sp_cnt: [u8; 8], sp_zero_insight: bool, + rendering: bool, + buffered_read: u8, /* IO */ mem: &'a mut VMem, - scr: &'b mut Screen + scr: &'a mut Screen, } -impl<'a, 'b> PPU<'a, 'b> { +impl<'a> PPU<'a> { + pub fn write_ctl(&mut self, data: u8) { + self.ppuctl = data; + self.t = (self.t & 0x73ff) | ((data as u16 & 3) << 10); + } + + pub fn write_mask(&mut self, data: u8) { + self.ppumask = data; + } + + pub fn read_status(&mut self) -> u8 { + let res = (self.ppustatus & !0x1fu8) | (self.reg & 0x1f); + self.ppustatus &= !PPU::FLAG_VBLANK; + self.w = false; + res + } + + pub fn write_oamaddr(&mut self, data: u8) { + self.oamaddr = data; + } + + pub fn write_oamdata(&mut self, data: u8) { + if self.rendering { return } + unsafe { + let oam_raw = &mut transmute::<[Sprite; 64], [u8; 256]>(self.oam); + oam_raw[self.oamaddr as usize] = data; + self.oamaddr = self.oamaddr.wrapping_add(1); + } + } + + pub fn read_oamdata(&self) -> u8 { + unsafe { + let oam_raw = &transmute::<[Sprite; 64], [u8; 256]>(self.oam); + oam_raw[self.oamaddr as usize] + } + } + + pub fn write_scroll(&mut self, data: u8) { + let data = data as u16; + match self.w { + false => { + self.t = (self.t & 0x7fe0) | (data >> 3); + self.x = (data & 0x07) as u8; + self.w = true; + }, + true => { + self.t = (self.t & 0x0c1f) | ((data & 0xf8) << 2) | ((data & 0x07) << 12); + self.w = false; + } + } + } + + pub fn write_addr(&mut self, data: u8) { + let data = data as u16; + match self.w { + false => { + self.t = (self.t & 0x00ff) | ((data & 0x3f) << 8); + self.w = true; + }, + true => { + self.t = (self.t & 0xff00) | data; + self.v = self.t; + self.w = false; + } + } + } + + pub fn read_data(&mut self) -> u8 { + let data = self.mem.read(self.v); + let res = if self.v < 0x3f00 { + let prev = self.buffered_read; + self.buffered_read = data; + prev + } else { + self.buffered_read = self.mem.read(self.v - 0x1000); + data + }; + self.v = self.v.wrapping_add(match self.get_vram_inc() { + 0 => 1, + _ => 32 + }); + res + } + + pub fn write_data(&mut self, data: u8) { + self.mem.write(self.v, data); + self.v = self.v.wrapping_add(match self.get_vram_inc() { + 0 => 1, + _ => 32 + }); + } + + pub fn write_oamdma(&mut self, data: u8, cpu: &mut CPU) { + let mut addr = (data as u16) << 8; + unsafe { + let oam_raw = &mut transmute::<[Sprite; 64], [u8; 256]>(self.oam); + for _ in 0..0x100 { + oam_raw[self.oamaddr as usize] = cpu.mem.read(addr); + addr = addr.wrapping_add(1); + self.oamaddr = self.oamaddr.wrapping_add(1); + } + } + cpu.cycle += 1; + cpu.cycle += cpu.cycle & 1; + cpu.cycle += 512; + } + #[inline(always)] fn get_spritesize(&self) -> u8 {(self.ppuctl >> 5) & 1} + #[inline(always)] fn get_flag_nmi(&self) -> bool { (self.ppuctl >> 7) == 1 } + #[inline(always)] fn get_vram_inc(&self) -> u8 { (self.ppuctl >> 2) & 1} + #[inline(always)] fn get_show_leftmost_bg(&self) -> bool { (self.ppumask >> 1) & 1 == 1} + #[inline(always)] fn get_show_leftmost_sp(&self) -> bool { (self.ppumask >> 2) & 1 == 1} + #[inline(always)] fn get_show_bg(&self) -> bool { (self.ppumask >> 3) & 1 == 1} + #[inline(always)] fn get_show_sp(&self) -> bool { (self.ppumask >> 4) & 1 == 1} + const FLAG_OVERFLOW: u8 = 1 << 5; + const FLAG_SPRITE_ZERO: u8 = 1 << 6; + const FLAG_VBLANK: u8 = 1 << 7; #[inline(always)] fn fetch_nametable_byte(&mut self) { @@ -88,6 +209,7 @@ impl<'a, 'b> PPU<'a, 'b> { (self.v >> 12) | 0x8); } + #[inline(always)] fn load_bgtile(&mut self) { /* load the tile bitmap to high 8 bits of bitmap, * assume the high 8 bits are zeros */ @@ -121,6 +243,7 @@ impl<'a, 'b> PPU<'a, 'b> { self.bg_palette[1] >>= d; } + #[inline(always)] fn wrapping_inc_cx(&mut self) { match self.v & 0x001f { 31 => { @@ -131,6 +254,7 @@ impl<'a, 'b> PPU<'a, 'b> { } } + #[inline(always)] fn wrapping_inc_y(&mut self) { match (self.v & 0x7000) == 0x7000 { false => self.v += 0x1000, /* fine y < 7 */ @@ -156,6 +280,7 @@ impl<'a, 'b> PPU<'a, 'b> { self.v = (self.v & !0x7be0u16) | (self.t & 0x7be0); } + #[inline(always)] fn clear_sprite(&mut self) { self.oam2 = [Sprite{y: 0xff, tile: 0xff, attr: 0xff, x: 0xff}; 8]; } @@ -185,11 +310,11 @@ impl<'a, 'b> PPU<'a, 'b> { } let mut m = 0; unsafe { - let oam_raw = transmute::<[Sprite; 64], [[u8; 4]; 64]>(self.oam); + let oam_raw = &transmute::<[Sprite; 64], [[u8; 4]; 64]>(self.oam); while n < 64 { let y = oam_raw[n][m] as u16; if y <= self.scanline && self.scanline < y + h { - self.ppustatus |= 1 << 5; /* set overflow */ + self.ppustatus |= PPU::FLAG_OVERFLOW; /* set overflow */ } else { m = (m + 1) & 3; /* emulates hardware bug */ } @@ -198,6 +323,7 @@ impl<'a, 'b> PPU<'a, 'b> { } } + #[inline(always)] fn reverse_byte(mut x: u8) -> u8 { x = ((x & 0xaa) >> 1) | ((x & 0x55) << 1); x = ((x & 0xcc) >> 2) | ((x & 0x33) << 2); @@ -234,23 +360,42 @@ impl<'a, 'b> PPU<'a, 'b> { } } + #[inline(always)] + fn get_bg_pidx(&self) -> u8 { + if self.get_show_bg() { + ((self.bg_bitmap[1] & 1) << 1) as u8 | (self.bg_bitmap[0] & 1) as u8 + } else { 0 } + } + + #[inline(always)] + fn get_sp_pidx(&self, i: usize) -> u8 { + if self.get_show_sp() { + ((self.sp_bitmap[i][1] & 1) << 1) | (self.sp_bitmap[i][0] & 1) + } else { 0 } + } + fn render_pixel(&mut self) { - let bg_pidx = ((self.bg_bitmap[1] & 1) << 1) | (self.bg_bitmap[0] & 1); + let x = self.cycle - 1; + let bg_pidx = + if x >= 8 || self.get_show_leftmost_bg() {self.get_bg_pidx()} + else {0}; let mut sp_pidx = 0x0; let mut sp_idx = 0; let mut pri = 0x1; - for i in 0..8 { - if self.sp_cnt[i] != 0 { continue; } /* not active */ - match ((self.sp_bitmap[i][1] & 1) << 1) | (self.sp_bitmap[i][0] & 1) { - 0x0 => (), - pidx => { - if self.sp_zero_insight && bg_pidx != 0 && i == 0 { - self.ppustatus |= 1 << 6; /* set sprite zero hit */ + if x >= 8 || self.get_show_leftmost_sp() { + for i in 0..8 { + if self.sp_cnt[i] != 0 { continue; } /* not active */ + match self.get_sp_pidx(i) { + 0x0 => (), + pidx => { + if self.sp_zero_insight && bg_pidx != 0 && i == 0 { + self.ppustatus |= PPU::FLAG_SPRITE_ZERO; /* set sprite zero hit */ + } + sp_pidx = pidx; + sp_idx = i; + pri = (self.oam2[i].attr >> 5) & 1; + break; } - sp_pidx = pidx; - sp_idx = i; - pri = (self.oam2[i].attr >> 5) & 1; - break; } } } @@ -270,6 +415,50 @@ impl<'a, 'b> PPU<'a, 'b> { }); } + pub fn new(mem: &'a mut VMem, scr: &'a mut Screen) -> Self { + let ppuctl = 0x00; + let ppumask = 0x00; + let ppustatus = 0xa0; + let oamaddr = 0x00; + let w = false; + let buffered_read = 0x00; + let cycle = 370; + let scanline = 240; + PPU { + nametable_ram: [0; 2048], + palette_ram: [0; 32], + scanline, + ppuctl, + ppumask, + ppustatus, + oamaddr, + reg: 0, + x: 0, v: 0, t: 0, w, cycle, + bg_bitmap: [0; 2], + bg_palette: [0; 2], + bg_nt: 0, bg_attr: 0, + bg_bit_low: 0, bg_bit_high: 0, + oam: [Sprite{y: 0, tile: 0, attr: 0, x: 0}; 64], + oam2: [Sprite{y: 0, tile: 0, attr: 0, x: 0}; 8], + sp_bitmap: [[0; 2]; 8], + sp_cnt: [0; 8], + sp_zero_insight: false, + rendering: false, + buffered_read, + mem, scr + } + } + + pub fn reset(&mut self) { + self.ppuctl = 0x00; + self.ppumask = 0x00; + self.ppustatus = self.ppustatus & 0x80; + self.w = false; + self.buffered_read = 0x00; + self.cycle = 370; + self.scanline = 240; + } + pub fn tick(&mut self) -> bool { let cycle = self.cycle; if cycle == 0 { @@ -278,17 +467,17 @@ impl<'a, 'b> PPU<'a, 'b> { } let visible = self.scanline < 240; let pre_render = self.scanline == 261; - let fill = pre_render || visible; + self.rendering = pre_render || visible; if pre_render { if cycle == 1 { - self.vblank = false; - /* clear sprite zero hit & overflow */ - self.ppustatus &= !((1 << 6) | (1 << 5)); + /* clear vblank, sprite zero hit & overflow */ + self.ppustatus &= !(PPU::FLAG_VBLANK | + PPU::FLAG_SPRITE_ZERO | PPU::FLAG_OVERFLOW); } else if 279 < cycle && cycle < 305 { self.reset_y(); } } - if fill { + if self.rendering { let shifting = 0 < cycle && cycle < 257; /* 1..256 */ let fetch = shifting || (320 < cycle && cycle < 337); if fetch { /* 1..256 and 321..336 */ @@ -336,9 +525,10 @@ impl<'a, 'b> PPU<'a, 'b> { self.shift_sprites(); } } else if self.scanline == 241 && cycle == 1 { - self.vblank = true; + self.scr.render(); + self.ppustatus |= PPU::FLAG_VBLANK; self.cycle += 1; - return true /* trigger cpu's NMI */ + return self.get_flag_nmi(); /* trigger cpu's NMI */ } self.cycle += 1; if self.cycle > 340 { -- cgit v1.2.3