better cgb init

This commit is contained in:
Alex Janka 2023-04-20 14:53:22 +10:00
parent c64861fbf5
commit c56e167c6b
4 changed files with 157 additions and 50 deletions

View file

@ -1,10 +1,2 @@
use once_cell::sync::OnceCell;
// Hz // Hz
pub const CLOCK_SPEED: usize = 4194304; pub const CLOCK_SPEED: usize = 4194304;
pub(crate) static STATIC_IS_CGB: OnceCell<bool> = OnceCell::new();
pub(crate) fn is_cgb() -> bool {
STATIC_IS_CGB.get().copied().unwrap_or(false)
}

View file

@ -8,9 +8,8 @@ use connect::{
AudioOutput, CameraWrapper, CameraWrapperRef, EmulatorMessage, EmulatorOptions, NoCamera, AudioOutput, CameraWrapper, CameraWrapperRef, EmulatorMessage, EmulatorOptions, NoCamera,
PocketCamera, Renderer, RomFile, SerialTarget, PocketCamera, Renderer, RomFile, SerialTarget,
}; };
use constants::{is_cgb, STATIC_IS_CGB};
use processor::{ use processor::{
memory::{mmio::gpu::Colour, rom::CgbRomType, Rom}, memory::{mmio::gpu::Colour, rom::CgbRomType, OutputTargets, Rom},
Cpu, CpuSaveState, Cpu, CpuSaveState,
}; };
use std::{ use std::{
@ -77,14 +76,14 @@ where
RomFile::Raw(data) => Rom::load(data, None, camera.clone()), RomFile::Raw(data) => Rom::load(data, None, camera.clone()),
}; };
let _ = STATIC_IS_CGB.set(rom.rom_type == CgbRomType::CgbOnly || options.cgb_mode); let cgb = rom.rom_type == CgbRomType::CgbOnly || options.cgb_mode;
options.window.prepare(WIDTH, HEIGHT); options.window.prepare(WIDTH, HEIGHT);
options.window.set_title(format!( options.window.set_title(format!(
"{} on {} on {}", "{} on {} on {}",
rom.get_title(), rom.get_title(),
rom.mbc_type(), rom.mbc_type(),
if is_cgb() { "CGB" } else { "DMG" } if cgb { "CGB" } else { "DMG" }
)); ));
let bootrom_enabled = options.bootrom.is_some(); let bootrom_enabled = options.bootrom.is_some();
@ -103,14 +102,17 @@ where
receiver, receiver,
Cpu::new( Cpu::new(
Memory::init( Memory::init(
cgb,
bootrom, bootrom,
rom, rom,
OutputTargets::new(
options.window, options.window,
options.output, options.output,
options.serial_target, options.serial_target,
options.tile_window, options.tile_window,
camera, camera,
), ),
),
bootrom_enabled, bootrom_enabled,
), ),
) )

View file

@ -1,6 +1,8 @@
use std::marker::PhantomData;
pub use self::rom::Rom; pub use self::rom::Rom;
use self::{ use self::{
addresses::{Address, AddressMarker, IoAddress}, addresses::{Address, AddressMarker, CgbIoAddress, IoAddress},
mmio::{ mmio::{
apu::ApuSaveState, apu::ApuSaveState,
gpu::{Colour, GpuSaveState}, gpu::{Colour, GpuSaveState},
@ -11,7 +13,6 @@ use self::{
}; };
use crate::{ use crate::{
connect::{AudioOutput, CameraWrapperRef, JoypadState, PocketCamera, Renderer, SerialTarget}, connect::{AudioOutput, CameraWrapperRef, JoypadState, PocketCamera, Renderer, SerialTarget},
constants::is_cgb,
Cpu, Cpu,
}; };
@ -22,6 +23,63 @@ pub(crate) mod addresses;
pub mod mmio; pub mod mmio;
pub(crate) mod rom; pub(crate) mod rom;
#[serde_with::serde_as]
#[derive(Serialize, Deserialize, Clone)]
pub struct Wram {
#[serde_as(as = "[_; 4096]")]
bank_0: [u8; 4096],
banks: WramBanks,
}
#[serde_with::serde_as]
#[derive(Serialize, Deserialize, Clone)]
enum WramBanks {
Dmg {
#[serde_as(as = "Box<[_; 4096]>")]
bank: Box<[u8; 4096]>,
},
Cgb {
#[serde_as(as = "Box<[[_; 4096]; 7]>")]
banks: Box<[[u8; 4096]; 7]>,
selected: usize,
},
}
impl Wram {
fn new(cgb: bool) -> Self {
Self {
bank_0: [0; 4096],
banks: if cgb {
WramBanks::Cgb {
banks: Box::new([[0; 4096]; 7]),
selected: 0,
}
} else {
WramBanks::Dmg {
bank: Box::new([0; 4096]),
}
},
}
}
fn get_banked(&self, address: usize) -> u8 {
match &self.banks {
WramBanks::Dmg { bank } => bank[address],
WramBanks::Cgb { banks, selected } => banks[*selected][address],
}
}
fn set_banked(&mut self, address: usize, data: u8) {
match self.banks {
WramBanks::Dmg { ref mut bank } => bank[address] = data,
WramBanks::Cgb {
ref mut banks,
selected,
} => banks[selected][address] = data,
}
}
}
pub struct Memory<ColourFormat, R, C> pub struct Memory<ColourFormat, R, C>
where where
ColourFormat: From<Colour> + Clone, ColourFormat: From<Colour> + Clone,
@ -30,7 +88,7 @@ where
{ {
bootrom: Option<Vec<u8>>, bootrom: Option<Vec<u8>>,
rom: Rom<C>, rom: Rom<C>,
ram: [u8; 8192], ram: Wram,
cpu_ram: [u8; 128], cpu_ram: [u8; 128],
pub(super) interrupts: Interrupts, pub(super) interrupts: Interrupts,
pub(super) ime: bool, pub(super) ime: bool,
@ -53,8 +111,7 @@ where
R: Renderer<ColourFormat>, R: Renderer<ColourFormat>,
{ {
rom: RomSaveState, rom: RomSaveState,
#[serde_as(as = "[_; 8192]")] ram: Wram,
ram: [u8; 8192],
#[serde_as(as = "[_; 128]")] #[serde_as(as = "[_; 128]")]
cpu_ram: [u8; 128], cpu_ram: [u8; 128],
pub(super) interrupts: Interrupts, pub(super) interrupts: Interrupts,
@ -78,7 +135,7 @@ where
pub fn create<C: PocketCamera + Send + 'static>(memory: &Memory<ColourFormat, R, C>) -> Self { pub fn create<C: PocketCamera + Send + 'static>(memory: &Memory<ColourFormat, R, C>) -> Self {
Self { Self {
rom: RomSaveState::create(&memory.rom), rom: RomSaveState::create(&memory.rom),
ram: memory.ram, ram: memory.ram.clone(),
cpu_ram: memory.cpu_ram, cpu_ram: memory.cpu_ram,
interrupts: memory.interrupts, interrupts: memory.interrupts,
ime: memory.ime, ime: memory.ime,
@ -94,6 +151,44 @@ where
} }
} }
pub(crate) struct OutputTargets<ColourFormat, R, C>
where
ColourFormat: From<Colour> + Clone,
R: Renderer<ColourFormat>,
C: PocketCamera + Send + 'static,
{
window: R,
audio: AudioOutput,
serial_target: SerialTarget,
tile_window: Option<R>,
camera: CameraWrapperRef<C>,
_phantom: PhantomData<ColourFormat>,
}
impl<ColourFormat, R, C> OutputTargets<ColourFormat, R, C>
where
ColourFormat: From<Colour> + Clone,
R: Renderer<ColourFormat>,
C: PocketCamera + Send + 'static,
{
pub(crate) fn new(
window: R,
audio: AudioOutput,
serial_target: SerialTarget,
tile_window: Option<R>,
camera: CameraWrapperRef<C>,
) -> Self {
Self {
window,
audio,
serial_target,
tile_window,
camera,
_phantom: PhantomData,
}
}
}
impl<ColourFormat, R, C> Memory<ColourFormat, R, C> impl<ColourFormat, R, C> Memory<ColourFormat, R, C>
where where
ColourFormat: From<Colour> + Clone, ColourFormat: From<Colour> + Clone,
@ -101,18 +196,15 @@ where
C: PocketCamera + Send + 'static, C: PocketCamera + Send + 'static,
{ {
pub(crate) fn init( pub(crate) fn init(
cgb: bool,
bootrom: Option<Vec<u8>>, bootrom: Option<Vec<u8>>,
rom: Rom<C>, rom: Rom<C>,
window: R, output: OutputTargets<ColourFormat, R, C>,
output: AudioOutput,
serial_target: SerialTarget,
tile_window: Option<R>,
camera: CameraWrapperRef<C>,
) -> Self { ) -> Self {
Self { Self {
bootrom, bootrom,
rom, rom,
ram: [0x0; 8192], ram: Wram::new(cgb),
cpu_ram: [0x0; 128], cpu_ram: [0x0; 128],
interrupts: Interrupts::default(), interrupts: Interrupts::default(),
ime: false, ime: false,
@ -120,11 +212,11 @@ where
user_mode: false, user_mode: false,
dma: OamDma::default(), dma: OamDma::default(),
joypad: Joypad::default(), joypad: Joypad::default(),
gpu: Gpu::new(window, tile_window), gpu: Gpu::new(output.window, output.tile_window),
apu: Apu::new(output), apu: Apu::new(output.audio),
serial: Serial::new(serial_target), serial: Serial::new(output.serial_target),
timers: Timer::init(), timers: Timer::init(),
camera, camera: output.camera,
} }
} }
@ -156,9 +248,9 @@ where
} }
Address::Vram(address) => self.gpu.get_vram(address), Address::Vram(address) => self.gpu.get_vram(address),
Address::CartRam(address) => self.rom.get_ram(address), Address::CartRam(address) => self.rom.get_ram(address),
Address::WorkRam(address) => self.ram[address.get_local() as usize], Address::WorkRam(address) => self.ram.bank_0[address.get_local() as usize],
Address::BankedWorkRam(address) => self.ram[(address.get_local() + 0x1000) as usize], Address::BankedWorkRam(address) => self.ram.get_banked((address.get_local()) as usize),
Address::MirroredRam(address) => self.ram[address.get_local() as usize], Address::MirroredRam(address) => self.ram.bank_0[address.get_local() as usize],
Address::Oam(address) => self.gpu.get_oam(address), Address::Oam(address) => self.gpu.get_oam(address),
Address::Prohibited(_) => 0xFF, Address::Prohibited(_) => 0xFF,
Address::Io(address) => self.get_io(address), Address::Io(address) => self.get_io(address),
@ -193,11 +285,11 @@ where
} }
Address::Vram(address) => self.gpu.set_vram(address, data), Address::Vram(address) => self.gpu.set_vram(address, data),
Address::CartRam(address) => self.rom.set_ram(address, data), Address::CartRam(address) => self.rom.set_ram(address, data),
Address::WorkRam(address) => self.ram[address.get_local() as usize] = data, Address::WorkRam(address) => self.ram.bank_0[address.get_local() as usize] = data,
Address::BankedWorkRam(address) => { Address::BankedWorkRam(address) => {
self.ram[(address.get_local() + 0x1000) as usize] = data self.ram.set_banked(address.get_local() as usize, data)
} }
Address::MirroredRam(address) => self.ram[address.get_local() as usize] = data, Address::MirroredRam(address) => self.ram.bank_0[address.get_local() as usize] = data,
Address::Oam(address) => self.gpu.set_oam(address, data), Address::Oam(address) => self.gpu.set_oam(address, data),
Address::Prohibited(_) => {} Address::Prohibited(_) => {}
Address::Io(address) => { Address::Io(address) => {
@ -244,9 +336,15 @@ where
0xFF4B => self.gpu.get_wx(), 0xFF4B => self.gpu.get_wx(),
0x0..0xFF40 | 0xFF4C..=0xFFFF => unreachable!(), 0x0..0xFF40 | 0xFF4C..=0xFFFF => unreachable!(),
}, },
IoAddress::Cgb(_) => { IoAddress::Cgb(address) => {
if is_cgb() { if let WramBanks::Cgb { banks: _, selected } = &self.ram.banks {
todo!(); match address {
CgbIoAddress::WramBank => ((*selected) & 0b111) as u8,
CgbIoAddress::Unused(v) => {
println!("attempt to get 0x{v:0>4X}");
todo!()
}
}
} else { } else {
0xFF 0xFF
} }
@ -290,11 +388,17 @@ where
0xFF4B => self.gpu.update_wx(data), 0xFF4B => self.gpu.update_wx(data),
0x0..0xFF40 | 0xFF4C..=0xFFFF => unreachable!(), 0x0..0xFF40 | 0xFF4C..=0xFFFF => unreachable!(),
}, },
IoAddress::Cgb(_) => { IoAddress::Cgb(address) => {
if is_cgb() { if let WramBanks::Cgb { banks: _, selected } = &mut self.ram.banks {
match address {
CgbIoAddress::WramBank => *selected = (data & 0b111).max(1) as usize,
CgbIoAddress::Unused(v) => {
println!("attempt to set 0x{v:0>4X} to 0x{data:0>2X}");
todo!() todo!()
} }
} }
}
}
IoAddress::Unused(_) => {} IoAddress::Unused(_) => {}
} }
} }

View file

@ -40,7 +40,10 @@ pub(crate) enum IoAddress {
Unused(u16), Unused(u16),
} }
pub(crate) enum CgbIoAddress {} pub(crate) enum CgbIoAddress {
WramBank,
Unused(u16),
}
pub(crate) enum Address { pub(crate) enum Address {
Rom(RomAddress), Rom(RomAddress),
@ -105,8 +108,7 @@ impl TryInto<IoAddress> for u16 {
0xFF30..0xFF40 => Ok(IoAddress::WaveRam(self.try_into().unwrap())), 0xFF30..0xFF40 => Ok(IoAddress::WaveRam(self.try_into().unwrap())),
0xFF40..0xFF4C => Ok(IoAddress::Video(self.try_into().unwrap())), 0xFF40..0xFF4C => Ok(IoAddress::Video(self.try_into().unwrap())),
0xFF4D..0xFF78 => Ok(IoAddress::Cgb(self.try_into().unwrap())), 0xFF4D..0xFF78 => Ok(IoAddress::Cgb(self.try_into().unwrap())),
0x0..0xFF00 => Err(AddressError::OutOfBounds), 0x0..0xFF00 | 0xFFFF => Err(AddressError::OutOfBounds),
0xFFFF => Err(AddressError::OutOfBounds),
_ => Ok(IoAddress::Unused(self)), _ => Ok(IoAddress::Unused(self)),
} }
} }
@ -116,7 +118,11 @@ impl TryInto<CgbIoAddress> for u16 {
type Error = AddressError; type Error = AddressError;
fn try_into(self) -> Result<CgbIoAddress, Self::Error> { fn try_into(self) -> Result<CgbIoAddress, Self::Error> {
todo!() match self {
0xFF70 => Ok(CgbIoAddress::WramBank),
0x0..0xFF4D | 0xFF78..=0xFFFF => Err(AddressError::OutOfBounds),
_ => Ok(CgbIoAddress::Unused(self)),
}
} }
} }
@ -147,7 +153,10 @@ impl AddressMarker for IoAddress {
impl AddressMarker for CgbIoAddress { impl AddressMarker for CgbIoAddress {
fn inner(&self) -> u16 { fn inner(&self) -> u16 {
todo!() match self {
CgbIoAddress::Unused(v) => *v,
CgbIoAddress::WramBank => 0xFF70,
}
} }
} }