diff --git a/src/mmu.rs b/src/mmu.rs index d1b980a53879817718ebefe05f0a52f4e009346d..cf8f41734490e82c21b007132474299bf305db25 100644 --- a/src/mmu.rs +++ b/src/mmu.rs @@ -4,7 +4,6 @@ use std::sync::Mutex; use crate::{ apu::Apu, - debugln, dma::Dma, gb::{Components, GameBoyConfig, GameBoyMode, GameBoySpeed}, pad::Pad, @@ -13,6 +12,7 @@ use crate::{ serial::Serial, timer::Timer, util::SharedThread, + warnln, }; pub const BOOT_SIZE_DMG: usize = 256; @@ -319,9 +319,9 @@ impl Mmu { } pub fn read(&mut self, addr: u16) -> u8 { - match addr & 0xf000 { - // BOOT (256 B) + ROM0 (4 KB/16 KB) - 0x0000 => { + match addr { + // 0x0000-0x0FFF - BOOT (256 B) + ROM0 (4 KB/16 KB) + 0x0000..=0x0fff => { // in case the boot mode is active and the // address is withing boot memory reads from it if self.boot_active && addr <= 0x00fe { @@ -342,209 +342,194 @@ impl Mmu { self.rom.read(addr) } - // ROM 0 (12 KB/16 KB) - 0x1000 | 0x2000 | 0x3000 => self.rom.read(addr), - - // ROM 1 (Banked) (16 KB) - 0x4000 | 0x5000 | 0x6000 | 0x7000 => self.rom.read(addr), - - // Graphics: VRAM (8 KB) - 0x8000 | 0x9000 => self.ppu.read(addr), - - // External RAM (8 KB) - 0xa000 | 0xb000 => self.rom.read(addr), - - // Working RAM 0 (4 KB) - 0xc000 => self.ram[(addr & 0x0fff) as usize], - - // Working RAM 1 (Banked) (4KB) - 0xd000 => self.ram[(self.ram_offset + (addr & 0x0fff)) as usize], - - // Working RAM Shadow - 0xe000 => self.ram[(addr & 0x1fff) as usize], - - // Working RAM Shadow, I/O, Zero-page RAM - 0xf000 => match addr & 0x0f00 { - 0x000 | 0x100 | 0x200 | 0x300 | 0x400 | 0x500 | 0x600 | 0x700 | 0x800 | 0x900 - | 0xa00 | 0xb00 | 0xc00 | 0xd00 => self.ram[(addr & 0x1fff) as usize], - 0xe00 => self.ppu.read(addr), - 0xf00 => match addr & 0x00ff { - // 0xFF01-0xFF02 - Serial data transfer - 0x01..=0x02 => self.serial.read(addr), - - // 0xFF0F — IF: Interrupt flag - 0x0f => - { - #[allow(clippy::bool_to_int_with_if)] - (if self.ppu.int_vblank() { 0x01 } else { 0x00 } - | if self.ppu.int_stat() { 0x02 } else { 0x00 } - | if self.timer.int_tima() { 0x04 } else { 0x00 } - | if self.serial.int_serial() { 0x08 } else { 0x00 } - | if self.pad.int_pad() { 0x10 } else { 0x00 } - | 0xe0) - } + // 0x1000-0x3FFF - ROM 0 (12 KB/16 KB) + // 0x4000-0x7FFF - ROM 1 (Banked) (16 KB) + 0x1000..=0x7fff => self.rom.read(addr), + + // 0x8000-0x9FFF - Graphics: VRAM (8 KB) + 0x8000..=0x9fff => self.ppu.read(addr), + + // 0xA000-0xBFFF - External RAM (8 KB) + 0xa000..=0xbfff => self.rom.read(addr), + + // 0xC000-0xCFFF - Working RAM 0 (4 KB) + 0xc000..=0xcfff => self.ram[(addr & 0x0fff) as usize], + + // 0xD000..=0xDFFF - Working RAM 1 (Banked) (4KB) + 0xd000..=0xdfff => self.ram[(self.ram_offset + (addr & 0x0fff)) as usize], + + // 0xE000..=0xFDFF - Working RAM Shadow + 0xe000..=0xfdff => self.ram[(addr & 0x1fff) as usize], + + // 0xFE00-0xFE9F - Object attribute memory (OAM) + 0xfe00..=0xfe9f => self.ppu.read(addr), + + // 0xFEA0-0xFEFF - Not Usable + 0xfea0..=0xfeff => 0xff, + + // 0xFF00 - Joypad input + 0xff00 => self.pad.read(addr), + + // 0xFF01-0xFF02 - Serial data transfer + 0xff01..=0xff02 => self.serial.read(addr), + + // 0xFF04-0xFF07 - Timer and divider + 0xff04..=0xff07 => self.timer.read(addr), + + // 0xFF0F — IF: Interrupt flag + 0xff0f => + { + #[allow(clippy::bool_to_int_with_if)] + (if self.ppu.int_vblank() { 0x01 } else { 0x00 } + | if self.ppu.int_stat() { 0x02 } else { 0x00 } + | if self.timer.int_tima() { 0x04 } else { 0x00 } + | if self.serial.int_serial() { 0x08 } else { 0x00 } + | if self.pad.int_pad() { 0x10 } else { 0x00 } + | 0xe0) + } + + // 0xFF10-0xFF26 — Audio + // 0xFF10-0xFF26 — Wave pattern + 0xff10..=0xff26 | 0xff30..=0xff3f => self.apu.read(addr), + + // 0xFF40-0xFF45 - PPU registers + // 0xFF47-0xFF4B - PPU registers + 0xff40..=0xff45 | 0xff47..=0xff4b => self.ppu.read(addr), + + // 0xFF46 — DMA: OAM DMA source address & start + 0xff46 => self.dma.read(addr), + + // 0xFF4C - KEY0: Compatibility flag (CGB only) + 0xff4c => self.key0, + + // 0xFF4D - KEY1: Speed switching (CGB only) + 0xff4d => (if self.switching { 0x01 } else { 0x00 }) | ((self.speed as u8) << 7) | 0x7e, + + // 0xFF4F - VRAM Bank Select (CGB only) + 0xff4f => self.ppu.read(addr), + + // 0xFF50 - Boot active flag + 0xff50 => u8::from(!self.boot_active), + + // 0xFF51-0xFF55 - VRAM DMA (HDMA) (CGB only) + 0xff51..=0xff55 => self.dma.read(addr), + + // 0xFF68-0xFF6B - BG / OBJ Palettes (CGB only) + 0xff68..=0xff6b => self.ppu.read(addr), + + // 0xFF70 - SVBK: WRAM bank (CGB only) + 0xff70 => (self.ram_bank & 0x07) | 0xf8, - // 0xFF4C - KEY0: Compatibility flag (CGB only) - 0x4c => self.key0, - - // 0xFF4D - KEY1: Speed switching (CGB only) - 0x4d => (false as u8) | ((self.speed as u8) << 7), - - // 0xFF50 - Boot active flag - 0x50 => u8::from(!self.boot_active), - - // 0xFF70 - SVBK: WRAM bank (CGB only) - 0x70 => self.ram_bank & 0x07, - - // 0xFF80-0xFFFE - High RAM (HRAM) - 0x80..=0xfe => self.ppu.read(addr), - - // 0xFFFF — IE: Interrupt enable - 0xff => self.ie, - - // Other registers - _ => match addr & 0x00f0 { - 0x00 => match addr & 0x00ff { - 0x00 => self.pad.read(addr), - 0x04..=0x07 => self.timer.read(addr), - _ => { - debugln!("Reading from unknown IO control 0x{:04x}", addr); - 0x00 - } - }, - 0x10 | 0x20 | 0x30 => self.apu.read(addr), - 0x40 | 0x60 | 0x70 => match addr & 0x00ff { - // 0xFF46 — DMA: OAM DMA source address & start - 0x0046 => self.dma.read(addr), - - // VRAM related read - _ => self.ppu.read(addr), - }, - 0x50 => match addr & 0x00ff { - 0x51..=0x55 => self.dma.read(addr), - _ => { - debugln!("Reading from unknown IO control 0x{:04x}", addr); - 0x00 - } - }, - _ => { - debugln!("Reading from unknown IO control 0x{:04x}", addr); - 0x00 - } - }, - }, - addr => panic!("Reading from unknown location 0x{:04x}", addr), - }, - - addr => panic!("Reading from unknown location 0x{:04x}", addr), + // 0xFF80-0xFFFE - High RAM (HRAM) + 0xff80..=0xfffe => self.ppu.read(addr), + + // 0xFFFF — IE: Interrupt enable + 0xffff => self.ie, + + addr => { + warnln!("Reading from unknown location 0x{:04x}", addr); + 0xff + } } } pub fn write(&mut self, addr: u16, value: u8) { - match addr & 0xf000 { - // BOOT (256 B) + ROM0 (4 KB/16 KB) - 0x0000 => self.rom.write(addr, value), + match addr { + // 0x0000-0x0FFF - BOOT (256 B) + ROM0 (4 KB/16 KB) + // 0x1000-0x3FFF - ROM 0 (12 KB/16 KB) + // 0x4000-0x7FFF - ROM 1 (Banked) (16 KB) + 0x0000..=0x7fff => self.rom.write(addr, value), + + // 0x8000-0x9FFF - Graphics: VRAM (8 KB) + 0x8000..=0x9fff => self.ppu.write(addr, value), + + // 0xA000-0xBFFF - External RAM (8 KB) + 0xa000..=0xbfff => self.rom.write(addr, value), - // ROM 0 (12 KB/16 KB) - 0x1000 | 0x2000 | 0x3000 => self.rom.write(addr, value), + // 0xC000-0xCFFF - Working RAM 0 (4 KB) + 0xc000..=0xcfff => self.ram[(addr & 0x0fff) as usize] = value, - // ROM 1 (Banked) (16 KB) - 0x4000 | 0x5000 | 0x6000 | 0x7000 => self.rom.write(addr, value), + // 0xD000..=0xDFFF - Working RAM 1 (Banked) (4KB) + 0xd000..=0xdfff => self.ram[(self.ram_offset + (addr & 0x0fff)) as usize] = value, - // Graphics: VRAM (8 KB) - 0x8000 | 0x9000 => self.ppu.write(addr, value), + // 0xE000..=0xFDFF - Working RAM Shadow + 0xe000..=0xfdff => self.ram[(addr & 0x1fff) as usize] = value, - // External RAM (8 KB) - 0xa000 | 0xb000 => self.rom.write(addr, value), + // 0xFE00-0xFE9F - Object attribute memory (OAM) + 0xfe00..=0xfe9f => self.ppu.write(addr, value), + + // 0xFEA0-0xFEFF - Not Usable + 0xfea0..=0xfeff => {} + + // 0xFF00 - Joypad input + 0xff00 => self.pad.write(addr, value), + + // 0xFF01-0xFF02 - Serial data transfer + 0xff01..=0xff02 => self.serial.write(addr, value), + + // 0xFF04-0xFF07 - Timer and divider + 0xff04..=0xff07 => self.timer.write(addr, value), + + // 0xFF0F — IF: Interrupt flag + 0xff0f => { + self.ppu.set_int_vblank(value & 0x01 == 0x01); + self.ppu.set_int_stat(value & 0x02 == 0x02); + self.timer.set_int_tima(value & 0x04 == 0x04); + self.serial.set_int_serial(value & 0x08 == 0x08); + self.pad.set_int_pad(value & 0x10 == 0x10); + } - // Working RAM 0 (4 KB) - 0xc000 => self.ram[(addr & 0x0fff) as usize] = value, + // 0xFF10-0xFF26 — Audio + // 0xFF10-0xFF26 — Wave pattern + 0xff10..=0xff26 | 0xff30..=0xff3f => self.apu.write(addr, value), - // Working RAM 1 (Banked) (4KB) - 0xd000 => self.ram[(self.ram_offset + (addr & 0x0fff)) as usize] = value, + // 0xFF40-0xFF45 - PPU registers + // 0xFF47-0xFF4B - PPU registers + 0xff40..=0xff45 | 0xff47..=0xff4b => self.ppu.write(addr, value), - // Working RAM Shadow - 0xe000 => self.ram[(addr & 0x1fff) as usize] = value, + // 0xFF46 — DMA: OAM DMA source address & start + 0xff46 => self.dma.write(addr, value), - // Working RAM Shadow, I/O, Zero-page RAM - 0xf000 => match addr & 0x0f00 { - 0x000 | 0x100 | 0x200 | 0x300 | 0x400 | 0x500 | 0x600 | 0x700 | 0x800 | 0x900 - | 0xa00 | 0xb00 | 0xc00 | 0xd00 => { - self.ram[(addr & 0x1fff) as usize] = value; + // 0xFF4C - KEY0: Compatibility flag (CGB only) + 0xff4c => { + self.key0 = value; + if value == 0x04 { + self.ppu().set_dmg_compat(true); } - 0xe00 => self.ppu.write(addr, value), - 0xf00 => match addr & 0x00ff { - // 0xFF01-0xFF02 - Serial data transfer - 0x01..=0x02 => self.serial.write(addr, value), - - // 0xFF0F — IF: Interrupt flag - 0x0f => { - self.ppu.set_int_vblank(value & 0x01 == 0x01); - self.ppu.set_int_stat(value & 0x02 == 0x02); - self.timer.set_int_tima(value & 0x04 == 0x04); - self.serial.set_int_serial(value & 0x08 == 0x08); - self.pad.set_int_pad(value & 0x10 == 0x10); - } + } - // 0xFF4C - KEY0: Compatibility flag (CGB only) - 0x4c => { - self.key0 = value; - if value == 0x04 { - self.ppu().set_dmg_compat(true); - } - } + // 0xFF4D - KEY1: Speed switching (CGB only) + 0xff4d => self.switching = value & 0x01 == 0x01, - // 0xFF4D - KEY1: Speed switching (CGB only) - 0x4d => { - self.switching = value & 0x01 == 0x01; - } + // 0xFF4F - VRAM Bank Select (CGB only) + 0xff4f => self.ppu.write(addr, value), - // 0xFF50 - Boot active flag - 0x50 => self.boot_active = value == 0x00, - - // 0xFF70 - SVBK: WRAM bank (CGB only) - 0x70 => { - let mut ram_bank = value & 0x07; - if ram_bank == 0x0 { - ram_bank = 0x1; - } - self.ram_bank = ram_bank; - self.ram_offset = self.ram_bank as u16 * 0x1000; - } + // 0xFF50 - Boot active flag + 0xff50 => self.boot_active = value == 0x00, + + // 0xFF51-0xFF55 - VRAM DMA (HDMA) (CGB only) + 0xff51..=0xff55 => self.dma.write(addr, value), + + // 0xFF68-0xFF6B - BG / OBJ Palettes (CGB only) + 0xff68..=0xff6b => self.ppu.write(addr, value), + + // 0xFF70 - SVBK: WRAM bank (CGB only) + 0xff70 => { + let mut ram_bank = value & 0x07; + if ram_bank == 0x0 { + ram_bank = 0x1; + } + self.ram_bank = ram_bank; + self.ram_offset = self.ram_bank as u16 * 0x1000; + } + + // 0xFF80-0xFFFE - High RAM (HRAM) + 0xff80..=0xfffe => self.ppu.write(addr, value), + + // 0xFFFF — IE: Interrupt enable + 0xffff => self.ie = value, - // 0xFF80-0xFFFE - High RAM (HRAM) - 0x80..=0xfe => self.ppu.write(addr, value), - - // 0xFFFF — IE: Interrupt enable - 0xff => self.ie = value, - - // Other registers - _ => match addr & 0x00f0 { - 0x00 => match addr & 0x00ff { - 0x00 => self.pad.write(addr, value), - 0x04..=0x07 => self.timer.write(addr, value), - _ => debugln!("Writing to unknown IO control 0x{:04x}", addr), - }, - 0x10 | 0x20 | 0x30 => self.apu.write(addr, value), - 0x40 | 0x60 | 0x70 => match addr & 0x00ff { - // 0xFF46 — DMA: OAM DMA source address & start - 0x0046 => self.dma.write(addr, value), - - // VRAM related write - _ => self.ppu.write(addr, value), - }, - #[allow(clippy::single_match)] - 0x50 => match addr & 0x00ff { - 0x51..=0x55 => self.dma.write(addr, value), - _ => debugln!("Writing to unknown IO control 0x{:04x}", addr), - }, - _ => debugln!("Writing to unknown IO control 0x{:04x}", addr), - }, - }, - addr => panic!("Writing to unknown location 0x{:04x}", addr), - }, - - addr => panic!("Writing to unknown location 0x{:04x}", addr), + addr => warnln!("Writing to unknown location 0x{:04x}", addr), } } diff --git a/src/ppu.rs b/src/ppu.rs index af0fd2dd45cbb02635237cb97f2209037db94fd0..ac1a73be110eab8c3ee0da0de5c95d4f101c4c1f 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -834,15 +834,7 @@ impl Ppu { // 0xFF6B — OCPD/OBPD (CGB only) 0xff6b => self.palettes_color[1][self.palette_address_obj as usize], // 0xFF6C — OPRI (CGB only) - 0xff6c => - { - #[allow(clippy::bool_to_int_with_if)] - if self.obj_priority { - 0x01 - } else { - 0x00 - } - } + 0xff6c => (if self.obj_priority { 0x01 } else { 0x00 }) | 0xfe, _ => { warnln!("Reading from unknown PPU location 0x{:04x}", addr); 0xff diff --git a/src/rom.rs b/src/rom.rs index ca7f65feb692f15545fd6b7f6e00d118e4cae721..572c86ac1ee59f3973ab4b82e6a6ca81312e5d92 100644 --- a/src/rom.rs +++ b/src/rom.rs @@ -425,13 +425,11 @@ impl Cartridge { } pub fn read(&mut self, addr: u16) -> u8 { - match addr & 0xf000 { + match addr { // 0x0000-0x7FFF: 16 KiB ROM bank 00 & 16 KiB ROM Bank 01–NN - 0x0000 | 0x1000 | 0x2000 | 0x3000 | 0x4000 | 0x5000 | 0x6000 | 0x7000 => { - (self.handler.read_rom)(self, addr) - } + 0x0000..=0x7fff => (self.handler.read_rom)(self, addr), // 0xA000-0xBFFF: 8 KiB External RAM - 0xa000 | 0xb000 => (self.handler.read_ram)(self, addr), + 0xa000..=0xbfff => (self.handler.read_ram)(self, addr), _ => { debugln!("Reading from unknown Cartridge control 0x{:04x}", addr); 0x00 @@ -440,13 +438,11 @@ impl Cartridge { } pub fn write(&mut self, addr: u16, value: u8) { - match addr & 0xf000 { + match addr { // 0x0000-0x7FFF: 16 KiB ROM bank 00 & 16 KiB ROM Bank 01–NN - 0x0000 | 0x1000 | 0x2000 | 0x3000 | 0x4000 | 0x5000 | 0x6000 | 0x7000 => { - (self.handler.write_rom)(self, addr, value) - } + 0x0000..=0x7fff => (self.handler.write_rom)(self, addr, value), // 0xA000-0xBFFF: 8 KiB External RAM - 0xa000 | 0xb000 => (self.handler.write_ram)(self, addr, value), + 0xa000..=0xbfff => (self.handler.write_ram)(self, addr, value), _ => debugln!("Writing to unknown Cartridge address 0x{:04x}", addr), } } @@ -984,9 +980,9 @@ pub static NO_MBC: Mbc = Mbc { pub static MBC1: Mbc = Mbc { name: "MBC1", read_rom: |rom: &Cartridge, addr: u16| -> u8 { - match addr & 0xf000 { - 0x0000 | 0x1000 | 0x2000 | 0x3000 => rom.rom_data[addr as usize], - 0x4000 | 0x5000 | 0x6000 | 0x7000 => *rom + match addr { + 0x0000..=0x3fff => rom.rom_data[addr as usize], + 0x4000..=0x7fff => *rom .rom_data .get(rom.rom_offset + (addr - 0x4000) as usize) .unwrap_or(&0x0), @@ -997,13 +993,13 @@ pub static MBC1: Mbc = Mbc { } }, write_rom: |rom: &mut Cartridge, addr: u16, value: u8| { - match addr & 0xf000 { + match addr { // RAM enabled flag - 0x0000 | 0x1000 => { + 0x0000..=0x1fff => { rom.ram_enabled = (value & 0x0f) == 0x0a; } // ROM bank selection 5 lower bits - 0x2000 | 0x3000 => { + 0x2000..=0x3fff => { let mut rom_bank = value as u16 & 0x1f; rom_bank &= rom.rom_bank_count * 2 - 1; if rom_bank == 0 { @@ -1012,7 +1008,7 @@ pub static MBC1: Mbc = Mbc { rom.set_rom_bank(rom_bank); } // RAM bank selection and ROM bank selection upper bits - 0x4000 | 0x5000 => { + 0x4000..=0x5fff => { let ram_bank = value & 0x03; if ram_bank as u16 >= rom.ram_bank_count { return; @@ -1020,7 +1016,7 @@ pub static MBC1: Mbc = Mbc { rom.set_ram_bank(ram_bank); } // ROM mode selection - 0x6000 | 0x7000 => { + 0x6000..=0x7fff => { if value == 0x1 && rom.rom_bank_count > 32 { unimplemented!("Advanced ROM banking mode for MBC1 is not implemented"); } @@ -1046,9 +1042,9 @@ pub static MBC1: Mbc = Mbc { pub static MBC3: Mbc = Mbc { name: "MBC3", read_rom: |rom: &Cartridge, addr: u16| -> u8 { - match addr & 0xf000 { - 0x0000 | 0x1000 | 0x2000 | 0x3000 => rom.rom_data[addr as usize], - 0x4000 | 0x5000 | 0x6000 | 0x7000 => *rom + match addr { + 0x0000..=0x3fff => rom.rom_data[addr as usize], + 0x4000..=0x7fff => *rom .rom_data .get(rom.rom_offset + (addr - 0x4000) as usize) .unwrap_or(&0x0), @@ -1059,13 +1055,13 @@ pub static MBC3: Mbc = Mbc { } }, write_rom: |rom: &mut Cartridge, addr: u16, value: u8| { - match addr & 0xf000 { + match addr { // RAM enabled flag - 0x0000 | 0x1000 => { + 0x0000..=0x1fff => { rom.ram_enabled = (value & 0x0f) == 0x0a; } // ROM bank selection - 0x2000 | 0x3000 => { + 0x2000..=0x3fff => { let mut rom_bank = value as u16 & 0x7f; rom_bank &= rom.rom_bank_count * 2 - 1; if rom_bank == 0 { @@ -1074,7 +1070,7 @@ pub static MBC3: Mbc = Mbc { rom.set_rom_bank(rom_bank); } // RAM bank selection - 0x4000 | 0x5000 => { + 0x4000..=0x5fff => { let ram_bank = value & 0x03; if ram_bank as u16 >= rom.ram_bank_count { return; @@ -1102,9 +1098,9 @@ pub static MBC3: Mbc = Mbc { pub static MBC5: Mbc = Mbc { name: "MBC5", read_rom: |rom: &Cartridge, addr: u16| -> u8 { - match addr & 0xf000 { - 0x0000 | 0x1000 | 0x2000 | 0x3000 => rom.rom_data[addr as usize], - 0x4000 | 0x5000 | 0x6000 | 0x7000 => *rom + match addr { + 0x0000..=0x3fff => rom.rom_data[addr as usize], + 0x4000..=0x7fff => *rom .rom_data .get(rom.rom_offset + (addr - 0x4000) as usize) .unwrap_or(&0x0), @@ -1115,9 +1111,9 @@ pub static MBC5: Mbc = Mbc { } }, write_rom: |rom: &mut Cartridge, addr: u16, value: u8| { - match addr & 0xf000 { + match addr { // RAM enabled flag - 0x0000 | 0x1000 => { + 0x0000..=0x1fff => { rom.ram_enabled = (value & 0x0f) == 0x0a; } // ROM bank selection 8 lower bits @@ -1131,7 +1127,7 @@ pub static MBC5: Mbc = Mbc { rom.set_rom_bank(rom_bank); } // RAM bank selection - 0x4000 | 0x5000 => { + 0x4000..=0x5fff => { let mut ram_bank = value & 0x0f; // handles the rumble flag for the cartridges diff --git a/src/state.rs b/src/state.rs index 065f0af5256cf56e2b4659b711d1ab1abf593464..8c4c917d0102fa113502b3b79db41577200450f6 100644 --- a/src/state.rs +++ b/src/state.rs @@ -88,6 +88,17 @@ pub trait State { fn to_gb(&self, gb: &mut GameBoy) -> Result<(), Error>; } +pub trait StateBox { + /// Obtains a new instance of the state from the provided + /// `GameBoy` instance and returns it as a boxed value. + fn from_gb(gb: &mut GameBoy) -> Result<Box<Self>, Error> + where + Self: Sized; + + /// Applies the state to the provided `GameBoy` instance. + fn to_gb(&self, gb: &mut GameBoy) -> Result<(), Error>; +} + #[cfg_attr(feature = "wasm", wasm_bindgen)] #[derive(Default)] pub struct BosState { @@ -248,16 +259,16 @@ impl Serialize for BosState { } } -impl State for BosState { - fn from_gb(gb: &mut GameBoy) -> Result<Self, Error> { - Ok(Self { +impl StateBox for BosState { + fn from_gb(gb: &mut GameBoy) -> Result<Box<Self>, Error> { + Ok(Box::new(Self { magic: BOS_MAGIC_UINT, version: BOS_VERSION, block_count: 2, info: Some(BosInfo::from_gb(gb)?), image_buffer: Some(BosImageBuffer::from_gb(gb)?), - bess: BessState::from_gb(gb)?, - }) + bess: *BessState::from_gb(gb)?, + })) } fn to_gb(&self, gb: &mut GameBoy) -> Result<(), Error> { @@ -606,16 +617,16 @@ impl Serialize for BessState { } } -impl State for BessState { - fn from_gb(gb: &mut GameBoy) -> Result<Self, Error> { - Ok(Self { +impl StateBox for BessState { + fn from_gb(gb: &mut GameBoy) -> Result<Box<Self>, Error> { + Ok(Box::new(Self { footer: BessFooter::default(), name: BessName::from_gb(gb)?, info: BessInfo::from_gb(gb)?, core: BessCore::from_gb(gb)?, mbc: BessMbc::from_gb(gb)?, end: BessBlock::from_magic(String::from("END ")), - }) + })) } fn to_gb(&self, gb: &mut GameBoy) -> Result<(), Error> { @@ -1630,9 +1641,48 @@ impl StateManager { mod tests { use boytacean_encoding::zippy::{decode_zippy, encode_zippy}; - use crate::gb::GameBoy; + use crate::{gb::GameBoy, state::State}; + + use super::{BessCore, SaveStateFormat, StateManager}; - use super::{SaveStateFormat, StateManager}; + #[test] + fn test_bess_core() { + let mut gb = GameBoy::default(); + gb.load(true).unwrap(); + gb.load_rom_file("res/roms/test/firstwhite.gb", None) + .unwrap(); + let bess_core = BessCore::from_gb(&mut gb).unwrap(); + assert_eq!(bess_core.model, "GDB "); + assert_eq!(bess_core.pc, 0x0000); + assert_eq!(bess_core.af, 0x0000); + assert_eq!(bess_core.bc, 0x0000); + assert_eq!(bess_core.de, 0x0000); + assert_eq!(bess_core.hl, 0x0000); + assert_eq!(bess_core.sp, 0x0000); + assert_eq!(bess_core.ime, false); + assert_eq!(bess_core.ie, 0x00); + assert_eq!(bess_core.execution_mode, 0); + assert_eq!(bess_core.io_registers.len(), 128); + assert_eq!( + bess_core.io_registers, + [ + 63, 0, 0, 255, 0, 0, 0, 248, 255, 255, 255, 255, 255, 255, 255, 224, 128, 63, 0, + 255, 191, 255, 63, 0, 255, 191, 127, 255, 159, 255, 191, 255, 255, 0, 0, 191, 0, 0, + 240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 134, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 126, 255, 254, 0, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 249, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 + ] + ); + assert_eq!(bess_core.ram.size, 0x2000); + assert_eq!(bess_core.vram.size, 0x2000); + assert_eq!(bess_core.mbc_ram.size, 0x2000); + assert_eq!(bess_core.oam.size, 0x00a0); + assert_eq!(bess_core.hram.size, 0x007f); + assert_eq!(bess_core.background_palettes.size, 0x0000); + assert_eq!(bess_core.object_palettes.size, 0x0000); + } #[test] fn test_load_bos() { @@ -1667,7 +1717,7 @@ mod tests { let encoded = encode_zippy(&data).unwrap(); let decoded = decode_zippy(&encoded).unwrap(); assert_eq!(data, decoded); - assert_eq!(encoded.len(), 804); + assert_eq!(encoded.len(), 811); assert_eq!(decoded.len(), 25154); } }