diff options
-rw-r--r-- | src/main.rs | 4 | ||||
-rw-r--r-- | src/memory.rs | 8 | ||||
-rw-r--r-- | src/mos6502.rs | 59 | ||||
-rw-r--r-- | src/ppu.rs | 62 |
4 files changed, 84 insertions, 49 deletions
diff --git a/src/main.rs b/src/main.rs index 2b66c55..45bece5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -274,13 +274,11 @@ fn main() { let mut cpu = mos6502::CPU::new(memory::CPUMemory::new(&mut ppu, &mapper, Some(&p1ctl), None)); let ptr = &mut cpu as *mut mos6502::CPU; cpu.mem.init(ptr); - loop { cpu.step(); while cpu.cycle > 0 { - if ppu.tick() || ppu.tick() || ppu.tick() { - cpu.trigger_nmi(); + cpu.trigger_delayed_nmi() } cpu.cycle -= 1; } diff --git a/src/memory.rs b/src/memory.rs index fc8732c..f8b6632 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -68,7 +68,13 @@ impl<'a> VMem for CPUMemory<'a> { unsafe{(*self.sram.get())[(addr & 0x07ff) as usize] = data;} } else if addr < 0x4000 { match addr & 0x7 { - 0x0 => ppu.write_ctl(data), + 0x0 => { + let old = ppu.get_flag_nmi(); + ppu.write_ctl(data); + if !old && ppu.try_nmi() && ppu.vblank { + cpu.trigger_delayed_nmi() + } /* toggle NMI flag can generate multiple ints */ + }, 0x1 => ppu.write_mask(data), 0x3 => ppu.write_oamaddr(data), 0x4 => ppu.write_oamdata(data), diff --git a/src/mos6502.rs b/src/mos6502.rs index e95910f..9dd3d85 100644 --- a/src/mos6502.rs +++ b/src/mos6502.rs @@ -677,7 +677,8 @@ mod addr { enum IntType { NMI, - IRQ + IRQ, + DelayedNMI } pub struct CPU<'a> { @@ -746,35 +747,34 @@ impl<'a> CPU<'a> { //let cycle0 = self.cycle; if self.int.is_some() { match self.int { - Some(IntType::NMI) => self.nmi(), - Some(IntType::IRQ) => self.irq(), + Some(IntType::NMI) => {self.nmi(); self.int = None; return}, + Some(IntType::IRQ) => {self.irq(); self.int = None; return}, + Some(IntType::DelayedNMI) => self.trigger_nmi(), _ => () } - self.int = None; - } else { - let pc = self.pc; - let opcode = self.mem.read(pc) as usize; - /* - let len = INST_LENGTH[opcode]; - let mut code = vec![0; len as usize]; - for i in 0..len as u16 { - code[i as usize] = self.mem.read(pc + i); - } - println!("0x{:04x} {} a:{:02x} x:{:02x} y:{:02x} s: {:02x} sp: {:02x}", - pc, disasm::parse(opcode as u8, &code[1..]), - self.a, self.x, self.y, self.status, self.sp); - */ - /* update opr pointing to operands of current inst */ - self.opr = pc.wrapping_add(1); - /* update program counter pointing to next inst */ - self.pc = pc.wrapping_add(INST_LENGTH[opcode] as u16); - /* get effective address based on addressing mode */ - self.acc = false; - let e = addr::ADDR_MODES[opcode](self) * INST_EXTRA_CYCLE[opcode]; - /* execute the inst */ - ops::OPS[opcode](self); - self.cycle += (INST_CYCLE[opcode] + e) as u32; } + let pc = self.pc; + let opcode = self.mem.read(pc) as usize; + /* + let len = INST_LENGTH[opcode]; + let mut code = vec![0; len as usize]; + for i in 0..len as u16 { + code[i as usize] = self.mem.read(pc + i); + } + println!("0x{:04x} {} a:{:02x} x:{:02x} y:{:02x} s: {:02x} sp: {:02x}", + pc, disasm::parse(opcode as u8, &code[1..]), + self.a, self.x, self.y, self.status, self.sp); + */ + /* update opr pointing to operands of current inst */ + self.opr = pc.wrapping_add(1); + /* update program counter pointing to next inst */ + self.pc = pc.wrapping_add(INST_LENGTH[opcode] as u16); + /* get effective address based on addressing mode */ + self.acc = false; + let e = addr::ADDR_MODES[opcode](self) * INST_EXTRA_CYCLE[opcode]; + /* execute the inst */ + ops::OPS[opcode](self); + self.cycle += (INST_CYCLE[opcode] + e) as u32; //(self.cycle - cycle0) as u8 } @@ -795,6 +795,11 @@ impl<'a> CPU<'a> { } #[inline(always)] + pub fn trigger_delayed_nmi(&mut self) { + self.int = Some(IntType::DelayedNMI); + } + + #[inline(always)] pub fn trigger_irq(&mut self) { if self.get_int() == 0 { self.int = Some(match self.int { @@ -19,7 +19,7 @@ struct Sprite { } pub struct PPU<'a> { - scanline: u16, + pub scanline: u16, /* registers */ ppuctl: u8, ppumask: u8, @@ -33,7 +33,7 @@ pub struct PPU<'a> { t: u16, /* temporary vram addr */ w: bool, /* first/second write toggle */ f: bool, /* if it is an odd frame */ - cycle: u16, /* cycle in the current scanline */ + pub cycle: u16, /* cycle in the current scanline */ /* rendering regs & latches */ /* background register (current two tiles) */ bg_pixel: u64, @@ -48,9 +48,9 @@ pub struct PPU<'a> { sp_pixel: [u32; 8], sp_idx: [usize; 8], sp_cnt: [u8; 8], - rendering: bool, + pub vblank: bool, buffered_read: u8, - early_read: bool, + early_read: Option<bool>, /* IO */ mem: PPUMemory<'a>, scr: &'a Screen, @@ -75,8 +75,12 @@ impl<'a> PPU<'a> { let res = (self.ppustatus & !0x1fu8) | (self.reg & 0x1f); self.ppustatus &= !PPU::FLAG_VBLANK; self.w = false; - if self.scanline == 241 && self.cycle == 0 { - self.early_read = true; + if self.scanline == 241 { + match self.cycle { + 1 => self.early_read = Some(true), /* read before cycle 1 */ + 2 | 3 => self.early_read = Some(false), /* read on cycle 1 and 2 */ + _ => () + } } res } @@ -185,7 +189,7 @@ impl<'a> PPU<'a> { } #[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)] pub 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} @@ -445,8 +449,8 @@ impl<'a> PPU<'a> { let ppustatus = 0xa0; let oamaddr = 0x00; let buffered_read = 0x00; - let cycle = 340; - let scanline = 240; + let cycle = 0; + let scanline = 261; PPU { scanline, ppuctl, @@ -463,9 +467,9 @@ impl<'a> PPU<'a> { sp_idx: [0x100; 8], sp_pixel: [0; 8], sp_cnt: [0; 8], - rendering: false, + vblank: false, buffered_read, - early_read: false, + early_read: None, mem, scr } } @@ -480,6 +484,11 @@ impl<'a> PPU<'a> { self.scanline = 240; } + #[inline(always)] + pub fn try_nmi(&mut self) -> bool { + self.get_flag_vblank() && self.get_flag_nmi() + } + pub fn tick(&mut self) -> bool { let cycle = self.cycle; if cycle == 0 { @@ -525,6 +534,8 @@ impl<'a> PPU<'a> { * scanline */ self.reset_cx(); self.fetch_sprite(); + self.cycle = 258; + return false } if pre_line && cycle == 339 && self.f { self.scanline = 0; @@ -535,21 +546,36 @@ impl<'a> PPU<'a> { } } else { if !rendering { self.bg_pixel = 0 } - if self.scanline == 241 && cycle == 1 { - if !self.early_read { - self.ppustatus |= PPU::FLAG_VBLANK; + if self.scanline == 241 { + match cycle { + 1 => { + match self.early_read { + Some(true) => (), + _ => self.ppustatus |= PPU::FLAG_VBLANK + } + self.vblank = true; + self.scr.render(); + self.cycle = 2; + return false + }, + 3 => { + let b = self.early_read.is_none(); + self.early_read = None; + self.cycle = 4; + return b && self.try_nmi() + }, + _ => () } - self.scr.render(); - self.cycle = 2; - self.early_read = false; - return !self.early_read && self.get_flag_nmi(); /* trigger cpu's NMI */ } } if pre_line && cycle == 1 { /* clear vblank, sprite zero hit & overflow */ + self.vblank = false; self.ppustatus &= !(PPU::FLAG_VBLANK | PPU::FLAG_SPRITE_ZERO | PPU::FLAG_OVERFLOW); self.bg_pixel = 0; + self.cycle = 2; + return false } self.cycle += 1; if self.cycle > 340 { |