reznez 0.0.0

The high accuracy NES Emulator
Documentation
use crate::apu::apu_registers::ApuRegisters;
use crate::memory::cpu::cpu_address::CpuAddress;
use crate::memory::cpu::cpu_internal_ram::CpuInternalRam;
use crate::memory::cpu::ports::Ports;
use crate::memory::cpu::stack::Stack;
use crate::memory::mapper::{Mapper, MapperParams};
use crate::memory::ppu::ppu_address::PpuAddress;
use crate::memory::ppu::ppu_internal_ram::PpuInternalRam;
use crate::memory::read_result::ReadResult;
use crate::ppu::name_table::name_table::NameTable;
use crate::ppu::name_table::name_table_mirroring::NameTableMirroring;
use crate::ppu::name_table::name_table_quadrant::NameTableQuadrant;
use crate::ppu::palette::palette_table::PaletteTable;
use crate::ppu::palette::system_palette::SystemPalette;
use crate::ppu::pattern_table::{PatternTable, PatternTableSide};
use crate::ppu::register::ppu_registers::PpuRegisters;
use crate::ppu::sprite::oam::Oam;

pub const NMI_VECTOR_LOW: CpuAddress     = CpuAddress::new(0xFFFA);
pub const NMI_VECTOR_HIGH: CpuAddress    = CpuAddress::new(0xFFFB);
pub const RESET_VECTOR_LOW: CpuAddress   = CpuAddress::new(0xFFFC);
pub const RESET_VECTOR_HIGH: CpuAddress  = CpuAddress::new(0xFFFD);
pub const IRQ_VECTOR_LOW: CpuAddress     = CpuAddress::new(0xFFFE);
pub const IRQ_VECTOR_HIGH: CpuAddress    = CpuAddress::new(0xFFFF);

pub struct Memory {
    mapper: Box<dyn Mapper>,
    mapper_params: MapperParams,
    cpu_internal_ram: CpuInternalRam,
    ppu_internal_ram: PpuInternalRam,
    oam: Oam,
    ports: Ports,
    ppu_registers: PpuRegisters,
    apu_registers: ApuRegisters,
    system_palette: SystemPalette,
    cpu_cycle: i64,
}

impl Memory {
    pub fn new(
        mapper: Box<dyn Mapper>, 
        mapper_params: MapperParams,
        ports: Ports,
        system_palette: SystemPalette,
    ) -> Memory {
        Memory {
            mapper,
            mapper_params,
            cpu_internal_ram: CpuInternalRam::new(),
            ppu_internal_ram: PpuInternalRam::new(),
            oam: Oam::new(),
            ports,
            ppu_registers: PpuRegisters::new(),
            apu_registers: ApuRegisters::new(),
            system_palette,
            cpu_cycle: 0,
        }
    }

    pub fn as_cpu_memory(&mut self) -> CpuMemory {
        CpuMemory { memory: self }
    }

    pub fn as_ppu_memory(&mut self) -> PpuMemory {
        PpuMemory { memory: self }
    }

    pub fn mapper(&self) -> &dyn Mapper {
        &*self.mapper
    }

    pub fn mapper_mut(&mut self) -> &mut dyn Mapper {
        &mut *self.mapper
    }

    pub fn mapper_params(&self) -> &MapperParams {
        &self.mapper_params
    }

    pub fn stack_pointer(&self) -> u8 {
        self.cpu_internal_ram.stack_pointer
    }

    #[inline]
    pub fn ppu_regs(&self) -> &PpuRegisters {
        &self.ppu_registers
    }

    pub fn apu_regs(&self) -> &ApuRegisters {
        &self.apu_registers
    }

    pub fn apu_regs_mut(&mut self) -> &mut ApuRegisters {
        &mut self.apu_registers
    }

    pub fn cpu_cycle(&self) -> i64 {
        self.cpu_cycle
    }

    pub fn cpu_peek(&self, address: CpuAddress) -> ReadResult {
        self.mapper.cpu_peek(
            &self.mapper_params,
            &self.cpu_internal_ram,
            &self.ppu_internal_ram,
            &self.oam,
            &self.ports,
            &self.ppu_registers,
            &self.apu_registers,
            address,
        )
    }
}

pub struct CpuMemory<'a> {
    memory: &'a mut Memory,
}

impl<'a> CpuMemory<'a> {
    #[inline]
    pub fn peek(&self, address: CpuAddress) -> ReadResult {
        self.memory.mapper.cpu_peek(
            &self.memory.mapper_params,
            &self.memory.cpu_internal_ram,
            &self.memory.ppu_internal_ram,
            &self.memory.oam,
            &self.memory.ports,
            &self.memory.ppu_registers,
            &self.memory.apu_registers,
            address,
        )
    }

    #[inline]
    pub fn read(&mut self, address: CpuAddress) -> ReadResult {
        self.memory.mapper.cpu_read(
            &mut self.memory.mapper_params,
            &self.memory.cpu_internal_ram,
            &self.memory.ppu_internal_ram,
            &self.memory.oam,
            &mut self.memory.ports,
            &mut self.memory.ppu_registers,
            &mut self.memory.apu_registers,
            address,
        )
    }

    #[inline]
    pub fn write(&mut self, address: CpuAddress, value: u8) {
        self.memory.mapper.cpu_write(
            &mut self.memory.mapper_params,
            &mut self.memory.cpu_internal_ram,
            &mut self.memory.ppu_internal_ram,
            &mut self.memory.oam,
            &mut self.memory.ports,
            &mut self.memory.ppu_registers,
            &mut self.memory.apu_registers,
            address,
            value,
        );
    }

    pub fn ports(&self) -> &Ports {
        &self.memory.ports
    }

    pub fn take_dmc_dma_pending_address(&mut self) -> Option<CpuAddress> {
        self.memory.apu_registers.dmc.take_dma_pending_address()
    }

    pub fn set_dmc_sample_buffer(&mut self, value: u8) {
        self.memory.apu_registers.dmc.set_sample_buffer(value);
    }

    #[inline]
    pub fn stack(&mut self) -> Stack {
        self.memory.cpu_internal_ram.stack()
    }

    #[inline]
    pub fn stack_pointer(&self) -> u8 {
        self.memory.cpu_internal_ram.stack_pointer
    }

    #[inline]
    pub fn stack_pointer_mut(&mut self) -> &mut u8 {
        &mut self.memory.cpu_internal_ram.stack_pointer
    }

    pub fn stack_pointer_address(&self) -> CpuAddress {
        CpuAddress::from_low_high(self.stack_pointer(), 0x01)
    }

    pub fn nmi_vector(&mut self) -> CpuAddress {
        self.address_from_vector(NMI_VECTOR_LOW)
    }

    pub fn reset_vector(&mut self) -> CpuAddress {
        self.address_from_vector(RESET_VECTOR_LOW)
    }

    pub fn irq_vector(&mut self) -> CpuAddress {
        self.address_from_vector(IRQ_VECTOR_LOW)
    }

    pub fn cpu_cycle(&self) -> i64 {
        self.memory.cpu_cycle
    }

    pub fn increment_cpu_cycle(&mut self) {
        self.memory.cpu_cycle += 1;
    }

    pub fn set_cpu_cycle(&mut self, cycle: i64) {
        self.memory.cpu_cycle = cycle;
    }

    pub fn process_end_of_cpu_cycle(&mut self) {
        self.memory.mapper.on_end_of_cpu_cycle(self.memory.cpu_cycle);
    }

    fn address_from_vector(&mut self, mut vector: CpuAddress) -> CpuAddress {
        CpuAddress::from_low_high(
            self.read(vector).expect("Read open bus."),
            self.read(vector.inc()).expect("Read open bus."),
        )
    }
}

pub struct PpuMemory<'a> {
    memory: &'a mut Memory,
}

impl<'a> PpuMemory<'a> {
    #[inline]
    pub fn read(&mut self, address: PpuAddress) -> u8 {
        self.memory.mapper.ppu_read(&mut self.memory.mapper_params, &self.memory.ppu_internal_ram, address, true)
    }

    #[inline]
    pub fn write(&mut self, address: PpuAddress, value: u8) {
        self.memory.mapper.ppu_write(&mut self.memory.mapper_params, &mut self.memory.ppu_internal_ram, address, value);
    }

    pub fn oam(&self) -> &Oam {
        &self.memory.oam
    }

    pub fn oam_mut(&mut self) -> &mut Oam {
        &mut self.memory.oam
    }

    pub fn process_end_of_ppu_cycle(&mut self) {
        self.memory.mapper.on_end_of_ppu_cycle();
    }

    pub fn process_current_ppu_address(&mut self, address: PpuAddress) {
        self.memory.mapper.process_current_ppu_address(address);
    }

    #[inline]
    pub fn regs(&self) -> &PpuRegisters {
        &self.memory.ppu_registers
    }

    #[inline]
    pub fn regs_mut(&mut self) -> &mut PpuRegisters {
        &mut self.memory.ppu_registers
    }

    pub fn name_table_mirroring(&self) -> NameTableMirroring {
        self.memory.mapper_params.name_table_mirroring()
    }

    #[inline]
    pub fn pattern_table(&self, side: PatternTableSide) -> PatternTable {
        self.memory.mapper_params.pattern_table(side)
    }

    #[inline]
    pub fn background_pattern_table(&self) -> PatternTable {
        self.pattern_table(self.regs().background_table_side())
    }

    #[inline]
    pub fn sprite_pattern_table(&self) -> PatternTable {
        self.pattern_table(self.regs().sprite_table_side())
    }

    #[inline]
    pub fn name_table(&self, quadrant: NameTableQuadrant) -> NameTable {
        NameTable::new(
            self.memory
                .mapper
                .raw_name_table(self.memory.mapper_params.name_table_mirroring(), &self.memory.ppu_internal_ram, quadrant),
        )
    }

    #[inline]
    pub fn palette_table(&self) -> PaletteTable {
        PaletteTable::new(
            self.memory.ppu_internal_ram.palette_ram.to_slice(),
            &self.memory.system_palette,
        )
    }
}

#[cfg(test)]
mod tests {
    use crate::memory::memory::test_data;
    use crate::memory::memory::PpuAddress;
    use crate::ppu::register::registers::ctrl::AddressIncrement;

    #[test]
    fn mirrored_3000s() {
        let mut memory = test_data::memory();
        let mut memory = memory.as_ppu_memory();
        let mut address = PpuAddress::from_u16(0x2000);
        let mut value = 1;
        while address < PpuAddress::from_u16(0x2F00) {
            memory.write(address, value);
            let low_value = memory.read(address);
            let high_value = memory.read(PpuAddress::from_u16(address.to_u16() + 0x1000));
            assert_eq!(low_value, value);
            assert_eq!(low_value, high_value);

            value = value.wrapping_add(1);
            address.advance(AddressIncrement::Right);
        }

        let mut address = PpuAddress::from_u16(0x3000);
        let mut value = 111;
        while address < PpuAddress::from_u16(0x3F00) {
            memory.write(address, value);
            let high_value = memory.read(address);
            let low_value = memory.read(PpuAddress::from_u16(address.to_u16() - 0x1000));
            assert_eq!(low_value, value);
            assert_eq!(low_value, high_value);

            value = value.wrapping_add(1);
            address.advance(AddressIncrement::Right);
        }
    }
}

#[cfg(test)]
pub mod test_data {
    use crate::cartridge::cartridge;
    use crate::cartridge::cartridge::Cartridge;
    use crate::memory::cpu::ports;
    use crate::memory::mappers::mapper000::Mapper000;
    use crate::ppu::palette::system_palette;

    use super::*;

    pub fn memory() -> Memory {
        memory_with_cartridge(&cartridge::test_data::cartridge())
    }

    pub fn memory_with_cartridge(cartridge: &Cartridge) -> Memory {
        let mapper = Mapper000;
        let mapper_params = mapper.initial_layout().make_mapper_params(cartridge);
        Memory::new(
            Box::new(mapper),
            mapper_params,
            ports::test_data::ports(),
            system_palette::test_data::system_palette(),
        )
    }
}