librsmsx 0.4.6

a MSX emulator written in rust, a port from gomsx
Documentation
use std::io::Read;
use std::{cell::RefCell, fs::File, rc::Rc};

use serde::{Deserialize, Serialize};

use crate::libs::cartridges::{
    get_cart_type, CartType, MapperASCII8, MapperKonami4, MapperKonami5,
};
use crate::libs::ppi::PPIData;
use crate::prelude::PPI;

// use super::cartridges::{get_cart_type, CartType, MapperASCII8, MapperKonami5};
// use super::ppi::{PPIData, PPI};

pub const PAGE_SIZE: usize = 0x4000;
pub const MAX_PAGES: usize = 4;
pub const SLOTS_PER_PAGE: usize = 4;
pub const FULL_MEMORY_SIZE: usize = SLOTS_PER_PAGE * MAX_PAGES * PAGE_SIZE;

pub struct NullMapper {}
impl Default for NullMapper {
    fn default() -> Self {
        Self::new()
    }
}

impl NullMapper {
    pub fn new() -> Self {
        Self {}
    }
}

impl Mapper for NullMapper {
    fn read_byte(&self, _address: u16) -> u8 {
        0
    }

    fn write_byte(&mut self, _address: u16, _value: u8) {}
}

pub trait Mapper {
    fn is_void(&self) -> bool {
        true
    }
    fn read_byte(&self, address: u16) -> u8;
    fn write_byte(&mut self, address: u16, value: u8);
}

#[derive(Serialize, Deserialize, Debug, Clone)]
#[allow(non_snake_case)]
pub struct MemoryData {
    pub(crate) contents: Vec<u8>, //[u8; 4 * 4 * 0x4000],
    pub(crate) can_write: [bool; SLOTS_PER_PAGE * MAX_PAGES],
    pub(crate) slot_mapper: isize,
}
impl Default for MemoryData {
    fn default() -> Self {
        Self::new()
    }
}

impl MemoryData {
    pub fn new() -> Self {
        Self {
            contents: vec![0; FULL_MEMORY_SIZE],
            can_write: [true; SLOTS_PER_PAGE * MAX_PAGES],
            slot_mapper: -1,
        }
    }
    pub fn read_byte(&self, pos: usize) -> u8 {
        self.contents[pos]
    }
}

// TODO: Secondary mapper (0xFFFF)
pub type MemoryAccessor = Rc<RefCell<Memory>>;

pub struct Memory {
    pub(crate) data: MemoryData,
    pub(crate) ppi: Rc<RefCell<PPI>>,
    mapper: Rc<RefCell<dyn Mapper>>,
}

impl Memory {
    pub fn new(ppi: Rc<RefCell<PPI>>) -> Self {
        Self {
            data: MemoryData::default(),
            ppi,
            mapper: Rc::new(RefCell::new(NullMapper::new())),
        }
    }
    // pub fn save_state(&self) -> Memory {
    //     let mut m = Memory::new(self.ppi.clone());
    //     m.contents = self.contents.clone();
    //     m.can_write = self.can_write;
    //     m.slot_mapper = self.slot_mapper;
    //     // m.mapper = self.mapper;
    //     m.mapper = self.mapper.clone();
    //     m
    // }
    // pub fn restore_state(&mut self, m: Memory) {
    //     self.contents = m.contents.clone();
    //     self.can_write = m.can_write;
    //     self.slot_mapper = m.slot_mapper;
    //     self.mapper = m.mapper.clone();
    //     self.ppi = m.ppi;
    // }
    pub fn load_bios_basic(&mut self, fname: &str) {
        let mut f = File::open(fname).unwrap();
        let mut buffer = Vec::new();
        f.read_to_end(&mut buffer).unwrap();
        self.load(&buffer, 0, 0);
        if buffer.len() > PAGE_SIZE {
            // Load BASIC, if present
            self.load(&buffer[PAGE_SIZE..], 1, 0);
        }
    }

    pub fn load_rom(&mut self, fname: &str, slot: usize, mapper_type: &str) {
        let mut f = File::open(fname).unwrap();
        let mut buffer = Vec::new();
        f.read_to_end(&mut buffer).unwrap();
        match get_cart_type(&buffer) {
            CartType::KONAMI4 => {
                log::info!("Loading ROM {} to slot 1 as type KONAMI4", fname);
                let mut mapper_konami4 = MapperKonami4::new();
                mapper_konami4.init(&buffer);
                self.set_mapper(Rc::new(RefCell::new(mapper_konami4)), slot);
                return;
            }
            CartType::KONAMI5 => {
                log::info!("Loading ROM {} to slot 1 as type KONAMI5", fname);
                let mut mapper_konami5 = MapperKonami5::new();
                mapper_konami5.init(&buffer);
                self.set_mapper(Rc::new(RefCell::new(mapper_konami5)), slot);
                return;
            }
            CartType::ASCII8KB => {
                log::info!("Loading ROM {} to slot 1 as type ASCII8KB", fname);
                let mut mapper_ascii8 = MapperASCII8::new();
                mapper_ascii8.init(&buffer);
                self.set_mapper(Rc::new(RefCell::new(mapper_ascii8)), slot);
                return;
            }
            CartType::NORMAL => {
                log::info!("Cartridge is type NORMAL");
            }
            _ => {
                unimplemented!()
            }
        }
        log::info!("Trying to load as a standard cartridge...");

        if !mapper_type.is_empty() && mapper_type == "KONAMI4" {
            let mut mapper_konami4 = MapperKonami4::new();
            mapper_konami4.init(&buffer);
            self.set_mapper(Rc::new(RefCell::new(mapper_konami4)), slot);
            return;
        }
        let num_of_pages = buffer.len() / PAGE_SIZE;
        match num_of_pages {
            1 => {
                // Load ROM to page 1, slot 1
                // TODO: mirrored????
                log::info!("Loading ROM {} to slot 1 (16KB)", fname);
                self.load(&buffer, 1, slot);
            }
            2 => {
                // Load ROM to slot 1. Mirrored pg1&pg2 <=> pg3&pg4
                log::info!("Loading ROM {} to slot 1 (32KB)", fname);
                self.load(&buffer, 0, slot);
                self.load(&buffer, 1, slot);
                self.load(&buffer[PAGE_SIZE..], 2, slot);
                self.load(&buffer[PAGE_SIZE..], 3, slot);
            }
            4 => {
                log::info!("Loading ROM {} to slot 1 (64KB)", fname);
                self.load(&buffer, 0, slot);
                self.load(&buffer[PAGE_SIZE..], 1, slot);
                self.load(&buffer[2 * PAGE_SIZE..], 2, slot);
                self.load(&buffer[3 * PAGE_SIZE..], 3, slot);
            }
            _ => {
                log::error!("ROM size not supported")
            }
        }
    }

    // Loads 16k (one page)
    pub fn load(&mut self, data: &[u8], page: usize, slot: usize) {
        let base_addr = (page * MAX_PAGES + slot) * PAGE_SIZE;
        self.data.contents[base_addr..(PAGE_SIZE + base_addr)].copy_from_slice(&data[..PAGE_SIZE]);
        self.data.can_write[page * MAX_PAGES + slot] = false;
    }

    pub fn set_mapper(&mut self, mapper: Rc<RefCell<dyn Mapper>>, slot: usize) {
        log::info!("Loading MegaROM in slot {}", slot);
        self.mapper = mapper;
        self.data.slot_mapper = slot as isize;
    }

    pub fn read_byte(&self, address: u16) -> u8 {
        self.read_byte_internal(address)
    }

    pub fn read_byte_with_slots(&self, page: usize, slot: usize, address: u16) -> u8 {
        assert!(page < MAX_PAGES);
        assert!(slot < SLOTS_PER_PAGE);
        assert!((address as usize) < PAGE_SIZE);
        self.data.contents[(page * MAX_PAGES + slot) * PAGE_SIZE + address as usize]
    }

    // ReadByteInternal reads a byte from address without taking
    // into account contention.
    pub fn read_byte_internal(&self, address: u16) -> u8 {
        let page = (address as usize) / PAGE_SIZE;
        let slot = self.ppi.borrow().data.pg_slots[page];

        if !self.mapper.borrow().is_void()
            && self.data.slot_mapper == slot
            && (page == 1 || page == 2)
        {
            return self.mapper.borrow().read_byte(address);
        }

        let delta = (address as usize) - page * PAGE_SIZE;
        // return self.contents[page][slot as usize][delta];
        self.data.contents[(page * SLOTS_PER_PAGE + slot as usize) * PAGE_SIZE + delta]
    }

    // WriteByte writes a byte at address taking into account
    // contention.
    pub fn write_byte(&mut self, address: u16, value: u8) {
        self.write_byte_internal(address, value)
    }

    // WriteByteInternal writes a byte at address without taking
    // into account contention.
    fn write_byte_internal(&mut self, address: u16, value: u8) {
        let page = (address as usize) / PAGE_SIZE;
        let slot = self.ppi.borrow().data.pg_slots[page];

        if !self.mapper.borrow().is_void()
            && self.data.slot_mapper == slot
            && (page == 1 || page == 2)
        {
            self.mapper.borrow_mut().write_byte(address, value);
            return;
        }

        if self.data.can_write[page * SLOTS_PER_PAGE + slot as usize] {
            let delta = (address as usize) - page * PAGE_SIZE;
            // return self.contents[page][slot as usize][delta];
            self.data.contents[(page * SLOTS_PER_PAGE + slot as usize) * PAGE_SIZE + delta] = value;
        }
    }

    pub fn contend_read(&mut self, _address: u16, _time: isize) {
        //panic("ContendRead not implemented")
    }

    pub fn contend_read_no_mreq(&mut self, _address: u16, _time: isize) {
        //panic("ContendReadNoMreq not implemented")
    }

    pub fn contend_read_no_mreq_loop(&mut self, _address: u16, _time: isize, _count: usize) {
        //panic("ContendReadNoMreq_loop not implemented")
    }

    pub fn contend_write_no_mreq(&mut self, _address: u16, _time: isize) {
        //panic("ContendWriteNoMreq not implemented")
    }

    pub fn contend_write_no_mreq_loop(&mut self, _address: u16, _time: isize, _count: usize) {
        //panic("ContendWriteNoMreq_loop not implemented")
    }
    pub fn get_data(&self) -> MemoryData {
        self.data.clone()
    }
    pub fn set_data(&mut self, data: MemoryData) {
        self.data = data;
    }
    pub fn get_ppi_data(&self) -> PPIData {
        self.ppi.borrow().get_data()
    }
    pub fn set_ppi_data(&mut self, data: PPIData) {
        self.ppi.borrow_mut().set_data(data);
    }
}