├── src ├── tests │ ├── mod.rs │ └── cart.rs ├── lib.rs ├── controller.rs ├── ports.rs ├── memory │ ├── mapper2.rs │ ├── mod.rs │ └── mapper1.rs ├── console.rs ├── cart.rs ├── ppu.rs ├── cpu.rs └── apu.rs ├── .gitignore ├── test_roms └── palette.nes ├── CHANGELOG.md ├── Cargo.toml ├── benches └── console_benchmark.rs ├── LICENSE.md ├── README.md └── Cargo.lock /src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | mod cart; 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /roms 2 | /target 3 | **/*.rs.bk 4 | -------------------------------------------------------------------------------- /test_roms/palette.nes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cronokirby/ludus/HEAD/test_roms/palette.nes -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.2.2 - Unreleased 2 | * Remove `Console::print_cpu` and `Console::print_ram` methods. 3 | * Implement iNES mapper 1 4 | * Make `Console::step_frame` advance to the start of the next frame. 5 | 6 | ## 0.2.1 - June 6, 2019 7 | * First real release. 8 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod apu; 2 | pub mod cart; 3 | pub mod console; 4 | pub mod controller; 5 | pub(crate) mod cpu; 6 | pub(crate) mod memory; 7 | pub mod ports; 8 | pub(crate) mod ppu; 9 | 10 | pub use cart::{Cart, CartReadingError}; 11 | pub use console::Console; 12 | pub use controller::ButtonState; 13 | pub use ports::{AudioDevice, PixelBuffer, VideoDevice, NES_HEIGHT, NES_WIDTH}; 14 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ludus" 3 | version = "0.2.2" 4 | description = "headless NES emulator crate" 5 | license = "MIT" 6 | authors = ["Lúcás Meier "] 7 | homepage = "https://github.com/cronokirby/ludus" 8 | repository = "https://github.com/cronokirby/ludus" 9 | readme = "README.md" 10 | 11 | [dev-dependencies] 12 | criterion = "0.2" 13 | 14 | [[bench]] 15 | name = "console_benchmark" 16 | harness = false -------------------------------------------------------------------------------- /benches/console_benchmark.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate criterion; 3 | extern crate ludus; 4 | 5 | use criterion::Criterion; 6 | use criterion::black_box; 7 | use ludus::*; 8 | 9 | #[derive(Clone, Copy)] 10 | pub struct NullDevice; 11 | 12 | impl AudioDevice for NullDevice { 13 | fn push_sample(&mut self, _sample: f32) { 14 | } 15 | } 16 | 17 | impl VideoDevice for NullDevice { 18 | fn blit_pixels(&mut self, _pixels: &PixelBuffer) { 19 | } 20 | } 21 | 22 | fn step_frame(console: &mut Console) { 23 | console.step_frame(&mut NullDevice, &mut NullDevice); 24 | } 25 | 26 | fn criterion_benchmark(c: &mut Criterion) { 27 | let rom_bytes = include_bytes!("../test_roms/palette.nes"); 28 | c.bench_function("console palette", move |b| { 29 | let cart = Cart::from_bytes(rom_bytes).unwrap(); 30 | let mut console = Console::new(cart, 44000); 31 | b.iter(|| step_frame(black_box(&mut console))) 32 | }); 33 | } 34 | 35 | criterion_group!(benches, criterion_benchmark); 36 | criterion_main!(benches); -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (C) 2018 Lúcás Meier 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /src/controller.rs: -------------------------------------------------------------------------------- 1 | #[derive(Default)] 2 | pub struct ButtonState { 3 | pub a: bool, 4 | pub b: bool, 5 | pub select: bool, 6 | pub start: bool, 7 | pub up: bool, 8 | pub down: bool, 9 | pub left: bool, 10 | pub right: bool, 11 | } 12 | 13 | /// Represents a controller 14 | #[derive(Default)] 15 | pub(crate) struct Controller { 16 | /// A bitfield of the buttons, in the following order: 17 | /// A, B, Select, Start, Up, Down, Left, Right 18 | buttons: [bool; 8], 19 | /// What button is currently being read 20 | index: u8, 21 | strobe: bool, 22 | } 23 | 24 | impl Controller { 25 | pub fn new() -> Self { 26 | Controller::default() 27 | } 28 | 29 | pub fn set_buttons(&mut self, buttons: ButtonState) { 30 | self.buttons = [ 31 | buttons.a, 32 | buttons.b, 33 | buttons.select, 34 | buttons.start, 35 | buttons.up, 36 | buttons.down, 37 | buttons.left, 38 | buttons.right, 39 | ]; 40 | } 41 | 42 | pub fn read(&mut self) -> u8 { 43 | let index = self.index as usize; 44 | let res = if *self.buttons.get(index).unwrap_or(&false) { 45 | 1 46 | } else { 47 | 0 48 | }; 49 | self.index += 1; 50 | if self.strobe { 51 | self.index = 0; 52 | } 53 | res 54 | } 55 | 56 | pub fn write(&mut self, value: u8) { 57 | self.strobe = value & 1 == 1; 58 | if self.strobe { 59 | self.index = 0; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/ports.rs: -------------------------------------------------------------------------------- 1 | /// This represents an audio device we can push samples to. 2 | /// 3 | /// The APU will dump its samples into an object implementing 4 | /// this Trait as it generates them. 5 | pub trait AudioDevice { 6 | fn push_sample(&mut self, sample: f32); 7 | } 8 | 9 | /// This represents the width of the display in pixels 10 | pub const NES_WIDTH: usize = 256; 11 | /// This represents the height of the display in pixels 12 | pub const NES_HEIGHT: usize = 240; 13 | const BUFFER_PIXELS: usize = NES_WIDTH * NES_HEIGHT; 14 | 15 | /// Represents a buffer of pixels the PPU writes to. 16 | /// 17 | /// The pixels can be read as a slice of u32 values in ARGB format, in row order. 18 | /// 19 | /// The default value for the pixel buffer is completely transparent. 20 | /// 21 | /// This struct is somewhat large, so it should be boxed when included 22 | /// in another struct to avoid blowing up the stack. 23 | pub struct PixelBuffer([u32; BUFFER_PIXELS]); 24 | 25 | impl Default for PixelBuffer { 26 | /// This returns a completely transparent buffer of pixels. 27 | fn default() -> Self { 28 | PixelBuffer([0; BUFFER_PIXELS]) 29 | } 30 | } 31 | 32 | impl AsRef<[u32]> for PixelBuffer { 33 | /// This will return the pixels row by row, in ARGB (big endian) format. 34 | fn as_ref(&self) -> &[u32] { 35 | &self.0 36 | } 37 | } 38 | 39 | impl PixelBuffer { 40 | pub(crate) fn write(&mut self, x: usize, y: usize, argb: u32) { 41 | let index = NES_WIDTH * y + x; 42 | self.0[index] = argb; 43 | } 44 | } 45 | 46 | /// This represents a video device we can write a pixel buffer to. 47 | /// 48 | /// When implementing this trait, the device should be scaled to a factor 49 | /// of NES_WIDTH * NES_HEIGHT, and be able to accept a pixel buffer of those 50 | /// dimensions. 51 | pub trait VideoDevice { 52 | /// Transfer a buffer of pixels onto this device. 53 | fn blit_pixels(&mut self, pixels: &PixelBuffer); 54 | } 55 | -------------------------------------------------------------------------------- /src/tests/cart.rs: -------------------------------------------------------------------------------- 1 | use super::super::cart::*; 2 | 3 | // Makes an ines file with anything filling the PRG and CHR 4 | // 0xFF is used as a marker file for the beginning of PRG and CHR 5 | fn make_ines( 6 | mirroring: Mirroring, 7 | has_battery: bool, 8 | trainer: bool, 9 | mapper: u8, 10 | prg_chunks: usize, 11 | chr_chunks: usize, 12 | ) -> Vec { 13 | let trainer_offset = if trainer { 512 } else { 0 }; 14 | let mut buffer = { 15 | let rom_size = 0x4000 * prg_chunks + 0x2000 * chr_chunks; 16 | Vec::with_capacity(16 + trainer_offset + rom_size) 17 | }; 18 | let mut flag6 = 0; 19 | if mirroring.is_vertical() { 20 | flag6 |= 0b1; 21 | } 22 | if has_battery { 23 | flag6 |= 0b10; 24 | } 25 | if trainer { 26 | flag6 |= 0b100; 27 | } 28 | flag6 |= (mapper & 0x0F) << 4; 29 | let flag7 = (mapper & 0xF0) << 4; 30 | buffer.push(0x4E); 31 | buffer.push(0x45); 32 | buffer.push(0x53); 33 | buffer.push(0x1A); 34 | buffer.push(prg_chunks as u8); 35 | buffer.push(chr_chunks as u8); 36 | buffer.push(flag6); 37 | buffer.push(flag7); 38 | for _ in 8..16 { 39 | buffer.push(0); 40 | } 41 | for _ in 0..trainer_offset { 42 | buffer.push(0x1); 43 | } 44 | buffer.push(0xFF); 45 | for _ in 1..prg_chunks * 0x4000 { 46 | buffer.push(0x2); 47 | } 48 | buffer.push(0xFF); 49 | for _ in 1..chr_chunks * 0x2000 { 50 | buffer.push(0x3); 51 | } 52 | buffer 53 | } 54 | 55 | #[test] 56 | fn cart_decoding() { 57 | let buffer = make_ines(Mirroring::Horizontal, true, false, 1, 1, 1); 58 | let cart_res = Cart::from_bytes(&buffer); 59 | assert!(cart_res.is_ok()); 60 | let cart = cart_res.unwrap(); // we just asserted, so it's ok 61 | assert_eq!(cart.prg[0], 0xFF); 62 | assert_eq!(cart.chr[0], 0xFF); 63 | assert_eq!(cart.mapper, 1); 64 | assert!(!cart.mirroring.is_vertical()); 65 | assert_eq!(cart.has_battery, true); 66 | } 67 | -------------------------------------------------------------------------------- /src/memory/mapper2.rs: -------------------------------------------------------------------------------- 1 | use crate::cart::{Cart, Mirroring}; 2 | use crate::memory::Mapper; 3 | 4 | pub struct Mapper2 { 5 | cart: Cart, 6 | prg_banks: u8, 7 | prgbank1: usize, 8 | prgbank2: usize, 9 | } 10 | 11 | impl Mapper2 { 12 | pub fn new(cart: Cart) -> Self { 13 | let prg_banks = cart.prg.len() / 0x4000; 14 | let prgbank1 = 0; 15 | let prgbank2 = prg_banks - 1; 16 | Mapper2 { 17 | cart, 18 | prg_banks: prg_banks as u8, 19 | prgbank1, 20 | prgbank2, 21 | } 22 | } 23 | } 24 | 25 | impl Mapper for Mapper2 { 26 | fn read(&self, address: u16) -> u8 { 27 | match address { 28 | a if a < 0x2000 => self.cart.chr[a as usize], 29 | a if a >= 0xC000 => { 30 | let shifted = (address - 0xC000) as usize; 31 | let index = self.prgbank2 * 0x4000 + shifted; 32 | self.cart.prg[index] 33 | } 34 | a if a >= 0x8000 => { 35 | let shifted = (address - 0x8000) as usize; 36 | let index = self.prgbank1 * 0x4000 + shifted; 37 | self.cart.prg[index] 38 | } 39 | a if a >= 0x6000 => { 40 | let shifted = (address - 0x6000) as usize; 41 | self.cart.sram[shifted] 42 | } 43 | a => { 44 | panic!("Mapper2 unhandled read at {:X}", a); 45 | } 46 | } 47 | } 48 | 49 | fn mirroring_mode(&self) -> Mirroring { 50 | self.cart.mirroring 51 | } 52 | 53 | fn write(&mut self, address: u16, value: u8) { 54 | match address { 55 | a if a < 0x2000 => self.cart.chr[a as usize] = value, 56 | a if a >= 0x8000 => { 57 | let bank = value % self.prg_banks; 58 | self.prgbank1 = bank as usize; 59 | } 60 | a if a >= 0x6000 => { 61 | let shifted = (address - 0x6000) as usize; 62 | self.cart.sram[shifted] = value; 63 | } 64 | a => { 65 | panic!("Mapper2 unhandled write at {:X}", a); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/console.rs: -------------------------------------------------------------------------------- 1 | use crate::apu::APU; 2 | use crate::cart::Cart; 3 | use crate::controller::ButtonState; 4 | use crate::cpu::CPU; 5 | use crate::memory::MemoryBus; 6 | use crate::ports::{AudioDevice, VideoDevice}; 7 | use crate::ppu::PPU; 8 | 9 | /// Used to act as an owner of everything needed to run a game 10 | /// Is also responsible for holding ram, 11 | /// as well as communication between processors. 12 | pub struct Console { 13 | apu: APU, 14 | cpu: CPU, 15 | ppu: PPU, 16 | } 17 | 18 | impl Console { 19 | pub fn new(cart: Cart, sample_rate: u32) -> Self { 20 | let mut memory = MemoryBus::with_cart(cart); 21 | let ppu = PPU::new(&mut memory); 22 | let cpu = CPU::new(memory); 23 | Console { 24 | apu: APU::new(sample_rate), 25 | cpu, 26 | ppu, 27 | } 28 | } 29 | 30 | /// Advance the console by a single CPU cycle. 31 | /// 32 | /// This needs access to the audio and video devices, because the APU 33 | /// may generate audio samples, and the PPU may generate a frame. 34 | pub fn step<'a, A, V>(&'a mut self, audio: &mut A, video: &mut V) -> i32 35 | where 36 | A: AudioDevice, 37 | V: VideoDevice, 38 | { 39 | let cpucycles = self.cpu.step(); 40 | let m = &mut self.cpu.mem; 41 | for _ in 0..cpucycles * 3 { 42 | self.ppu.step(m, video); 43 | } 44 | for _ in 0..cpucycles { 45 | self.apu.step(m, audio); 46 | } 47 | cpucycles 48 | } 49 | 50 | /// Advance the console by a certain number of micro seconds. 51 | pub fn step_micros<'a, A, V>(&'a mut self, audio: &mut A, video: &mut V, micros: u32) 52 | where 53 | A: AudioDevice, 54 | V: VideoDevice, 55 | { 56 | // This emulates 1.79 cpu cycles per microsecond 57 | let mut cpu_cycles = ((micros * 179) / 100) as i32; 58 | while cpu_cycles > 0 { 59 | cpu_cycles -= self.step(audio, video); 60 | } 61 | } 62 | 63 | /// Advance the console until the next frame. 64 | /// 65 | /// Unlike the other step methods, this is not based on timing, but 66 | /// based on waiting until the ppu actually generates a video frame. 67 | /// This is more useful for applications that want to do something 68 | /// at the start of every frame, like playing the next frame of input 69 | /// from a recorded script, or things like that. 70 | pub fn step_frame<'a, A, V>(&'a mut self, audio: &mut A, video: &mut V) 71 | where 72 | A: AudioDevice, 73 | V: VideoDevice, 74 | { 75 | let mut frame_happened = false; 76 | while !frame_happened { 77 | let cpucycles = self.cpu.step(); 78 | let m = &mut self.cpu.mem; 79 | for _ in 0..cpucycles * 3 { 80 | frame_happened = self.ppu.step(m, video) || frame_happened; 81 | } 82 | for _ in 0..cpucycles { 83 | self.apu.step(m, audio); 84 | } 85 | } 86 | } 87 | 88 | pub fn update_controller(&mut self, buttons: ButtonState) { 89 | self.cpu.set_buttons(buttons); 90 | } 91 | 92 | /// Resets everything to it's initial state 93 | pub fn reset(&mut self) { 94 | self.cpu.reset(); 95 | self.cpu.mem.reset(); 96 | self.ppu.reset(&mut self.cpu.mem); 97 | self.ppu.clear_vbuffers(); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/memory/mod.rs: -------------------------------------------------------------------------------- 1 | mod mapper1; 2 | mod mapper2; 3 | 4 | use super::apu::APUState; 5 | use super::cart::{Cart, MapperID, Mirroring}; 6 | use super::controller::Controller; 7 | use super::cpu::CPUState; 8 | use super::ppu::PPUState; 9 | 10 | /// Used to abstract over the different types of Mappers 11 | pub trait Mapper { 12 | fn read(&self, address: u16) -> u8; 13 | fn mirroring_mode(&self) -> Mirroring; 14 | fn write(&mut self, address: u16, value: u8); 15 | } 16 | 17 | impl Mapper { 18 | /// Dynamically assigns the correct mapper based on the cart. 19 | /// Returns an error if the mapper is unkown 20 | pub fn with_cart(cart: Cart) -> Box { 21 | match cart.mapper { 22 | MapperID::M1 => Box::new(mapper1::Mapper1::new(cart)), 23 | MapperID::M2 => Box::new(mapper2::Mapper2::new(cart)), 24 | } 25 | } 26 | } 27 | 28 | /// Holds cart memory 29 | pub(crate) struct MemoryBus { 30 | // Contains the mapper logic for interfacing with the cart 31 | // Each mapper has a different structure depending on what it 32 | // might need to keep track of, so we need to use dynamic dispatch. 33 | pub mapper: Box, 34 | pub apu: APUState, 35 | pub cpu: CPUState, 36 | pub ppu: PPUState, 37 | // public for access by the cpu 38 | pub controller1: Controller, 39 | controller2: Controller, 40 | ram: [u8; 0x2000], 41 | } 42 | 43 | impl MemoryBus { 44 | /// Creates a memory bus from a c 45 | pub fn with_cart(cart: Cart) -> Self { 46 | let mapper = Mapper::with_cart(cart); 47 | MemoryBus { 48 | mapper, 49 | apu: APUState::new(), 50 | cpu: CPUState::new(), 51 | ppu: PPUState::new(), 52 | controller1: Controller::new(), 53 | controller2: Controller::new(), 54 | ram: [0; 0x2000], 55 | } 56 | } 57 | 58 | /// Clears ram as well as cpu and ppu state 59 | pub fn reset(&mut self) { 60 | for byte in self.ram.iter_mut() { 61 | *byte = 0; 62 | } 63 | self.cpu = CPUState::new(); 64 | self.ppu = PPUState::new(); 65 | } 66 | 67 | pub fn cpu_read(&mut self, address: u16) -> u8 { 68 | match address { 69 | a if a < 0x2000 => self.ram[(a % 0x800) as usize], 70 | a if a < 0x4000 => { 71 | let adr = 0x2000 + a % 8; 72 | self.ppu.read_register(&*self.mapper, adr) 73 | } 74 | 0x4014 => self.ppu.read_register(&*self.mapper, 0x4014), 75 | 0x4015 => self.apu.read_register(address), 76 | 0x4016 => self.controller1.read(), 77 | 0x4017 => self.controller2.read(), 78 | a if a >= 0x6000 => self.mapper.read(address), 79 | a => { 80 | panic!("Unhandled CPU read at {:X}", a); 81 | } 82 | } 83 | } 84 | 85 | pub fn cpu_write(&mut self, address: u16, value: u8) { 86 | match address { 87 | a if a < 0x2000 => self.ram[(a % 0x800) as usize] = value, 88 | a if a < 0x4000 => { 89 | let adr = 0x2000 + a % 8; 90 | self.ppu.write_register(&mut *self.mapper, adr, value); 91 | } 92 | a if a < 0x4014 => self.apu.write_register(a, value), 93 | 0x4014 => { 94 | self.ppu.write_register(&mut *self.mapper, 0x4014, value); 95 | self.write_dma(value); 96 | } 97 | 0x4015 => self.apu.write_register(address, value), 98 | 0x4016 => { 99 | self.controller1.write(value); 100 | self.controller2.write(value); 101 | } 102 | 0x4017 => self.apu.write_register(address, value), 103 | a if a >= 0x6000 => self.mapper.write(address, value), 104 | a => { 105 | panic!("Unhandled CPU write at {:X}", a); 106 | } 107 | } 108 | } 109 | 110 | fn write_dma(&mut self, value: u8) { 111 | let mut address = u16::from(value) << 8; 112 | // Stall for DMA 113 | self.cpu.add_stall(513); 114 | for _ in 0..256 { 115 | let oam_address = self.ppu.oam_address as usize; 116 | self.ppu.oam.0[oam_address] = self.cpu_read(address); 117 | self.ppu.oam_address = self.ppu.oam_address.wrapping_add(1); 118 | address += 1; 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/cart.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryFrom; 2 | 3 | /// Represents the possible errors when decoding a Cart 4 | #[derive(Clone, Copy, Debug)] 5 | pub enum CartReadingError { 6 | UnrecognisedFormat, 7 | UnknownMapper(u8), 8 | } 9 | 10 | /// Represents the type of mirroring present on a cartridge 11 | #[derive(Clone, Copy, Debug, PartialEq)] 12 | pub enum Mirroring { 13 | /// Tables start wrapping horizontally 14 | Horizontal, 15 | /// Tables start wrapping vertically 16 | Vertical, 17 | /// Every mirror points to the first table 18 | SingleLower, 19 | /// Every mirror points to the second table 20 | SingleUpper, 21 | } 22 | 23 | impl From for Mirroring { 24 | /// Create a mirroring from a boolean, with true representing vertical 25 | fn from(mirroring: u8) -> Self { 26 | match mirroring { 27 | 0 => Mirroring::SingleLower, 28 | 1 => Mirroring::SingleUpper, 29 | 2 => Mirroring::Vertical, 30 | _ => Mirroring::Horizontal, 31 | } 32 | } 33 | } 34 | 35 | impl Mirroring { 36 | /// Returns true if mirroring is Vertical 37 | pub fn is_vertical(self) -> bool { 38 | self == Mirroring::Vertical 39 | } 40 | 41 | /// Mirrors an address >= 0x2000 42 | pub(crate) fn mirror_address(self, address: u16) -> u16 { 43 | let address = (address - 0x2000) % 0x1000; 44 | let table = match (self, address / 0x400) { 45 | (Mirroring::Horizontal, 0) => 0, 46 | (Mirroring::Horizontal, 1) => 0, 47 | (Mirroring::Horizontal, 2) => 1, 48 | (Mirroring::Horizontal, 3) => 1, 49 | (Mirroring::Vertical, 0) => 0, 50 | (Mirroring::Vertical, 1) => 1, 51 | (Mirroring::Vertical, 2) => 0, 52 | (Mirroring::Vertical, 3) => 1, 53 | (Mirroring::SingleLower, _) => 0, 54 | (Mirroring::SingleUpper, _) => 1, 55 | _ => 0, 56 | }; 57 | 0x2000 + table * 0x400 + (address % 0x400) 58 | } 59 | } 60 | 61 | /// This represents the different type of mappers this crate supports. 62 | /// 63 | /// In theory, the mapper id in a cart could be any byte, but only a small subset 64 | /// of mappers were actually used. 65 | #[derive(Clone, Copy, Debug)] 66 | pub enum MapperID { 67 | /// The mapper used for 0x0 and 0x2 68 | M2, 69 | /// iNES mapper 0x1 70 | M1, 71 | } 72 | 73 | impl TryFrom for MapperID { 74 | type Error = CartReadingError; 75 | 76 | fn try_from(byte: u8) -> Result { 77 | match byte { 78 | 0 => Ok(MapperID::M2), 79 | 1 => Ok(MapperID::M1), 80 | 2 => Ok(MapperID::M2), 81 | _ => Err(CartReadingError::UnknownMapper(byte)), 82 | } 83 | } 84 | } 85 | 86 | /// Represents an NES Cartridge 87 | /// The PRG and CHR roms vary in sizes between carts, 88 | /// which is why they're stored in Vecs. 89 | pub struct Cart { 90 | /// Represents the PRG ROM, in multiple 16KB chunks 91 | pub prg: Vec, 92 | /// Represents the CHR ROM, in multiple 8KB chunks 93 | pub chr: Vec, 94 | /// The SRAM, always 8KB 95 | pub sram: [u8; 0x2000], 96 | /// The ID of the Mapper this cart uses 97 | pub mapper: MapperID, 98 | /// What type of mirroring is used in this cart 99 | pub mirroring: Mirroring, 100 | /// Indicates whether or not a battery backed RAM is present 101 | pub has_battery: bool, 102 | } 103 | 104 | impl Cart { 105 | /// Reads a buffer of bytes into a Cart, 106 | /// detecting and parsing the format automatically. 107 | pub fn from_bytes(buffer: &[u8]) -> Result { 108 | if buffer[0..4] == [0x4E, 0x45, 0x53, 0x1A] { 109 | Cart::from_ines(buffer) 110 | } else { 111 | Err(CartReadingError::UnrecognisedFormat) 112 | } 113 | } 114 | 115 | /// Reads an INES formatted buffer, including the header 116 | fn from_ines(buffer: &[u8]) -> Result { 117 | let prg_chunks = buffer[4] as usize; 118 | let chr_chunks = buffer[5] as usize; 119 | let flag6 = buffer[6]; 120 | let flag7 = buffer[7]; 121 | let trainer_offset = if flag6 & 0b100 > 0 { 512 } else { 0 }; 122 | let prg_start = 16 + trainer_offset; 123 | let prg_end = prg_start + 0x4000 * prg_chunks; 124 | let chr_end = prg_end + 0x2000 * chr_chunks; 125 | let mapper = MapperID::try_from((flag6 >> 4) | (flag7 & 0xF0))?; 126 | let mirroring = if flag6 & 1 != 0 { 127 | Mirroring::Vertical 128 | } else { 129 | Mirroring::Horizontal 130 | }; 131 | let chr = if chr_chunks == 0 { 132 | vec![0; 0x2000] 133 | } else { 134 | buffer[prg_end..chr_end].to_vec() 135 | }; 136 | Ok(Cart { 137 | prg: buffer[prg_start..prg_end].to_vec(), 138 | chr, 139 | mapper, 140 | sram: [0; 0x2000], 141 | mirroring, 142 | has_battery: flag6 & 0b10 > 0, 143 | }) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![crates.io](https://img.shields.io/crates/v/ludus.svg)](https://crates.io/crates/ludus) 2 | [![docs.rs](https://docs.rs/ludus/badge.svg)](https://docs.rs/ludus/) 3 | 4 | # ludus 5 | 6 | **Ludus** is a crate providing the core logic of an NES emulator. Unlike other 7 | crates, **Ludus** is not a standalone application. Instead, **Ludus** is a crate 8 | designed to be easily embedded in an application. For an example of using 9 | **Ludus** to make a GUI emulator, see 10 | [ludus-emu](https://github.com/cronokirby/ludus-emu). 11 | 12 | The advantage of being headless is that **Ludus** is easily useable in contexts 13 | outside of a standalone application. For example, this crate could be used 14 | to train agents the play NES games, or to generate screenshots from NES games, 15 | or to generate plots of RAM, etc. By being headless, **Ludus** can be used 16 | in your own emulator application in whatever way you want. 17 | 18 | ## Features 19 | - CPU emulation 20 | - Video emulation 21 | - Audio emulation 22 | - Parsing rom data from `.ines` files. 23 | - Mappers 0, 1, and 2, so many common games. 24 | 25 | ## Usage 26 | Let's first import the main types used in **Ludus**: 27 | ```rust 28 | use ludus::*; 29 | ``` 30 | 31 | The main emulator type is `Console`. Before we can create a `Console`, we need 32 | a cartridge to play. We can create a `Cart` type by reading an `.ines` file. 33 | 34 | ```rust 35 | let bytes: &[u8] = read_ines_bytes(); 36 | let cart = Cart::from_bytes(bytes).unwrap(); 37 | ``` 38 | 39 | Creating a cartridge will naturally fail if the ROM data wasn't valid. 40 | 41 | Once we have a cartridge, we can create a console to play this cartridge: 42 | ```rust 43 | let console = Console::new(cart, sample_rate); 44 | ``` 45 | 46 | Creating a console requires a cartridge, as well as a sample rate for the audio 47 | process unit (APU). Normally, if you're using some crate that allows you to play 48 | audio to a device, you should have access to this sample_rate. 49 | 50 | At any point in time we can reset the console like so: 51 | ```rust 52 | console.reset(); 53 | ``` 54 | 55 | We can also update the state of the buttons using the `ButtonState` struct: 56 | ```rust 57 | let mut buttons = ButtonState::default(); 58 | buttons.a = true; 59 | console.update_controller(buttons); 60 | ``` 61 | 62 | Now to actually start doing some emulation, we need to `step` the console forward. 63 | Anytime we advance emulation however, the APU might generate audio samples, and 64 | the PPU might generate video frames. To handle these, we need to provide a device 65 | that can handle the audio samples, and a device to handle the video frames. 66 | 67 | For handling audio, we have the `AudioDevice` trait: 68 | ```rust 69 | trait AudioDevice { 70 | fn push_sample(&mut self, sample: f32) 71 | } 72 | ``` 73 | A device implementing this trait should be able to receive an audio sample, 74 | in the range `[-1, 1]` and to audio stuff with that information. The sample rate 75 | passed to the console determines how often the APU will generate samples and push 76 | them to this device. 77 | 78 | For handling video, we have the `VideoDevice` trait: 79 | ```rust 80 | trait VideoDevice { 81 | fn blit_pixels(&mut self, pixels: &PixelBuffer) 82 | } 83 | ``` 84 | This device should be able to receive a frame of pixels, and display that on 85 | screen, or whatever else you might want to do with the video data. 86 | The pixelbuffer contains 256x240 ARGB pixels, in row major format. 87 | 88 | If you don't want to handle audio or video, you can simple create an empty struct 89 | that does nothing for both traits: 90 | 91 | ```rust 92 | #[derive(Clone, Copy)] 93 | pub struct NullDevice; 94 | 95 | impl AudioDevice for NullDevice { 96 | fn push_sample(&mut self, sample: f32) { 97 | } 98 | } 99 | 100 | impl VideoDevice for NullDevice { 101 | fn blit_pixels(&mut self, pixels: &PixelBuffer) { 102 | } 103 | } 104 | ``` 105 | 106 | Now that we have the devices set up, we can start doing some emulation. 107 | 108 | The simplest method to advance the console is `step`: 109 | ```rust 110 | pub fn step<'a, A, V>(&'a mut self, audio: &mut A, video: &mut V) -> i32 where 111 | A: AudioDevice, 112 | V: VideoDevice, 113 | ``` 114 | This will advance the `Console` forward by one cpu cycle. This is only useful 115 | if you want to be able to see things advance very very slowly. If you're 116 | something automated, like a bot, you want to use `step_frame` instead, since 117 | most games won't even look at input more than once per frame anyways. 118 | 119 | The next method is `step_micros`: 120 | ```rust 121 | pub fn step_micros<'a, A, V>( 122 | &'a mut self, 123 | audio: &mut A, 124 | video: &mut V, 125 | micros: u32 126 | ) where 127 | A: AudioDevice, 128 | V: VideoDevice, 129 | ``` 130 | 131 | This method will instead advance the emulator by a certain number of microseconds. 132 | This is the most useful method if you're implementing your own GUI and want 133 | to advance the emulator in some kind of game loop. 134 | 135 | An example of doing such a loop might look like this: 136 | ```rust 137 | let mut old = Instant::now(); 138 | loop { 139 | let now = Instant::now(); 140 | let duration = now.duration_since(old); 141 | old = now; 142 | console.step_micros(audio, video, duration.subsec_micros()); 143 | } 144 | ``` 145 | 146 | The final method allows you to advance the emulator by a full frame: 147 | ```rust 148 | pub fn step_frame<'a, A, V>(&'a mut self, audio: &mut A, video: &mut V) where 149 | A: AudioDevice, 150 | V: VideoDevice, 151 | ``` 152 | This is useful if you're training a bot, because games will only look at input 153 | once per frame. So you'd set input for that frame, then advance once frame, then 154 | set input, etc. Note that this is not based on *timing* like the other methods, 155 | but by waiting for the ppu to reach the end of the current frame. 156 | 157 | ## Resources 158 | 159 | I relied heavily on this very nicely written open source emulator: https://github.com/fogleman/nes. 160 | 161 | This page https://wiki.nesdev.com/w/index.php/NES_reference_guide was and still is my bible as I work on this project; kudos to the many people who've contributed in some way over the years. 162 | -------------------------------------------------------------------------------- /src/memory/mapper1.rs: -------------------------------------------------------------------------------- 1 | use crate::cart::{Cart, Mirroring}; 2 | use crate::memory::Mapper; 3 | 4 | const PRG_BANK_SIZE: usize = 0x4000; 5 | const CHR_BANK_SIZE: usize = 0x1000; 6 | 7 | struct ShiftRegister { 8 | register: u8, 9 | count: u8, 10 | } 11 | 12 | impl Default for ShiftRegister { 13 | fn default() -> Self { 14 | ShiftRegister { 15 | register: 0x10, 16 | count: 0, 17 | } 18 | } 19 | } 20 | 21 | impl ShiftRegister { 22 | // This returns Some if the shifting is done, and we have a final value 23 | fn shift(&mut self, value: u8) -> Option { 24 | self.register >>= 1; 25 | self.register |= (value & 1) << 4; 26 | self.count += 1; 27 | if self.count == 5 { 28 | let ret = self.register; 29 | *self = ShiftRegister::default(); 30 | Some(ret) 31 | } else { 32 | None 33 | } 34 | } 35 | } 36 | 37 | #[derive(Clone, Copy, Debug, PartialEq)] 38 | enum PRGSwitching { 39 | /// We switch 32KB banks at a time 40 | DoubleBank, 41 | /// We fix the first bank, and can switch the second 42 | Fix0, 43 | /// We fix the second bank, and can switch the first 44 | Fix1, 45 | } 46 | 47 | impl From for PRGSwitching { 48 | fn from(mode: u8) -> Self { 49 | match mode { 50 | 2 => PRGSwitching::Fix0, 51 | 3 => PRGSwitching::Fix1, 52 | _ => PRGSwitching::DoubleBank, 53 | } 54 | } 55 | } 56 | 57 | /// Represents the 32KB bank of PRG data 58 | struct PRGBanks { 59 | /// How many 16KB banks exist 60 | count: u8, 61 | /// The index of the first bank 62 | bank_0: usize, 63 | /// The index of the second bank 64 | bank_1: usize, 65 | /// This tells us how banks are switched around 66 | switching: PRGSwitching, 67 | control: u8, 68 | } 69 | 70 | impl PRGBanks { 71 | fn from_cart(cart: &Cart) -> Self { 72 | let count = cart.prg.len() / PRG_BANK_SIZE; 73 | PRGBanks { 74 | count: count as u8, 75 | bank_0: 0, 76 | bank_1: count - 1, 77 | switching: PRGSwitching::Fix1, 78 | control: 0, 79 | } 80 | } 81 | 82 | // Returns an index into cart.prg 83 | fn index(&self, address: u16) -> usize { 84 | if address >= 0xC000 { 85 | let shift = (address - 0xC000) as usize; 86 | self.bank_1 * PRG_BANK_SIZE + shift 87 | } else if address >= 0x8000 { 88 | let shift = (address - 0x8000) as usize; 89 | self.bank_0 * PRG_BANK_SIZE + shift 90 | } else { 91 | unreachable!("Address out of PRG bounds: {:X}", address); 92 | } 93 | } 94 | 95 | fn set_switching>(&mut self, switching: S) { 96 | let into = switching.into(); 97 | if self.switching != into { 98 | self.switching = into; 99 | let control = self.control; 100 | self.write(control); 101 | } 102 | } 103 | 104 | fn write(&mut self, control: u8) { 105 | self.control = control; 106 | match self.switching { 107 | PRGSwitching::Fix0 => { 108 | let bank = (control & 0xF) % self.count; 109 | self.bank_1 = bank as usize; 110 | } 111 | PRGSwitching::Fix1 => { 112 | let bank = (control & 0xF) % self.count; 113 | self.bank_0 = bank as usize; 114 | } 115 | PRGSwitching::DoubleBank => { 116 | let bank_0 = (control & 0xE) % self.count; 117 | self.bank_0 = bank_0 as usize; 118 | self.bank_1 = (bank_0 + 1) as usize; 119 | } 120 | } 121 | } 122 | } 123 | 124 | #[derive(Clone, Copy, Debug, PartialEq)] 125 | enum CHRSwitching { 126 | /// Switch a single bank at a time 127 | Single, 128 | /// Switch both banks at a time 129 | Double, 130 | } 131 | 132 | impl From for CHRSwitching { 133 | fn from(mode: u8) -> Self { 134 | match mode { 135 | 0 => CHRSwitching::Double, 136 | _ => CHRSwitching::Single, 137 | } 138 | } 139 | } 140 | 141 | struct CHRBanks { 142 | count: u8, 143 | bank_0: usize, 144 | bank_1: usize, 145 | switching: CHRSwitching, 146 | lower_control: u8, 147 | upper_control: u8, 148 | } 149 | 150 | impl CHRBanks { 151 | fn from_cart(cart: &Cart) -> Self { 152 | let count = cart.chr.len() / CHR_BANK_SIZE; 153 | CHRBanks { 154 | count: count as u8, 155 | bank_0: 0, 156 | bank_1: count - 1, 157 | switching: CHRSwitching::Single, 158 | lower_control: 0, 159 | upper_control: 0, 160 | } 161 | } 162 | 163 | fn index(&self, address: u16) -> usize { 164 | let index = if address < 0x1000 { 165 | let shift = address as usize; 166 | self.bank_0 * CHR_BANK_SIZE + shift 167 | } else if address < 0x2000 { 168 | let shift = (address - 0x1000) as usize; 169 | self.bank_1 * CHR_BANK_SIZE + shift 170 | } else { 171 | unreachable!("Address out of CHR bounds: {:X}", address); 172 | }; 173 | index 174 | } 175 | 176 | fn set_switching>(&mut self, switching: S) { 177 | let into = switching.into(); 178 | if self.switching != into { 179 | self.switching = into; 180 | let lower = self.lower_control; 181 | let upper = self.upper_control; 182 | self.write_lower(lower); 183 | self.write_upper(upper); 184 | } 185 | } 186 | 187 | fn write_lower(&mut self, control: u8) { 188 | self.lower_control = control; 189 | if self.switching == CHRSwitching::Double { 190 | let bank_0 = (control & 0x1E) % self.count; 191 | self.bank_0 = bank_0 as usize; 192 | self.bank_1 = (bank_0 + 1) as usize; 193 | } else { 194 | let bank = (control & 0x1F) % self.count; 195 | self.bank_0 = bank as usize; 196 | } 197 | } 198 | 199 | fn write_upper(&mut self, control: u8) { 200 | self.upper_control = control; 201 | if self.switching == CHRSwitching::Double { 202 | return; 203 | } 204 | let bank = (control & 0x1F) % self.count; 205 | self.bank_1 = bank as usize; 206 | } 207 | } 208 | 209 | /// The mapper for iNES 1. 210 | /// 211 | /// More info: https://wiki.nesdev.com/w/index.php/MMC1 212 | pub struct Mapper1 { 213 | /// The cartridge data 214 | cart: Cart, 215 | /// The program bank structure 216 | prg: PRGBanks, 217 | /// The graphical data bank structure 218 | chr: CHRBanks, 219 | /// This contains the current value of the shift register. 220 | /// 221 | /// This register is part of the behavior of the mapper, and can be used 222 | /// to control the bank switching behavior. 223 | shift_register: ShiftRegister, 224 | } 225 | 226 | impl Mapper1 { 227 | pub fn new(cart: Cart) -> Self { 228 | let prg = PRGBanks::from_cart(&cart); 229 | let chr = CHRBanks::from_cart(&cart); 230 | Mapper1 { 231 | cart, 232 | prg: prg, 233 | chr: chr, 234 | shift_register: ShiftRegister::default(), 235 | } 236 | } 237 | 238 | fn write_control(&mut self, control: u8) { 239 | let mirroring = Mirroring::from(control & 3); 240 | self.cart.mirroring = mirroring; 241 | let prg_mode = (control >> 2) & 3; 242 | self.prg.set_switching(prg_mode); 243 | let chr_mode = (control >> 4) & 1; 244 | self.chr.set_switching(chr_mode); 245 | } 246 | 247 | fn write_shift(&mut self, address: u16, shift: u8) { 248 | if address >= 0xE000 { 249 | self.prg.write(shift); 250 | } else if address >= 0xC000 { 251 | self.chr.write_upper(shift); 252 | } else if address >= 0xA000 { 253 | self.chr.write_lower(shift); 254 | } else { 255 | self.write_control(shift); 256 | } 257 | } 258 | } 259 | 260 | impl Mapper for Mapper1 { 261 | fn read(&self, address: u16) -> u8 { 262 | if address < 0x2000 { 263 | self.cart.chr[self.chr.index(address)] 264 | } else if address >= 0x8000 { 265 | self.cart.prg[self.prg.index(address)] 266 | } else if address >= 0x6000 { 267 | let shift = address - 0x6000; 268 | self.cart.sram[shift as usize] 269 | } else { 270 | panic!("Mapper1 unhandled read at {:X}", address); 271 | } 272 | } 273 | 274 | fn mirroring_mode(&self) -> Mirroring { 275 | self.cart.mirroring 276 | } 277 | 278 | fn write(&mut self, address: u16, value: u8) { 279 | if address < 0x2000 { 280 | self.cart.chr[self.chr.index(address)] = value; 281 | } else if address >= 0x8000 { 282 | if value & 0x80 != 0 { 283 | self.shift_register = ShiftRegister::default(); 284 | self.write_control(0xC); 285 | } else if let Some(shift) = self.shift_register.shift(value) { 286 | self.write_shift(address, shift); 287 | } 288 | } else if address >= 0x6000 { 289 | let shift = address - 0x6000; 290 | self.cart.sram[shift as usize] = value; 291 | } else { 292 | panic!("Mapper1 unhandled write at {:X}", address); 293 | } 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "arrayvec" 5 | version = "0.4.10" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | dependencies = [ 8 | "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", 9 | ] 10 | 11 | [[package]] 12 | name = "atty" 13 | version = "0.2.11" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | dependencies = [ 16 | "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", 17 | "termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 18 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 19 | ] 20 | 21 | [[package]] 22 | name = "autocfg" 23 | version = "0.1.4" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | 26 | [[package]] 27 | name = "bitflags" 28 | version = "1.1.0" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | 31 | [[package]] 32 | name = "byteorder" 33 | version = "1.3.1" 34 | source = "registry+https://github.com/rust-lang/crates.io-index" 35 | 36 | [[package]] 37 | name = "cast" 38 | version = "0.2.2" 39 | source = "registry+https://github.com/rust-lang/crates.io-index" 40 | 41 | [[package]] 42 | name = "cfg-if" 43 | version = "0.1.9" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | 46 | [[package]] 47 | name = "clap" 48 | version = "2.33.0" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | dependencies = [ 51 | "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 52 | "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 53 | "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 54 | ] 55 | 56 | [[package]] 57 | name = "cloudabi" 58 | version = "0.0.3" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | dependencies = [ 61 | "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 62 | ] 63 | 64 | [[package]] 65 | name = "criterion" 66 | version = "0.2.11" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | dependencies = [ 69 | "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 70 | "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 71 | "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", 72 | "criterion-plot 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 73 | "csv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", 74 | "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 75 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 76 | "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", 77 | "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 78 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 79 | "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 80 | "rand_xoshiro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 81 | "rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 82 | "rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 83 | "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", 84 | "serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", 85 | "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", 86 | "tinytemplate 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 87 | "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", 88 | ] 89 | 90 | [[package]] 91 | name = "criterion-plot" 92 | version = "0.3.1" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | dependencies = [ 95 | "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 96 | "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 97 | "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 98 | ] 99 | 100 | [[package]] 101 | name = "crossbeam-deque" 102 | version = "0.2.0" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | dependencies = [ 105 | "crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 106 | "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 107 | ] 108 | 109 | [[package]] 110 | name = "crossbeam-epoch" 111 | version = "0.3.1" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | dependencies = [ 114 | "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", 115 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 116 | "crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 117 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 118 | "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 119 | "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", 120 | "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 121 | ] 122 | 123 | [[package]] 124 | name = "crossbeam-utils" 125 | version = "0.2.2" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | dependencies = [ 128 | "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", 129 | ] 130 | 131 | [[package]] 132 | name = "csv" 133 | version = "1.0.7" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | dependencies = [ 136 | "csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 137 | "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 138 | "ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 139 | "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", 140 | ] 141 | 142 | [[package]] 143 | name = "csv-core" 144 | version = "0.1.5" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | dependencies = [ 147 | "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 148 | ] 149 | 150 | [[package]] 151 | name = "either" 152 | version = "1.5.2" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | 155 | [[package]] 156 | name = "fuchsia-cprng" 157 | version = "0.1.1" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | 160 | [[package]] 161 | name = "itertools" 162 | version = "0.8.0" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | dependencies = [ 165 | "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 166 | ] 167 | 168 | [[package]] 169 | name = "itoa" 170 | version = "0.4.4" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | 173 | [[package]] 174 | name = "lazy_static" 175 | version = "1.3.0" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | 178 | [[package]] 179 | name = "libc" 180 | version = "0.2.58" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | 183 | [[package]] 184 | name = "ludus" 185 | version = "0.2.2" 186 | dependencies = [ 187 | "criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 188 | ] 189 | 190 | [[package]] 191 | name = "memchr" 192 | version = "2.2.0" 193 | source = "registry+https://github.com/rust-lang/crates.io-index" 194 | dependencies = [ 195 | "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", 196 | ] 197 | 198 | [[package]] 199 | name = "memoffset" 200 | version = "0.2.1" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | 203 | [[package]] 204 | name = "nodrop" 205 | version = "0.1.13" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | 208 | [[package]] 209 | name = "num-traits" 210 | version = "0.2.8" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | dependencies = [ 213 | "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 214 | ] 215 | 216 | [[package]] 217 | name = "num_cpus" 218 | version = "1.10.0" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | dependencies = [ 221 | "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", 222 | ] 223 | 224 | [[package]] 225 | name = "numtoa" 226 | version = "0.1.0" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | 229 | [[package]] 230 | name = "proc-macro2" 231 | version = "0.4.30" 232 | source = "registry+https://github.com/rust-lang/crates.io-index" 233 | dependencies = [ 234 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 235 | ] 236 | 237 | [[package]] 238 | name = "quote" 239 | version = "0.6.12" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | dependencies = [ 242 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 243 | ] 244 | 245 | [[package]] 246 | name = "rand_core" 247 | version = "0.3.1" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | dependencies = [ 250 | "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 251 | ] 252 | 253 | [[package]] 254 | name = "rand_core" 255 | version = "0.4.0" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | 258 | [[package]] 259 | name = "rand_os" 260 | version = "0.1.3" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | dependencies = [ 263 | "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 264 | "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 265 | "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", 266 | "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 267 | "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 268 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 269 | ] 270 | 271 | [[package]] 272 | name = "rand_xoshiro" 273 | version = "0.1.0" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | dependencies = [ 276 | "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 277 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 278 | ] 279 | 280 | [[package]] 281 | name = "rayon" 282 | version = "1.0.3" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | dependencies = [ 285 | "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 286 | "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 287 | "rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)", 288 | ] 289 | 290 | [[package]] 291 | name = "rayon-core" 292 | version = "1.4.1" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | dependencies = [ 295 | "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 296 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 297 | "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", 298 | "num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", 299 | ] 300 | 301 | [[package]] 302 | name = "rdrand" 303 | version = "0.4.0" 304 | source = "registry+https://github.com/rust-lang/crates.io-index" 305 | dependencies = [ 306 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 307 | ] 308 | 309 | [[package]] 310 | name = "redox_syscall" 311 | version = "0.1.54" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | 314 | [[package]] 315 | name = "redox_termios" 316 | version = "0.1.1" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | dependencies = [ 319 | "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", 320 | ] 321 | 322 | [[package]] 323 | name = "ryu" 324 | version = "0.2.8" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | 327 | [[package]] 328 | name = "same-file" 329 | version = "1.0.4" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | dependencies = [ 332 | "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 333 | ] 334 | 335 | [[package]] 336 | name = "scopeguard" 337 | version = "0.3.3" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | 340 | [[package]] 341 | name = "serde" 342 | version = "1.0.92" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | 345 | [[package]] 346 | name = "serde_derive" 347 | version = "1.0.92" 348 | source = "registry+https://github.com/rust-lang/crates.io-index" 349 | dependencies = [ 350 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 351 | "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", 352 | "syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)", 353 | ] 354 | 355 | [[package]] 356 | name = "serde_json" 357 | version = "1.0.39" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | dependencies = [ 360 | "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 361 | "ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 362 | "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", 363 | ] 364 | 365 | [[package]] 366 | name = "syn" 367 | version = "0.15.34" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | dependencies = [ 370 | "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", 371 | "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", 372 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 373 | ] 374 | 375 | [[package]] 376 | name = "termion" 377 | version = "1.5.2" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | dependencies = [ 380 | "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", 381 | "numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 382 | "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", 383 | "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 384 | ] 385 | 386 | [[package]] 387 | name = "textwrap" 388 | version = "0.11.0" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | dependencies = [ 391 | "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 392 | ] 393 | 394 | [[package]] 395 | name = "tinytemplate" 396 | version = "1.0.2" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | dependencies = [ 399 | "serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)", 400 | "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", 401 | ] 402 | 403 | [[package]] 404 | name = "unicode-width" 405 | version = "0.1.5" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | 408 | [[package]] 409 | name = "unicode-xid" 410 | version = "0.1.0" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | 413 | [[package]] 414 | name = "walkdir" 415 | version = "2.2.7" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | dependencies = [ 418 | "same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 419 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 420 | "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 421 | ] 422 | 423 | [[package]] 424 | name = "winapi" 425 | version = "0.3.7" 426 | source = "registry+https://github.com/rust-lang/crates.io-index" 427 | dependencies = [ 428 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 429 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 430 | ] 431 | 432 | [[package]] 433 | name = "winapi-i686-pc-windows-gnu" 434 | version = "0.4.0" 435 | source = "registry+https://github.com/rust-lang/crates.io-index" 436 | 437 | [[package]] 438 | name = "winapi-util" 439 | version = "0.1.2" 440 | source = "registry+https://github.com/rust-lang/crates.io-index" 441 | dependencies = [ 442 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 443 | ] 444 | 445 | [[package]] 446 | name = "winapi-x86_64-pc-windows-gnu" 447 | version = "0.4.0" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | 450 | [metadata] 451 | "checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" 452 | "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" 453 | "checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf" 454 | "checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" 455 | "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" 456 | "checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427" 457 | "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" 458 | "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" 459 | "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 460 | "checksum criterion 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0363053954f3e679645fc443321ca128b7b950a6fe288cf5f9335cc22ee58394" 461 | "checksum criterion-plot 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76f9212ddf2f4a9eb2d401635190600656a1f88a932ef53d06e7fa4c7e02fb8e" 462 | "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" 463 | "checksum crossbeam-epoch 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "927121f5407de9956180ff5e936fe3cf4324279280001cd56b669d28ee7e9150" 464 | "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" 465 | "checksum csv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9044e25afb0924b5a5fc5511689b0918629e85d68ea591e5e87fbf1e85ea1b3b" 466 | "checksum csv-core 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa5cdef62f37e6ffe7d1f07a381bc0db32b7a3ff1cac0de56cb0d81e71f53d65" 467 | "checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" 468 | "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 469 | "checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" 470 | "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" 471 | "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" 472 | "checksum libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "6281b86796ba5e4366000be6e9e18bf35580adf9e63fbe2294aadb587613a319" 473 | "checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" 474 | "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" 475 | "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" 476 | "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" 477 | "checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" 478 | "checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" 479 | "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" 480 | "checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" 481 | "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 482 | "checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" 483 | "checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" 484 | "checksum rand_xoshiro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "03b418169fb9c46533f326efd6eed2576699c44ca92d3052a066214a8d828929" 485 | "checksum rayon 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "373814f27745b2686b350dd261bfd24576a6fb0e2c5919b3a2b6005f820b0473" 486 | "checksum rayon-core 1.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b055d1e92aba6877574d8fe604a63c8b5df60f60e5982bf7ccbb1338ea527356" 487 | "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 488 | "checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252" 489 | "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" 490 | "checksum ryu 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "b96a9549dc8d48f2c283938303c4b5a77aa29bfbc5b54b084fb1630408899a8f" 491 | "checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" 492 | "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" 493 | "checksum serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)" = "32746bf0f26eab52f06af0d0aa1984f641341d06d8d673c693871da2d188c9be" 494 | "checksum serde_derive 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)" = "46a3223d0c9ba936b61c0d2e3e559e3217dbfb8d65d06d26e8b3c25de38bae3e" 495 | "checksum serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)" = "5a23aa71d4a4d43fdbfaac00eff68ba8a06a51759a89ac3304323e800c4dd40d" 496 | "checksum syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)" = "a1393e4a97a19c01e900df2aec855a29f71cf02c402e2f443b8d2747c25c5dbe" 497 | "checksum termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dde0593aeb8d47accea5392b39350015b5eccb12c0d98044d856983d89548dea" 498 | "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 499 | "checksum tinytemplate 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4574b75faccaacddb9b284faecdf0b544b80b6b294f3d062d325c5726a209c20" 500 | "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" 501 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 502 | "checksum walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1" 503 | "checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" 504 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 505 | "checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" 506 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 507 | -------------------------------------------------------------------------------- /src/ppu.rs: -------------------------------------------------------------------------------- 1 | use super::memory::{Mapper, MemoryBus}; 2 | 3 | use crate::ports::{PixelBuffer, VideoDevice}; 4 | 5 | const PALETTE: [u32; 64] = [ 6 | 0xFF75_7575, 7 | 0xFF27_1B8F, 8 | 0xFF00_00AB, 9 | 0xFF47_009F, 10 | 0xFF8F_0077, 11 | 0xFFAB_0013, 12 | 0xFFA7_0000, 13 | 0xFF7F_0B00, 14 | 0xFF43_2F00, 15 | 0xFF00_4700, 16 | 0xFF00_5100, 17 | 0xFF00_3F17, 18 | 0xFF1B_3F5F, 19 | 0xFF00_0000, 20 | 0xFF00_0000, 21 | 0xFF00_0000, 22 | 0xFFBC_BCBC, 23 | 0xFF00_73EF, 24 | 0xFF23_3BEF, 25 | 0xFF83_00F3, 26 | 0xFFBF_00BF, 27 | 0xFFE7_005B, 28 | 0xFFDB_2B00, 29 | 0xFFCB_4F0F, 30 | 0xFF8B_7300, 31 | 0xFF00_9700, 32 | 0xFF00_AB00, 33 | 0xFF00_933B, 34 | 0xFF00_838B, 35 | 0xFF00_0000, 36 | 0xFF00_0000, 37 | 0xFF00_0000, 38 | 0xFFFF_FFFF, 39 | 0xFF3F_BFFF, 40 | 0xFF5F_97FF, 41 | 0xFFA7_8BFD, 42 | 0xFFF7_7BFF, 43 | 0xFFFF_77B7, 44 | 0xFFFF_7763, 45 | 0xFFFF_9B3B, 46 | 0xFFF3_BF3F, 47 | 0xFF83_D313, 48 | 0xFF4F_DF4B, 49 | 0xFF58_F898, 50 | 0xFF00_EBDB, 51 | 0xFF00_0000, 52 | 0xFF00_0000, 53 | 0xFF00_0000, 54 | 0xFFFF_FFFF, 55 | 0xFFAB_E7FF, 56 | 0xFFC7_D7FF, 57 | 0xFFD7_CBFF, 58 | 0xFFFF_C7FF, 59 | 0xFFFF_C7DB, 60 | 0xFFFF_BFB3, 61 | 0xFFFF_DBAB, 62 | 0xFFFF_E7A3, 63 | 0xFFE3_FFA3, 64 | 0xFFAB_F3BF, 65 | 0xFFB3_FFCF, 66 | 0xFF9F_FFF3, 67 | 0xFF00_0000, 68 | 0xFF00_0000, 69 | 0xFF00_0000, 70 | ]; 71 | 72 | struct NameTables([u8; 2048]); 73 | 74 | impl Default for NameTables { 75 | fn default() -> Self { 76 | NameTables([0; 2048]) 77 | } 78 | } 79 | 80 | impl NameTables { 81 | fn read(&self, addr: u16) -> u8 { 82 | self.0[(addr % 2048) as usize] 83 | } 84 | 85 | fn write(&mut self, addr: u16, val: u8) { 86 | self.0[(addr % 2048) as usize] = val; 87 | } 88 | } 89 | 90 | pub struct OAM(pub [u8; 256]); 91 | 92 | impl Default for OAM { 93 | fn default() -> Self { 94 | OAM([0; 256]) 95 | } 96 | } 97 | 98 | /// Represents openly modifiable PPU state 99 | #[derive(Default)] 100 | pub struct PPUState { 101 | // Memory 102 | palettes: [u8; 32], 103 | nametables: NameTables, 104 | pub oam: OAM, // public to allow cpu DMA transfer 105 | /// Current vram address (15 bit) 106 | pub v: u16, // Public for access during CPU IO reading 107 | /// Temporary vram address (15 bit) 108 | t: u16, 109 | /// Write toggle (1 bit) 110 | w: u8, 111 | /// Fine x scroll (3 bit) 112 | x: u8, 113 | register: u8, 114 | // Nmi flags 115 | nmi_occurred: bool, 116 | nmi_output: bool, 117 | nmi_previous: bool, 118 | nmi_delay: u8, 119 | 120 | // $2000 PPUCTRL 121 | // 0: $2000, 1: $2400, 2: $2800, 3: $2C00 122 | flg_nametable: u8, 123 | // 0: add 1, 1: add 32 124 | pub flg_increment: u8, // Pub for access during Bus IO 125 | // 0: $0000, 1: $1000 126 | flg_spritetable: u8, 127 | // 0: $0000, 1: $1000 128 | flg_backgroundtable: u8, 129 | // 0: 8x8, 1: 8x16 130 | flg_spritesize: u8, 131 | // 0: read EXT, 1: write EXT 132 | flg_masterslave: u8, 133 | 134 | // $2001 PPUMASK 135 | // 0: color, 1: grayscale 136 | flg_grayscale: u8, 137 | // 0: hide, 1: show 138 | flg_showleftbg: u8, 139 | // 0: hide, 1: sho 140 | flg_showleftsprites: u8, 141 | // 0: hide, 1: show 142 | flg_showbg: u8, 143 | // 0: hide, 1: show 144 | flg_showsprites: u8, 145 | // 0: normal, 1: emphasized 146 | flg_redtint: u8, 147 | // 0: normal, 1: emphasized 148 | flg_greentint: u8, 149 | // 0: normal, 1: emphasized 150 | flg_bluetint: u8, 151 | 152 | // $2002 PPUSTATUS 153 | flg_sprite0hit: u8, 154 | flg_spriteoverflow: u8, 155 | 156 | // $2003 OAMADDR 157 | pub oam_address: u8, // Pub for DMA transfer 158 | 159 | // $2007 PPUDATA 160 | pub buffer_data: u8, // Pub for Bus access during CPU IO 161 | } 162 | 163 | impl PPUState { 164 | pub fn new() -> Self { 165 | PPUState::default() 166 | } 167 | 168 | fn nmi_change(&mut self) { 169 | let nmi = self.nmi_output && self.nmi_occurred; 170 | if nmi && !self.nmi_previous { 171 | self.nmi_delay = 15; 172 | } 173 | self.nmi_previous = nmi; 174 | } 175 | 176 | fn read(&self, mapper: &dyn Mapper, address: u16) -> u8 { 177 | let wrapped = address % 0x4000; 178 | match wrapped { 179 | a if a < 0x2000 => mapper.read(a), 180 | a if a < 0x3F00 => { 181 | let mode = mapper.mirroring_mode(); 182 | let mirrored = mode.mirror_address(a); 183 | self.nametables.read(mirrored) 184 | } 185 | a if a < 0x4000 => self.read_palette(a % 32), 186 | a => { 187 | panic!("Unhandled PPU memory read at {:X}", a); 188 | } 189 | } 190 | } 191 | 192 | fn write(&mut self, mapper: &mut dyn Mapper, address: u16, value: u8) { 193 | let wrapped = address % 0x4000; 194 | match wrapped { 195 | a if a < 0x2000 => mapper.write(a, value), 196 | a if a < 0x3F00 => { 197 | let mode = mapper.mirroring_mode(); 198 | let mirrored = mode.mirror_address(a); 199 | self.nametables.write(mirrored, value); 200 | } 201 | a if a < 0x4000 => { 202 | self.write_palette(a % 32, value); 203 | } 204 | a => { 205 | panic!("Unhandled PPU memory write at {:X}", a); 206 | } 207 | } 208 | } 209 | 210 | fn read_palette(&self, address: u16) -> u8 { 211 | let wrapped = if address >= 16 && address % 4 == 0 { 212 | address - 16 213 | } else { 214 | address 215 | }; 216 | self.palettes[wrapped as usize] 217 | } 218 | 219 | fn write_palette(&mut self, address: u16, value: u8) { 220 | let wrapped = if address >= 16 && address % 4 == 0 { 221 | address - 16 222 | } else { 223 | address 224 | }; 225 | self.palettes[wrapped as usize] = value; 226 | } 227 | 228 | /// Needs the wrapper because it might read from CHR data 229 | pub fn read_register(&mut self, m: &dyn Mapper, address: u16) -> u8 { 230 | match address { 231 | 0x2002 => self.read_status(), 232 | 0x2004 => self.read_oam_data(), 233 | 0x2007 => self.read_data(m), 234 | _ => 0, 235 | } 236 | } 237 | 238 | fn read_status(&mut self) -> u8 { 239 | let mut res = self.register & 0x1F; 240 | res |= self.flg_spriteoverflow << 5; 241 | res |= self.flg_sprite0hit << 6; 242 | if self.nmi_occurred { 243 | res |= 1 << 7; 244 | } 245 | self.nmi_occurred = false; 246 | self.nmi_change(); 247 | self.w = 0; 248 | res 249 | } 250 | 251 | fn read_oam_data(&self) -> u8 { 252 | self.oam.0[self.oam_address as usize] 253 | } 254 | 255 | fn read_data(&mut self, mapper: &dyn Mapper) -> u8 { 256 | let v = self.v; 257 | let mut value = self.read(mapper, v); 258 | if v % 0x4000 < 0x3F00 { 259 | std::mem::swap(&mut self.buffer_data, &mut value); 260 | } else { 261 | let read = self.read(mapper, v - 0x1000); 262 | self.buffer_data = read; 263 | } 264 | if self.flg_increment == 0 { 265 | self.v += 1; 266 | } else { 267 | self.v += 32; 268 | } 269 | value 270 | } 271 | 272 | pub fn write_register(&mut self, mapper: &mut dyn Mapper, address: u16, value: u8) { 273 | self.register = value; 274 | match address { 275 | 0x2000 => self.write_control(value), 276 | 0x2001 => self.write_mask(value), 277 | 0x2003 => self.write_oam_address(value), 278 | 0x2004 => self.write_oam_data(value), 279 | 0x2005 => self.write_scroll(value), 280 | 0x2006 => self.write_address(value), 281 | 0x2007 => self.write_data(mapper, value), 282 | // This case can never be reached, since the address is % 8, 283 | _ => {} 284 | } 285 | } 286 | 287 | // See: https://wiki.nesdev.com/w/index.php/PPU_registers#PPUCTRL 288 | fn write_control(&mut self, value: u8) { 289 | self.flg_nametable = value & 2; 290 | self.flg_increment = (value >> 2) & 1; 291 | self.flg_spritetable = (value >> 3) & 1; 292 | self.flg_backgroundtable = (value >> 4) & 1; 293 | self.flg_spritesize = (value >> 5) & 1; 294 | self.flg_masterslave = (value >> 6) & 1; 295 | self.nmi_output = (value >> 7) & 1 == 1; 296 | self.nmi_change(); 297 | self.t = (self.t & 0xF3FF) | ((u16::from(value) & 0x03) << 10); 298 | } 299 | 300 | fn write_mask(&mut self, value: u8) { 301 | self.flg_grayscale = value & 1; 302 | self.flg_showleftbg = (value >> 1) & 1; 303 | self.flg_showleftsprites = (value >> 2) & 1; 304 | self.flg_showbg = (value >> 3) & 1; 305 | self.flg_showsprites = (value >> 4) & 1; 306 | self.flg_redtint = (value >> 5) & 1; 307 | self.flg_greentint = (value >> 6) & 1; 308 | self.flg_bluetint = (value >> 7) & 1; 309 | } 310 | 311 | fn write_oam_address(&mut self, value: u8) { 312 | self.oam_address = value; 313 | } 314 | 315 | fn write_oam_data(&mut self, value: u8) { 316 | let a = self.oam_address as usize; 317 | self.oam.0[a] = value; 318 | self.oam_address = self.oam_address.wrapping_add(1); 319 | } 320 | 321 | fn write_scroll(&mut self, value: u8) { 322 | if self.w == 0 { 323 | self.t = (self.t & 0x7FE0) | (u16::from(value) >> 3); 324 | self.x = value & 0x7; 325 | self.w = 1; 326 | } else { 327 | let s1 = (u16::from(value) & 0x7) << 12; 328 | self.t = (self.t & 0xC1F) | ((u16::from(value) & 0xF8) << 2) | s1; 329 | self.w = 0; 330 | } 331 | } 332 | 333 | fn write_address(&mut self, value: u8) { 334 | if self.w == 0 { 335 | self.t = (self.t & 0x80FF) | ((u16::from(value) & 0x3F) << 8); 336 | self.w = 1; 337 | } else { 338 | self.t = (self.t & 0xFF00) | u16::from(value); 339 | self.v = self.t; 340 | self.w = 0; 341 | } 342 | } 343 | 344 | fn write_data(&mut self, mapper: &mut Mapper, value: u8) { 345 | let v = self.v; 346 | self.write(mapper, v, value); 347 | if self.flg_increment == 0 { 348 | self.v += 1; 349 | } else { 350 | self.v += 32; 351 | } 352 | } 353 | 354 | fn copy_y(&mut self) { 355 | let mask = 0b0111_1011_1110_0000; 356 | self.v = (self.v & !mask) | (self.t & mask); 357 | } 358 | 359 | fn increment_x(&mut self) { 360 | if self.v & 0x001F == 31 { 361 | self.v &= 0xFFE0; 362 | self.v ^= 0x0400; 363 | } else { 364 | self.v += 1; 365 | } 366 | } 367 | 368 | fn increment_y(&mut self) { 369 | if self.v & 0x7000 != 0x7000 { 370 | self.v += 0x1000; 371 | } else { 372 | self.v &= 0x8FFF; 373 | let y = match (self.v & 0x3E0) >> 5 { 374 | 29 => { 375 | self.v ^= 0x800; 376 | 0 377 | } 378 | 31 => 0, 379 | val => val + 1, 380 | }; 381 | self.v = (self.v & 0xFC1F) | (y << 5); 382 | } 383 | } 384 | 385 | fn copy_x(&mut self) { 386 | let mask = 0b0000_0100_0001_1111; 387 | self.v = (self.v & !mask) | (self.t & mask); 388 | } 389 | } 390 | 391 | /// Represents the PPU 392 | pub(crate) struct PPU { 393 | cycle: i32, 394 | scanline: i32, 395 | 396 | // This need to be boxed to avoid blowing up the stack 397 | v_buffer: Box, 398 | 399 | // Background temporary variables 400 | nametable_byte: u8, 401 | attributetable_byte: u8, 402 | lowtile_byte: u8, 403 | hightile_byte: u8, 404 | tiledata: u64, 405 | 406 | /// Even / odd frame flag (1 bit) 407 | f: u8, 408 | // Sprite temp variables 409 | sprite_count: i32, 410 | sprite_patterns: [u32; 8], 411 | sprite_positions: [u8; 8], 412 | sprite_priorities: [u8; 8], 413 | sprite_indices: [u8; 8], //mem: Rc> 414 | } 415 | 416 | impl PPU { 417 | /// Creates a new PPU 418 | pub fn new(m: &mut MemoryBus) -> Self { 419 | let mut ppu = PPU { 420 | cycle: 0, 421 | scanline: 0, 422 | v_buffer: Box::new(PixelBuffer::default()), 423 | nametable_byte: 0, 424 | attributetable_byte: 0, 425 | lowtile_byte: 0, 426 | hightile_byte: 0, 427 | tiledata: 0, 428 | f: 0, 429 | sprite_count: 0, 430 | sprite_patterns: [0; 8], 431 | sprite_positions: [0; 8], 432 | sprite_priorities: [0; 8], 433 | sprite_indices: [0; 8], 434 | }; 435 | ppu.reset(m); 436 | ppu 437 | } 438 | 439 | /// Resets the PPU to its initial state 440 | pub fn reset(&mut self, m: &mut MemoryBus) { 441 | self.cycle = 340; 442 | self.scanline = 240; 443 | m.ppu.write_control(0); 444 | m.ppu.write_mask(0); 445 | m.ppu.write_oam_address(0); 446 | } 447 | 448 | /// Used to clear vbuffers to make image completely neutral 449 | /// This isn't called in the standard reset. 450 | pub fn clear_vbuffers(&mut self) { 451 | self.v_buffer = Box::new(PixelBuffer::default()); 452 | } 453 | 454 | fn fetch_nametable_byte(&mut self, m: &mut MemoryBus) { 455 | let v = m.ppu.v; 456 | let address = 0x2000 | (v & 0x0FFF); 457 | self.nametable_byte = m.ppu.read(&*m.mapper, address); 458 | } 459 | 460 | fn fetch_attributetable_byte(&mut self, m: &mut MemoryBus) { 461 | let v = m.ppu.v; 462 | let address = 0x23C0 | (v & 0x0C00) | ((v >> 4) & 0x38) | ((v >> 2) & 0x07); 463 | let shift = ((v >> 4) & 4) | (v & 2); 464 | let read = m.ppu.read(&*m.mapper, address); 465 | self.attributetable_byte = ((read >> shift) & 3) << 2; 466 | } 467 | 468 | fn fetch_lowtile_byte(&mut self, m: &mut MemoryBus) { 469 | let fine_y = (m.ppu.v >> 12) & 7; 470 | let table = m.ppu.flg_backgroundtable; 471 | let tile = u16::from(self.nametable_byte); 472 | let address = 0x1000 * u16::from(table) + tile * 16 + fine_y; 473 | self.lowtile_byte = m.ppu.read(&*m.mapper, address); 474 | } 475 | 476 | fn fetch_hightile_byte(&mut self, m: &mut MemoryBus) { 477 | let fine_y = (m.ppu.v >> 12) & 7; 478 | let table = m.ppu.flg_backgroundtable; 479 | let tile = u16::from(self.nametable_byte); 480 | let address = 0x1000 * u16::from(table) + tile * 16 + fine_y; 481 | self.hightile_byte = m.ppu.read(&*m.mapper, address + 8); 482 | } 483 | 484 | fn store_tiledata(&mut self) { 485 | let mut data: u32 = 0; 486 | for _ in 0..8 { 487 | let a = self.attributetable_byte; 488 | let p1 = (self.lowtile_byte & 0x80) >> 7; 489 | let p2 = (self.hightile_byte & 0x80) >> 6; 490 | self.lowtile_byte <<= 1; 491 | self.hightile_byte <<= 1; 492 | data <<= 4; 493 | data |= u32::from(a | p1 | p2); 494 | } 495 | self.tiledata |= u64::from(data); 496 | } 497 | 498 | fn fetch_sprite_pattern(&self, m: &mut MemoryBus, i: usize, mut row: i32) -> u32 { 499 | let mut tile = m.ppu.oam.0[i * 4 + 1]; 500 | let attributes = m.ppu.oam.0[i * 4 + 2]; 501 | let address = if m.ppu.flg_spritesize == 0 { 502 | if attributes & 0x80 == 0x80 { 503 | row = 7 - row; 504 | } 505 | let table = m.ppu.flg_spritetable; 506 | 0x1000 * u16::from(table) + u16::from(tile) * 16 + (row as u16) 507 | } else { 508 | if attributes & 0x80 == 0x80 { 509 | row = 15 - row; 510 | } 511 | let table = tile & 1; 512 | tile &= 0xFE; 513 | if row > 7 { 514 | tile += 1; 515 | row -= 8; 516 | } 517 | 0x1000 * u16::from(table) + u16::from(tile) * 16 + (row as u16) 518 | }; 519 | let a = (attributes & 3) << 2; 520 | let mut lowtile_byte = m.ppu.read(&*m.mapper, address); 521 | let mut hightile_byte = m.ppu.read(&*m.mapper, address + 8); 522 | let mut data: u32 = 0; 523 | for _ in 0..8 { 524 | let (p1, p2) = if attributes & 0x40 == 0x40 { 525 | let p1 = lowtile_byte & 1; 526 | let p2 = (hightile_byte & 1) << 1; 527 | lowtile_byte >>= 1; 528 | hightile_byte >>= 1; 529 | (p1, p2) 530 | } else { 531 | let p1 = (lowtile_byte & 0x80) >> 7; 532 | let p2 = (hightile_byte & 0x80) >> 6; 533 | lowtile_byte <<= 1; 534 | hightile_byte <<= 1; 535 | (p1, p2) 536 | }; 537 | data <<= 4; 538 | data |= u32::from(a | p1 | p2); 539 | } 540 | data 541 | } 542 | 543 | fn evaluate_sprites(&mut self, m: &mut MemoryBus) { 544 | let h: i32 = if m.ppu.flg_spritesize == 0 { 8 } else { 16 }; 545 | let mut count = 0; 546 | for i in 0..64 { 547 | let y = m.ppu.oam.0[i * 4]; 548 | let a_reg = m.ppu.oam.0[i * 4 + 2]; 549 | let x = m.ppu.oam.0[i * 4 + 3]; 550 | let row = self.scanline - i32::from(y); 551 | if row < 0 || row >= h { 552 | continue; 553 | } 554 | if count < 8 { 555 | let pattern = self.fetch_sprite_pattern(m, i, row); 556 | self.sprite_patterns[count] = pattern; 557 | self.sprite_positions[count] = x; 558 | self.sprite_priorities[count] = (a_reg >> 5) & 1; 559 | self.sprite_indices[count] = i as u8; 560 | } 561 | count += 1; 562 | } 563 | if count > 8 { 564 | count = 8; 565 | m.ppu.flg_spriteoverflow = 1; 566 | } 567 | self.sprite_count = count as i32; 568 | } 569 | 570 | fn set_vblank(&mut self, m: &mut MemoryBus, video: &mut impl VideoDevice) { 571 | video.blit_pixels(self.v_buffer.as_ref()); 572 | m.ppu.nmi_occurred = true; 573 | m.ppu.nmi_change(); 574 | } 575 | 576 | fn clear_vblank(&self, m: &mut MemoryBus) { 577 | m.ppu.nmi_occurred = false; 578 | m.ppu.nmi_change(); 579 | } 580 | 581 | fn fetch_tiledata(&self) -> u32 { 582 | (self.tiledata >> 32) as u32 583 | } 584 | 585 | fn background_pixel(&mut self, m: &mut MemoryBus) -> u8 { 586 | if m.ppu.flg_showbg == 0 { 587 | 0 588 | } else { 589 | let data = self.fetch_tiledata() >> ((7 - m.ppu.x) * 4); 590 | (data & 0x0F) as u8 591 | } 592 | } 593 | 594 | fn sprite_pixel(&mut self, m: &mut MemoryBus) -> (u8, u8) { 595 | if m.ppu.flg_showsprites == 0 { 596 | (0, 0) 597 | } else { 598 | for i in 0..self.sprite_count { 599 | let sp_off = i32::from(self.sprite_positions[i as usize]); 600 | let mut offset = (self.cycle - 1) - sp_off; 601 | if offset < 0 || offset > 7 { 602 | continue; 603 | } 604 | offset = 7 - offset; 605 | let shift = (offset * 4) as u8; 606 | let pattern = self.sprite_patterns[i as usize]; 607 | let color = ((pattern >> shift) & 0x0F) as u8; 608 | if color % 4 == 0 { 609 | continue; 610 | } 611 | return (i as u8, color); 612 | } 613 | (0, 0) 614 | } 615 | } 616 | 617 | fn render_pixel(&mut self, m: &mut MemoryBus) { 618 | let x = self.cycle - 1; 619 | let y = self.scanline; 620 | let mut background = self.background_pixel(m); 621 | let (i, mut sprite) = self.sprite_pixel(m); 622 | if x < 8 && m.ppu.flg_showleftbg == 0 { 623 | background = 0; 624 | } 625 | if x < 8 && m.ppu.flg_showleftsprites == 0 { 626 | sprite = 0; 627 | } 628 | let bg = background % 4 != 0; 629 | let sp = sprite % 4 != 0; 630 | let color = match (bg, sp) { 631 | (false, false) => 0, 632 | (false, true) => sprite | 0x10, 633 | (true, false) => background, 634 | (true, true) => { 635 | let ind = i as usize; 636 | if self.sprite_indices[ind] == 0 && x < 255 { 637 | m.ppu.flg_sprite0hit = 1; 638 | } 639 | if self.sprite_priorities[ind] == 0 { 640 | sprite | 0x10 641 | } else { 642 | background 643 | } 644 | } 645 | }; 646 | let mut color_index = m.ppu.read_palette(u16::from(color)) % 64; 647 | if m.ppu.flg_grayscale != 0 { 648 | color_index &= 0x30; 649 | } 650 | let argb = PALETTE[color_index as usize]; 651 | self.v_buffer.write(x as usize, y as usize, argb); 652 | } 653 | 654 | /// Steps the ppu forward 655 | pub fn step(&mut self, m: &mut MemoryBus, video: &mut impl VideoDevice) -> bool { 656 | self.tick(m); 657 | let rendering = m.ppu.flg_showbg != 0 || m.ppu.flg_showsprites != 0; 658 | let preline = self.scanline == 261; 659 | let visibleline = self.scanline < 240; 660 | let renderline = preline || visibleline; 661 | let prefetch_cycle = self.cycle >= 321 && self.cycle <= 336; 662 | let visible_cycle = self.cycle >= 1 && self.cycle <= 256; 663 | let fetch_cycle = prefetch_cycle || visible_cycle; 664 | 665 | // Background logic 666 | if rendering { 667 | if visibleline && visible_cycle { 668 | self.render_pixel(m) 669 | } 670 | if renderline && fetch_cycle { 671 | self.tiledata <<= 4; 672 | match self.cycle % 8 { 673 | 1 => self.fetch_nametable_byte(m), 674 | 3 => self.fetch_attributetable_byte(m), 675 | 5 => self.fetch_lowtile_byte(m), 676 | 7 => self.fetch_hightile_byte(m), 677 | 0 => self.store_tiledata(), 678 | _ => {} 679 | } 680 | } 681 | if preline && self.cycle >= 280 && self.cycle <= 304 { 682 | m.ppu.copy_y(); 683 | } 684 | if renderline { 685 | if fetch_cycle && self.cycle % 8 == 0 { 686 | m.ppu.increment_x(); 687 | } 688 | if self.cycle == 256 { 689 | m.ppu.increment_y(); 690 | } 691 | if self.cycle == 257 { 692 | m.ppu.copy_x(); 693 | } 694 | } 695 | } 696 | 697 | // Sprite logic 698 | if rendering && self.cycle == 257 { 699 | if visibleline { 700 | self.evaluate_sprites(m); 701 | } else { 702 | self.sprite_count = 0; 703 | } 704 | } 705 | 706 | let mut frame_happened = false; 707 | // Vblank logic 708 | if self.scanline == 241 && self.cycle == 1 { 709 | self.set_vblank(m, video); 710 | frame_happened = true; 711 | } 712 | if preline && self.cycle == 1 { 713 | self.clear_vblank(m); 714 | m.ppu.flg_sprite0hit = 0; 715 | m.ppu.flg_spriteoverflow = 0; 716 | } 717 | frame_happened 718 | } 719 | 720 | fn tick(&mut self, m: &mut MemoryBus) { 721 | if m.ppu.nmi_delay > 0 { 722 | m.ppu.nmi_delay -= 1; 723 | let was_nmi = m.ppu.nmi_output && m.ppu.nmi_occurred; 724 | if m.ppu.nmi_delay == 0 && was_nmi { 725 | m.cpu.set_nmi(); 726 | } 727 | } 728 | let show_something = m.ppu.flg_showbg != 0 || m.ppu.flg_showsprites != 0; 729 | let should_reset = self.f == 1 && self.scanline == 261 && self.cycle == 339; 730 | if show_something && should_reset { 731 | self.cycle = 0; 732 | self.scanline = 0; 733 | self.f ^= 1; 734 | return; 735 | } 736 | 737 | self.cycle += 1; 738 | if self.cycle > 340 { 739 | self.cycle = 0; 740 | self.scanline += 1; 741 | if self.scanline > 261 { 742 | self.scanline = 0; 743 | self.f ^= 1; 744 | } 745 | } 746 | } 747 | } 748 | -------------------------------------------------------------------------------- /src/cpu.rs: -------------------------------------------------------------------------------- 1 | use super::memory::MemoryBus; 2 | use crate::controller::ButtonState; 3 | 4 | // The various addressing modes of each opcode 5 | const OP_MODES: [u8; 256] = [ 6 | 6, 7, 6, 7, 11, 11, 11, 11, 6, 5, 4, 5, 1, 1, 1, 1, 10, 9, 6, 9, 12, 12, 12, 12, 6, 3, 6, 3, 2, 7 | 2, 2, 2, 1, 7, 6, 7, 11, 11, 11, 11, 6, 5, 4, 5, 1, 1, 1, 1, 10, 9, 6, 9, 12, 12, 12, 12, 6, 3, 8 | 6, 3, 2, 2, 2, 2, 6, 7, 6, 7, 11, 11, 11, 11, 6, 5, 4, 5, 1, 1, 1, 1, 10, 9, 6, 9, 12, 12, 12, 9 | 12, 6, 3, 6, 3, 2, 2, 2, 2, 6, 7, 6, 7, 11, 11, 11, 11, 6, 5, 4, 5, 8, 1, 1, 1, 10, 9, 6, 9, 10 | 12, 12, 12, 12, 6, 3, 6, 3, 2, 2, 2, 2, 5, 7, 5, 7, 11, 11, 11, 11, 6, 5, 6, 5, 1, 1, 1, 1, 10, 11 | 9, 6, 9, 12, 12, 13, 13, 6, 3, 6, 3, 2, 2, 3, 3, 5, 7, 5, 7, 11, 11, 11, 11, 6, 5, 6, 5, 1, 1, 12 | 1, 1, 10, 9, 6, 9, 12, 12, 13, 13, 6, 3, 6, 3, 2, 2, 3, 3, 5, 7, 5, 7, 11, 11, 11, 11, 6, 5, 6, 13 | 5, 1, 1, 1, 1, 10, 9, 6, 9, 12, 12, 12, 12, 6, 3, 6, 3, 2, 2, 2, 2, 5, 7, 5, 7, 11, 11, 11, 11, 14 | 6, 5, 6, 5, 1, 1, 1, 1, 10, 9, 6, 9, 12, 12, 12, 12, 6, 3, 6, 3, 2, 2, 2, 2, 15 | ]; 16 | 17 | // The size of each instruction in bytes 18 | // we sacrifice space to avoid casting 19 | const OP_SIZES: [u16; 256] = [ 20 | 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, 21 | 3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, 22 | 1, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, 23 | 1, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, 24 | 2, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 0, 3, 0, 0, 25 | 2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, 26 | 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, 27 | 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, 28 | ]; 29 | 30 | // How many cycles each instruction takes 31 | const OP_CYCLES: [i32; 256] = [ 32 | 7, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6, 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, 33 | 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6, 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, 34 | 6, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6, 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, 35 | 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6, 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, 36 | 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, 2, 6, 2, 6, 4, 4, 4, 4, 2, 5, 2, 5, 5, 5, 5, 5, 37 | 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, 2, 5, 2, 5, 4, 4, 4, 4, 2, 4, 2, 4, 4, 4, 4, 4, 38 | 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, 39 | 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, 40 | ]; 41 | 42 | // The op codes which add a cycle when crossing pages accessing memory 43 | // doesn't include branch instructions, since the page crossing check 44 | // happens when the branch is known to be successful or not 45 | const EXTRA_PAGECYCLE_OPS: [u8; 23] = [ 46 | 0x7D, 0x79, 0x71, 0x3D, 0x39, 0x31, 0xDD, 0xD9, 0xD1, 0x5D, 0x59, 0x51, 0xBD, 0xB9, 0xB1, 0xBE, 47 | 0xBC, 0x1D, 0x19, 0x11, 0xFD, 0xF9, 0xF1, 48 | ]; 49 | 50 | /// Represents the type of addressing an op uses 51 | #[derive(Clone, Copy)] 52 | enum Addressing { 53 | Absolute, 54 | AbsoluteX, 55 | AbsoluteY, 56 | Accumulator, 57 | Immediate, 58 | Implied, 59 | IndexedIndirect, 60 | Indirect, 61 | IndirectIndexed, 62 | Relative, 63 | ZeroPage, 64 | ZeroPageX, 65 | ZeroPageY, 66 | } 67 | 68 | impl Addressing { 69 | // This can't handle every byte, but is only used 70 | // for a more compact idea. 71 | fn from_byte(mode: u8) -> Self { 72 | use self::Addressing::*; 73 | let modes = [ 74 | Absolute, 75 | AbsoluteX, 76 | AbsoluteY, 77 | Accumulator, 78 | Immediate, 79 | Implied, 80 | IndexedIndirect, 81 | Indirect, 82 | IndirectIndexed, 83 | Relative, 84 | ZeroPage, 85 | ZeroPageX, 86 | ZeroPageY, 87 | ]; 88 | modes[mode as usize - 1] 89 | } 90 | } 91 | 92 | /// Represents the different types of Interrupts the CPU might deal with 93 | #[derive(Clone)] 94 | enum Interrupt { 95 | NMI, 96 | IRQ, 97 | } 98 | 99 | /// Returns true if two addresses return different pages 100 | fn pages_differ(a: u16, b: u16) -> bool { 101 | a & 0xFF00 != b & 0xFF 102 | } 103 | 104 | /// Returns the number of extra cycles used by a branch instruction 105 | fn branch_cycles(pc: u16, address: u16) -> i32 { 106 | if pages_differ(pc, address) { 107 | 2 108 | } else { 109 | 1 110 | } 111 | } 112 | 113 | /// Represents public CPU state 114 | #[derive(Default)] 115 | pub struct CPUState { 116 | /// Represents the interrupt, None representing no interrupt 117 | interrupt: Option, 118 | /// Used to add stalls to cpu cycles 119 | stall: i32, 120 | } 121 | 122 | impl CPUState { 123 | pub fn new() -> Self { 124 | CPUState::default() 125 | } 126 | 127 | pub fn set_nmi(&mut self) { 128 | self.interrupt = Some(Interrupt::NMI); 129 | } 130 | 131 | pub fn set_irq(&mut self) { 132 | self.interrupt = Some(Interrupt::IRQ); 133 | } 134 | 135 | pub fn clear_interrupt(&mut self) { 136 | self.interrupt = None; 137 | } 138 | 139 | pub fn add_stall(&mut self, amount: i32) { 140 | self.stall += amount; 141 | } 142 | } 143 | 144 | /// Represents possible CPU interrupts 145 | /// Represents the CPU 146 | pub(crate) struct CPU { 147 | /// Program counter 148 | pc: u16, 149 | /// Stack pointer 150 | sp: u8, 151 | /// Accumulator Register 152 | a: u8, 153 | /// X Register 154 | x: u8, 155 | /// Y Register 156 | y: u8, 157 | // Flags are represented as multiple bytes for more conveniant use 158 | // bools could be used instead, but bytes make for better arithmetic 159 | /// Carry Flag 160 | c: u8, 161 | /// Zero Flag 162 | z: u8, 163 | /// Interrupt disable Flag 164 | i: u8, 165 | /// Decimal mode Flag 166 | d: u8, 167 | /// Break command Flag 168 | b: u8, 169 | /// Unused Flag 170 | u: u8, 171 | /// Overflow Flag 172 | v: u8, 173 | /// Negative Flag 174 | n: u8, 175 | /// Shared acess to the memory bus along with the ppu, 176 | pub mem: MemoryBus, 177 | } 178 | 179 | impl CPU { 180 | /// Creates a new CPU 181 | pub fn new(mem: MemoryBus) -> Self { 182 | let mut cpu = CPU { 183 | pc: 0, 184 | sp: 0, 185 | a: 0, 186 | x: 0, 187 | y: 0, 188 | c: 0, 189 | z: 0, 190 | i: 0, 191 | d: 0, 192 | b: 0, 193 | u: 0, 194 | v: 0, 195 | n: 0, 196 | mem, 197 | }; 198 | cpu.reset(); 199 | cpu 200 | } 201 | 202 | /// Resets the CPU to its initial powerup state. 203 | pub fn reset(&mut self) { 204 | self.pc = self.read16(0xFFFC); 205 | self.sp = 0xFD; 206 | self.set_flags(0x24); 207 | } 208 | 209 | /// Sets the buttons for controller 1 210 | pub fn set_buttons(&mut self, buttons: ButtonState) { 211 | self.mem.controller1.set_buttons(buttons); 212 | } 213 | 214 | fn set_flags(&mut self, flags: u8) { 215 | self.c = flags & 1; 216 | self.z = (flags >> 1) & 1; 217 | self.i = (flags >> 2) & 1; 218 | self.d = (flags >> 3) & 1; 219 | self.b = (flags >> 4) & 1; 220 | self.u = (flags >> 5) & 1; 221 | self.v = (flags >> 6) & 1; 222 | self.n = (flags >> 7) & 1; 223 | } 224 | 225 | fn get_flags(&self) -> u8 { 226 | let mut r = 0; 227 | r |= self.c; 228 | r |= self.z << 1; 229 | r |= self.i << 2; 230 | r |= self.d << 3; 231 | r |= self.b << 4; 232 | r |= self.u << 5; 233 | r |= self.v << 6; 234 | r |= self.n << 7; 235 | r 236 | } 237 | 238 | pub fn read(&mut self, address: u16) -> u8 { 239 | self.mem.cpu_read(address) 240 | } 241 | 242 | fn read16(&mut self, address: u16) -> u16 { 243 | let lo = self.read(address); 244 | let hi = self.read(address + 1); 245 | u16::from_be_bytes([hi, lo]) 246 | } 247 | 248 | /// Emulates a software bug where only the lower bit wraps around 249 | fn read16bug(&mut self, a: u16) -> u16 { 250 | let b = (a & 0xFF00) | ((a + 1) & 0xFF); 251 | let lo = self.read(a); 252 | let hi = self.read(b); 253 | u16::from_be_bytes([hi, lo]) 254 | } 255 | 256 | fn write(&mut self, address: u16, value: u8) { 257 | self.mem.cpu_write(address, value); 258 | } 259 | 260 | fn push(&mut self, value: u8) { 261 | let sp = u16::from(self.sp); 262 | self.write(0x100 | sp, value); 263 | self.sp = self.sp.wrapping_sub(1); 264 | } 265 | 266 | fn push16(&mut self, value: u16) { 267 | let hi = (value >> 8) as u8; 268 | let lo = (value & 0xFF) as u8; 269 | self.push(hi); 270 | self.push(lo); 271 | } 272 | 273 | fn pull(&mut self) -> u8 { 274 | self.sp = self.sp.wrapping_add(1); 275 | let sp = u16::from(self.sp); 276 | self.read(0x100 | sp) 277 | } 278 | 279 | fn pull16(&mut self) -> u16 { 280 | let lo = self.pull(); 281 | let hi = self.pull(); 282 | u16::from_be_bytes([hi, lo]) 283 | } 284 | 285 | fn set_z(&mut self, r: u8) { 286 | self.z = match r { 287 | 0 => 1, 288 | _ => 0, 289 | } 290 | } 291 | 292 | fn set_n(&mut self, r: u8) { 293 | self.n = match r & 0x80 { 294 | 0 => 0, 295 | _ => 1, 296 | } 297 | } 298 | 299 | fn set_zn(&mut self, r: u8) { 300 | self.set_z(r); 301 | self.set_n(r); 302 | } 303 | 304 | fn php(&mut self) { 305 | let flags = self.get_flags(); 306 | self.push(flags | 0x10); 307 | } 308 | 309 | fn compare(&mut self, a: u8, b: u8) { 310 | self.set_zn(a.wrapping_sub(b)); 311 | if a >= b { 312 | self.c = 1; 313 | } else { 314 | self.c = 0; 315 | }; 316 | } 317 | 318 | fn nmi(&mut self) { 319 | let pc = self.pc; 320 | self.push16(pc); 321 | self.php(); 322 | self.pc = self.read16(0xFFFA); 323 | self.i = 1; 324 | } 325 | 326 | fn irq(&mut self) { 327 | let pc = self.pc; 328 | self.push16(pc); 329 | self.php(); 330 | self.pc = self.read16(0xFFFE); 331 | self.i = 1; 332 | } 333 | 334 | /// Steps the cpu forward by a single instruction 335 | /// Returns the number of cycles passed 336 | pub fn step(&mut self) -> i32 { 337 | // Stall for a single cycle if stall cycles are still done 338 | if self.mem.cpu.stall > 0 { 339 | self.mem.cpu.stall -= 1; 340 | return 1; 341 | } 342 | let mut cycles = 0; 343 | let interrupt = { 344 | let cpustate = &mut self.mem.cpu; 345 | let i = cpustate.interrupt.clone(); 346 | cpustate.clear_interrupt(); 347 | i 348 | }; 349 | match interrupt { 350 | None => {} 351 | Some(Interrupt::NMI) => { 352 | self.nmi(); 353 | cycles += 7; 354 | } 355 | Some(Interrupt::IRQ) => { 356 | self.irq(); 357 | cycles += 7; 358 | } 359 | } 360 | 361 | let opcode = { 362 | let pc = self.pc; 363 | self.read(pc) 364 | }; 365 | // We now fetch the adress based on what type of addressing the 366 | // opcode requires, and set the page crossed, in order to 367 | // increment the cycles if necessary. 368 | let mut page_crossed = false; 369 | let addressing = Addressing::from_byte(OP_MODES[opcode as usize]); 370 | let address = match addressing { 371 | Addressing::Absolute => { 372 | let pc = self.pc.wrapping_add(1); 373 | self.read16(pc) 374 | } 375 | Addressing::AbsoluteX => { 376 | let pc = self.pc.wrapping_add(1); 377 | let read = self.read16(pc); 378 | let address = read.wrapping_add(u16::from(self.x)); 379 | page_crossed = pages_differ(read, address); 380 | address 381 | } 382 | Addressing::AbsoluteY => { 383 | let pc = self.pc.wrapping_add(1); 384 | let read = self.read16(pc); 385 | let address = read.wrapping_add(u16::from(self.y)); 386 | page_crossed = pages_differ(read, address); 387 | address 388 | } 389 | Addressing::Accumulator => 0, 390 | Addressing::Immediate => self.pc.wrapping_add(1), 391 | Addressing::Implied => 0, 392 | Addressing::IndexedIndirect => { 393 | let next = self.pc.wrapping_add(1); 394 | let added = self.read(next).wrapping_add(self.x); 395 | self.read16bug(u16::from(added)) 396 | } 397 | Addressing::Indirect => { 398 | let next = self.pc.wrapping_add(1); 399 | let read = self.read16(next); 400 | self.read16bug(read) 401 | } 402 | Addressing::IndirectIndexed => { 403 | let pc = self.pc.wrapping_add(1); 404 | let next = u16::from(self.read(pc)); 405 | let read = self.read16bug(next); 406 | let address = read.wrapping_add(u16::from(self.y)); 407 | page_crossed = pages_differ(address, read); 408 | address 409 | } 410 | Addressing::Relative => { 411 | let pc = self.pc.wrapping_add(1); 412 | let offset = u16::from(self.read(pc)); 413 | // treating this as a signed integer 414 | let nxt = self.pc.wrapping_add(2).wrapping_add(offset); 415 | if offset < 0x80 { 416 | nxt 417 | } else { 418 | nxt.wrapping_sub(0x100) 419 | } 420 | } 421 | Addressing::ZeroPage => { 422 | let next = self.pc.wrapping_add(1); 423 | u16::from(self.read(next)) 424 | } 425 | Addressing::ZeroPageX => { 426 | let next = self.pc.wrapping_add(1); 427 | let added = self.read(next).wrapping_add(self.x); 428 | // we don't need to & 0xFF here, since added is a byte 429 | u16::from(added) 430 | } 431 | Addressing::ZeroPageY => { 432 | let next = self.pc.wrapping_add(1); 433 | let added = self.read(next).wrapping_add(self.y); 434 | u16::from(added) 435 | } 436 | }; 437 | 438 | self.pc += OP_SIZES[opcode as usize]; 439 | cycles += OP_CYCLES[opcode as usize]; 440 | if page_crossed && EXTRA_PAGECYCLE_OPS.contains(&opcode) { 441 | cycles += 1; 442 | } 443 | // todo, actually emulate 444 | match opcode { 445 | // ADC 446 | 0x69 | 0x65 | 0x75 | 0x6D | 0x7D | 0x79 | 0x61 | 0x71 => { 447 | let a = self.a; 448 | let b = self.read(address); 449 | let c = self.c; 450 | let a2 = a.wrapping_add(b).wrapping_add(c); 451 | self.a = a2; 452 | self.set_zn(a2); 453 | if u32::from(a) + u32::from(b) + u32::from(c) > 0xFF { 454 | self.c = 1; 455 | } else { 456 | self.c = 0; 457 | } 458 | if (a ^ b) & 0x80 == 0 && (a ^ a2) & 0x80 != 0 { 459 | self.v = 1; 460 | } else { 461 | self.v = 0; 462 | } 463 | } 464 | // AND 465 | 0x29 | 0x25 | 0x35 | 0x2D | 0x3D | 0x39 | 0x21 | 0x31 => { 466 | let a = self.a & self.read(address); 467 | self.a = a; 468 | self.set_zn(a); 469 | } 470 | // ASL 471 | 0x0A | 0x06 | 0x16 | 0x0E | 0x1E => match addressing { 472 | Addressing::Accumulator => { 473 | self.c = (self.a >> 7) & 1; 474 | let a = self.a << 1; 475 | self.a = a; 476 | self.set_zn(a); 477 | } 478 | _ => { 479 | let mut value = self.read(address); 480 | self.c = (value >> 7) & 1; 481 | value <<= 1; 482 | self.write(address, value); 483 | self.set_zn(value); 484 | } 485 | }, 486 | // BCC 487 | 0x90 => { 488 | if self.c == 0 { 489 | let pc = self.pc; 490 | self.pc = address; 491 | cycles += branch_cycles(pc, address); 492 | } 493 | } 494 | // BCS 495 | 0xB0 => { 496 | if self.c != 0 { 497 | let pc = self.pc; 498 | self.pc = address; 499 | cycles += branch_cycles(pc, address) 500 | } 501 | } 502 | // BEQ 503 | 0xF0 => { 504 | if self.z != 0 { 505 | let pc = self.pc; 506 | self.pc = address; 507 | cycles += branch_cycles(pc, address); 508 | } 509 | } 510 | // BIT 511 | 0x24 | 0x2C => { 512 | let value = self.read(address); 513 | self.v = (value >> 6) & 1; 514 | let a = self.a; 515 | self.set_z(value & a); 516 | self.set_n(value); 517 | } 518 | // BMI 519 | 0x30 => { 520 | if self.n != 0 { 521 | let pc = self.pc; 522 | self.pc = address; 523 | cycles += branch_cycles(pc, address); 524 | } 525 | } 526 | // BNE 527 | 0xD0 => { 528 | if self.z == 0 { 529 | let pc = self.pc; 530 | self.pc = address; 531 | cycles += branch_cycles(pc, address); 532 | } 533 | } 534 | // BPL 535 | 0x10 => { 536 | if self.n == 0 { 537 | let pc = self.pc; 538 | self.pc = address; 539 | cycles += branch_cycles(pc, address); 540 | } 541 | } 542 | // BVC 543 | 0x50 => { 544 | if self.v == 0 { 545 | let pc = self.pc; 546 | self.pc = address; 547 | cycles += branch_cycles(pc, address); 548 | } 549 | } 550 | // BVS 551 | 0x70 => { 552 | if self.v != 0 { 553 | let pc = self.pc; 554 | self.pc = address; 555 | cycles += branch_cycles(pc, address); 556 | } 557 | } 558 | // BRK 559 | 0x00 => { 560 | let pc = self.pc; 561 | self.push16(pc); 562 | self.php(); 563 | self.i = 1; 564 | self.pc = self.read16(0xFFFE); 565 | } 566 | // CLC 567 | 0x18 => self.c = 0, 568 | // CLD 569 | 0xD8 => self.d = 0, 570 | // CLI 571 | 0x58 => self.i = 0, 572 | // CLV 573 | 0xB8 => self.v = 0, 574 | // CMP 575 | 0xC9 | 0xC5 | 0xD5 | 0xCD | 0xDD | 0xD9 | 0xC1 | 0xD1 => { 576 | let value = self.read(address); 577 | let a = self.a; 578 | self.compare(a, value); 579 | } 580 | // CPX 581 | 0xE0 | 0xE4 | 0xEC => { 582 | let value = self.read(address); 583 | let x = self.x; 584 | self.compare(x, value); 585 | } 586 | // CPY 587 | 0xC0 | 0xC4 | 0xCC => { 588 | let value = self.read(address); 589 | let y = self.y; 590 | self.compare(y, value); 591 | } 592 | // DEC 593 | 0xC6 | 0xD6 | 0xCE | 0xDE => { 594 | let value = self.read(address).wrapping_sub(1); 595 | self.write(address, value); 596 | self.set_zn(value); 597 | } 598 | // DEX 599 | 0xCA => { 600 | let x = self.x.wrapping_sub(1); 601 | self.x = x; 602 | self.set_zn(x); 603 | } 604 | // DEY 605 | 0x88 => { 606 | let y = self.y.wrapping_sub(1); 607 | self.y = y; 608 | self.set_zn(y); 609 | } 610 | // EOR 611 | 0x49 | 0x45 | 0x55 | 0x4D | 0x5D | 0x59 | 0x41 | 0x51 => { 612 | let a = self.a ^ self.read(address); 613 | self.a = a; 614 | self.set_zn(a); 615 | } 616 | // INC 617 | 0xE6 | 0xF6 | 0xEE | 0xFE => { 618 | let value = self.read(address).wrapping_add(1); 619 | self.write(address, value); 620 | self.set_zn(value); 621 | } 622 | // INX 623 | 0xE8 => { 624 | let x = self.x.wrapping_add(1); 625 | self.x = x; 626 | self.set_zn(x); 627 | } 628 | // INY 629 | 0xC8 => { 630 | let y = self.y.wrapping_add(1); 631 | self.y = y; 632 | self.set_zn(y); 633 | } 634 | // JMP 635 | 0x4C | 0x6C => self.pc = address, 636 | // JSR 637 | 0x20 => { 638 | let minus = self.pc.wrapping_sub(1); 639 | self.push16(minus); 640 | self.pc = address; 641 | } 642 | // LDA 643 | 0xA9 | 0xA5 | 0xB5 | 0xAD | 0xBD | 0xB9 | 0xA1 | 0xB1 => { 644 | let a = self.read(address); 645 | self.a = a; 646 | self.set_zn(a); 647 | } 648 | // LDX 649 | 0xA2 | 0xA6 | 0xB6 | 0xAE | 0xBE => { 650 | let a = self.read(address); 651 | self.x = a; 652 | self.set_zn(a); 653 | } 654 | // LDY 655 | 0xA0 | 0xA4 | 0xB4 | 0xAC | 0xBC => { 656 | let y = self.read(address); 657 | self.y = y; 658 | self.set_zn(y); 659 | } 660 | // LSR 661 | 0x4A | 0x46 | 0x56 | 0x4E | 0x5E => match addressing { 662 | Addressing::Accumulator => { 663 | self.c = self.a & 1; 664 | let a = self.a >> 1; 665 | self.a = a; 666 | self.set_zn(a); 667 | } 668 | _ => { 669 | let mut value = self.read(address); 670 | self.c = value & 1; 671 | value >>= 1; 672 | self.write(address, value); 673 | self.set_zn(value); 674 | } 675 | }, 676 | // NOP 677 | 0xEA => {} 678 | // ORA 679 | 0x09 | 0x05 | 0x15 | 0x0D | 0x1D | 0x19 | 0x01 | 0x11 => { 680 | let a = self.a | self.read(address); 681 | self.a = a; 682 | self.set_zn(a); 683 | } 684 | // PLA 685 | 0x68 => { 686 | let a = self.pull(); 687 | self.a = a; 688 | self.set_zn(a); 689 | } 690 | // PHA 691 | 0x48 => { 692 | let a = self.a; 693 | self.push(a); 694 | } 695 | // PHP 696 | 0x08 => self.php(), 697 | // PLP 698 | 0x28 => { 699 | let p = self.pull(); 700 | self.set_flags((p & 0xEF) | 0x20); 701 | } 702 | // ROL 703 | 0x2A | 0x26 | 0x36 | 0x2E | 0x3E => match addressing { 704 | Addressing::Accumulator => { 705 | let c = self.c; 706 | self.c = (self.a >> 7) & 1; 707 | let a = (self.a << 1) | c; 708 | self.a = a; 709 | self.set_zn(a); 710 | } 711 | _ => { 712 | let c = self.c; 713 | let mut value = self.read(address); 714 | self.c = (value >> 7) & 1; 715 | value = (value << 1) | c; 716 | self.write(address, value); 717 | self.set_zn(value); 718 | } 719 | }, 720 | // ROR 721 | 0x6A | 0x66 | 0x76 | 0x6E | 0x7E => match addressing { 722 | Addressing::Accumulator => { 723 | let c = self.c; 724 | self.c = self.a & 1; 725 | let a = (self.a >> 1) | (c << 7); 726 | self.a = a; 727 | self.set_zn(a); 728 | } 729 | _ => { 730 | let c = self.c; 731 | let mut value = self.read(address); 732 | self.c = value & 1; 733 | value = (value >> 1) | (c << 7); 734 | self.write(address, value); 735 | self.set_zn(value); 736 | } 737 | }, 738 | // RTI 739 | 0x40 => { 740 | let p = self.pull(); 741 | self.set_flags((p & 0xEF) | 0x20); 742 | self.pc = self.pull16(); 743 | } 744 | // RTS 745 | 0x60 => self.pc = self.pull16().wrapping_add(1), 746 | // SBC 747 | 0xE9 | 0xE5 | 0xF5 | 0xED | 0xFD | 0xF9 | 0xE1 | 0xF1 => { 748 | let a = self.a; 749 | let b = self.read(address); 750 | let c = self.c; 751 | let a2 = a.wrapping_sub(b).wrapping_sub(1 - c); 752 | self.a = a2; 753 | self.set_zn(a2); 754 | if i32::from(a) - i32::from(b) - (1 - i32::from(c)) >= 0 { 755 | self.c = 1; 756 | } else { 757 | self.c = 0; 758 | } 759 | if (a ^ b) & 0x80 != 0 && (a ^ a2) & 0x80 != 0 { 760 | self.v = 1; 761 | } else { 762 | self.v = 0; 763 | } 764 | } 765 | // SEC 766 | 0x38 => self.c = 1, 767 | // SED 768 | 0xF8 => self.d = 1, 769 | // SEI 770 | 0x78 => self.i = 1, 771 | // STA 772 | 0x85 | 0x95 | 0x8D | 0x9D | 0x99 | 0x81 | 0x91 => { 773 | let a = self.a; 774 | self.write(address, a); 775 | } 776 | // STX 777 | 0x86 | 0x96 | 0x8E => { 778 | let x = self.x; 779 | self.write(address, x); 780 | } 781 | // STY 782 | 0x84 | 0x94 | 0x8C => { 783 | let y = self.y; 784 | self.write(address, y); 785 | } 786 | // TAX 787 | 0xAA => { 788 | let a = self.a; 789 | self.x = a; 790 | self.set_zn(a); 791 | } 792 | // TAY 793 | 0xA8 => { 794 | let a = self.a; 795 | self.y = a; 796 | self.set_zn(a); 797 | } 798 | // TSX 799 | 0xBA => { 800 | let sp = self.sp; 801 | self.x = sp; 802 | self.set_zn(sp); 803 | } 804 | // TXA 805 | 0x8A => { 806 | let x = self.x; 807 | self.a = x; 808 | self.set_zn(x); 809 | } 810 | // TXS 811 | 0x9A => self.sp = self.x, 812 | // TYA 813 | 0x98 => { 814 | let y = self.y; 815 | self.a = y; 816 | self.set_zn(y); 817 | } 818 | _ => panic!("Unimplented Op {:02X}", opcode), 819 | } 820 | cycles 821 | } 822 | } 823 | -------------------------------------------------------------------------------- /src/apu.rs: -------------------------------------------------------------------------------- 1 | use super::memory::MemoryBus; 2 | 3 | use std::f32::consts::PI; 4 | use crate::ports::AudioDevice; 5 | 6 | const LENGTH_TABLE: [u8; 32] = [ 7 | 10, 254, 20, 2, 40, 4, 80, 6, 160, 8, 60, 10, 14, 12, 26, 14, 12, 16, 24, 18, 48, 20, 96, 22, 8 | 192, 24, 72, 26, 16, 28, 32, 30, 9 | ]; 10 | 11 | const DUTY_TABLE: [[u8; 8]; 4] = [ 12 | [0, 1, 0, 0, 0, 0, 0, 0], 13 | [0, 1, 1, 0, 0, 0, 0, 0], 14 | [0, 1, 1, 1, 1, 0, 0, 0], 15 | [1, 0, 0, 1, 1, 1, 1, 1], 16 | ]; 17 | 18 | const TRIANGLE_TABLE: [u8; 32] = [ 19 | 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 20 | 13, 14, 15, 21 | ]; 22 | 23 | const NOISE_TABLE: [u16; 16] = [ 24 | 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068, 25 | ]; 26 | 27 | const DMC_TABLE: [u8; 16] = [ 28 | 214, 190, 170, 160, 143, 127, 113, 107, 95, 80, 71, 64, 53, 42, 36, 27, 29 | ]; 30 | 31 | /// Constructs a new tnd table 32 | fn make_pulse_table() -> [f32; 31] { 33 | let mut arr = [0.0; 31]; 34 | for (i, item) in arr.iter_mut().enumerate() { 35 | *item = 95.52 / (8128.0 / (i as f32) + 100.0); 36 | } 37 | arr 38 | } 39 | 40 | /// Constructs a new pulse table 41 | fn make_tnd_table() -> [f32; 203] { 42 | let mut arr = [0.0; 203]; 43 | for (i, item) in arr.iter_mut().enumerate() { 44 | *item = 163.67 / (24329.0 / (i as f32) + 100.0); 45 | } 46 | arr 47 | } 48 | 49 | /// Represents a first order filter, implementing the following formula: 50 | /// y_n = b0 * x_n + b1 * x_(n-1) - a y_(n-1) 51 | struct Filter { 52 | b0: f32, 53 | b1: f32, 54 | a: f32, 55 | /// Caches the previous x value 56 | prev_x: f32, 57 | prev_y: f32, 58 | } 59 | 60 | /// Used to calculate the frequency constants used in Filters 61 | fn frequency_constants(sample_rate: u32, cutoff: f32) -> (f32, f32) { 62 | let c = (sample_rate as f32) / PI / cutoff; 63 | let a0 = 1.0 / (1.0 + c); 64 | (c, a0) 65 | } 66 | 67 | impl Filter { 68 | /// Constructs a new low pass filter 69 | fn low_pass(sample_rate: u32, cutoff: f32) -> Filter { 70 | let (c, a0) = frequency_constants(sample_rate, cutoff); 71 | Filter { 72 | b0: a0, 73 | b1: a0, 74 | a: (1.0 - c) * a0, 75 | prev_x: 0.0, 76 | prev_y: 0.0, 77 | } 78 | } 79 | 80 | /// Constructs a new high pass filter 81 | fn high_pass(sample_rate: u32, cutoff: f32) -> Filter { 82 | let (c, a0) = frequency_constants(sample_rate, cutoff); 83 | Filter { 84 | b0: c * a0, 85 | b1: -c * a0, 86 | a: (1.0 - c) * a0, 87 | prev_x: 0.0, 88 | prev_y: 0.0, 89 | } 90 | } 91 | 92 | fn step(&mut self, x: f32) -> f32 { 93 | let y = self.b0 * x + self.b1 * self.prev_x - self.a * self.prev_y; 94 | self.prev_y = y; 95 | self.prev_x = x; 96 | y 97 | } 98 | } 99 | 100 | /// Represents the collection of filters applied to the output of the APU 101 | struct FilterChain { 102 | high1: Filter, 103 | high2: Filter, 104 | low: Filter, 105 | } 106 | 107 | impl FilterChain { 108 | fn new(sample_rate: u32) -> Self { 109 | FilterChain { 110 | high1: Filter::high_pass(sample_rate, 90.0), 111 | high2: Filter::high_pass(sample_rate, 440.0), 112 | low: Filter::low_pass(sample_rate, 14000.0), 113 | } 114 | } 115 | 116 | fn step(&mut self, x: f32) -> f32 { 117 | let x1 = self.high1.step(x); 118 | let x2 = self.high2.step(x1); 119 | self.low.step(x2) 120 | } 121 | } 122 | 123 | /// Represents the Square signal generator of the APU 124 | struct Square { 125 | /// Whether or not this generator is turned on 126 | enabled: bool, 127 | /// Whether or not the generator is on the first channel 128 | first_channel: bool, 129 | /// Whether or not to decrement the length value 130 | length_enabled: bool, 131 | /// The current point in the length timer 132 | length_value: u8, 133 | /// Timer period used for timing sweeps 134 | timer_period: u16, 135 | /// The current timer value for sweeps 136 | timer_value: u16, 137 | /// Along with `duty_value`, determines what duty to apply 138 | duty_mode: u8, 139 | duty_value: u8, 140 | /// Used to sweep timing 141 | sweep_reload: bool, 142 | /// Whether or not sweeps will trigger 143 | sweep_enabled: bool, 144 | /// Whether or not to reverse sweeps 145 | sweep_negate: bool, 146 | /// Used as a shift for timing sweeps 147 | sweep_shift: u8, 148 | /// Used as the period length for sweeps 149 | sweep_period: u8, 150 | /// The current value of the sweep 151 | sweep_value: u8, 152 | /// Enables envelope 153 | envelope_enabled: bool, 154 | /// Uses for timing envelope sounds 155 | envelope_loop: bool, 156 | envelope_start: bool, 157 | /// Used for keeping track of the current position in the envelope 158 | envelope_period: u8, 159 | /// Current envelope value 160 | envelope_value: u8, 161 | /// Current envelope volume 162 | envelope_volume: u8, 163 | /// Base volume 164 | constant_volume: u8, 165 | } 166 | 167 | impl Square { 168 | fn new(first_channel: bool) -> Self { 169 | Square { 170 | enabled: false, 171 | first_channel, 172 | length_enabled: false, 173 | length_value: 0, 174 | timer_period: 0, 175 | timer_value: 0, 176 | duty_mode: 0, 177 | duty_value: 0, 178 | sweep_reload: false, 179 | sweep_enabled: false, 180 | sweep_negate: false, 181 | sweep_shift: 0, 182 | sweep_period: 0, 183 | sweep_value: 0, 184 | envelope_enabled: false, 185 | envelope_loop: false, 186 | envelope_start: false, 187 | envelope_period: 0, 188 | envelope_value: 0, 189 | envelope_volume: 0, 190 | constant_volume: 0, 191 | } 192 | } 193 | 194 | fn write_control(&mut self, value: u8) { 195 | self.duty_mode = (value >> 6) & 3; 196 | self.length_enabled = (value >> 5) & 1 == 0; 197 | self.envelope_loop = (value >> 5) & 1 == 1; 198 | self.envelope_enabled = (value >> 4) & 1 == 0; 199 | self.envelope_period = value & 15; 200 | self.constant_volume = value & 15; 201 | self.envelope_start = true; 202 | } 203 | 204 | fn write_sweep(&mut self, value: u8) { 205 | self.sweep_enabled = (value >> 7) & 1 == 1; 206 | self.sweep_period = ((value >> 4) & 7) + 1; 207 | self.sweep_negate = (value >> 3) & 1 == 1; 208 | self.sweep_shift = value & 7; 209 | self.sweep_reload = true; 210 | } 211 | 212 | fn write_low_timer(&mut self, value: u8) { 213 | self.timer_period = (self.timer_period & 0xFF00) | u16::from(value); 214 | } 215 | 216 | fn write_high_timer(&mut self, value: u8) { 217 | self.length_value = LENGTH_TABLE[(value >> 3) as usize]; 218 | let shifted = u16::from(value & 7) << 8; 219 | self.timer_period = (self.timer_period & 0xFF) | shifted; 220 | self.envelope_start = true; 221 | self.duty_value = 0; 222 | } 223 | 224 | fn step_timer(&mut self) { 225 | if self.timer_value == 0 { 226 | self.timer_value = self.timer_period; 227 | self.duty_value = (self.duty_value + 1) % 8; 228 | } else { 229 | self.timer_value -= 1; 230 | } 231 | } 232 | 233 | fn step_envelope(&mut self) { 234 | if self.envelope_start { 235 | self.envelope_volume = 15; 236 | self.envelope_value = self.envelope_period; 237 | self.envelope_start = false; 238 | } else if self.envelope_value > 0 { 239 | self.envelope_value -= 1; 240 | } else { 241 | if self.envelope_volume > 0 { 242 | self.envelope_volume -= 1; 243 | } else if self.envelope_loop { 244 | self.envelope_volume = 15; 245 | } 246 | self.envelope_value = self.envelope_period; 247 | } 248 | } 249 | 250 | fn sweep(&mut self) { 251 | let delta = self.timer_period >> self.sweep_shift; 252 | if self.sweep_negate { 253 | self.timer_period -= delta; 254 | if self.first_channel { 255 | self.timer_period -= 1; 256 | } 257 | } else { 258 | self.timer_period += delta; 259 | } 260 | } 261 | 262 | fn step_sweep(&mut self) { 263 | if self.sweep_reload { 264 | if self.sweep_enabled && self.sweep_value == 0 { 265 | self.sweep(); 266 | } 267 | self.sweep_value = self.sweep_period; 268 | self.sweep_reload = false; 269 | } else if self.sweep_value > 0 { 270 | self.sweep_value -= 1; 271 | } else { 272 | if self.sweep_enabled { 273 | self.sweep(); 274 | } 275 | self.sweep_value = self.sweep_period; 276 | } 277 | } 278 | 279 | fn step_length(&mut self) { 280 | if self.length_enabled && self.length_value > 0 { 281 | self.length_value -= 1; 282 | } 283 | } 284 | 285 | fn output(&self) -> u8 { 286 | if !self.enabled { 287 | return 0; 288 | } 289 | if self.length_value == 0 { 290 | return 0; 291 | } 292 | let (i1, i2) = (self.duty_mode as usize, self.duty_value as usize); 293 | if DUTY_TABLE[i1][i2] == 0 { 294 | return 0; 295 | } 296 | if self.timer_period < 8 || self.timer_period > 0x7FF { 297 | return 0; 298 | } 299 | if self.envelope_enabled { 300 | self.envelope_volume 301 | } else { 302 | self.constant_volume 303 | } 304 | } 305 | } 306 | 307 | /// Represents the triangle signal simulator 308 | struct Triangle { 309 | /// Whether or not output is enabled 310 | enabled: bool, 311 | /// Like in Pulse, these are used to control output generation 312 | length_enabled: bool, 313 | length_value: u8, 314 | /// Keeps track of the reset value of the timer 315 | timer_period: u16, 316 | /// Counts down to 0 before resetting to timer_period 317 | timer_value: u16, 318 | /// Used for manipulating the duty of the signal 319 | duty_value: u8, 320 | /// Used to keep track of the max value of the period 321 | counter_period: u8, 322 | /// Counts down to 0 before restting to counter_period 323 | counter_value: u8, 324 | /// Controls whether or not the value will wrap around 325 | counter_reload: bool, 326 | } 327 | 328 | impl Triangle { 329 | fn new() -> Self { 330 | Triangle { 331 | enabled: false, 332 | length_enabled: false, 333 | length_value: 0, 334 | timer_period: 0, 335 | timer_value: 0, 336 | duty_value: 0, 337 | counter_period: 0, 338 | counter_value: 0, 339 | counter_reload: false, 340 | } 341 | } 342 | 343 | fn write_control(&mut self, value: u8) { 344 | self.length_enabled = (value >> 7) & 1 == 0; 345 | self.counter_period = value & 0x7F; 346 | } 347 | 348 | fn write_low_timer(&mut self, value: u8) { 349 | let low = u16::from(value); 350 | self.timer_period = (self.timer_period & 0xFF00) | low; 351 | } 352 | 353 | fn write_high_timer(&mut self, value: u8) { 354 | self.length_value = LENGTH_TABLE[(value >> 3) as usize]; 355 | let high = u16::from(value & 7) << 8; 356 | self.timer_period = (self.timer_period & 0xFF) | high; 357 | self.timer_value = self.timer_period; 358 | self.counter_reload = true; 359 | } 360 | 361 | fn step_timer(&mut self) { 362 | if self.timer_value == 0 { 363 | self.timer_value = self.timer_period; 364 | if self.length_value > 0 && self.counter_value > 0 { 365 | self.duty_value = (self.duty_value + 1) % 32; 366 | } 367 | } else { 368 | self.timer_value -= 1; 369 | } 370 | } 371 | 372 | fn step_length(&mut self) { 373 | if self.length_enabled && self.length_value > 0 { 374 | self.length_value -= 1; 375 | } 376 | } 377 | 378 | fn step_counter(&mut self) { 379 | if self.counter_reload { 380 | self.counter_value = self.counter_period; 381 | } else if self.counter_value > 0 { 382 | self.counter_value -= 1; 383 | } 384 | if self.length_enabled { 385 | self.counter_reload = false; 386 | } 387 | } 388 | 389 | fn output(&self) -> u8 { 390 | if !self.enabled || self.length_value == 0 || self.counter_value == 0 { 391 | 0 392 | } else { 393 | TRIANGLE_TABLE[self.duty_value as usize] 394 | } 395 | } 396 | } 397 | 398 | /// Represents the noise signal generator 399 | struct Noise { 400 | /// Whether or not output is enabled for this component 401 | enabled: bool, 402 | /// Which of 2 noise modes the generator is in 403 | mode: bool, 404 | shift_register: u16, 405 | /// Enables sweep timing 406 | length_enabled: bool, 407 | /// Value used for sweep timing 408 | length_value: u8, 409 | /// Used as the point of reset for the global timer 410 | timer_period: u16, 411 | /// Used to keep track of the state of the global timer 412 | timer_value: u16, 413 | /// Whether or not an envelope effect is enabled 414 | envelope_enabled: bool, 415 | /// Whether or not to loop back around at the end of an envelope 416 | envelope_loop: bool, 417 | /// Whether or not to trigger an envelope 418 | envelope_start: bool, 419 | /// The point at which to reset the envelope timer 420 | envelope_period: u8, 421 | /// Used to control the timing of the envelope effect 422 | envelope_value: u8, 423 | /// Used to control the volume of the envelope effect 424 | envelope_volume: u8, 425 | /// Background volume 426 | constant_volume: u8, 427 | } 428 | 429 | impl Noise { 430 | fn new(shift_register: u16) -> Self { 431 | Noise { 432 | enabled: false, 433 | mode: false, 434 | shift_register, 435 | length_enabled: true, 436 | length_value: 0, 437 | timer_period: 0, 438 | timer_value: 0, 439 | envelope_enabled: false, 440 | envelope_loop: false, 441 | envelope_start: false, 442 | envelope_period: 0, 443 | envelope_value: 0, 444 | envelope_volume: 0, 445 | constant_volume: 0, 446 | } 447 | } 448 | 449 | fn write_control(&mut self, value: u8) { 450 | self.length_enabled = (value >> 5) & 1 == 0; 451 | self.envelope_loop = (value >> 5) & 1 == 1; 452 | self.envelope_enabled = (value >> 4) & 1 == 0; 453 | self.envelope_period = value & 15; 454 | self.constant_volume = value & 15; 455 | self.envelope_start = true; 456 | } 457 | 458 | fn write_period(&mut self, value: u8) { 459 | self.mode = value & 0x80 == 0x80; 460 | self.timer_period = NOISE_TABLE[(value & 0xF) as usize]; 461 | } 462 | 463 | fn write_length(&mut self, value: u8) { 464 | self.length_value = LENGTH_TABLE[(value >> 3) as usize]; 465 | self.envelope_start = true; 466 | } 467 | 468 | fn step_timer(&mut self) { 469 | if self.timer_value == 0 { 470 | self.timer_value = self.timer_period; 471 | let shift = if self.mode { 6 } else { 1 }; 472 | let b1 = self.shift_register & 1; 473 | let b2 = (self.shift_register >> shift) & 1; 474 | self.shift_register >>= 1; 475 | self.shift_register |= (b1 ^ b2) << 14; 476 | } else { 477 | self.timer_value -= 1; 478 | } 479 | } 480 | 481 | fn step_envelope(&mut self) { 482 | if self.envelope_start { 483 | self.envelope_volume = 15; 484 | self.envelope_value = self.envelope_period; 485 | self.envelope_start = false; 486 | } else if self.envelope_value > 0 { 487 | self.envelope_value -= 1; 488 | } else { 489 | if self.envelope_volume > 0 { 490 | self.envelope_volume -= 1; 491 | } else if self.envelope_loop { 492 | self.envelope_volume = 15; 493 | } 494 | self.envelope_value = self.envelope_period; 495 | } 496 | } 497 | 498 | fn step_length(&mut self) { 499 | if self.length_enabled && self.length_value > 0 { 500 | self.length_value -= 1; 501 | } 502 | } 503 | 504 | fn output(&mut self) -> u8 { 505 | if !self.enabled || self.length_value == 0 || self.shift_register & 1 == 1 { 506 | 0 507 | } else if self.envelope_enabled { 508 | self.envelope_volume 509 | } else { 510 | self.constant_volume 511 | } 512 | } 513 | } 514 | 515 | /// Generator for DMC Samples 516 | struct DMC { 517 | /// Whether or not output is enabled for this generator 518 | enabled: bool, 519 | /// Current output value 520 | value: u8, 521 | /// The address of the current sample 522 | sample_address: u16, 523 | /// The length of the current sample 524 | sample_length: u16, 525 | /// The current address being read from 526 | current_address: u16, 527 | /// The current length left to read 528 | current_length: u16, 529 | /// Contains the value of shifts for effects 530 | shift_register: u8, 531 | bit_count: u8, 532 | /// The point at which the tick resets 533 | tick_period: u8, 534 | /// The current value of the tick 535 | tick_value: u8, 536 | /// Whether or not to loop back at the end of a sound cycle 537 | do_loop: bool, 538 | /// Whether or not an irq ocurred 539 | irq: bool, 540 | } 541 | 542 | impl DMC { 543 | fn new() -> Self { 544 | DMC { 545 | enabled: false, 546 | value: 0, 547 | sample_address: 0, 548 | sample_length: 0, 549 | current_address: 0, 550 | current_length: 0, 551 | shift_register: 0, 552 | bit_count: 0, 553 | tick_period: 0, 554 | tick_value: 0, 555 | do_loop: false, 556 | irq: false, 557 | } 558 | } 559 | 560 | fn write_control(&mut self, value: u8) { 561 | self.irq = value & 0x80 == 0x80; 562 | self.do_loop = value & 0x40 == 0x40; 563 | self.tick_period = DMC_TABLE[(value & 0xF) as usize]; 564 | } 565 | 566 | fn write_value(&mut self, value: u8) { 567 | self.value = value & 0x7F; 568 | } 569 | 570 | fn write_address(&mut self, value: u8) { 571 | self.sample_address = 0xC000 | (u16::from(value) << 6); 572 | } 573 | 574 | fn write_length(&mut self, value: u8) { 575 | self.sample_length = (u16::from(value) << 4) | 1; 576 | } 577 | 578 | fn restart(&mut self) { 579 | self.current_address = self.sample_address; 580 | self.current_length = self.sample_length; 581 | } 582 | 583 | fn step_timer(&mut self, read: u8) -> bool { 584 | if self.enabled { 585 | let stall = self.step_reader(read); 586 | if self.tick_value == 0 { 587 | self.tick_value = self.tick_period; 588 | self.step_shifter(); 589 | } else { 590 | self.tick_value -= 1; 591 | } 592 | stall 593 | } else { 594 | false 595 | } 596 | } 597 | 598 | // returns whether or not to stall 599 | fn step_reader(&mut self, read: u8) -> bool { 600 | if self.current_length > 0 && self.bit_count == 0 { 601 | self.shift_register = read; 602 | self.bit_count = 8; 603 | self.current_address = self.current_address.wrapping_add(1); 604 | if self.current_address == 0 { 605 | self.current_address = 0x8000; 606 | } 607 | self.current_length -= 1; 608 | if self.current_length == 0 && self.do_loop { 609 | self.restart(); 610 | } 611 | true 612 | } else { 613 | false 614 | } 615 | } 616 | 617 | fn step_shifter(&mut self) { 618 | if self.bit_count != 0 { 619 | if self.shift_register & 1 == 1 { 620 | if self.value <= 125 { 621 | self.value += 2; 622 | } 623 | } else if self.value >= 2 { 624 | self.value -= 2; 625 | } 626 | self.shift_register >>= 1; 627 | self.bit_count -= 1; 628 | } 629 | } 630 | 631 | fn output(&self) -> u8 { 632 | self.value 633 | } 634 | } 635 | 636 | /// Contains registers that are written to across the memory bus 637 | pub struct APUState { 638 | /// The first square output generator 639 | square1: Square, 640 | /// The second square output generator 641 | square2: Square, 642 | /// The triangle output generator 643 | triangle: Triangle, 644 | /// The noise output generator 645 | noise: Noise, 646 | /// The DMC sample generator 647 | dmc: DMC, 648 | /// The current frame period 649 | frame_period: u8, 650 | /// Whether or not to trigger IRQs 651 | frame_irq: bool, 652 | } 653 | 654 | impl Default for APUState { 655 | fn default() -> Self { 656 | APUState { 657 | square1: Square::new(true), 658 | square2: Square::new(false), 659 | triangle: Triangle::new(), 660 | noise: Noise::new(1), 661 | dmc: DMC::new(), 662 | frame_period: 0, 663 | frame_irq: false, 664 | } 665 | } 666 | } 667 | 668 | impl APUState { 669 | pub fn new() -> Self { 670 | APUState::default() 671 | } 672 | 673 | pub fn read_register(&self, address: u16) -> u8 { 674 | match address { 675 | 0x4015 => self.read_status(), 676 | // Some addresses may be read by bad games 677 | _ => 0, 678 | } 679 | } 680 | 681 | fn read_status(&self) -> u8 { 682 | let mut result = 0; 683 | if self.square1.length_value > 0 { 684 | result |= 1; 685 | } 686 | if self.square2.length_value > 0 { 687 | result |= 2; 688 | } 689 | if self.triangle.length_value > 0 { 690 | result |= 4; 691 | } 692 | if self.noise.length_value > 0 { 693 | result |= 8; 694 | } 695 | if self.dmc.current_length > 0 { 696 | result |= 16; 697 | } 698 | result 699 | } 700 | 701 | pub fn write_register(&mut self, address: u16, value: u8) { 702 | match address { 703 | 0x4000 => self.square1.write_control(value), 704 | 0x4001 => self.square1.write_sweep(value), 705 | 0x4002 => self.square1.write_low_timer(value), 706 | 0x4003 => self.square1.write_high_timer(value), 707 | 0x4004 => self.square2.write_control(value), 708 | 0x4005 => self.square2.write_sweep(value), 709 | 0x4006 => self.square2.write_low_timer(value), 710 | 0x4007 => self.square2.write_high_timer(value), 711 | 0x4008 => self.triangle.write_control(value), 712 | 0x4009 => {} 713 | 0x4010 => self.dmc.write_control(value), 714 | 0x4011 => self.dmc.write_value(value), 715 | 0x4012 => self.dmc.write_address(value), 716 | 0x4013 => self.dmc.write_length(value), 717 | 0x400A => self.triangle.write_low_timer(value), 718 | 0x400B => self.triangle.write_high_timer(value), 719 | 0x400C => self.noise.write_control(value), 720 | 0x400D => {} 721 | 0x400E => self.noise.write_period(value), 722 | 0x400F => self.noise.write_length(value), 723 | 0x4015 => self.write_control(value), 724 | 0x4017 => self.write_frame_counter(value), 725 | // We may want to panic here 726 | _ => {} 727 | } 728 | } 729 | 730 | fn write_control(&mut self, value: u8) { 731 | self.square1.enabled = value & 1 == 1; 732 | self.square2.enabled = value & 2 == 2; 733 | self.triangle.enabled = value & 4 == 4; 734 | self.noise.enabled = value & 8 == 8; 735 | self.dmc.enabled = value & 16 == 16; 736 | if !self.square1.enabled { 737 | self.square1.length_value = 0; 738 | } 739 | if !self.square2.enabled { 740 | self.square2.length_value = 0; 741 | } 742 | if !self.triangle.enabled { 743 | self.triangle.length_value = 0; 744 | } 745 | if !self.noise.enabled { 746 | self.noise.length_value = 0; 747 | } 748 | if !self.dmc.enabled { 749 | self.dmc.current_length = 0; 750 | } else if self.dmc.current_length == 0 { 751 | self.dmc.restart(); 752 | } 753 | } 754 | 755 | fn write_frame_counter(&mut self, value: u8) { 756 | self.frame_period = 4 + ((value >> 7) & 1); 757 | self.frame_irq = (value >> 6) & 1 == 0; 758 | // Catching up with the frame period 759 | if self.frame_period == 5 { 760 | self.step_envelope(); 761 | self.step_sweep(); 762 | self.step_length(); 763 | } 764 | } 765 | 766 | fn step_envelope(&mut self) { 767 | self.square1.step_envelope(); 768 | self.square2.step_envelope(); 769 | self.triangle.step_counter(); 770 | self.noise.step_envelope(); 771 | } 772 | 773 | fn step_sweep(&mut self) { 774 | self.square1.step_sweep(); 775 | self.square2.step_sweep(); 776 | } 777 | 778 | fn step_length(&mut self) { 779 | self.square1.step_length(); 780 | self.square2.step_length(); 781 | self.triangle.step_length(); 782 | self.noise.step_length(); 783 | } 784 | } 785 | 786 | /// Represents the audio processing unit 787 | pub(crate) struct APU { 788 | /// The chain of filters used on the output of the generators 789 | filter: FilterChain, 790 | // The 2 tables used to find the height of the wave output 791 | pulse_table: [f32; 31], 792 | tnd_table: [f32; 203], 793 | /// Used to time frame ticks 794 | frame_tick: u16, 795 | /// Used to time sample ticks 796 | sample_tick: u16, 797 | /// The number of ticks after which to reset the sample tick. 798 | /// This is determined from the sample rate at runtime 799 | sample_cap: u16, 800 | /// The current frame value 801 | frame_value: u8, 802 | } 803 | 804 | impl APU { 805 | pub fn new(sample_rate: u32) -> Self { 806 | // We need to round up, otherwise we'll slowly add latency to the music 807 | let sample_cap = (1_790_000 / sample_rate) as u16 + 1; 808 | let tnd_table = make_tnd_table(); 809 | let pulse_table = make_pulse_table(); 810 | APU { 811 | filter: FilterChain::new(sample_rate), 812 | tnd_table, 813 | pulse_table, 814 | frame_tick: 0, 815 | sample_tick: 0, 816 | sample_cap, 817 | frame_value: 0, 818 | } 819 | } 820 | 821 | /// Steps the apu forward by one CPU tick 822 | pub fn step(&mut self, m: &mut MemoryBus, audio: &mut impl AudioDevice) { 823 | // step timer 824 | self.frame_tick += 1; 825 | // we can use the first bit of the frame_tick as an even odd flag 826 | let toggle = self.frame_tick & 1 == 0; 827 | self.step_timer(m, toggle); 828 | // This is equivalent to firing at roughly 240 hz 829 | if self.frame_tick >= 7458 { 830 | self.frame_tick = 0; 831 | self.step_framecounter(m); 832 | } 833 | self.sample_tick += 1; 834 | if self.sample_tick >= self.sample_cap { 835 | self.sample_tick = 0; 836 | self.send_sample(m, audio); 837 | } 838 | } 839 | 840 | fn send_sample(&mut self, m: &mut MemoryBus, audio: &mut impl AudioDevice) { 841 | let output = self.output(m); 842 | let filtered = self.filter.step(output); 843 | audio.push_sample(filtered) 844 | } 845 | 846 | fn output(&mut self, m: &mut MemoryBus) -> f32 { 847 | let p1 = m.apu.square1.output(); 848 | let p2 = m.apu.square2.output(); 849 | let t = m.apu.triangle.output(); 850 | let n = m.apu.noise.output(); 851 | let d = m.apu.dmc.output(); 852 | // TODO: figure out if these bound checks are a bug somewhere else 853 | let pulse_out = self.pulse_table[(p1 + p2) as usize]; 854 | let tnd_out = self.tnd_table[(3 * t + 2 * n + d) as usize]; 855 | pulse_out + tnd_out 856 | } 857 | 858 | fn step_timer(&mut self, m: &mut MemoryBus, toggle: bool) { 859 | if toggle { 860 | m.apu.square1.step_timer(); 861 | m.apu.square2.step_timer(); 862 | m.apu.noise.step_timer(); 863 | let address = m.apu.dmc.current_address; 864 | let read = m.cpu_read(address); 865 | if m.apu.dmc.step_timer(read) { 866 | m.cpu.add_stall(4); 867 | } 868 | } 869 | m.apu.triangle.step_timer(); 870 | } 871 | 872 | fn step_framecounter(&mut self, m: &mut MemoryBus) { 873 | match m.apu.frame_period { 874 | 4 => { 875 | self.frame_value = (self.frame_value + 1) % 4; 876 | match self.frame_value { 877 | 0 | 2 => m.apu.step_envelope(), 878 | 1 => { 879 | m.apu.step_envelope(); 880 | m.apu.step_sweep(); 881 | m.apu.step_length(); 882 | } 883 | 3 => { 884 | m.apu.step_envelope(); 885 | m.apu.step_sweep(); 886 | m.apu.step_length(); 887 | self.fire_irq(m); 888 | } 889 | // This can't happen because of the module 4 890 | _ => {} 891 | } 892 | } 893 | 5 => { 894 | self.frame_value = (self.frame_value + 1) % 5; 895 | match self.frame_value { 896 | 1 | 3 => m.apu.step_envelope(), 897 | 0 | 2 => { 898 | m.apu.step_envelope(); 899 | m.apu.step_sweep(); 900 | m.apu.step_length(); 901 | } 902 | // We don't want to do anything for 5 903 | _ => {} 904 | } 905 | } 906 | _ => {} 907 | } 908 | } 909 | 910 | fn fire_irq(&self, m: &mut MemoryBus) { 911 | if m.apu.frame_irq { 912 | m.cpu.set_irq(); 913 | } 914 | } 915 | } 916 | --------------------------------------------------------------------------------