aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDeterminant <ted.sybil@gmail.com>2017-11-11 20:05:49 -0500
committerDeterminant <ted.sybil@gmail.com>2017-11-11 20:05:49 -0500
commit0edff96acccceb102fd97a9200f5504200d5fa50 (patch)
tree02216a47b2090175a48a8c207113adffb146ba3e
parenta600c658f7905e747bd9233b141de147972ef04c (diff)
...
-rw-r--r--src/cartridge.rs40
-rw-r--r--src/main.rs32
-rw-r--r--src/mapper.rs23
-rw-r--r--src/memory.rs139
-rw-r--r--src/mos6502.rs23
-rw-r--r--src/ppu.rs240
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<IntType>,
- 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 {