aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.rs4
-rw-r--r--src/memory.rs8
-rw-r--r--src/mos6502.rs59
-rw-r--r--src/ppu.rs62
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 {
diff --git a/src/ppu.rs b/src/ppu.rs
index c0f5379..928fe7f 100644
--- a/src/ppu.rs
+++ b/src/ppu.rs
@@ -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 {