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
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,
PocketCamera, Renderer, RomFile, SerialTarget,
};
use constants::{is_cgb, STATIC_IS_CGB};
use processor::{
memory::{mmio::gpu::Colour, rom::CgbRomType, Rom},
memory::{mmio::gpu::Colour, rom::CgbRomType, OutputTargets, Rom},
Cpu, CpuSaveState,
};
use std::{
@ -77,14 +76,14 @@ where
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.set_title(format!(
"{} on {} on {}",
rom.get_title(),
rom.mbc_type(),
if is_cgb() { "CGB" } else { "DMG" }
if cgb { "CGB" } else { "DMG" }
));
let bootrom_enabled = options.bootrom.is_some();
@ -103,13 +102,16 @@ where
receiver,
Cpu::new(
Memory::init(
cgb,
bootrom,
rom,
options.window,
options.output,
options.serial_target,
options.tile_window,
camera,
OutputTargets::new(
options.window,
options.output,
options.serial_target,
options.tile_window,
camera,
),
),
bootrom_enabled,
),

View file

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

View file

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