1use crate::events;
2use crate::rom::Cartridge;
3
4use olympia_core::address;
5
6pub(crate) const DMA_REGISTER_ADDR: u16 = 0xff46;
7pub(crate) const LCD_CONTROL_ADDR: u16 = 0xFF40;
8pub(crate) const LCD_STATUS_ADDR: u16 = 0xFF41;
9pub(crate) const SCROLL_Y_ADDR: u16 = 0xFF42;
10pub(crate) const SCROLL_X_ADDR: u16 = 0xFF43;
11pub(crate) const WINDOW_Y_ADDR: u16 = 0xFF4A;
12pub(crate) const WINDOW_X_ADDR: u16 = 0xFF4B;
13pub(crate) const CURRENT_LINE_ADDR: u16 = 0xFF44;
14pub(crate) const LINE_CHECK_ADDR: u16 = 0xFF45;
15pub(crate) const INTERRUPT_ENABLE_ADDR: u16 = 0xffff;
16pub(crate) const INTERRUPT_FLAG_ADDR: u16 = 0xff0f;
17
18#[derive(PartialEq, Eq, Debug)]
19pub struct MemoryRegion {
20 pub start: u16,
21 pub last: u16,
22 pub len: u16,
23 pub name: &'static str,
24}
25
26impl MemoryRegion {
27 const fn new(start: u16, len: u16, name: &'static str) -> MemoryRegion {
28 MemoryRegion {
29 start,
30 len,
31 name,
32 last: start + (len - 1),
33 }
34 }
35
36 pub fn contains(&self, addr: u16) -> bool {
37 addr >= self.start && addr <= self.last
38 }
39}
40
41pub const STATIC_ROM: MemoryRegion = MemoryRegion::new(0x0000, 0x4000, "staticrom");
42pub const SWITCHABLE_ROM: MemoryRegion = MemoryRegion::new(0x4000, 0x4000, "switchrom");
43pub const CARTRIDGE_ROM: MemoryRegion = MemoryRegion::new(0x0000, 0x4000, "rom");
44pub const VRAM: MemoryRegion = MemoryRegion::new(0x8000, 0x2000, "vram");
45pub const CARTRIDGE_RAM: MemoryRegion = MemoryRegion::new(0xA000, 0x2000, "cartram");
46pub const SYS_RAM: MemoryRegion = MemoryRegion::new(0xC000, 0x2000, "sysram");
47pub const SYS_RAM_MIRROR: MemoryRegion = MemoryRegion::new(0xE000, 0x1E00, "sysram_mirror");
48pub const OAM_RAM: MemoryRegion = MemoryRegion::new(0xFE00, 0xA0, "oamram");
49pub const MEM_REGISTERS: MemoryRegion = MemoryRegion::new(0xFF00, 0x80, "memregisters");
50pub const CPU_RAM: MemoryRegion = MemoryRegion::new(0xFF80, 0x7F, "cpuram");
51
52#[derive(PartialEq, Eq, Debug, Clone)]
53pub enum MemoryError {
55 InvalidRomAddress(u16),
60 InvalidRamAddress(u16),
65 UnmappedAddress(u16),
69}
70
71pub type MemoryResult<T> = Result<T, MemoryError>;
72
73pub(crate) struct MemoryIterator<'a> {
74 addr: address::LiteralAddress,
75 mem: &'a Memory,
76}
77
78impl<'a> Iterator for MemoryIterator<'a> {
79 type Item = u8;
80
81 fn next(&mut self) -> Option<Self::Item> {
82 let val = self.mem.read_u8(self.addr);
83 self.addr = self.addr.next();
84 Some(val.unwrap_or(0))
85 }
86}
87
88fn masked_write(current: &mut u8, new: u8, mask: u8) {
89 *current = (new & mask) | (*current & !mask);
90}
91
92pub struct MemoryRegisters {
93 pub(crate) dma: u8,
96 pub(crate) lcdc: u8,
101 pub(crate) lcdstat: u8,
104 pub(crate) scy: u8,
106 pub(crate) scx: u8,
108 pub(crate) ly: u8,
110 pub(crate) lyc: u8,
112 pub(crate) wy: u8,
114 pub(crate) wx: u8,
116 pub(crate) iflag: u8,
118 pub(crate) ie: u8,
120}
121
122impl MemoryRegisters {
123 fn new() -> MemoryRegisters {
124 MemoryRegisters {
125 dma: 0,
126 lcdc: 0x91,
127 lcdstat: 0,
128 scy: 0,
129 scx: 0,
130 ly: 0,
131 lyc: 0,
132 wy: 0,
133 wx: 0,
134 iflag: 0,
135 ie: 0,
136 }
137 }
138
139 fn read(&self, addr: u16) -> Option<u8> {
140 match addr {
141 DMA_REGISTER_ADDR => Some(self.dma),
142 LCD_CONTROL_ADDR => Some(self.lcdc),
143 LCD_STATUS_ADDR => Some(self.lcdstat),
144 SCROLL_Y_ADDR => Some(self.scy),
145 SCROLL_X_ADDR => Some(self.scx),
146 CURRENT_LINE_ADDR => Some(self.ly),
147 LINE_CHECK_ADDR => Some(self.lyc),
148 WINDOW_Y_ADDR => Some(self.wy),
149 WINDOW_X_ADDR => Some(self.wx),
150 INTERRUPT_FLAG_ADDR => Some(self.iflag),
151 INTERRUPT_ENABLE_ADDR => Some(self.ie),
152 _ => None,
153 }
154 }
155
156 fn write(&mut self, addr: u16, value: u8) {
157 match addr {
158 DMA_REGISTER_ADDR => self.dma = value,
159 LCD_CONTROL_ADDR => self.lcdc = value,
160 LCD_STATUS_ADDR => masked_write(&mut self.lcdstat, value, 0b0111_1100),
163 SCROLL_Y_ADDR => self.scy = value,
164 SCROLL_X_ADDR => self.scx = value,
165 CURRENT_LINE_ADDR => (), LINE_CHECK_ADDR => self.lyc = value,
167 WINDOW_Y_ADDR => self.wy = value,
168 WINDOW_X_ADDR => self.wx = value,
169 INTERRUPT_FLAG_ADDR => masked_write(&mut self.iflag, value, 0x1F),
170 INTERRUPT_ENABLE_ADDR => masked_write(&mut self.ie, value, 0x1F),
171 _ => (),
172 }
173 }
174}
175
176fn is_mem_register(addr: u16) -> bool {
177 MEM_REGISTERS.contains(addr) || addr == 0xffff
178}
179
180pub struct MemoryData {
181 cpuram: [u8; 127],
182 oamram: [u8; 160],
183 sysram: [u8; 0x2000],
184 vram: [u8; 0x2000],
185 cartridge: Cartridge,
186 pub(crate) registers: MemoryRegisters,
187}
188
189pub struct Memory {
190 data: MemoryData,
191 pub events: events::EventEmitter<events::MemoryWriteEvent>,
192}
193
194impl Memory {
195 pub fn new(cartridge: Cartridge) -> Memory {
196 Memory {
197 data: MemoryData {
198 cpuram: [0u8; 127],
199 oamram: [0u8; 160],
200 sysram: [0u8; 0x2000],
201 vram: [0u8; 0x2000],
202 cartridge,
203 registers: MemoryRegisters::new(),
204 },
205 events: events::EventEmitter::new(),
206 }
207 }
208
209 pub fn registers(&self) -> &MemoryRegisters {
210 &self.data.registers
211 }
212
213 pub fn registers_mut(&mut self) -> &mut MemoryRegisters {
214 &mut self.data.registers
215 }
216
217 pub fn read_u8<A: Into<address::LiteralAddress>>(&self, target: A) -> MemoryResult<u8> {
218 let address::LiteralAddress(addr) = target.into();
219 if CARTRIDGE_ROM.contains(addr) {
220 self.data
221 .cartridge
222 .read(addr)
223 .map_err(|_| MemoryError::InvalidRomAddress(addr))
224 } else if VRAM.contains(addr) {
225 Ok(self.data.vram[(addr - VRAM.start) as usize])
226 } else if CARTRIDGE_RAM.contains(addr) {
227 self.data
228 .cartridge
229 .read(addr)
230 .map_err(|_| MemoryError::InvalidRamAddress(addr))
231 } else if SYS_RAM.contains(addr) {
232 Ok(self.data.sysram[(addr - SYS_RAM.start) as usize])
233 } else if SYS_RAM_MIRROR.contains(addr) {
234 Ok(self.data.sysram[(addr - SYS_RAM_MIRROR.start) as usize])
235 } else if OAM_RAM.contains(addr) {
236 Ok(self.data.oamram[(addr - OAM_RAM.start) as usize])
237 } else if CPU_RAM.contains(addr) {
238 Ok(self.data.cpuram[(addr - CPU_RAM.start) as usize])
239 } else if is_mem_register(addr) {
240 self.data
241 .registers
242 .read(addr)
243 .ok_or_else(|| MemoryError::UnmappedAddress(addr))
244 } else {
245 Err(MemoryError::UnmappedAddress(addr))
246 }
247 }
248
249 pub fn write_u8<A: Into<address::LiteralAddress>>(
250 &mut self,
251 target: A,
252 value: u8,
253 ) -> MemoryResult<()> {
254 let address = target.into();
255 let addr = address.0;
256 let write_result = if CARTRIDGE_ROM.contains(addr) {
257 self.data
258 .cartridge
259 .write(addr, value)
260 .map_err(|_| MemoryError::InvalidRomAddress(addr))
261 } else if VRAM.contains(addr) {
262 self.data.vram[(addr - VRAM.start) as usize] = value;
263 Ok(())
264 } else if CARTRIDGE_RAM.contains(addr) {
265 self.data
266 .cartridge
267 .write(addr, value)
268 .map_err(|_| MemoryError::InvalidRamAddress(addr))
269 } else if SYS_RAM.contains(addr) {
270 self.data.sysram[(addr - SYS_RAM.start) as usize] = value;
271 Ok(())
272 } else if SYS_RAM_MIRROR.contains(addr) {
273 self.data.sysram[(addr - SYS_RAM_MIRROR.start) as usize] = value;
274 Ok(())
275 } else if OAM_RAM.contains(addr) {
276 self.data.oamram[(addr - OAM_RAM.start) as usize] = value;
277 Ok(())
278 } else if is_mem_register(addr) {
279 self.data.registers.write(addr, value);
280 Ok(())
281 } else if CPU_RAM.contains(addr) {
282 self.data.cpuram[(addr - CPU_RAM.start) as usize] = value;
283 Ok(())
284 } else {
285 Err(MemoryError::UnmappedAddress(addr))
286 };
287
288 if write_result.is_ok() {
289 let new_value = self.read_u8(address).unwrap_or(0xFF);
292 self.events
293 .emit(events::MemoryWriteEvent::new(address, value, new_value));
294 }
295
296 write_result
297 }
298
299 pub(crate) fn offset_iter(&self, start: address::LiteralAddress) -> MemoryIterator {
300 MemoryIterator {
301 addr: start,
302 mem: &self,
303 }
304 }
305}
306
307#[cfg(test)]
308mod tests {
309 use super::*;
310 use alloc::boxed::Box;
311 use alloc::rc::Rc;
312 use alloc::vec::Vec;
313
314 #[test]
315 fn test_write_vram() {
316 let cartridge = Cartridge::from_data(vec![0u8; 0x8000]).unwrap();
317 let mut memory = Memory::new(cartridge);
318
319 memory.write_u8(VRAM.start, 0xff).unwrap();
320 assert_eq!(memory.data.vram[0], 0xff);
321 }
322
323 #[test]
324 fn test_write_sysram() {
325 let cartridge = Cartridge::from_data(vec![0u8; 0x8000]).unwrap();
326 let mut memory = Memory::new(cartridge);
327
328 memory.write_u8(SYS_RAM.start, 0xff).unwrap();
329 assert_eq!(memory.data.sysram[0], 0xff);
330 }
331
332 #[test]
333 fn test_write_sysram_mirror() {
334 let cartridge = Cartridge::from_data(vec![0u8; 0x8000]).unwrap();
335 let mut memory = Memory::new(cartridge);
336
337 memory.write_u8(SYS_RAM_MIRROR.start, 0xff).unwrap();
338 assert_eq!(memory.data.sysram[0], 0xff);
339 }
340
341 #[test]
342 fn test_write_oamram() {
343 let cartridge = Cartridge::from_data(vec![0u8; 0x8000]).unwrap();
344 let mut memory = Memory::new(cartridge);
345
346 memory.write_u8(OAM_RAM.start, 0xff).unwrap();
347 assert_eq!(memory.data.oamram[0], 0xff);
348 }
349
350 #[test]
351 fn test_write_cpuram() {
352 let cartridge = Cartridge::from_data(vec![0u8; 0x8000]).unwrap();
353 let mut memory = Memory::new(cartridge);
354
355 memory.write_u8(CPU_RAM.start, 0xff).unwrap();
356 assert_eq!(memory.data.cpuram[0], 0xff);
357 }
358
359 #[test]
360 fn test_read_vram() {
361 let cartridge = Cartridge::from_data(vec![0u8; 0x8000]).unwrap();
362 let mut memory = Memory::new(cartridge);
363 memory.data.vram[0] = 0xff;
364
365 assert_eq!(memory.read_u8(VRAM.start).unwrap(), 0xff);
366 }
367
368 #[test]
369 fn test_read_sysram() {
370 let cartridge = Cartridge::from_data(vec![0u8; 0x8000]).unwrap();
371 let mut memory = Memory::new(cartridge);
372 memory.data.sysram[0] = 0xff;
373
374 assert_eq!(memory.read_u8(SYS_RAM.start).unwrap(), 0xff);
375 }
376
377 #[test]
378 fn test_read_sysram_mirror() {
379 let cartridge = Cartridge::from_data(vec![0u8; 0x8000]).unwrap();
380 let mut memory = Memory::new(cartridge);
381 memory.data.sysram[0] = 0xff;
382
383 assert_eq!(memory.read_u8(SYS_RAM_MIRROR.start).unwrap(), 0xff);
384 }
385
386 #[test]
387 fn test_read_oamram() {
388 let cartridge = Cartridge::from_data(vec![0u8; 0x8000]).unwrap();
389 let mut memory = Memory::new(cartridge);
390 memory.data.oamram[0] = 0xff;
391
392 assert_eq!(memory.read_u8(OAM_RAM.start).unwrap(), 0xff);
393 }
394
395 #[test]
396 fn test_read_cpuram() {
397 let cartridge = Cartridge::from_data(vec![0u8; 0x8000]).unwrap();
398 let mut memory = Memory::new(cartridge);
399 memory.data.cpuram[0] = 0xff;
400
401 assert_eq!(memory.read_u8(CPU_RAM.start).unwrap(), 0xff);
402 }
403
404 #[test]
405 fn test_dma() {
406 let cartridge = Cartridge::from_data(vec![0u8; 0x8000]).unwrap();
407 let mut memory = Memory::new(cartridge);
408
409 memory.write_u8(DMA_REGISTER_ADDR, 0x12).unwrap();
410
411 assert_eq!(memory.data.registers.dma, 0x12);
412
413 memory.data.registers.dma = 0x34;
414 assert_eq!(memory.read_u8(DMA_REGISTER_ADDR).unwrap(), 0x34);
415 }
416
417 #[test]
418 fn test_interrupt_registers() {
419 let cartridge = Cartridge::from_data(vec![0u8; 0x8000]).unwrap();
420 let mut memory = Memory::new(cartridge);
421
422 memory.write_u8(INTERRUPT_FLAG_ADDR, 0xFF).unwrap();
423 memory.write_u8(INTERRUPT_ENABLE_ADDR, 0xFE).unwrap();
424
425 assert_eq!(memory.data.registers.iflag, 0x1F);
426 assert_eq!(memory.data.registers.ie, 0x1E);
427
428 memory.data.registers.iflag = 0x04;
429 memory.data.registers.ie = 0x12;
430
431 assert_eq!(memory.read_u8(INTERRUPT_FLAG_ADDR).unwrap(), 0x04);
432 assert_eq!(memory.read_u8(INTERRUPT_ENABLE_ADDR).unwrap(), 0x12);
433 }
434
435 #[test]
436 fn test_lcd_registers() {
437 let cartridge = Cartridge::from_data(vec![0u8; 0x8000]).unwrap();
438 let mut memory = Memory::new(cartridge);
439
440 memory.data.registers.lcdc = 0;
441 memory.data.registers.lcdstat = 3;
442
443 memory.write_u8(LCD_STATUS_ADDR, 0xFC).unwrap();
444 memory.write_u8(LCD_CONTROL_ADDR, 0xFF).unwrap();
445 memory.write_u8(SCROLL_Y_ADDR, 0xAA).unwrap();
446 memory.write_u8(SCROLL_X_ADDR, 0x33).unwrap();
447 memory.write_u8(WINDOW_Y_ADDR, 0x3A).unwrap();
448 memory.write_u8(WINDOW_X_ADDR, 0xA3).unwrap();
449
450 assert_eq!(memory.data.registers.lcdc, 0xFF);
451 assert_eq!(memory.data.registers.lcdstat, 0x7F);
452
453 assert_eq!(memory.read_u8(LCD_STATUS_ADDR).unwrap(), 0x7F);
454 assert_eq!(memory.read_u8(LCD_CONTROL_ADDR).unwrap(), 0xFF);
455 assert_eq!(memory.read_u8(SCROLL_Y_ADDR).unwrap(), 0xAA);
456 assert_eq!(memory.read_u8(SCROLL_X_ADDR).unwrap(), 0x33);
457 assert_eq!(memory.read_u8(WINDOW_Y_ADDR).unwrap(), 0x3A);
458 assert_eq!(memory.read_u8(WINDOW_X_ADDR).unwrap(), 0xA3);
459 }
460
461 #[test]
462 fn test_unmapped_address() {
463 let cartridge = Cartridge::from_data(vec![0u8; 0x8000]).unwrap();
464 let mut memory = Memory::new(cartridge);
465
466 let addr = 0xFEC0;
467
468 assert_eq!(
469 memory.read_u8(addr),
470 Err(MemoryError::UnmappedAddress(addr))
471 );
472 assert_eq!(
473 memory.write_u8(addr, 0xFE),
474 Err(MemoryError::UnmappedAddress(addr))
475 );
476 }
477
478 #[test]
479 fn test_write_event() {
480 use core::cell::RefCell;
481 let event_log: Rc<RefCell<Vec<events::MemoryWriteEvent>>> =
482 Rc::new(RefCell::new(Vec::new()));
483 let handler_log = Rc::clone(&event_log);
484
485 let handler = move |evt: &events::MemoryWriteEvent| {
486 handler_log.borrow_mut().push(evt.clone());
487 };
488
489 let cartridge = Cartridge::from_data(vec![0u8; 0x8000]).unwrap();
490 let mut memory = Memory::new(cartridge);
491 memory.events.on(Box::new(handler));
492
493 memory.write_u8(0x9000, 0x26).unwrap();
494
495 let actual_events = event_log.borrow();
496
497 assert_eq!(
498 *actual_events,
499 vec![events::MemoryWriteEvent::new(0x9000.into(), 0x26, 0x26,).into()]
500 );
501 }
502
503 #[test]
504 fn test_write_unwriteable() {
505 use core::cell::RefCell;
506 let event_log: Rc<RefCell<Vec<events::MemoryWriteEvent>>> =
507 Rc::new(RefCell::new(Vec::new()));
508 let handler_log = Rc::clone(&event_log);
509
510 let handler = move |evt: &events::MemoryWriteEvent| {
511 handler_log.borrow_mut().push(evt.clone());
512 };
513
514 let cartridge = Cartridge::from_data(vec![0u8; 0x8000]).unwrap();
515 let mut memory = Memory::new(cartridge);
516 memory.events.on(Box::new(handler));
517
518 memory.write_u8(0x1000, 0x26).unwrap();
519
520 let actual_events = event_log.borrow();
521
522 assert_eq!(
523 *actual_events,
524 vec![events::MemoryWriteEvent::new(0x1000.into(), 0x26, 0x00,).into()]
525 );
526 }
527}