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}