GBA internal memory

3use crate::api::{GbaEmulatorConfig, GbaLoadError};
4use bincode::{Decode, Encode};
5use jgenesis_common::boxedarray::BoxedByteArray;
6use jgenesis_common::debug::{DebugBytesView, DebugMemoryView};
7use jgenesis_common::num::GetBit;
8use std::array;
10const BIOS_ROM_LEN: usize = 16 * 1024;
11const IWRAM_LEN: usize = 32 * 1024;
12const EWRAM_LEN: usize = 256 * 1024;
13
14#[derive(Debug, Clone, Encode, Decode)]
15pub struct MemoryControl {
16    pub sram_cycles: u64,
17    // There are only 3 waitstate areas, but making arrays len 4 avoids a bounds check on access
18    pub cartridge_n_cycles: [u64; 4],
19    pub cartridge_s_cycles: [u64; 4],
20    pub prefetch_enabled: bool,
21    pub raw_value: u16,
22}
23
24impl MemoryControl {
25    const SRAM_CYCLES: [u64; 4] = [5, 4, 3, 9];
26    const CARTRIDGE_N_CYCLES: [u64; 4] = [5, 4, 3, 9];
27    const CARTRIDGE_0_S_CYCLES: [u64; 2] = [3, 2];
28    const CARTRIDGE_1_S_CYCLES: [u64; 2] = [5, 2];
29    const CARTRIDGE_2_S_CYCLES: [u64; 2] = [9, 2];
30
31    pub fn new() -> Self {
32        Self {
33            sram_cycles: Self::SRAM_CYCLES[0],
34            cartridge_n_cycles: array::from_fn(|_| Self::CARTRIDGE_N_CYCLES[0]),
35            cartridge_s_cycles: [
36                Self::CARTRIDGE_0_S_CYCLES[0],
37                Self::CARTRIDGE_1_S_CYCLES[0],
38                Self::CARTRIDGE_2_S_CYCLES[0],
39                0,
40            ],
41            prefetch_enabled: false,
42            raw_value: 0x0000,
43        }
44    }
45
46    pub fn read(&self) -> u16 {
47        self.raw_value
48    }
49
50    pub fn write(&mut self, value: u16) {
51        self.sram_cycles = Self::SRAM_CYCLES[(value & 3) as usize];
52        self.cartridge_n_cycles[0] = Self::CARTRIDGE_N_CYCLES[((value >> 2) & 3) as usize];
53        self.cartridge_n_cycles[1] = Self::CARTRIDGE_N_CYCLES[((value >> 5) & 3) as usize];
54        self.cartridge_n_cycles[2] = Self::CARTRIDGE_N_CYCLES[((value >> 8) & 3) as usize];
55        self.cartridge_s_cycles[0] = Self::CARTRIDGE_0_S_CYCLES[usize::from(value.bit(4))];
56        self.cartridge_s_cycles[1] = Self::CARTRIDGE_1_S_CYCLES[usize::from(value.bit(7))];
57        self.cartridge_s_cycles[2] = Self::CARTRIDGE_2_S_CYCLES[usize::from(value.bit(10))];
58        self.prefetch_enabled = value.bit(14);
59
60        // Highest bit is not writable; DK King of Swing depends on this
61        self.raw_value = value & 0x7FFF;
62
63        log::debug!("WAITCNT write: {value:04X}");
64        log::debug!("  SRAM cycles: {}", self.sram_cycles);
65        log::debug!("  Cartridge 0 N cycles: {}", self.cartridge_n_cycles[0]);
66        log::debug!("  Cartridge 0 S cycles: {}", self.cartridge_s_cycles[0]);
67        log::debug!("  Cartridge 1 N cycles: {}", self.cartridge_n_cycles[1]);
68        log::debug!("  Cartridge 1 S cycles: {}", self.cartridge_s_cycles[1]);
69        log::debug!("  Cartridge 2 N cycles: {}", self.cartridge_n_cycles[2]);
70        log::debug!("  Cartridge 2 S cycles: {}", self.cartridge_s_cycles[2]);
71        log::debug!("  Cartridge ROM prefetch enabled: {}", self.prefetch_enabled);
72    }
73
74    pub fn rom_n_cycles(&self, address: u32) -> u64 {
75        self.cartridge_n_cycles[wait_states_area(address)]
76    }
77
78    pub fn rom_s_cycles(&self, address: u32) -> u64 {
79        self.cartridge_s_cycles[wait_states_area(address)]
80    }
81}
82
83fn wait_states_area(address: u32) -> usize {
84    // Area 0: $08000000-$09FFFFFF
85    // Area 1: $0A000000-$0BFFFFFF
86    // Area 2: $0C000000-$0DFFFFFF
87    debug_assert!((0x08000000..0x0E000000).contains(&address), "{address:08X}");
88    ((address >> 25) & 3) as usize
89}
90
91#[derive(Debug, Clone, Encode, Decode)]
92pub struct Memory {
93    bios_rom: BoxedByteArray<BIOS_ROM_LEN>,
94    iwram: BoxedByteArray<IWRAM_LEN>,
95    ewram: BoxedByteArray<EWRAM_LEN>,
96    memory_control: MemoryControl,
97    post_boot: bool,
98}
99
100impl Memory {
101    pub fn new(bios_rom: Vec<u8>, config: GbaEmulatorConfig) -> Result<Self, GbaLoadError> {
102        if bios_rom.len() != BIOS_ROM_LEN {
103            return Err(GbaLoadError::InvalidBiosLength {
104                expected: BIOS_ROM_LEN,
105                actual: bios_rom.len(),
106            });
107        }
108
109        let bios_rom: Box<[u8; BIOS_ROM_LEN]> = bios_rom.into_boxed_slice().try_into().unwrap();
110
111        Ok(Self {
112            bios_rom: bios_rom.into(),
113            iwram: BoxedByteArray::new(),
114            ewram: BoxedByteArray::new(),
115            memory_control: MemoryControl::new(),
116            post_boot: config.skip_bios_animation,
117        })
118    }
119
120    pub fn read_bios_rom(&self, address: u32) -> u32 {
121        let word_addr = (address as usize) & (BIOS_ROM_LEN - 1) & !3;
122        u32::from_le_bytes(self.bios_rom[word_addr..word_addr + 4].try_into().unwrap())
123    }
124
125    pub fn read_iwram_byte(&self, address: u32) -> u8 {
126        read_byte(&self.iwram, address)
127    }
128
129    pub fn read_iwram_halfword(&self, address: u32) -> u16 {
130        read_halfword(&self.iwram, address)
131    }
132
133    pub fn read_iwram_word(&self, address: u32) -> u32 {
134        read_word(&self.iwram, address)
135    }
136
137    pub fn read_ewram_byte(&self, address: u32) -> u8 {
138        read_byte(&self.ewram, address)
139    }
140
141    pub fn read_ewram_halfword(&self, address: u32) -> u16 {
142        read_halfword(&self.ewram, address)
143    }
144
145    pub fn read_ewram_word(&self, address: u32) -> u32 {
146        read_word(&self.ewram, address)
147    }
148
149    pub fn write_iwram_byte(&mut self, address: u32, value: u8) {
150        write_byte(&mut self.iwram, address, value);
151    }
152
153    pub fn write_iwram_halfword(&mut self, address: u32, value: u16) {
154        write_halfword(&mut self.iwram, address, value);
155    }
156
157    pub fn write_iwram_word(&mut self, address: u32, value: u32) {
158        write_word(&mut self.iwram, address, value);
159    }
160
161    pub fn write_ewram_byte(&mut self, address: u32, value: u8) {
162        write_byte(&mut self.ewram, address, value);
163    }
164
165    pub fn write_ewram_halfword(&mut self, address: u32, value: u16) {
166        write_halfword(&mut self.ewram, address, value);
167    }
168
169    pub fn write_ewram_word(&mut self, address: u32, value: u32) {
170        write_word(&mut self.ewram, address, value);
171    }
172
173    pub fn control(&self) -> &MemoryControl {
174        &self.memory_control
175    }
176
177    // $4000204: WAITCNT (Waitstate control)
178    pub fn read_waitcnt(&self) -> u16 {
179        self.memory_control.read()
180    }
181
182    // $4000204: WAITCNT (Waitstate control)
183    pub fn write_waitcnt(&mut self, value: u16) {
184        self.memory_control.write(value);
185    }
186
187    // $4000300: POSTFLG (Post boot flag)
188    pub fn read_postflg(&self) -> u8 {
189        self.post_boot.into()
190    }
191
192    // $4000300: POSTFLG (Post boot flag)
193    pub fn write_postflg(&mut self, value: u8) {
194        // POSTFLG can only change from 0 to 1
195        self.post_boot |= value.bit(0);
196        log::trace!("POSTFLG write {value:02X}");
197    }
198
199    pub fn clone_bios_rom(&mut self) -> Vec<u8> {
200        self.bios_rom.to_vec()
201    }
202
203    pub fn debug_ewram_view(&mut self) -> impl DebugMemoryView {
204        DebugBytesView(self.ewram.as_mut_slice())
205    }
206
207    pub fn debug_iwram_view(&mut self) -> impl DebugMemoryView {
208        DebugBytesView(self.iwram.as_mut_slice())
209    }
210
211    pub fn debug_bios_view(&mut self) -> impl DebugMemoryView {
212        DebugBytesView(self.bios_rom.as_mut_slice())
213    }
214}
215
216fn read_byte<const LEN: usize>(memory: &[u8; LEN], address: u32) -> u8 {
217    let memory_addr = (address as usize) & (LEN - 1);
218    memory[memory_addr]
219}
220
221fn read_halfword<const LEN: usize>(memory: &[u8; LEN], address: u32) -> u16 {
222    let memory_addr = (address as usize) & (LEN - 1) & !1;
223    u16::from_le_bytes(memory[memory_addr..memory_addr + 2].try_into().unwrap())
224}
225
226fn read_word<const LEN: usize>(memory: &[u8; LEN], address: u32) -> u32 {
227    let memory_addr = (address as usize) & (LEN - 1) & !3;
228    u32::from_le_bytes(memory[memory_addr..memory_addr + 4].try_into().unwrap())
229}
230
231fn write_byte<const LEN: usize>(memory: &mut [u8; LEN], address: u32, value: u8) {
232    let memory_addr = (address as usize) & (LEN - 1);
233    memory[memory_addr] = value;
234}
235
236fn write_halfword<const LEN: usize>(memory: &mut [u8; LEN], address: u32, value: u16) {
237    let memory_addr = (address as usize) & (LEN - 1) & !1;
238    memory[memory_addr..memory_addr + 2].copy_from_slice(&value.to_le_bytes());
239}
240
241fn write_word<const LEN: usize>(memory: &mut [u8; LEN], address: u32, value: u32) {
242    let memory_addr = (address as usize) & (LEN - 1) & !3;
243    memory[memory_addr..memory_addr + 4].copy_from_slice(&value.to_le_bytes());
244}