diff options
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/cartridge.rs | 37 | ||||
-rw-r--r-- | src/main.rs | 175 | ||||
-rw-r--r-- | src/mapper.rs | 55 | ||||
-rw-r--r-- | src/memory.rs | 86 | ||||
-rw-r--r-- | src/mos6502.rs | 75 | ||||
-rw-r--r-- | src/ppu.rs | 96 |
7 files changed, 358 insertions, 167 deletions
@@ -4,3 +4,4 @@ version = "0.1.0" authors = ["Determinant <[email protected]>"] [dependencies] +sdl2 = "0.31" diff --git a/src/cartridge.rs b/src/cartridge.rs index f96c234..b8d2b68 100644 --- a/src/cartridge.rs +++ b/src/cartridge.rs @@ -1,3 +1,4 @@ +#![allow(dead_code)] use core::cell::RefCell; #[derive(Copy, Clone)] @@ -16,23 +17,33 @@ pub enum BankType { } pub struct Cartridge { - chr_rom: [u8; 8192], - prg_rom: RefCell<[u8; 8192]>, - sram: [u8; 8192], + chr_rom: RefCell<Vec<u8>>, + prg_rom: RefCell<Vec<u8>>, + sram: RefCell<Vec<u8>>, pub mirror_type: MirrorType } 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 + pub fn get_size(&self, kind: BankType) -> usize { + match kind { + BankType::PrgRom => self.prg_rom.borrow().len(), + BankType::ChrRom => self.chr_rom.borrow().len(), + BankType::Sram => self.sram.borrow().len() } } + pub fn get_bank(&self, base: usize, size: usize, kind: BankType) -> *mut [u8] { + &mut (match kind { + BankType::PrgRom => self.prg_rom.borrow_mut(), + BankType::ChrRom => self.chr_rom.borrow_mut(), + BankType::Sram => self.sram.borrow_mut(), + })[base..base + size] + } + pub fn new(chr_rom: Vec<u8>, + prg_rom: Vec<u8>, + sram: Vec<u8>, + mirror_type: MirrorType) -> Self { + Cartridge{chr_rom: RefCell::new(chr_rom), + prg_rom: RefCell::new(prg_rom), + sram: RefCell::new(sram), mirror_type} + } } diff --git a/src/main.rs b/src/main.rs index 447fa85..cd39d39 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,24 +4,181 @@ mod mos6502; mod ppu; mod cartridge; mod mapper; -use core::ptr::null_mut; + +use std::fs::File; +use std::io::Read; +use core::cell::RefCell; +use core::intrinsics::transmute; +use cartridge::*; + + +extern crate sdl2; + +use sdl2::pixels::Color; +use sdl2::rect::Rect; struct Window { + buff: RefCell<[[u8; 256]; 240]> } 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 put(&self, x: u8, y: u8, color: u8) { + self.buff.borrow_mut()[y as usize][x as usize] = color; } fn render(&self) { - println!("a frame has been redrawn"); + println!("a frame has been redrawn:"); + for r in self.buff.borrow().iter() { + for c in r.iter() { + print!("{:02x}", c); + } + println!(""); + } + } +} + +struct SDLWindow { + canvas: RefCell<sdl2::render::WindowCanvas> +} + +impl SDLWindow { + fn new() -> Self { + let sdl_context = sdl2::init().unwrap(); + let video_subsystem = sdl_context.video().unwrap(); + let window = video_subsystem.window("rust-sdl2 demo: Video", 256 * PIXEL_SIZE, 240 * PIXEL_SIZE) + .position_centered() + .opengl() + .build() + .unwrap(); + let mut canvas = window.into_canvas().build().unwrap(); + canvas.set_draw_color(Color::RGB(255, 255, 255)); + canvas.clear(); + canvas.present(); + SDLWindow { + canvas: RefCell::new(canvas) + } } } +const PIXEL_SIZE: u32 = 2; +const COLORS: [u32; 64] = [ + 0x666666, 0x002A88, 0x1412A7, 0x3B00A4, 0x5C007E, 0x6E0040, 0x6C0600, 0x561D00, + 0x333500, 0x0B4800, 0x005200, 0x004F08, 0x00404D, 0x000000, 0x000000, 0x000000, + 0xADADAD, 0x155FD9, 0x4240FF, 0x7527FE, 0xA01ACC, 0xB71E7B, 0xB53120, 0x994E00, + 0x6B6D00, 0x388700, 0x0C9300, 0x008F32, 0x007C8D, 0x000000, 0x000000, 0x000000, + 0xFFFEFF, 0x64B0FF, 0x9290FF, 0xC676FF, 0xF36AFF, 0xFE6ECC, 0xFE8170, 0xEA9E22, + 0xBCBE00, 0x88D800, 0x5CE430, 0x45E082, 0x48CDDE, 0x4F4F4F, 0x000000, 0x000000, + 0xFFFEFF, 0xC0DFFF, 0xD3D2FF, 0xE8C8FF, 0xFBC2FF, 0xFEC4EA, 0xFECCC5, 0xF7D8A5, + 0xE4E594, 0xCFEF96, 0xBDF4AB, 0xB3F3CC, 0xB5EBF2, 0xB8B8B8, 0x000000, 0x000000, +]; + +fn get_rgb(color: u8) -> Color { + let c = COLORS[color as usize]; + Color::RGB((c >> 16) as u8, ((c >> 8) & 0xff) as u8, (c & 0xff) as u8) +} + +impl ppu::Screen for SDLWindow { + fn put(&self, x: u8, y: u8, color: u8) { + let mut canvas = self.canvas.borrow_mut(); + println!("put {} at {}, {}", color, x, y); + canvas.set_draw_color(get_rgb(color)); + canvas.draw_rect(Rect::new((x as u32 * PIXEL_SIZE) as i32, + (y as u32 * PIXEL_SIZE) as i32, + PIXEL_SIZE, PIXEL_SIZE)); + } + + fn render(&self) { + let mut canvas = self.canvas.borrow_mut(); + canvas.present(); + canvas.clear(); + } +} + +#[repr(C, packed)] +struct INesHeader { + magic: [u8; 4], + prg_rom_nbanks: u8, + chr_rom_nbanks: u8, + flags6: u8, + flags7: u8, + prg_ram_nbanks: u8, + flags9: u8, + flags10: u8, + padding: [u8; 5] +} + 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); + let fname = std::env::args().nth(1).unwrap(); + let mut file = File::open(fname).unwrap(); + let mut rheader = [0; 16]; + println!("read {}", file.read(&mut rheader[..]).unwrap()); + let header = unsafe{transmute::<[u8; 16], INesHeader>(rheader)}; + let mirror = match ((header.flags6 >> 2) & 2) | (header.flags6 & 1) { + 0 => MirrorType::Horizontal, + 1 => MirrorType::Vertical, + 2 => MirrorType::Single0, + 3 => MirrorType::Single1, + _ => MirrorType::Four, + }; + let mapper_id = (header.flags7 & 0xf0) | (header.flags6 >> 4); + println!("maigc:{} prg:{} chr:{} mirror:{} mapper:{}", + std::str::from_utf8(&header.magic).unwrap(), + header.prg_rom_nbanks, + header.chr_rom_nbanks, + mirror as u8, + mapper_id); + if header.flags6 & 0x04 == 0x04 { + let mut trainer: [u8; 512] = unsafe{std::mem::uninitialized()}; + file.read(&mut trainer[..]); + println!("skipping trainer"); + } + + let prg_len = header.prg_rom_nbanks as usize * 0x4000; + let mut chr_len = header.chr_rom_nbanks as usize * 0x2000; + if chr_len == 0 { + chr_len = 0x2000; + } + let mut prg_rom = Vec::<u8>::with_capacity(prg_len); + let mut chr_rom = Vec::<u8>::with_capacity(chr_len); + unsafe { + prg_rom.set_len(prg_len); + chr_rom.set_len(chr_len); + } + let sram = vec![0; 0x4000]; + println!("read prg {}", file.read(&mut prg_rom[..]).unwrap()); + for (i, v) in prg_rom.iter().enumerate() { + print!("{:02x} ", v); + if i & 15 == 15 { + println!(" {:04x}", i); + } + } + println!("read chr {}", file.read(&mut chr_rom[..]).unwrap()); + for (i, v) in chr_rom.iter().enumerate() { + print!("{:02x} ", v); + if i & 15 == 15 { + println!(""); + } + } + let cart = cartridge::Cartridge::new(chr_rom, prg_rom, sram, mirror); + //let win = Window {buff: RefCell::new([[0; 256]; 240])}; + let win = SDLWindow::new(); + let mapper = mapper::Mapper2::new(&cart); + let pmem = memory::PPUMemory::new(&mapper, &cart); + let mem = memory::CPUMemory::new(&mapper); + let mut ppu = ppu::PPU::new(&pmem, &win); + let mut cpu = mos6502::CPU::new(&mem); + mem.init(&mut cpu, &mut ppu); + + loop { + cpu.step(); + //println!("cpu at 0x{:04x}", cpu.get_pc()); + while cpu.cycle > 0 { + for _ in 0..3 { + if ppu.tick() { + println!("triggering nmi"); + cpu.trigger_nmi(); + } + } + cpu.cycle -= 1; + } + } } diff --git a/src/mapper.rs b/src/mapper.rs index 7522454..c54acb4 100644 --- a/src/mapper.rs +++ b/src/mapper.rs @@ -1,40 +1,44 @@ #![allow(dead_code)] use memory::VMem; use cartridge::{Cartridge, BankType}; +use core::cell::RefCell; + pub struct Mapper2<'a> { cart: &'a Cartridge, - prg_bank1: &'a [u8], - prg_bank2: &'a [u8], - chr_bank: &'a mut [u8], - sram: &'a mut [u8], - bank_num: usize + prg_bank1: RefCell<&'a [u8]>, + prg_bank2: RefCell<&'a [u8]>, + chr_bank: RefCell<&'a mut [u8]>, + sram: RefCell<&'a mut [u8]>, + prg_nbank: usize } impl<'a> VMem for Mapper2<'a> { fn read(&self, addr: u16) -> u8 { let addr = addr as usize; - if addr < 0x2000 { - self.chr_bank[addr] - } else if addr >= 0xc000 { - self.prg_bank2[addr - 0xc000] - } else if addr >= 0x8000 { - self.prg_bank1[addr - 0x8000] + if addr < 0x2000 { /* 0x2000 size bank */ + self.chr_bank.borrow()[addr] + } else if addr >= 0xc000 { /* 0x4000 size bank */ + self.prg_bank2.borrow()[addr - 0xc000] + } else if addr >= 0x8000 { /* 0x4000 size bank */ + self.prg_bank1.borrow()[addr - 0x8000] } else if addr >= 0x6000 { - self.sram[addr - 0x6000] + self.sram.borrow()[addr - 0x6000] } else { panic!("unmapped address: 0x{:04x}", addr) } } - fn write(&mut self, addr: u16, data: u8) { + fn write(&self, addr: u16, data: u8) { let addr = addr as usize; if addr < 0x2000 { - self.chr_bank[addr] = data; + self.chr_bank.borrow_mut()[addr] = data; } else if addr >= 0x8000 { - self.prg_bank1 = unsafe {&*self.cart.get_bank( - data as usize % self.bank_num, BankType::PrgRom)}; + (*self.prg_bank1.borrow_mut()) = unsafe { + &*self.cart.get_bank(((data as usize) % self.prg_nbank) << 14, + 0x4000, + BankType::PrgRom)}; } else if addr >= 0x6000 { - self.sram[addr - 0x6000] = data; + self.sram.borrow_mut()[addr - 0x6000] = data; } else { panic!("invalid write to address: 0x{:04x}", addr); } @@ -42,15 +46,16 @@ impl<'a> VMem for Mapper2<'a> { } impl<'a> Mapper2<'a> { - pub fn new(cart: &'a mut Cartridge) -> Self { - let bank_num = cart.get_bank_num(BankType::PrgRom); + pub fn new(cart: *const Cartridge) -> Self { unsafe { - Mapper2{cart, - 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} + let cart = &*cart; + let nbank = cart.get_size(BankType::PrgRom) >> 14; + Mapper2{cart: &cart, + prg_bank1: RefCell::new(&*cart.get_bank(0, 0x4000, BankType::PrgRom)), + prg_bank2: RefCell::new(&*cart.get_bank((nbank - 1) << 14, 0x4000, BankType::PrgRom)), + chr_bank: RefCell::new(&mut *cart.get_bank(0, 0x2000, BankType::ChrRom)), + sram: RefCell::new(&mut *cart.get_bank(0, 0x2000, BankType::Sram)), + prg_nbank: nbank} } } } diff --git a/src/memory.rs b/src/memory.rs index f506a86..9b5f322 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,44 +1,52 @@ +#![allow(dead_code)] use ppu::PPU; use mos6502::CPU; use cartridge::{MirrorType, Cartridge}; -use core::cell::{RefCell, RefMut}; +use core::cell::{RefCell, Cell}; +use core::ptr::null_mut; pub trait VMem { fn read(&self, addr: u16) -> u8; - fn write(&mut self, addr: u16, data: u8); + fn write(&self, addr: u16, data: u8); } pub struct CPUMemory<'a> { - internal: [u8; 2048], - ppu: *mut PPU<'a>, - cpu: *mut CPU<'a>, - mapper: &'a mut VMem + sram: RefCell<[u8; 2048]>, + ppu: Cell<*mut PPU<'a>>, + cpu: Cell<*mut CPU<'a>>, + mapper: &'a 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, + pub fn new(mapper: &'a VMem) -> Self { + CPUMemory{sram: RefCell::new([0; 2048]), + cpu: Cell::new(null_mut()), + ppu: Cell::new(null_mut()), mapper} } + + pub fn init(&self, + cpu: *mut CPU<'a>, + ppu: *mut PPU<'a>) { + self.cpu.set(cpu); + self.ppu.set(ppu); + } } impl<'a> VMem for CPUMemory<'a> { fn read(&self, addr: u16) -> u8 { match addr { _ => if addr < 0x2000 { - self.internal[(addr & 0x07ff) as usize] + self.sram.borrow()[(addr & 0x07ff) as usize] } else if addr < 0x4000 { - let ppu = unsafe {&mut *self.ppu}; + let ppu = unsafe {&mut *self.ppu.get()}; 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) + _ => 0 } - } else if addr < 0x4020 { + } else if addr < 0x6000 { println!("feeding dummy data for 0x{:04x}", addr); 0 } else { @@ -46,11 +54,11 @@ impl<'a> VMem for CPUMemory<'a> { } } } - fn write(&mut self, addr: u16, data: u8) { - let ppu = unsafe {&mut *self.ppu}; - let cpu = unsafe {&mut *self.cpu}; + fn write(&self, addr: u16, data: u8) { + let ppu = unsafe {&mut *self.ppu.get()}; + let cpu = unsafe {&mut *self.cpu.get()}; if addr < 0x2000 { - self.internal[(addr & 0x07ff) as usize] = data; + self.sram.borrow_mut()[(addr & 0x07ff) as usize] = data; } else if addr < 0x4000 { match addr & 0x7 { 0x0 => ppu.write_ctl(data), @@ -67,6 +75,7 @@ impl<'a> VMem for CPUMemory<'a> { 0x4014 => ppu.write_oamdma(data, cpu), _ => println!("ignore writing for 0x{:04x}", addr) } + } else if addr < 0x6000 { } else { self.mapper.write(addr, data) } @@ -74,20 +83,20 @@ impl<'a> VMem for CPUMemory<'a> { } pub struct PPUMemory<'a> { - pattern_table: [u8; 0x2000], - nametable: [u8; 0x1000], - palette: [u8; 0x20], + pattern_table: RefCell<[u8; 0x2000]>, + nametable: RefCell<[u8; 0x1000]>, + palette: RefCell<[u8; 0x20]>, cart: &'a Cartridge, - mapper: &'a mut VMem, + mapper: &'a VMem, } impl<'a> PPUMemory<'a> { - pub fn new(mapper: &'a mut VMem, + pub fn new(mapper: &'a VMem, cart: &'a Cartridge) -> Self { PPUMemory{ - pattern_table: [0; 0x2000], - nametable: [0; 0x1000], - palette: [0; 0x20], + pattern_table: RefCell::new([0; 0x2000]), + nametable: RefCell::new([0; 0x1000]), + palette: RefCell::new([0; 0x20]), cart, mapper} } @@ -115,31 +124,36 @@ fn mirror_palette(addr: u16) -> u16 { } impl<'a> VMem for PPUMemory<'a> { - fn read(&self, addr: u16) -> u8 { + fn read(&self, mut addr: u16) -> u8 { + addr &= 0x3fff; if addr < 0x2000 { - //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] + self.nametable.borrow() + [(get_mirror_addr(kind, addr) & 0x07ff) as usize] } else if addr < 0x4000 { - self.palette[mirror_palette(addr & 0x1f) as usize] + self.palette.borrow() + [mirror_palette(addr & 0x1f) as usize] } else { panic!("invalid ppu read access at 0x{:04x}", addr) } } - fn write(&mut self, addr: u16, data: u8) { + fn write(&self, mut addr: u16, data: u8) { + addr &= 0x3fff; + println!("writing 0x{:02x} to 0x{:04x}", data, addr); if addr < 0x2000 { - //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 + self.nametable.borrow_mut() + [(get_mirror_addr(kind, addr) & 0x07ff) as usize] = data } else if addr < 0x4000 { - self.palette[mirror_palette(addr & 0x1f) as usize] = data + self.palette.borrow_mut() + [mirror_palette(addr & 0x1f) as usize] = data } else { - panic!("invalid ppu read access at 0x{:04x}", addr) + panic!("invalid ppu write access at 0x{:04x}", addr) } } diff --git a/src/mos6502.rs b/src/mos6502.rs index 58ac217..c439700 100644 --- a/src/mos6502.rs +++ b/src/mos6502.rs @@ -132,7 +132,7 @@ pub mod disasm { format!("${:02x}, y", code.next().unwrap()) } fn rel(code: T) -> String { - let b = *code.next().unwrap() as i8; + let b = *code.next().unwrap() as i8 as i16; if b >= 0 { format!("+${:02x}, x", b) } else { @@ -229,10 +229,9 @@ mod ops { let opr1 = cpu.a as u16; let opr2 = match cpu.addr_mode { AddrMode::Immediate => cpu.imm_val, - AddrMode::Accumulator => cpu.a, - AddrMode::EffAddr => cpu.mem.read(cpu.ea) + _ => cpu.mem.read(cpu.ea) } as u16; - let res = opr1 + opr2 + cpu.get_carry() as u16; + let res = opr1 + opr2 + (cpu.get_carry() as u16); let mut status = cpu.status & !(CARRY_FLAG | ZERO_FLAG | OVER_FLAG | NEG_FLAG); cpu.a = res as u8; status |= (res > 0xff) as u8; /* carry flag */ @@ -248,10 +247,9 @@ mod ops { let opr1 = cpu.a as u16; let opr2 = match cpu.addr_mode { AddrMode::Immediate => cpu.imm_val, - AddrMode::Accumulator => cpu.a, - AddrMode::EffAddr => cpu.mem.read(cpu.ea) + _ => cpu.mem.read(cpu.ea) } as u16; - let res = opr1 + (0xff - opr2) + cpu.get_carry() as u16; + let res = opr1 + (0xff - opr2) + (cpu.get_carry() as u16); let mut status = cpu.status & !(CARRY_FLAG | ZERO_FLAG | OVER_FLAG | NEG_FLAG); cpu.a = res as u8; status |= (res > 0xff) as u8; /* carry flag */ @@ -269,9 +267,9 @@ mod ops { AddrMode::Immediate => cpu.imm_val, _ => cpu.mem.read(cpu.ea) } as u16; - let res = opr1 - opr2; + let res = opr1.wrapping_sub(opr2); let mut status = cpu.status & !(CARRY_FLAG | ZERO_FLAG | NEG_FLAG); - status |= (res < 0x100) as u8; /* carry flag */ + status |= (res < 0x100) as u8; /* if opr1 >= opr2 */ check_zero!(status, res); check_neg!(status, res); cpu.status = status; @@ -286,16 +284,16 @@ mod ops { macro_rules! make_delta { ($f: ident, $d: expr) => ( fn $f(cpu: &mut CPU) { - let res = cpu.mem.read(cpu.ea) as u16 + $d; + let res = cpu.mem.read(cpu.ea).wrapping_add($d); let mut status = cpu.status & !(ZERO_FLAG | NEG_FLAG); - cpu.mem.write(cpu.ea, res as u8); + cpu.mem.write(cpu.ea, res); check_zero!(status, res); check_neg!(status, res); cpu.status = status; }); ($f: ident, $d: expr, $r: ident) => ( fn $f(cpu: &mut CPU) { - let res = cpu.$r as u16 + $d; + let res = cpu.$r.wrapping_add($d); let mut status = cpu.status & !(ZERO_FLAG | NEG_FLAG); cpu.$r = res as u8; check_zero!(status, res); @@ -514,8 +512,11 @@ mod ops { macro_rules! make_ld { ($f: ident, $r: ident) => (fn $f(cpu: &mut CPU) { let mut status = cpu.status & !(ZERO_FLAG | NEG_FLAG); - let res = cpu.mem.read(cpu.ea); - cpu.a = res; + let res = match cpu.addr_mode { + AddrMode::Immediate => cpu.imm_val, + _ => cpu.mem.read(cpu.ea) + }; + cpu.$r = res; check_zero!(status, res); check_neg!(status, res); cpu.status = status; @@ -612,20 +613,20 @@ mod addr { fn zpx(cpu: &mut CPU) { cpu.addr_mode = AddrMode::EffAddr; cpu.ea = (cpu.mem.read(cpu.opr) - .wrapping_add(cpu.x)) as u16; + .wrapping_add(cpu.x)) as u16; } fn zpy(cpu: &mut CPU) { cpu.addr_mode = AddrMode::EffAddr; cpu.ea = (cpu.mem.read(cpu.opr) - .wrapping_add(cpu.y)) as u16; + .wrapping_add(cpu.y)) as u16; } fn rel(cpu: &mut CPU) { cpu.addr_mode = AddrMode::EffAddr; let base = cpu.pc; let offset = cpu.mem.read(cpu.opr) as i8 as i16; - let sum = (base & 0xff) + offset as u16; + let sum = ((base & 0xff) as i16 + offset) as u16; cpu.ea = (base & 0xff00).wrapping_add(sum); cpu.cycle += (sum >> 8) as u32; } @@ -638,7 +639,7 @@ mod addr { fn abx(cpu: &mut CPU) { cpu.addr_mode = AddrMode::EffAddr; let base = read16!(cpu.mem, cpu.opr); - let sum = (base & 0xff) + cpu.x as u16; + let sum = (base & 0xff) + (cpu.x as u16); cpu.ea = (base & 0xff00).wrapping_add(sum); cpu.cycle += (sum >> 8) as u32; /* boundary cross if carry */ } @@ -646,7 +647,7 @@ mod addr { fn aby(cpu: &mut CPU) { cpu.addr_mode = AddrMode::EffAddr; let base = read16!(cpu.mem, cpu.opr); - let sum = (base & 0xff) + cpu.y as u16; + let sum = (base & 0xff) + (cpu.y as u16); cpu.ea = (base & 0xff00).wrapping_add(sum); cpu.cycle += (sum >> 8) as u32; /* boundary cross if carry */ } @@ -659,17 +660,15 @@ mod addr { fn xin(cpu: &mut CPU) { cpu.addr_mode = AddrMode::EffAddr; - let addr = cpu.mem.read( - cpu.mem.read(cpu.opr) - .wrapping_add(cpu.x) as u16) as u16; - cpu.ea = read16!(cpu.mem, addr); + cpu.ea = read16!(cpu.mem, + cpu.mem.read(cpu.opr) + .wrapping_add(cpu.x) as u16) as u16; } fn iny(cpu: &mut CPU) { cpu.addr_mode = AddrMode::EffAddr; - let addr = cpu.mem.read(cpu.mem.read(cpu.opr) as u16) as u16; - let base = read16!(cpu.mem, addr); - let sum = (base & 0xff) + cpu.y as u16; + let base = read16!(cpu.mem, cpu.mem.read(cpu.opr) as u16); + let sum = (base & 0xff) + (cpu.y as u16); cpu.ea = (base & 0xff00).wrapping_add(sum); cpu.cycle += (sum >> 8) as u32; /* boundary cross if carry */ } @@ -703,7 +702,7 @@ pub struct CPU<'a> { imm_val: u8, pub cycle: u32, int: Option<IntType>, - pub mem: &'a mut VMem + pub mem: &'a VMem } macro_rules! make_int { @@ -728,7 +727,7 @@ impl<'a> CPU<'a> { #[inline(always)] pub fn get_over(&self) -> u8 { (self.status >> 6) & 1 } #[inline(always)] pub fn get_neg(&self) -> u8 { (self.status >> 7) & 1 } - pub fn new(mem: &'a mut VMem) -> Self { + pub fn new(mem: &'a VMem) -> Self { let pc = read16!(mem, RESET_VECTOR as u16); /* nes power up state */ let a = 0; @@ -750,14 +749,21 @@ impl<'a> CPU<'a> { make_int!(irq, IRQ_VECTOR); pub fn step(&mut self) { - let pc = self.pc; match self.int { Some(IntType::NMI) => self.nmi(), Some(IntType::IRQ) => self.irq(), _ => () } self.int = None; + 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:{} x:{} y:{}", + pc, disasm::parse(opcode as u8, &code[1..]), self.a, self.x, self.y); /* update opr pointing to operands of current inst */ self.opr = pc.wrapping_add(1); /* update program counter pointing to next inst */ @@ -769,16 +775,7 @@ impl<'a> CPU<'a> { self.cycle += INST_CYCLE[opcode] as u32; } - pub fn powerup(&mut self) { - self.pc = read16!(self.mem, RESET_VECTOR as u16); - /* nes power up state */ - self.a = 0; - self.x = 0; - self.y = 0; - self.sp = 0xfd; - self.status = 0x34; - self.cycle = 0; - } + pub fn get_pc(&self) -> u16 { self.pc } pub fn reset(&mut self) { self.pc = read16!(self.mem, RESET_VECTOR as u16); @@ -4,7 +4,7 @@ use mos6502::CPU; use core::intrinsics::transmute; pub trait Screen { - fn put(&mut self, x: u8, y: u8, color: u8); + fn put(&self, x: u8, y: u8, color: u8); fn render(&self); } @@ -18,9 +18,6 @@ struct Sprite { } pub struct PPU<'a> { - /* internal srams */ - nametable_ram: [u8; 2048], - palette_ram: [u8; 32], scanline: u16, /* registers */ ppuctl: u8, @@ -38,7 +35,7 @@ pub struct PPU<'a> { /* rendering regs & latches */ /* background registers */ bg_bitmap: [u16; 2], - bg_palette: [u8; 2], + bg_palette: [u16; 2], /* background latches */ bg_nt: u8, bg_attr: u8, @@ -53,8 +50,8 @@ pub struct PPU<'a> { rendering: bool, buffered_read: u8, /* IO */ - mem: &'a mut VMem, - scr: &'a mut Screen, + mem: &'a VMem, + scr: &'a Screen, } impl<'a> PPU<'a> { @@ -196,7 +193,7 @@ impl<'a> PPU<'a> { /* 0x-??0 */ ((self.bg_nt as u16) << 4) | /* 0x---? (0 - 7) */ - (self.v >> 12) | 0x0); + ((self.v >> 12) & 7) | 0x0); } #[inline(always)] @@ -206,7 +203,7 @@ impl<'a> PPU<'a> { /* 0x-??0 */ ((self.bg_nt as u16) << 4) | /* 0x---? (8 - f) */ - (self.v >> 12) | 0x8); + ((self.v >> 12) & 7) | 0x8); } #[inline(always)] @@ -214,21 +211,25 @@ impl<'a> PPU<'a> { /* load the tile bitmap to high 8 bits of bitmap, * assume the high 8 bits are zeros */ assert!(self.bg_bitmap[0] >> 8 == 0 && - self.bg_bitmap[1] >> 8 == 0); - self.bg_bitmap[0] |= (self.bg_bit_low as u16) << 8; - self.bg_bitmap[1] |= (self.bg_bit_high as u16) << 8; - self.bg_palette[0] |= (self.bg_attr & 1) * 0xff; - self.bg_palette[1] |= ((self.bg_attr >> 1) & 1) * 0xff; + self.bg_bitmap[1] >> 8 == 0 && + self.bg_palette[0] >> 8 == 0 && + self.bg_palette[1] >> 8 == 0); + self.bg_bitmap[0] |= (PPU::reverse_byte(self.bg_bit_low) as u16) << 8; + self.bg_bitmap[1] |= (PPU::reverse_byte(self.bg_bit_high) as u16) << 8; + self.bg_palette[0] |= (self.bg_attr & 1) as u16 * 0xff00; + self.bg_palette[1] |= ((self.bg_attr >> 1) & 1) as u16 * 0xff00; } #[inline(always)] fn shift_sprites(&mut self) { for (i, c) in self.sp_cnt.iter_mut().enumerate() { + if self.oam2[i].y == 0xff { break } let c0 = *c; match c0 { 0 => { - self.sp_bitmap[i][0] >>= 1; - self.sp_bitmap[i][1] >>= 1; + let t = &mut self.sp_bitmap[i]; + t[0] = t[0].wrapping_shr(1); + t[1] = t[1].wrapping_shr(1); }, _ => *c = c0 - 1 } @@ -237,10 +238,12 @@ impl<'a> PPU<'a> { #[inline(always)] fn shift_bgtile(&mut self, d: u8) { - self.bg_bitmap[0] >>= d; - self.bg_bitmap[1] >>= d; - self.bg_palette[0] >>= d; - self.bg_palette[1] >>= d; + let t1 = &mut self.bg_bitmap; + let t2 = &mut self.bg_palette; + t1[0] = t1[0].wrapping_shr(d as u32); + t1[1] = t1[1].wrapping_shr(d as u32); + t2[0] = t2[0].wrapping_shr(d as u32); + t2[1] = t2[1].wrapping_shr(d as u32); } #[inline(always)] @@ -260,12 +263,12 @@ impl<'a> PPU<'a> { false => self.v += 0x1000, /* fine y < 7 */ true => { self.v &= !0x7000u16; /* fine y <- 0 */ - self.v = (self.v & !0x03e0u16) | - (match (self.v & 0x03e0) >> 5 { + let y = match (self.v & 0x03e0) >> 5 { 29 => {self.v ^= 0x0800; 0}, /* at bottom of scanline */ 31 => 0, /* do not switch nt */ y => y + 1 - }) << 5; + }; + self.v = (self.v & !0x03e0u16) | (y << 5); } } } @@ -333,25 +336,26 @@ impl<'a> PPU<'a> { fn fetch_sprite(&mut self) { /* we use scanline here because s.y is the (actual y) - 1 */ - for (i, v) in self.oam2.iter().enumerate() { - let vflip = (v.attr & 0x80) == 0x80; - let y0 = self.scanline - v.y as u16; + for (i, s) in self.oam2.iter().enumerate() { + if s.y == 0xff { break } + let vflip = (s.attr & 0x80) == 0x80; + let y0 = self.scanline - s.y as u16; let (ptable, tidx, y) = match self.get_spritesize() { 0 => { let y = if vflip {7 - y0 as u8} else {y0 as u8}; - ((self.ppuctl as u16 & 0x08) << 9, v.tile, y) + ((self.ppuctl as u16 & 0x08) << 9, s.tile, y) }, _ => { let y = if vflip {15 - y0 as u8} else {y0 as u8}; - ((v.tile as u16 & 1) << 12, - (v.tile & !1u8) | (y >> 3), + ((s.tile as u16 & 1) << 12, + (s.tile & !1u8) | (y >> 3), y & 0x7) } }; - self.sp_cnt[i] = v.x; + self.sp_cnt[i] = s.x; let mut low = self.mem.read(ptable | ((tidx as u16) << 4) | 0x0 | y as u16); let mut high = self.mem.read(ptable | ((tidx as u16) << 4) | 0x8 | y as u16); - if (v.attr & 0x40) == 0x40 { + if (s.attr & 0x40) != 0x40 { low = PPU::reverse_byte(low); high = PPU::reverse_byte(high); } @@ -375,6 +379,7 @@ impl<'a> PPU<'a> { } fn render_pixel(&mut self) { + println!("ppuctl:{} ppumask:{}", self.ppuctl, self.ppumask); let x = self.cycle - 1; let bg_pidx = if x >= 8 || self.get_show_leftmost_bg() {self.get_bg_pidx()} @@ -384,6 +389,7 @@ impl<'a> PPU<'a> { let mut pri = 0x1; if x >= 8 || self.get_show_leftmost_sp() { for i in 0..8 { + if self.oam2[i].y == 0xff { break } if self.sp_cnt[i] != 0 { continue; } /* not active */ match self.get_sp_pidx(i) { 0x0 => (), @@ -401,7 +407,7 @@ impl<'a> PPU<'a> { } assert!(0 < self.cycle && self.cycle < 257); assert!(self.scanline < 240); - self.scr.put(self.cycle as u8 - 1, + self.scr.put((self.cycle - 1) as u8, self.scanline as u8, if (pri == 0 || bg_pidx == 0) && sp_pidx != 0 { self.mem.read(0x3f10 | @@ -415,7 +421,7 @@ impl<'a> PPU<'a> { }); } - pub fn new(mem: &'a mut VMem, scr: &'a mut Screen) -> Self { + pub fn new(mem: &'a VMem, scr: &'a Screen) -> Self { let ppuctl = 0x00; let ppumask = 0x00; let ppustatus = 0xa0; @@ -425,8 +431,6 @@ impl<'a> PPU<'a> { let cycle = 370; let scanline = 240; PPU { - nametable_ram: [0; 2048], - palette_ram: [0; 32], scanline, ppuctl, ppumask, @@ -468,22 +472,24 @@ impl<'a> PPU<'a> { let visible = self.scanline < 240; let pre_render = self.scanline == 261; self.rendering = pre_render || visible; - if pre_render { - if cycle == 1 { - /* 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 self.rendering && (self.get_show_bg() || self.get_show_sp()) { + if pre_render { + if cycle == 1 { + /* 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 self.rendering { let shifting = 0 < cycle && cycle < 257; /* 1..256 */ let fetch = shifting || (320 < cycle && cycle < 337); if fetch { /* 1..256 and 321..336 */ match cycle & 0x7 { 1 => { - self.load_bgtile(); + if shifting { + self.load_bgtile(); + } self.fetch_nametable_byte(); }, 3 => self.fetch_attrtable_byte(), |