1use bitvec::prelude::*;
2use log::warn;
3use serde::{Deserialize, Serialize};
4use std::cmp::max;
5
6#[derive(Serialize, Deserialize)]
7pub struct Mbc2 {
8 rom_bank: u8,
9 rom_bank_mask: u8,
10 #[serde(with = "serde_bytes")]
11 ram: Vec<u8>,
12 ram_enable: bool,
13}
14
15impl Mbc2 {
16 pub fn new(rom: &crate::rom::Rom, internal_ram: Option<Vec<u8>>) -> Self {
17 if let Some(ram) = &internal_ram {
18 assert_eq!(ram.len(), 256);
19 }
20 let rom_bank_num = rom.rom_size / 0x4000;
21 assert!(rom_bank_num.is_power_of_two());
22 Self {
23 rom_bank: 1,
24 rom_bank_mask: rom_bank_num.saturating_sub(1) as u8,
25 ram: internal_ram.unwrap_or_else(|| vec![0; 0x100]),
26 ram_enable: false,
27 }
28 }
29}
30
31impl super::MbcTrait for Mbc2 {
32 fn read(&mut self, ctx: &mut impl super::Context, addr: u16) -> u8 {
33 match addr {
34 0x0000..=0x3FFF => ctx.rom().data[addr as usize],
35 0x4000..=0x7FFF => {
36 let offset = (self.rom_bank & self.rom_bank_mask) as usize * 0x4000;
37 ctx.rom().data[offset + (addr & 0x3FFF) as usize]
38 }
39 0xA000..=0xBFFF => {
40 if self.ram_enable {
41 let addr = (addr & 0x1FF) as usize;
42 let data = self.ram[addr / 2];
43 if addr % 2 == 0 {
44 data & 0xF
45 } else {
46 data >> 4
47 }
48 } else {
49 !0
50 }
51 }
52 _ => panic!("MBC2: Read ${addr:04X}"),
53 }
54 }
55 fn write(&mut self, _ctx: &mut impl super::Context, addr: u16, data: u8) {
56 match addr {
57 0x0000..=0x3FFF => {
58 if addr.view_bits::<Lsb0>()[8] {
59 self.rom_bank = max(1, data & 0xF);
60 } else {
61 self.ram_enable = data == 0x0A;
62 }
63 }
64 0xA000..=0xBFFF => {
65 if self.ram_enable {
66 let addr = (addr & 0x1FF) as usize;
67 let v = self.ram[addr / 2].view_bits_mut::<Lsb0>();
68 if addr % 2 == 0 {
69 v[0..=3].store(data & 0xF);
70 } else {
71 v[4..=7].store(data & 0xF);
72 }
73 }
74 }
75 _ => warn!("MBC2: Write ${addr:04X} = ${data:02X}"),
76 }
77 }
78 fn internal_ram(&self) -> Option<&[u8]> {
79 Some(&self.ram)
80 }
81}