tetanes 0.7.0

A NES Emulator written in Rust with SDL2 and WebAssembly support
Documentation
//! Memory types for dealing with bytes

use crate::{
    common::{Addr, Byte},
    mapper::*,
    serialization::Savable,
    NesResult,
};
use enum_dispatch::enum_dispatch;
use rand::Rng;
use std::{
    fmt,
    io::{Read, Write},
    ops::{Deref, DerefMut},
};

#[enum_dispatch(MapperType)]
pub trait MemRead {
    fn read(&mut self, _addr: Addr) -> Byte {
        0
    }
    fn peek(&self, _addr: Addr) -> Byte {
        0
    }
}
#[enum_dispatch(MapperType)]
pub trait MemWrite {
    fn write(&mut self, _addr: Addr, _val: Byte) {}
}
#[derive(Default, Clone)]
pub struct Memory {
    data: Vec<Byte>,
    writable: bool,
}

impl Memory {
    pub fn new() -> Self {
        Self::with_capacity(0)
    }

    pub fn with_capacity(capacity: usize) -> Self {
        let randomize = cfg!(not(feature = "no-randomize-ram"));
        let data = if randomize {
            let mut rng = rand::thread_rng();
            let mut data = Vec::with_capacity(capacity);
            for _ in 0..capacity {
                data.push(rng.gen_range(0x00, 0xFF));
            }
            data
        } else {
            vec![0; capacity]
        };
        Self {
            data,
            writable: true,
        }
    }

    pub fn from_bytes(bytes: &[Byte]) -> Self {
        let mut memory = Self::with_capacity(bytes.len());
        memory.data = bytes.to_vec();
        memory
    }

    pub fn rom(capacity: usize) -> Self {
        let mut rom = Self::with_capacity(capacity);
        rom.writable = false;
        rom
    }
    pub fn rom_from_bytes(bytes: &[Byte]) -> Self {
        let mut rom = Self::rom(bytes.len());
        rom.data = bytes.to_vec();
        rom
    }

    pub fn ram(capacity: usize) -> Self {
        Self::with_capacity(capacity)
    }
    pub fn ram_from_bytes(bytes: &[Byte]) -> Self {
        let mut ram = Self::ram(bytes.len());
        ram.data = bytes.to_vec();
        ram
    }

    pub fn extend(&mut self, memory: &Memory) {
        self.data.extend(&memory.data);
    }

    pub fn len(&self) -> usize {
        self.data.len()
    }
    pub fn is_empty(&self) -> bool {
        self.data.is_empty()
    }
    pub fn writable(&self) -> bool {
        self.writable
    }
    pub fn write_protect(&mut self, protect: bool) {
        self.writable = !protect;
    }
}

impl MemRead for Memory {
    fn read(&mut self, addr: Addr) -> Byte {
        self.peek(addr)
    }
    fn peek(&self, addr: Addr) -> Byte {
        let mut addr = addr as usize;
        let len = self.len();
        if addr >= len {
            addr %= len;
        }
        assert!(addr < len, "peek outside memory range, {}/{}", addr, len);
        self.data[addr]
    }
}

impl MemWrite for Memory {
    fn write(&mut self, addr: Addr, val: Byte) {
        if self.writable {
            let mut addr = addr as usize;
            let len = self.len();
            if addr >= len {
                addr %= len;
            }
            assert!(addr < len, "write outside memory range {}/{}", addr, len);
            self.data[addr] = val;
        }
    }
}

impl Savable for Memory {
    fn save<F: Write>(&self, fh: &mut F) -> NesResult<()> {
        self.data.save(fh)?;
        self.writable.save(fh)?;
        Ok(())
    }
    fn load<F: Read>(&mut self, fh: &mut F) -> NesResult<()> {
        self.data.load(fh)?;
        self.writable.load(fh)?;
        Ok(())
    }
}

impl Deref for Memory {
    type Target = [u8];
    fn deref(&self) -> &Self::Target {
        &self.data
    }
}

impl DerefMut for Memory {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.data
    }
}

#[derive(Default, Clone)]
struct Bank {
    start: usize,
    end: usize,
    address: usize,
}

impl Bank {
    fn new(start: Addr, end: Addr) -> Self {
        Self {
            start: start as usize,
            end: end as usize,
            address: start as usize,
        }
    }
}

impl Savable for Bank {
    fn save<F: Write>(&self, fh: &mut F) -> NesResult<()> {
        self.start.save(fh)?;
        self.end.save(fh)?;
        self.address.save(fh)?;
        Ok(())
    }
    fn load<F: Read>(&mut self, fh: &mut F) -> NesResult<()> {
        self.start.load(fh)?;
        self.end.load(fh)?;
        self.address.load(fh)?;
        Ok(())
    }
}

impl fmt::Debug for Bank {
    fn fmt(&self, f: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
        write!(
            f,
            "Bank {{ start: 0x{:04X}, end: 0x{:04X}, address: 0x{:04X} }}",
            self.start, self.end, self.address,
        )
    }
}

#[derive(Default, Debug, Clone)]
pub struct BankedMemory {
    banks: Vec<Bank>,
    window: usize,
    bank_shift: usize,
    bank_count: usize,
    memory: Memory,
}

impl BankedMemory {
    pub fn new(window: usize) -> Self {
        Self::ram(0x2000, window)
    }

    pub fn ram(capacity: usize, window: usize) -> Self {
        let memory = Memory::ram(capacity);
        Self {
            banks: Vec::new(),
            window,
            bank_shift: Self::bank_shift(window),
            bank_count: memory.len() / window,
            memory,
        }
    }

    pub fn from(memory: Memory, window: usize) -> Self {
        Self {
            banks: Vec::new(),
            window,
            bank_shift: Self::bank_shift(window),
            bank_count: memory.len() / window,
            memory,
        }
    }

    pub fn extend(&mut self, memory: &Memory) {
        self.memory.extend(memory);
        self.bank_count = self.memory.len() / self.window;
    }

    pub fn add_bank(&mut self, start: Addr, end: Addr) {
        self.banks.push(Bank::new(start, end));
        self.update_banks();
    }

    pub fn add_bank_range(&mut self, start: Addr, end: Addr) {
        for start in (start..end).step_by(self.window) {
            let end = start + (self.window as Addr).saturating_sub(1);
            self.banks.push(Bank::new(start, end));
        }
        self.update_banks();
    }

    pub fn set_bank(&mut self, bank_start: Addr, mut new_bank: usize) {
        if self.bank_count() > 0 && new_bank >= self.bank_count() {
            new_bank %= self.bank_count();
        }
        debug_assert!(
            new_bank < self.bank_count(),
            "new_bank is outside bankable range {} / {}",
            new_bank,
            self.bank_count()
        );

        let bank = self.get_bank(bank_start);
        debug_assert!(
            bank < self.banks.len(),
            "bank is outside bankable range {} / {}",
            bank,
            self.banks.len()
        );
        self.banks[bank].address = new_bank * self.window;
    }

    pub fn set_bank_range(&mut self, start: Addr, end: Addr, mut new_bank: usize) {
        if self.bank_count() > 0 && new_bank >= self.bank_count() {
            new_bank %= self.bank_count();
        }
        debug_assert!(
            new_bank < self.bank_count(),
            "new_bank is outside bankable range {} / {}",
            new_bank,
            self.bank_count()
        );

        let mut new_address = new_bank * self.window;
        for bank_start in (start..end).step_by(self.window) {
            let bank = self.get_bank(bank_start);
            debug_assert!(
                bank < self.banks.len(),
                "bank is outside bankable range {} / {}",
                bank,
                self.banks.len()
            );
            self.banks[bank].address = new_address;
            new_address += self.window;
        }
    }

    pub fn set_bank_mirror(&mut self, bank_start: Addr, mirror_bank: usize) {
        self.set_bank(bank_start, mirror_bank);
    }

    pub fn last_bank(&self) -> usize {
        self.bank_count.saturating_sub(1)
    }

    pub fn bank_count(&self) -> usize {
        self.bank_count
    }

    pub fn len(&self) -> usize {
        self.memory.len()
    }
    pub fn is_empty(&self) -> bool {
        self.memory.is_empty()
    }
    pub fn writable(&self) -> bool {
        self.memory.writable()
    }
    pub fn write_protect(&mut self, protect: bool) {
        self.memory.write_protect(protect);
    }

    fn update_banks(&mut self) {
        self.banks.sort_by(|a, b| a.start.cmp(&b.start));
        let mut address = 0x0000;
        for bank in self.banks.iter_mut() {
            bank.address = address;
            address += bank.end - bank.start + 1;
        }
    }

    pub fn get_bank(&self, addr: Addr) -> usize {
        let addr = addr as usize;
        let base_addr = if let Some(bank) = self.banks.first() {
            bank.start
        } else {
            0x0000
        };
        debug_assert!(addr >= base_addr, "address is less than base address");
        let mut bank = (addr - base_addr) >> self.bank_shift;
        if self.bank_count() > 0 && bank >= self.bank_count() {
            bank %= self.bank_count();
        }
        bank
    }

    fn translate_addr(&self, addr: Addr) -> usize {
        let bank = self.get_bank(addr);
        debug_assert!(bank < self.banks.len(), "bank is outside bankable range");
        let bank = &self.banks[bank];
        bank.address + (addr as usize - bank.start)
    }

    fn bank_shift(mut window: usize) -> usize {
        let mut shift = 0usize;
        while window > 0 {
            window >>= 1;
            shift += 1;
        }
        shift.saturating_sub(1)
    }
}

impl MemRead for BankedMemory {
    fn read(&mut self, addr: Addr) -> Byte {
        self.peek(addr)
    }
    fn peek(&self, addr: Addr) -> Byte {
        let mut addr = self.translate_addr(addr);
        let len = self.len();
        if addr >= len {
            addr %= len;
        }
        assert!(addr < len, "peek outside memory range, {}/{}", addr, len);
        self.memory[addr]
    }
}

impl MemWrite for BankedMemory {
    fn write(&mut self, addr: Addr, val: Byte) {
        if self.writable() {
            let mut addr = self.translate_addr(addr);
            let len = self.len();
            if addr >= len {
                addr %= len;
            }
            assert!(addr < len, "write outside memory range {}/{}", addr, len);
            self.memory[addr] = val;
        }
    }
}

impl Savable for BankedMemory {
    fn save<F: Write>(&self, fh: &mut F) -> NesResult<()> {
        self.banks.save(fh)?;
        self.window.save(fh)?;
        self.bank_shift.save(fh)?;
        self.bank_count.save(fh)?;
        self.memory.save(fh)?;
        Ok(())
    }
    fn load<F: Read>(&mut self, fh: &mut F) -> NesResult<()> {
        self.banks.load(fh)?;
        self.window.load(fh)?;
        self.bank_shift.load(fh)?;
        self.bank_count.load(fh)?;
        self.memory.load(fh)?;
        Ok(())
    }
}

impl Deref for BankedMemory {
    type Target = [u8];
    fn deref(&self) -> &Self::Target {
        &self.memory
    }
}

impl DerefMut for BankedMemory {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.memory
    }
}

impl fmt::Debug for Memory {
    fn fmt(&self, f: &mut fmt::Formatter) -> std::result::Result<(), fmt::Error> {
        write!(
            f,
            "Memory {{ data: {} KB, writable: {} }}",
            self.data.len() / 1024,
            self.writable
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn add_bank_range_test() {
        let mut memory = BankedMemory::ram(0xFFFF, 0x2000);
        memory.add_bank_range(0x6000, 0xFFFF);
        assert_eq!(memory.get_bank(0x6000), 0);
        assert_eq!(memory.get_bank(0x7FFF), 0);
        assert_eq!(memory.get_bank(0x8000), 1);
        assert_eq!(memory.get_bank(0x9FFF), 1);
        assert_eq!(memory.get_bank(0xA000), 2);
        assert_eq!(memory.get_bank(0xBFFF), 2);
        assert_eq!(memory.get_bank(0xC000), 3);
        assert_eq!(memory.get_bank(0xDFFF), 3);
        assert_eq!(memory.get_bank(0xE000), 4);
        assert_eq!(memory.get_bank(0xFFFF), 4);

        let mut memory = BankedMemory::ram(0xFFFF, 0x2000);
        memory.add_bank_range(0x8000, 0xBFFF);
        assert_eq!(memory.get_bank(0x8000), 0);
        assert_eq!(memory.get_bank(0x9FFF), 0);
        assert_eq!(memory.get_bank(0xA000), 1);
        assert_eq!(memory.get_bank(0xBFFF), 1);

        memory.add_bank(0x6000, 0x7FFF);
        assert_eq!(memory.get_bank(0x6000), 0);
        assert_eq!(memory.get_bank(0x8000), 1);

        let mut memory = BankedMemory::ram(0xFFFF, 0x0400);
        memory.add_bank_range(0x0000, 0x1FFF);
        assert_eq!(memory.get_bank(0x0000), 0);
        assert_eq!(memory.get_bank(0x03FF), 0);
        assert_eq!(memory.get_bank(0x0400), 1);
        assert_eq!(memory.get_bank(0x07FF), 1);
        assert_eq!(memory.get_bank(0x1C00), 7);
        assert_eq!(memory.get_bank(0x1FFF), 7);
    }

    #[test]
    fn add_bank_test() {
        let mut memory = BankedMemory::ram(0xFFFF, 0x4000);
        memory.add_bank(0x8000, 0xBFFF);
        memory.add_bank(0xC000, 0xFFFF);
        assert_eq!(memory.get_bank(0x8000), 0);
        assert_eq!(memory.get_bank(0x9FFF), 0);
        assert_eq!(memory.get_bank(0xA000), 0);
        assert_eq!(memory.get_bank(0xBFFF), 0);
        assert_eq!(memory.get_bank(0xC000), 1);
        assert_eq!(memory.get_bank(0xDFFF), 1);
        assert_eq!(memory.get_bank(0xE000), 1);
        assert_eq!(memory.get_bank(0xFFFF), 1);
    }

    #[test]
    fn peek_bank_test() {
        let size = 40 * 1024;
        let rom = Memory::ram(size);
        let mut memory = BankedMemory::from(rom, 0x2000);

        assert!(!memory.is_empty(), "memory non-empty");
        assert_eq!(memory.len(), size, "memory size");

        memory.add_bank_range(0x8000, 0xFFFF);
        memory.memory.write(0x0000, 1);
        memory.memory.write(0x0000 + 1, 2);
        memory.memory.write(0x2000, 3);
        memory.memory.write(0x2000 + 1, 4);
        memory.memory.write(0x4000, 5);
        memory.memory.write(0x4000 + 1, 6);
        memory.memory.write(0x6000, 7);
        memory.memory.write(0x6000 + 1, 8);

        assert_eq!(memory.peek(0x8000), 1);
        assert_eq!(memory.peek(0x8001), 2);
        assert_eq!(memory.peek(0xA000), 3);
        assert_eq!(memory.peek(0xA001), 4);
        assert_eq!(memory.peek(0xC000), 5);
        assert_eq!(memory.peek(0xC001), 6);
        assert_eq!(memory.peek(0xE000), 7);
        assert_eq!(memory.peek(0xE001), 8);
    }

    #[test]
    fn write_bank_test() {
        let size = 40 * 1024;
        let rom = Memory::ram(size);
        let mut memory = BankedMemory::from(rom, 0x2000);

        assert!(!memory.is_empty(), "memory non-empty");
        assert_eq!(memory.len(), size, "memory size");

        memory.add_bank_range(0x8000, 0xFFFF);
        memory.write(0x8000, 11);
        memory.write(0xA000, 22);
        memory.write(0xC000, 33);
        memory.write(0xE000, 44);

        assert_eq!(memory.memory.peek(0x0000), 11);
        assert_eq!(memory.memory.peek(0x2000), 22);
        assert_eq!(memory.memory.peek(0x4000), 33);
        assert_eq!(memory.memory.peek(0x6000), 44);

        memory.write_protect(true);
        memory.write(0x8000, 255);
        assert_eq!(memory.memory.peek(0x0000), 11);
    }

    #[test]
    fn set_bank_test() {
        let size = 128 * 1024;
        let rom = Memory::ram(size);
        let mut memory = BankedMemory::from(rom, 0x2000);

        assert!(!memory.is_empty(), "memory non-empty");
        assert_eq!(memory.len(), size, "memory size");
        let last_bank = memory.last_bank();
        assert_eq!(last_bank, 15, "bank count");

        memory.add_bank_range(0x8000, 0xFFFF);
        memory.write(0x8000, 11);
        memory.write(0xA000, 22);
        assert_eq!(memory.peek(0x8000), 11);

        memory.set_bank(0x8000, 1);
        assert_eq!(memory.peek(0x8000), 22);

        memory.write(0xA000, 33);
        assert_eq!(memory.peek(0x8000), 33);

        memory.set_bank(0x8000, 0);
        assert_eq!(memory.peek(0x8000), 11);

        memory.set_bank(0x8000, last_bank);
        memory.write(0x8000, 255);
        assert_eq!(memory.peek(0x8000), 255);
    }

    #[test]
    fn bank_mirroring_test() {
        pretty_env_logger::init_timed();

        let size = 128 * 1024;
        let rom = Memory::ram(size);
        let mut memory = BankedMemory::from(rom, 0x4000);

        assert!(!memory.is_empty(), "memory non-empty");
        assert_eq!(memory.len(), size, "memory size");

        memory.add_bank_range(0x8000, 0xFFFF);
        memory.set_bank_mirror(0xC000, 0);

        memory.write(0x8000, 11);
        memory.write(0xA000, 22);

        assert_eq!(memory.peek(0x8000), 11);
        assert_eq!(memory.peek(0xA000), 22);
        assert_eq!(memory.peek(0xC000), 11);
        assert_eq!(memory.peek(0xE000), 22);
    }
}