better cgb init
This commit is contained in:
parent
c64861fbf5
commit
c56e167c6b
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
|
@ -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,13 +102,16 @@ where
|
||||||
receiver,
|
receiver,
|
||||||
Cpu::new(
|
Cpu::new(
|
||||||
Memory::init(
|
Memory::init(
|
||||||
|
cgb,
|
||||||
bootrom,
|
bootrom,
|
||||||
rom,
|
rom,
|
||||||
options.window,
|
OutputTargets::new(
|
||||||
options.output,
|
options.window,
|
||||||
options.serial_target,
|
options.output,
|
||||||
options.tile_window,
|
options.serial_target,
|
||||||
camera,
|
options.tile_window,
|
||||||
|
camera,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
bootrom_enabled,
|
bootrom_enabled,
|
||||||
),
|
),
|
||||||
|
|
|
@ -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,9 +388,15 @@ 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 {
|
||||||
todo!()
|
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(_) => {}
|
IoAddress::Unused(_) => {}
|
||||||
|
|
|
@ -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,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue