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;
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>, 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]
}
}
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 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 {
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 => {
log::info!("Loading ROM {} to slot 1 (16KB)", fname);
self.load(&buffer, 1, slot);
}
2 => {
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")
}
}
}
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]
}
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;
self.data.contents[(page * SLOTS_PER_PAGE + slot as usize) * PAGE_SIZE + delta]
}
pub fn write_byte(&mut self, address: u16, value: u8) {
self.write_byte_internal(address, value)
}
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;
self.data.contents[(page * SLOTS_PER_PAGE + slot as usize) * PAGE_SIZE + delta] = value;
}
}
pub fn contend_read(&mut self, _address: u16, _time: isize) {
}
pub fn contend_read_no_mreq(&mut self, _address: u16, _time: isize) {
}
pub fn contend_read_no_mreq_loop(&mut self, _address: u16, _time: isize, _count: usize) {
}
pub fn contend_write_no_mreq(&mut self, _address: u16, _time: isize) {
}
pub fn contend_write_no_mreq_loop(&mut self, _address: u16, _time: isize, _count: usize) {
}
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);
}
}