Skip to main content

olympia_engine/gameboy/
memory.rs

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)]
53/// Represents a failure to read from memory.
54pub enum MemoryError {
55    /// The address maps to the Cartridge ROM area,
56    /// but the currently loaded cartridge does not have
57    /// ROM at this address. This can happen for MBC1/SROM cartridges
58    /// that have less than 8KB of storage
59    InvalidRomAddress(u16),
60    /// The address maps to the Cartridge RAM area,
61    /// but the currently loaded cartridge does not have
62    /// RAM at this address. This can happen for cartridges
63    /// that have < 2KB of RAM, including no RAM
64    InvalidRamAddress(u16),
65    /// The address maps to an area that is unmapped for the
66    /// current gameboy model. This can include areas that are unmapped in
67    /// all models, or registers that only exist on Game Boy Color
68    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    /// Write upper byte of start addresses here to trigger DMA transfers
94    /// to OAM RAM
95    pub(crate) dma: u8,
96    /// Bit 7 = LCD on/off, Bit 6 = Window code area, bit 5 = window on/off
97    /// bit 4 = BG tile area (1 = fully overlapping, 0 = 50% overlap)
98    /// bit 3 = BG code area, bit 2 = sprite size (1 = 8x16, 0 = 8x8)
99    /// bit 1  = object layer enable, bit 0 = bg layer enable
100    pub(crate) lcdc: u8,
101    /// Bits 3-6 control interrupts, bit 2 inverts line checks, bit 0-1
102    /// exposes current PPU mode
103    pub(crate) lcdstat: u8,
104    /// Scroll the screen vertically this many pixels
105    pub(crate) scy: u8,
106    /// Scroll the screen horizontally this many pixels
107    pub(crate) scx: u8,
108    /// Current line being drawn by the PPU
109    pub(crate) ly: u8,
110    /// Line to check LY against for interrupts on specific line
111    pub(crate) lyc: u8,
112    /// Y Pixel offset (in screen co-ordinates, not tile map) to start window
113    pub(crate) wy: u8,
114    /// X Pixel offset (in screen co-ordinates, not tile map) to start window
115    pub(crate) wx: u8,
116    /// Interrupts where their conditions have been triggered
117    pub(crate) iflag: u8,
118    /// Interrupts that are enabled and can cause CPU interrupts
119    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            // Top bit doesn't exist
161            // Lower two bits are mode flag
162            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 => (), // Read only
166            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            // need to read the actual new value in case of partial registers
290            // unmapped memory, or writes to ROM address space
291            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}