tgbr_core/mbc/
mod.rs

1mod mbc1;
2mod mbc2;
3mod mbc3;
4mod mbc5;
5
6use ambassador::{delegatable_trait, Delegate};
7use log::warn;
8use serde::{Deserialize, Serialize};
9
10use crate::{
11    context,
12    rom::{self, Rom},
13    util::trait_alias,
14};
15
16trait_alias!(pub trait Context = context::Rom + context::ExternalRam);
17
18#[allow(unused_variables)]
19#[delegatable_trait]
20pub trait MbcTrait {
21    fn read(&mut self, ctx: &mut impl Context, addr: u16) -> u8;
22    fn write(&mut self, ctx: &mut impl Context, addr: u16, data: u8) {}
23    fn internal_ram(&self) -> Option<&[u8]> {
24        None
25    }
26}
27
28#[derive(Serialize, Deserialize)]
29pub struct NullMbc {}
30
31impl NullMbc {
32    fn new(rom: &Rom) -> Self {
33        assert_eq!(
34            rom.rom_size,
35            32 * 1024,
36            "ROM only cartridge should be 32KiB"
37        );
38        Self {}
39    }
40}
41
42impl MbcTrait for NullMbc {
43    fn read(&mut self, ctx: &mut impl Context, addr: u16) -> u8 {
44        match addr {
45            0x0000..=0x3FFF => ctx.rom().data[addr as usize],
46            0x4000..=0x7FFF => ctx.rom().data[addr as usize],
47            0xA000..=0xBFFF => ctx.external_ram()[addr as usize - 0xA000],
48            _ => panic!("{addr:04X}"),
49        }
50    }
51    fn write(&mut self, ctx: &mut impl Context, addr: u16, data: u8) {
52        match addr {
53            0xA000..=0xBFFF => ctx.external_ram_mut()[addr as usize - 0xA000] = data,
54            _ => warn!("Invalid address write: ${addr:04X} = ${data:02X}"),
55        }
56    }
57}
58
59macro_rules! def_mbc {
60    ($($id:ident => $ty:ty,)*) => {
61        #[derive(Serialize, Deserialize, Delegate)]
62        #[delegate(MbcTrait)]
63        pub enum Mbc {
64            NullMbc(NullMbc),
65            $(
66                $id($ty),
67            )*
68        }
69
70        pub fn create_mbc(rom: &Rom, internal_ram: Option<Vec<u8>>) -> Mbc {
71            let cart_type = rom.cartridge_type.clone();
72            match cart_type.mbc {
73                None => Mbc::NullMbc(NullMbc::new(rom)),
74                $(
75                    Some(rom::Mbc::$id) => Mbc::$id(<$ty>::new(rom, internal_ram)),
76                )*
77                Some(mbc) => todo!("{} is currently unsupported", mbc),
78            }
79        }
80    }
81}
82
83def_mbc! {
84    Mbc1 => mbc1::Mbc1,
85    Mbc2 => mbc2::Mbc2,
86    Mbc3 => mbc3::Mbc3,
87    Mbc5 => mbc5::Mbc5,
88}