1//! Genesis memory map and 68000 + Z80 bus interfaces
2
3use bincode::{Decode, Encode};
4use genesis_config::GenesisEmulatorConfig;
5use jgenesis_common::boxedarray::{BoxedByteArray, BoxedWordArray};
6use jgenesis_common::cheats::CheatWordOverrides;
7use jgenesis_common::num::{GetBit, U16Ext};
8
9const MAIN_RAM_LEN_WORDS: usize = 64 * 1024 / 2;
10const AUDIO_RAM_LEN: usize = 8 * 1024;
11
12type RamCheatOverrides = CheatWordOverrides<0xFF0000, 0xFFFFFF>;
13
14#[derive(Debug, Clone, Encode, Decode)]
15pub struct Memory {
16    main_ram: BoxedWordArray<MAIN_RAM_LEN_WORDS>,
17    audio_ram: BoxedByteArray<AUDIO_RAM_LEN>,
18    ram_cheat_overrides: RamCheatOverrides,
19}
20
21impl Memory {
22    #[allow(clippy::missing_panics_doc)]
23    #[must_use]
24    pub fn new(config: &GenesisEmulatorConfig) -> Self {
25        Self {
26            main_ram: BoxedWordArray::new(),
27            audio_ram: BoxedByteArray::new(),
28            ram_cheat_overrides: RamCheatOverrides::new(&config.cheat_codes),
29        }
30    }
31
32    #[must_use]
33    pub fn read_main_ram_byte(&self, address: u32) -> u8 {
34        if let Some(cheat) = self.ram_cheat_overrides.get(address) {
35            return cheat.to_be_bytes()[(address & 1) as usize];
36        }
37
38        let word = self.main_ram[((address & 0xFFFF) >> 1) as usize];
39        word.to_be_bytes()[(address & 1) as usize]
40    }
41
42    #[must_use]
43    pub fn read_main_ram<const WORD: bool>(&self, address: u32) -> u16 {
44        if WORD {
45            self.read_main_ram_word(address)
46        } else {
47            self.read_main_ram_byte(address).into()
48        }
49    }
50
51    #[must_use]
52    pub fn read_main_ram_word(&self, address: u32) -> u16 {
53        if let Some(cheat) = self.ram_cheat_overrides.get(address) {
54            return cheat;
55        }
56
57        self.main_ram[((address & 0xFFFF) >> 1) as usize]
58    }
59
60    pub fn write_main_ram<const WORD: bool>(&mut self, address: u32, value: u16) {
61        if WORD {
62            self.write_main_ram_word(address, value);
63        } else {
64            self.write_main_ram_byte(address, value as u8);
65        }
66    }
67
68    pub fn write_main_ram_byte(&mut self, address: u32, value: u8) {
69        let word = &mut self.main_ram[((address & 0xFFFF) >> 1) as usize];
70        if !address.bit(0) {
71            word.set_msb(value);
72        } else {
73            word.set_lsb(value);
74        }
75    }
76
77    pub fn write_main_ram_word(&mut self, address: u32, value: u16) {
78        self.main_ram[((address & 0xFFFF) >> 1) as usize] = value;
79    }
80
81    #[must_use]
82    pub fn read_audio_ram(&self, address: u16) -> u8 {
83        self.audio_ram[(address & 0x1FFF) as usize]
84    }
85
86    pub fn write_audio_ram(&mut self, address: u16, value: u8) {
87        self.audio_ram[(address & 0x1FFF) as usize] = value;
88    }
89
90    pub fn reload_config(&mut self, config: &GenesisEmulatorConfig) {
91        self.ram_cheat_overrides.update_cheat_codes(&config.cheat_codes);
92    }
93
94    #[must_use]
95    pub fn debug_ram_view(&mut self) -> (&mut [u16], &mut [u8]) {
96        (self.main_ram.as_mut_slice(), self.audio_ram.as_mut_slice())
97    }
98}