address handling
This commit is contained in:
parent
7448d18424
commit
828da3e01f
|
@ -1,4 +1,4 @@
|
||||||
#![feature(exclusive_range_pattern, let_chains, bigint_helper_methods)]
|
#![feature(exclusive_range_pattern, let_chains, bigint_helper_methods, step_trait)]
|
||||||
|
|
||||||
use crate::{processor::memory::Memory, util::pause};
|
use crate::{processor::memory::Memory, util::pause};
|
||||||
use connect::{
|
use connect::{
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
pub use self::rom::Rom;
|
pub use self::rom::Rom;
|
||||||
use self::{
|
use self::{
|
||||||
|
addresses::{Address, AddressMarker, IoAddress},
|
||||||
mmio::{
|
mmio::{
|
||||||
apu::ApuSaveState,
|
apu::ApuSaveState,
|
||||||
gpu::{Colour, GpuSaveState},
|
gpu::{Colour, GpuSaveState},
|
||||||
|
@ -11,17 +12,16 @@ use self::{
|
||||||
use crate::{
|
use crate::{
|
||||||
connect::{AudioOutput, CameraWrapperRef, JoypadState, PocketCamera, Renderer, SerialTarget},
|
connect::{AudioOutput, CameraWrapperRef, JoypadState, PocketCamera, Renderer, SerialTarget},
|
||||||
processor::SplitRegister,
|
processor::SplitRegister,
|
||||||
verbose_println, Cpu,
|
Cpu,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod interrupts;
|
mod interrupts;
|
||||||
pub use interrupts::{Interrupt, Interrupts};
|
pub use interrupts::{Interrupt, Interrupts};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
pub(crate) mod addresses;
|
||||||
pub mod mmio;
|
pub mod mmio;
|
||||||
pub(crate) mod rom;
|
pub(crate) mod rom;
|
||||||
|
|
||||||
pub(crate) type Address = u16;
|
|
||||||
|
|
||||||
pub struct Memory<ColourFormat, R, C>
|
pub struct Memory<ColourFormat, R, C>
|
||||||
where
|
where
|
||||||
ColourFormat: From<Colour> + Clone,
|
ColourFormat: From<Colour> + Clone,
|
||||||
|
@ -124,132 +124,148 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&self, address: Address) -> u8 {
|
pub(crate) fn get<T>(&self, address: T) -> u8
|
||||||
|
where
|
||||||
|
T: Into<Address>,
|
||||||
|
{
|
||||||
|
let address: Address = address.into();
|
||||||
match address {
|
match address {
|
||||||
0x0..0x8000 => {
|
Address::Rom(address) => {
|
||||||
// rom access
|
// rom access
|
||||||
// todo - switchable rom banks
|
// todo - switchable rom banks
|
||||||
if let Some(bootrom) = &self.bootrom && (address as usize) < bootrom.len() {
|
if let Some(bootrom) = &self.bootrom && (address.inner() as usize) < bootrom.len() {
|
||||||
bootrom[address as usize]
|
bootrom[address.inner() as usize]
|
||||||
} else {
|
} else {
|
||||||
self.rom.get(address)
|
self.rom.get(address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0x8000..0xA000 => self.gpu.vram.get(address),
|
Address::Vram(address) => self.gpu.vram.get(address),
|
||||||
0xA000..0xC000 => {
|
Address::CartRam(address) => self.rom.get_ram(address),
|
||||||
// cart ram
|
Address::WorkRam(address) => self.ram[address.get_local() as usize],
|
||||||
self.rom.get_ram(address)
|
Address::BankedWorkRam(address) => self.ram[(address.get_local() + 0x1000) as usize],
|
||||||
}
|
Address::MirroredRam(address) => self.ram[address.get_local() as usize],
|
||||||
0xC000..0xE000 => self.ram[(address - 0xC000) as usize],
|
Address::Oam(address) => self.gpu.oam.get(address),
|
||||||
0xE000..0xFE00 => self.ram[(address - 0xE000) as usize],
|
Address::Prohibited(_) => 0xFF,
|
||||||
0xFE00..0xFEA0 => self.gpu.oam.get(address),
|
Address::Io(address) => self.get_io(address),
|
||||||
0xFEA0..0xFF00 => 0xFF,
|
Address::Hram(address) => self.cpu_ram[address.get_local() as usize],
|
||||||
0xFF00..0xFF4C => self.get_io(address),
|
Address::InterruptEnable(_) => self.interrupts.get_enable_register(),
|
||||||
0xFF4C..0xFF80 => 0xFF,
|
|
||||||
0xFF80..0xFFFF => self.cpu_ram[(address - 0xFF80) as usize],
|
|
||||||
0xFFFF => self.interrupts.get_enable_register(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(&mut self, address: Address, data: u8) {
|
pub(crate) fn set<T>(&mut self, address: T, data: u8)
|
||||||
|
where
|
||||||
|
T: Into<Address>,
|
||||||
|
{
|
||||||
|
let address: Address = address.into();
|
||||||
match address {
|
match address {
|
||||||
0x0..0x8000 => {
|
Address::Rom(address) => {
|
||||||
// change this with MBC code...
|
|
||||||
self.rom.set(address, data);
|
self.rom.set(address, data);
|
||||||
if self.rom.can_rumble() {
|
if self.rom.can_rumble() {
|
||||||
// rumble
|
// rumble
|
||||||
self.gpu.window.set_rumble(self.rom.is_rumbling())
|
self.gpu.window.set_rumble(self.rom.is_rumbling())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0x8000..0xA000 => self.gpu.vram.set(address, data),
|
Address::Vram(address) => self.gpu.vram.set(address, data),
|
||||||
0xA000..0xC000 => self.rom.set_ram(address, data),
|
Address::CartRam(address) => self.rom.set_ram(address, data),
|
||||||
0xC000..0xE000 => self.ram[(address - 0xC000) as usize] = data,
|
Address::WorkRam(address) => self.ram[address.get_local() as usize] = data,
|
||||||
0xE000..0xFE00 => self.ram[(address - 0xE000) as usize] = data,
|
Address::BankedWorkRam(address) => {
|
||||||
0xFE00..0xFEA0 => self.gpu.oam.set(address, data),
|
self.ram[(address.get_local() + 0x1000) as usize] = data
|
||||||
0xFEA0..0xFF00 => {}
|
|
||||||
0xFF00..0xFF4C => self.set_io(address, data),
|
|
||||||
0xFF50 => self.bootrom = None,
|
|
||||||
0xFF4C..0xFF50 | 0xFF51..0xFF80 => {}
|
|
||||||
0xFF80..0xFFFF => self.cpu_ram[(address - 0xFF80) as usize] = data,
|
|
||||||
0xFFFF => {
|
|
||||||
verbose_println!("interrupts set to {:#b}", data);
|
|
||||||
verbose_println!(" / {:#X}", data);
|
|
||||||
self.interrupts.set_enable_register(data);
|
|
||||||
}
|
}
|
||||||
|
Address::MirroredRam(address) => self.ram[address.get_local() as usize] = data,
|
||||||
|
Address::Oam(address) => self.gpu.oam.set(address, data),
|
||||||
|
Address::Prohibited(_) => {}
|
||||||
|
Address::Io(address) => {
|
||||||
|
if address.inner() == 0xFF50 {
|
||||||
|
self.bootrom = None
|
||||||
|
}
|
||||||
|
self.set_io(address, data)
|
||||||
|
}
|
||||||
|
Address::Hram(address) => self.cpu_ram[address.get_local() as usize] = data,
|
||||||
|
Address::InterruptEnable(_) => self.interrupts.set_enable_register(data),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_io(&self, address: Address) -> u8 {
|
fn get_io(&self, address: IoAddress) -> u8 {
|
||||||
// range: 0xFF00 - 0xFF4B inclusive
|
|
||||||
match address {
|
match address {
|
||||||
0xFF00 => self.joypad.as_register(),
|
IoAddress::Joypad => self.joypad.as_register(),
|
||||||
0xFF01 => self.serial.get_queued(),
|
IoAddress::Serial(address) => match address.inner() {
|
||||||
0xFF02 => self.serial.get_control(),
|
0xFF01 => self.serial.get_queued(),
|
||||||
0xFF04 => self.timers.get_div(),
|
0xFF02 => self.serial.get_control(),
|
||||||
0xFF05 => self.timers.get_tima(),
|
_ => unreachable!(),
|
||||||
0xFF06 => self.timers.get_tma(),
|
},
|
||||||
0xFF07 => self.timers.get_timer_control(),
|
IoAddress::Timer(address) => match address.inner() {
|
||||||
0xFF0F => self.interrupts.get_flag_register(),
|
0xFF04 => self.timers.get_div(),
|
||||||
0xFF10..0xFF40 => self.apu.get_register(address),
|
0xFF05 => self.timers.get_tima(),
|
||||||
0xFF40 => self.gpu.get_lcdc(),
|
0xFF06 => self.timers.get_tma(),
|
||||||
0xFF41 => self.gpu.get_lcd_status(),
|
0xFF07 => self.timers.get_timer_control(),
|
||||||
0xFF42 => self.gpu.get_scy(),
|
_ => unreachable!(),
|
||||||
0xFF43 => self.gpu.get_scx(),
|
},
|
||||||
0xFF44 => self.gpu.get_ly(),
|
IoAddress::InterruptFlag => self.interrupts.get_flag_register(),
|
||||||
0xFF45 => self.gpu.get_lyc(),
|
IoAddress::Audio(address) => self.apu.get_register(address),
|
||||||
0xFF46 => self.dma_addr,
|
IoAddress::WaveRam(address) => self.apu.get_wave_ram_register(address),
|
||||||
0xFF47 => self.gpu.get_bg_palette(),
|
IoAddress::Video(address) => match address.inner() {
|
||||||
0xFF48 => self.gpu.get_obj_palette_0(),
|
0xFF40 => self.gpu.get_lcdc(),
|
||||||
0xFF49 => self.gpu.get_obj_palette_1(),
|
0xFF41 => self.gpu.get_lcd_status(),
|
||||||
0xFF4A => self.gpu.get_wy(),
|
0xFF42 => self.gpu.get_scy(),
|
||||||
0xFF4B => self.gpu.get_wx(),
|
0xFF43 => self.gpu.get_scx(),
|
||||||
0xFF03 | 0xFF08..0xFF0F => 0xFF,
|
0xFF44 => self.gpu.get_ly(),
|
||||||
0x0..0xFF00 | 0xFF4C..=0xFFFF => panic!("passed wrong address to get_io"),
|
0xFF45 => self.gpu.get_lyc(),
|
||||||
|
0xFF46 => self.dma_addr,
|
||||||
|
0xFF47 => self.gpu.get_bg_palette(),
|
||||||
|
0xFF48 => self.gpu.get_obj_palette_0(),
|
||||||
|
0xFF49 => self.gpu.get_obj_palette_1(),
|
||||||
|
0xFF4A => self.gpu.get_wy(),
|
||||||
|
0xFF4B => self.gpu.get_wx(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
IoAddress::Unused(_) => 0xFF,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_io(&mut self, address: Address, data: u8) {
|
fn set_io(&mut self, address: IoAddress, data: u8) {
|
||||||
// range: 0xFF00 - 0xFF4B inclusive
|
|
||||||
match address {
|
match address {
|
||||||
0xFF00 => {
|
IoAddress::Joypad => self.joypad.mmio_write(data),
|
||||||
// joypad
|
IoAddress::Serial(address) => match address.inner() {
|
||||||
self.joypad.mmio_write(data);
|
0xFF01 => self.serial.update_queued(data),
|
||||||
}
|
0xFF02 => self.serial.update_control(data),
|
||||||
0xFF01 => self.serial.update_queued(data),
|
_ => unreachable!(),
|
||||||
0xFF02 => self.serial.update_control(data),
|
},
|
||||||
0xFF04 => self.timers.update_div(),
|
IoAddress::Timer(address) => match address.inner() {
|
||||||
0xFF05 => self.timers.update_tima(data),
|
0xFF04 => self.timers.update_div(),
|
||||||
0xFF06 => self.timers.update_tma(data),
|
0xFF05 => self.timers.update_tima(data),
|
||||||
0xFF07 => self.timers.update_timer_control(data),
|
0xFF06 => self.timers.update_tma(data),
|
||||||
0xFF0F => self.interrupts.set_flag_register(data),
|
0xFF07 => self.timers.update_timer_control(data),
|
||||||
0xFF10..0xFF40 => self.apu.mmio_write(address, data),
|
_ => unreachable!(),
|
||||||
0xFF40 => self.gpu.update_lcdc(data),
|
},
|
||||||
0xFF41 => self.gpu.update_lcd_status(data),
|
IoAddress::InterruptFlag => self.interrupts.set_flag_register(data),
|
||||||
0xFF42 => self.gpu.update_scy(data),
|
IoAddress::Audio(address) => self.apu.mmio_write(address, data),
|
||||||
0xFF43 => self.gpu.update_scx(data),
|
IoAddress::WaveRam(address) => self.apu.mmio_write_wave_ram(address, data),
|
||||||
0xFF45 => self.gpu.update_lyc(data),
|
IoAddress::Video(address) => match address.inner() {
|
||||||
0xFF46 => {
|
0xFF40 => self.gpu.update_lcdc(data),
|
||||||
if data > 0xDF {
|
0xFF41 => self.gpu.update_lcd_status(data),
|
||||||
panic!("dma transfer out of bounds: {data:#X}");
|
0xFF42 => self.gpu.update_scy(data),
|
||||||
|
0xFF43 => self.gpu.update_scx(data),
|
||||||
|
0xFF45 => self.gpu.update_lyc(data),
|
||||||
|
0xFF46 => {
|
||||||
|
if data > 0xDF {
|
||||||
|
panic!("dma transfer out of bounds: {data:#X}");
|
||||||
|
}
|
||||||
|
self.dma_addr = data;
|
||||||
|
let mut addr: u16 = 0x0;
|
||||||
|
addr.set_high(data);
|
||||||
|
for l in 0x0..0xA0 {
|
||||||
|
addr.set_low(l);
|
||||||
|
self.gpu.oam.data[l as usize] = self.get(addr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.dma_addr = data;
|
0xFF47 => self.gpu.update_bg_palette(data),
|
||||||
let mut addr: u16 = 0x0;
|
0xFF48 => self.gpu.update_obj_palette_0(data),
|
||||||
addr.set_high(data);
|
0xFF49 => self.gpu.update_obj_palette_1(data),
|
||||||
for l in 0x0..0xA0 {
|
0xFF4A => self.gpu.update_wy(data),
|
||||||
addr.set_low(l);
|
0xFF4B => self.gpu.update_wx(data),
|
||||||
self.gpu.oam.data[l as usize] = self.get(addr);
|
_ => unreachable!(),
|
||||||
}
|
},
|
||||||
}
|
IoAddress::Unused(_) => {}
|
||||||
0xFF47 => self.gpu.update_bg_palette(data),
|
|
||||||
0xFF48 => self.gpu.update_obj_palette_0(data),
|
|
||||||
0xFF49 => self.gpu.update_obj_palette_1(data),
|
|
||||||
0xFF4A => self.gpu.update_wy(data),
|
|
||||||
0xFF4B => self.gpu.update_wx(data),
|
|
||||||
0xFF03 | 0xFF08..0xFF0F | 0xFF44 => {
|
|
||||||
// read-only addresses
|
|
||||||
verbose_println!("BANNED write: {data:#X} to {address:#X}");
|
|
||||||
}
|
|
||||||
0x0..0xFF00 | 0xFF4C..=u16::MAX => panic!("passed wrong address to set_io"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
167
lib/src/processor/memory/addresses.rs
Normal file
167
lib/src/processor/memory/addresses.rs
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
use std::ops::{Add, Sub};
|
||||||
|
|
||||||
|
pub(crate) use self::types::*;
|
||||||
|
|
||||||
|
mod types;
|
||||||
|
|
||||||
|
pub(crate) type VramAddress = BoundedAddress<0x8000, 0xA000>;
|
||||||
|
pub(crate) type CartRamAddress = BoundedAddress<0xA000, 0xC000>;
|
||||||
|
pub(crate) type WorkRamAddress = BoundedAddress<0xC000, 0xD000>;
|
||||||
|
pub(crate) type BankedWorkRamAddress = BoundedAddress<0xD000, 0xE000>;
|
||||||
|
pub(crate) type MirroredRamAddress = BoundedAddress<0xE000, 0xFE00>;
|
||||||
|
pub(crate) type OamAddress = BoundedAddress<0xFE00, 0xFEA0>;
|
||||||
|
pub(crate) type ProhibitedAddress = BoundedAddress<0xFEA0, 0xFF00>;
|
||||||
|
pub(crate) type HramAddress = BoundedAddress<0xFF80, 0xFFFF>;
|
||||||
|
pub(crate) type InterruptEnable = ();
|
||||||
|
|
||||||
|
pub(crate) type Bank0Address = BoundedAddress<0x0, 0x4000>;
|
||||||
|
pub(crate) type MappedBankAddress = BoundedAddress<0x4000, 0x8000>;
|
||||||
|
|
||||||
|
pub(crate) type SerialAddress = BoundedAddress<0xFF01, 0xFF03>;
|
||||||
|
pub(crate) type TimerAddress = BoundedAddress<0xFF04, 0xFF08>;
|
||||||
|
pub(crate) type AudioAddress = BoundedAddress<0xFF10, 0xFF27>;
|
||||||
|
pub(crate) type WaveRamAddress = BoundedAddress<0xFF30, 0xFF40>;
|
||||||
|
pub(crate) type VideoAddress = BoundedAddress<0xFF40, 0xFF4C>;
|
||||||
|
|
||||||
|
pub(crate) enum RomAddress {
|
||||||
|
Bank0(Bank0Address),
|
||||||
|
MappedBank(MappedBankAddress),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) enum IoAddress {
|
||||||
|
Joypad,
|
||||||
|
Serial(SerialAddress),
|
||||||
|
Timer(TimerAddress),
|
||||||
|
InterruptFlag,
|
||||||
|
Audio(AudioAddress),
|
||||||
|
WaveRam(WaveRamAddress),
|
||||||
|
Video(VideoAddress),
|
||||||
|
Unused(u16),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) enum Address {
|
||||||
|
Rom(RomAddress),
|
||||||
|
Vram(VramAddress),
|
||||||
|
CartRam(CartRamAddress),
|
||||||
|
WorkRam(WorkRamAddress),
|
||||||
|
BankedWorkRam(BankedWorkRamAddress),
|
||||||
|
MirroredRam(MirroredRamAddress),
|
||||||
|
Oam(OamAddress),
|
||||||
|
Prohibited(ProhibitedAddress),
|
||||||
|
Io(IoAddress),
|
||||||
|
Hram(HramAddress),
|
||||||
|
InterruptEnable(InterruptEnable),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryInto<RomAddress> for u16 {
|
||||||
|
type Error = AddressError;
|
||||||
|
|
||||||
|
fn try_into(self) -> Result<RomAddress, Self::Error> {
|
||||||
|
match self {
|
||||||
|
0x0..0x4000 => Ok(RomAddress::Bank0(self.try_into().unwrap())),
|
||||||
|
0x4000..0x8000 => Ok(RomAddress::MappedBank(self.try_into().unwrap())),
|
||||||
|
_ => Err(AddressError::OutOfBounds),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddressMarker for RomAddress {
|
||||||
|
fn inner(&self) -> u16 {
|
||||||
|
match self {
|
||||||
|
RomAddress::Bank0(v) => v.inner(),
|
||||||
|
RomAddress::MappedBank(v) => v.inner(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u16> for Address {
|
||||||
|
fn from(value: u16) -> Self {
|
||||||
|
match value {
|
||||||
|
0x0..0x8000 => Address::Rom(value.try_into().unwrap()),
|
||||||
|
0x8000..0xA000 => Address::Vram(value.try_into().unwrap()),
|
||||||
|
0xA000..0xC000 => Address::CartRam(value.try_into().unwrap()),
|
||||||
|
0xC000..0xD000 => Address::WorkRam(value.try_into().unwrap()),
|
||||||
|
0xD000..0xE000 => Address::BankedWorkRam(value.try_into().unwrap()),
|
||||||
|
0xE000..0xFE00 => Address::MirroredRam(value.try_into().unwrap()),
|
||||||
|
0xFE00..0xFEA0 => Address::Oam(value.try_into().unwrap()),
|
||||||
|
0xFEA0..0xFF00 => Address::Prohibited(value.try_into().unwrap()),
|
||||||
|
0xFF00..0xFF80 => Address::Io(value.try_into().unwrap()),
|
||||||
|
0xFF80..0xFFFF => Address::Hram(value.try_into().unwrap()),
|
||||||
|
0xFFFF => Address::InterruptEnable(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryInto<IoAddress> for u16 {
|
||||||
|
type Error = AddressError;
|
||||||
|
|
||||||
|
fn try_into(self) -> Result<IoAddress, Self::Error> {
|
||||||
|
match self {
|
||||||
|
0xFF00 => Ok(IoAddress::Joypad),
|
||||||
|
0xFF01..0xFF03 => Ok(IoAddress::Serial(self.try_into().unwrap())),
|
||||||
|
0xFF04..0xFF08 => Ok(IoAddress::Timer(self.try_into().unwrap())),
|
||||||
|
0xFF0F => Ok(IoAddress::InterruptFlag),
|
||||||
|
0xFF10..0xFF27 => Ok(IoAddress::Audio(self.try_into().unwrap())),
|
||||||
|
0xFF30..0xFF40 => Ok(IoAddress::WaveRam(self.try_into().unwrap())),
|
||||||
|
0xFF40..0xFF4C => Ok(IoAddress::Video(self.try_into().unwrap())),
|
||||||
|
0x0..0xFF00 => Err(AddressError::OutOfBounds),
|
||||||
|
0xFFFF => Err(AddressError::OutOfBounds),
|
||||||
|
_ => Ok(IoAddress::Unused(self)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddressMarker for IoAddress {
|
||||||
|
fn inner(&self) -> u16 {
|
||||||
|
match self {
|
||||||
|
IoAddress::Joypad => 0xFF00,
|
||||||
|
IoAddress::Serial(v) => v.inner(),
|
||||||
|
IoAddress::Timer(v) => v.inner(),
|
||||||
|
IoAddress::InterruptFlag => 0xFF0F,
|
||||||
|
IoAddress::Audio(v) => v.inner(),
|
||||||
|
IoAddress::WaveRam(v) => v.inner(),
|
||||||
|
IoAddress::Video(v) => v.inner(),
|
||||||
|
IoAddress::Unused(v) => *v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AddressMarker for Address {
|
||||||
|
fn inner(&self) -> u16 {
|
||||||
|
match self {
|
||||||
|
Address::Rom(v) => v.inner(),
|
||||||
|
Address::Vram(v) => v.inner(),
|
||||||
|
Address::CartRam(v) => v.inner(),
|
||||||
|
Address::WorkRam(v) => v.inner(),
|
||||||
|
Address::BankedWorkRam(v) => v.inner(),
|
||||||
|
Address::MirroredRam(v) => v.inner(),
|
||||||
|
Address::Oam(v) => v.inner(),
|
||||||
|
Address::Prohibited(v) => v.inner(),
|
||||||
|
Address::Io(v) => v.inner(),
|
||||||
|
Address::Hram(v) => v.inner(),
|
||||||
|
Address::InterruptEnable(_) => 0xFFFF,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Address> for u16 {
|
||||||
|
fn from(value: Address) -> Self {
|
||||||
|
value.inner()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Add for Address {
|
||||||
|
type Output = Address;
|
||||||
|
|
||||||
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
|
self.inner().wrapping_add(rhs.inner()).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sub for Address {
|
||||||
|
type Output = Address;
|
||||||
|
|
||||||
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
|
self.inner().wrapping_sub(rhs.inner()).into()
|
||||||
|
}
|
||||||
|
}
|
65
lib/src/processor/memory/addresses/types.rs
Normal file
65
lib/src/processor/memory/addresses/types.rs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
use std::ops::{Add, Sub};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum AddressError {
|
||||||
|
OutOfBounds,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub(crate) struct BoundedAddress<const MIN: u16, const MAX: u16>(u16);
|
||||||
|
|
||||||
|
impl<const MIN: u16, const MAX: u16> Add<u16> for BoundedAddress<MIN, MAX> {
|
||||||
|
type Output = Option<BoundedAddress<MIN, MAX>>;
|
||||||
|
|
||||||
|
fn add(self, rhs: u16) -> Self::Output {
|
||||||
|
(self.0 + rhs).try_into().ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const MIN: u16, const MAX: u16> Sub<u16> for BoundedAddress<MIN, MAX> {
|
||||||
|
type Output = Option<BoundedAddress<MIN, MAX>>;
|
||||||
|
|
||||||
|
fn sub(self, rhs: u16) -> Self::Output {
|
||||||
|
(self.0 - rhs).try_into().ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const MIN: u16, const MAX: u16> TryInto<BoundedAddress<MIN, MAX>> for u16 {
|
||||||
|
type Error = AddressError;
|
||||||
|
|
||||||
|
fn try_into(self) -> Result<BoundedAddress<MIN, MAX>, Self::Error> {
|
||||||
|
if self >= MIN && self < MAX {
|
||||||
|
Ok(BoundedAddress(self))
|
||||||
|
} else {
|
||||||
|
Err(AddressError::OutOfBounds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const MIN: u16, const MAX: u16> BoundedAddress<MIN, MAX> {
|
||||||
|
pub(crate) fn get_local(&self) -> u16 {
|
||||||
|
self.0 - MIN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const MIN: u16, const MAX: u16> PartialEq for BoundedAddress<MIN, MAX> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.0 == other.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const MIN: u16, const MAX: u16> PartialOrd for BoundedAddress<MIN, MAX> {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
self.0.partial_cmp(&other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) trait AddressMarker {
|
||||||
|
fn inner(&self) -> u16;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const MIN: u16, const MAX: u16> AddressMarker for BoundedAddress<MIN, MAX> {
|
||||||
|
fn inner(&self) -> u16 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ use self::{
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
connect::AudioOutput,
|
connect::AudioOutput,
|
||||||
processor::memory::Address,
|
processor::memory::addresses::{AddressMarker, AudioAddress, WaveRamAddress},
|
||||||
util::{get_bit, set_or_clear_bit},
|
util::{get_bit, set_or_clear_bit},
|
||||||
};
|
};
|
||||||
use futures::executor;
|
use futures::executor;
|
||||||
|
@ -185,11 +185,11 @@ impl Apu {
|
||||||
self.output.send_rb.is_full()
|
self.output.send_rb.is_full()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_register(&self, addr: Address) -> u8 {
|
pub(crate) fn get_register(&self, addr: AudioAddress) -> u8 {
|
||||||
if self.apu_enable {
|
if self.apu_enable {
|
||||||
self.make_register(addr)
|
self.make_register(addr)
|
||||||
} else {
|
} else {
|
||||||
match addr {
|
match addr.inner() {
|
||||||
0xFF26 | 0xFF11 | 0xFF16 | 0xFF1B | 0xFF20 | 0xFF30..0xFF40 => {
|
0xFF26 | 0xFF11 | 0xFF16 | 0xFF1B | 0xFF20 | 0xFF30..0xFF40 => {
|
||||||
self.make_register(addr)
|
self.make_register(addr)
|
||||||
}
|
}
|
||||||
|
@ -198,8 +198,12 @@ impl Apu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_register(&self, addr: Address) -> u8 {
|
pub(crate) fn get_wave_ram_register(&self, addr: WaveRamAddress) -> u8 {
|
||||||
match addr {
|
self.channels.three.wave_ram.data[addr.get_local() as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_register(&self, addr: AudioAddress) -> u8 {
|
||||||
|
match addr.inner() {
|
||||||
0xFF10 => self.channels.one.get_sweep_register(),
|
0xFF10 => self.channels.one.get_sweep_register(),
|
||||||
0xFF11 => self.channels.one.get_length_timer_and_duty_cycle(),
|
0xFF11 => self.channels.one.get_length_timer_and_duty_cycle(),
|
||||||
0xFF12 => self.channels.one.get_volume_and_envelope(),
|
0xFF12 => self.channels.one.get_volume_and_envelope(),
|
||||||
|
@ -250,15 +254,13 @@ impl Apu {
|
||||||
// write-only registers
|
// write-only registers
|
||||||
0xFF13 | 0xFF18 | 0xFF1B | 0xFF1D | 0xFF20 => 0xFF,
|
0xFF13 | 0xFF18 | 0xFF1B | 0xFF1D | 0xFF20 => 0xFF,
|
||||||
// not registers
|
// not registers
|
||||||
0xFF15 | 0xFF1F | 0xFF27..0xFF30 => 0xFF,
|
0xFF15 | 0xFF1F => 0xFF,
|
||||||
// wave ram
|
_ => unreachable!(),
|
||||||
0xFF30..0xFF40 => self.channels.three.wave_ram.data[(addr - 0xFF30) as usize],
|
|
||||||
0x0..0xFF10 | 0xFF40..=0xFFFF => panic!("non-apu addr in apu"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mmio_write(&mut self, addr: Address, data: u8) {
|
pub(crate) fn mmio_write(&mut self, addr: AudioAddress, data: u8) {
|
||||||
match addr {
|
match addr.inner() {
|
||||||
0xFF10 => self.channels.one.update_sweep(data),
|
0xFF10 => self.channels.one.update_sweep(data),
|
||||||
0xFF11 => self.channels.one.update_length_timer_and_duty_cycle(data),
|
0xFF11 => self.channels.one.update_length_timer_and_duty_cycle(data),
|
||||||
0xFF12 => self.channels.one.update_volume_and_envelope(data),
|
0xFF12 => self.channels.one.update_volume_and_envelope(data),
|
||||||
|
@ -296,17 +298,19 @@ impl Apu {
|
||||||
0xFF26 => {
|
0xFF26 => {
|
||||||
if !self.apu_enable {
|
if !self.apu_enable {
|
||||||
for i in 0xFF10..0xFF20 {
|
for i in 0xFF10..0xFF20 {
|
||||||
self.mmio_write(i, 0x0);
|
self.mmio_write(i.try_into().unwrap(), 0x0);
|
||||||
}
|
}
|
||||||
for i in 0xFF21..0xFF25 {
|
for i in 0xFF21..0xFF25 {
|
||||||
self.mmio_write(i, 0x0);
|
self.mmio_write(i.try_into().unwrap(), 0x0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.apu_enable = (1 << 7) == (data & 0b10000000);
|
self.apu_enable = (1 << 7) == (data & 0b10000000);
|
||||||
}
|
}
|
||||||
0xFF30..0xFF40 => self.channels.three.update_wave_ram(addr, data),
|
_ => unreachable!(),
|
||||||
0xFF15 | 0xFF1F | 0xFF27..0xFF30 => {}
|
|
||||||
0x0..0xFF10 | 0xFF40..=0xFFFF => panic!("non-apu addr in apu"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn mmio_write_wave_ram(&mut self, addr: WaveRamAddress, data: u8) {
|
||||||
|
self.channels.three.update_wave_ram(addr, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
processor::memory::Address,
|
processor::memory::addresses::WaveRamAddress,
|
||||||
util::{get_bit, set_or_clear_bit, Nibbles},
|
util::{get_bit, set_or_clear_bit, Nibbles},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -461,8 +461,8 @@ impl WaveChannel {
|
||||||
set_or_clear_bit(0xFF, 6, self.length_enable)
|
set_or_clear_bit(0xFF, 6, self.length_enable)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn update_wave_ram(&mut self, addr: Address, data: u8) {
|
pub(super) fn update_wave_ram(&mut self, addr: WaveRamAddress, data: u8) {
|
||||||
let real_addr = (addr - 0xFF30) as usize;
|
let real_addr = addr.get_local() as usize;
|
||||||
if real_addr >= self.wave_ram.data.len() {
|
if real_addr >= self.wave_ram.data.len() {
|
||||||
panic!("sent the wrong address to update_wave_ram");
|
panic!("sent the wrong address to update_wave_ram");
|
||||||
}
|
}
|
||||||
|
|
|
@ -332,17 +332,17 @@ where
|
||||||
let mut objs = vec![];
|
let mut objs = vec![];
|
||||||
let effective_scanline = scanline + 16;
|
let effective_scanline = scanline + 16;
|
||||||
for i in (0xFE00..0xFE9F).step_by(4) {
|
for i in (0xFE00..0xFE9F).step_by(4) {
|
||||||
let y_pos = self.oam.get(i);
|
let y_pos = self.oam.get(i.try_into().unwrap());
|
||||||
if y_pos <= effective_scanline
|
if y_pos <= effective_scanline
|
||||||
&& (y_pos + self.lcdc.obj_size.get_height()) > effective_scanline
|
&& (y_pos + self.lcdc.obj_size.get_height()) > effective_scanline
|
||||||
{
|
{
|
||||||
// sprite is on line
|
// sprite is on line
|
||||||
let x_pos = self.oam.get(i + 1);
|
let x_pos = self.oam.get((i + 1).try_into().unwrap());
|
||||||
let mut tile_index = self.oam.get(i + 2);
|
let mut tile_index = self.oam.get((i + 2).try_into().unwrap());
|
||||||
if self.lcdc.obj_size == ObjSize::S8x16 {
|
if self.lcdc.obj_size == ObjSize::S8x16 {
|
||||||
tile_index = clear_bit(tile_index, 0);
|
tile_index = clear_bit(tile_index, 0);
|
||||||
}
|
}
|
||||||
let flags = self.oam.get(i + 3);
|
let flags = self.oam.get((i + 3).try_into().unwrap());
|
||||||
objs.push(Object {
|
objs.push(Object {
|
||||||
x: x_pos,
|
x: x_pos,
|
||||||
y: y_pos,
|
y: y_pos,
|
||||||
|
@ -380,11 +380,12 @@ where
|
||||||
object_row = self.lcdc.obj_size.get_height() - (object_row + 1);
|
object_row = self.lcdc.obj_size.get_height() - (object_row + 1);
|
||||||
}
|
}
|
||||||
let tile_row = object_row % 8;
|
let tile_row = object_row % 8;
|
||||||
let tile_addr = TiledataArea::D8000
|
let tile_addr = (TiledataArea::D8000
|
||||||
.get_addr(object.tile_index + if object_row >= 8 { 1 } else { 0 })
|
.get_addr(object.tile_index + if object_row >= 8 { 1 } else { 0 })
|
||||||
+ (tile_row as u16 * 2);
|
+ (tile_row as u16 * 2))
|
||||||
|
.unwrap();
|
||||||
let lsbs = self.vram.get(tile_addr);
|
let lsbs = self.vram.get(tile_addr);
|
||||||
let msbs = self.vram.get(tile_addr + 1);
|
let msbs = self.vram.get((tile_addr + 1).unwrap());
|
||||||
for px_x in 0..8 {
|
for px_x in 0..8 {
|
||||||
let x_addr = if object.flags.x_flip { px_x } else { 7 - px_x };
|
let x_addr = if object.flags.x_flip { px_x } else { 7 - px_x };
|
||||||
let lsb = get_bit(lsbs, x_addr);
|
let lsb = get_bit(lsbs, x_addr);
|
||||||
|
@ -437,14 +438,15 @@ where
|
||||||
let tilemap_column = (tile_line_x / 8) as u16;
|
let tilemap_column = (tile_line_x / 8) as u16;
|
||||||
|
|
||||||
let tile_px_x = tile_line_x % 8;
|
let tile_px_x = tile_line_x % 8;
|
||||||
let tile_addr = self
|
let tile_addr = (self
|
||||||
.lcdc
|
.lcdc
|
||||||
.tile_area
|
.tile_area
|
||||||
.get_addr(self.vram.get(tilemap.get_addr(row_addr + (tilemap_column))))
|
.get_addr(self.vram.get(tilemap.get_addr(row_addr + (tilemap_column))))
|
||||||
+ tiledata_offset as u16;
|
+ tiledata_offset as u16)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let lsbs = self.vram.get(tile_addr);
|
let lsbs = self.vram.get(tile_addr);
|
||||||
let msbs = self.vram.get(tile_addr + 1);
|
let msbs = self.vram.get((tile_addr + 1).unwrap());
|
||||||
let lsb = get_bit(lsbs, 7 - tile_px_x);
|
let lsb = get_bit(lsbs, 7 - tile_px_x);
|
||||||
let msb = get_bit(msbs, 7 - tile_px_x);
|
let msb = get_bit(msbs, 7 - tile_px_x);
|
||||||
let (colour, is_zero) = self.bg_palette.map_bits(lsb, msb);
|
let (colour, is_zero) = self.bg_palette.map_bits(lsb, msb);
|
||||||
|
|
|
@ -61,9 +61,9 @@ where
|
||||||
for tile_x in 0..16 {
|
for tile_x in 0..16 {
|
||||||
let tile_num = (tile_y * 16) + tile_x;
|
let tile_num = (tile_y * 16) + tile_x;
|
||||||
let data_begin = area.get_addr(tile_num);
|
let data_begin = area.get_addr(tile_num);
|
||||||
for px_y in 0..8 {
|
for px_y in 0..8_u16 {
|
||||||
let lsbs = memory.get((px_y * 2) + data_begin);
|
let lsbs = memory.get((data_begin + (px_y * 2)).unwrap());
|
||||||
let msbs = memory.get((px_y * 2) + data_begin + 1);
|
let msbs = memory.get((data_begin + (1 + (px_y * 2))).unwrap());
|
||||||
for px_x in 0..8 {
|
for px_x in 0..8 {
|
||||||
let real_px_y = (display_y * 8) + px_y as usize;
|
let real_px_y = (display_y * 8) + px_y as usize;
|
||||||
let real_px_x = (tile_x as usize * 8) + px_x as usize;
|
let real_px_x = (tile_x as usize * 8) + px_x as usize;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
processor::memory::Address,
|
processor::memory::addresses::{OamAddress, VramAddress},
|
||||||
util::{as_signed, get_bit},
|
util::{as_signed, get_bit},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -20,10 +20,10 @@ pub(super) enum TilemapArea {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TilemapArea {
|
impl TilemapArea {
|
||||||
pub(super) fn get_addr(&self, addr: u16) -> u16 {
|
pub(super) fn get_addr(&self, addr: u16) -> VramAddress {
|
||||||
match self {
|
match self {
|
||||||
TilemapArea::T9800 => 0x9800 + addr,
|
TilemapArea::T9800 => (0x9800 + addr).try_into().unwrap(),
|
||||||
TilemapArea::T9C00 => 0x9C00 + addr,
|
TilemapArea::T9C00 => (0x9C00 + addr).try_into().unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,10 +35,13 @@ pub(super) enum TiledataArea {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TiledataArea {
|
impl TiledataArea {
|
||||||
pub(super) fn get_addr(&self, addr: u8) -> u16 {
|
pub(super) fn get_addr(&self, addr: u8) -> VramAddress {
|
||||||
match self {
|
match self {
|
||||||
TiledataArea::D8000 => 0x8000 + ((addr as u16) * 16),
|
TiledataArea::D8000 => (0x8000 + ((addr as u16) * 16)).try_into().unwrap(),
|
||||||
TiledataArea::D9000 => 0x9000_u16.wrapping_add_signed((as_signed(addr) as i16) * 16),
|
TiledataArea::D9000 => 0x9000_u16
|
||||||
|
.wrapping_add_signed((as_signed(addr) as i16) * 16)
|
||||||
|
.try_into()
|
||||||
|
.unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,12 +227,12 @@ pub struct Vram {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vram {
|
impl Vram {
|
||||||
pub fn get(&self, address: Address) -> u8 {
|
pub(crate) fn get(&self, address: VramAddress) -> u8 {
|
||||||
self.data[(address - 0x8000) as usize]
|
self.data[address.get_local() as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(&mut self, address: Address, data: u8) {
|
pub(crate) fn set(&mut self, address: VramAddress, data: u8) {
|
||||||
self.data[(address - 0x8000) as usize] = data;
|
self.data[address.get_local() as usize] = data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,12 +250,12 @@ pub struct Oam {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Oam {
|
impl Oam {
|
||||||
pub fn get(&self, address: Address) -> u8 {
|
pub(crate) fn get(&self, address: OamAddress) -> u8 {
|
||||||
self.data[(address - 0xFE00) as usize]
|
self.data[address.get_local() as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(&mut self, address: Address, data: u8) {
|
pub(crate) fn set(&mut self, address: OamAddress, data: u8) {
|
||||||
self.data[(address - 0xFE00) as usize] = data;
|
self.data[address.get_local() as usize] = data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::connect::{CameraWrapperRef, PocketCamera as PocketCameraTrait};
|
||||||
connect::{CameraWrapperRef, PocketCamera as PocketCameraTrait},
|
|
||||||
processor::memory::Address,
|
|
||||||
};
|
|
||||||
use std::{
|
use std::{
|
||||||
fs::{File, OpenOptions},
|
fs::{File, OpenOptions},
|
||||||
io::{Read, Seek, SeekFrom, Write},
|
io::{Read, Seek, SeekFrom, Write},
|
||||||
|
@ -17,6 +14,8 @@ use self::mbcs::{
|
||||||
PocketCamera, PocketCameraSaveState,
|
PocketCamera, PocketCameraSaveState,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::addresses::{CartRamAddress, RomAddress};
|
||||||
|
|
||||||
mod mbcs;
|
mod mbcs;
|
||||||
|
|
||||||
struct MaybeBufferedSram {
|
struct MaybeBufferedSram {
|
||||||
|
@ -249,19 +248,19 @@ where
|
||||||
&self.title
|
&self.title
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn get(&self, address: Address) -> u8 {
|
pub(super) fn get(&self, address: RomAddress) -> u8 {
|
||||||
self.mbc.get(address)
|
self.mbc.get(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn get_ram(&self, address: Address) -> u8 {
|
pub(super) fn get_ram(&self, address: CartRamAddress) -> u8 {
|
||||||
self.mbc.get_ram(address)
|
self.mbc.get_ram(address)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn set(&mut self, address: Address, data: u8) {
|
pub(super) fn set(&mut self, address: RomAddress, data: u8) {
|
||||||
self.mbc.set(address, data);
|
self.mbc.set(address, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn set_ram(&mut self, address: Address, data: u8) {
|
pub(super) fn set_ram(&mut self, address: CartRamAddress, data: u8) {
|
||||||
self.mbc.set_ram(address, data);
|
self.mbc.set_ram(address, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::processor::memory::Address;
|
use crate::processor::memory::addresses::{CartRamAddress, RomAddress};
|
||||||
|
|
||||||
mod mbc1;
|
mod mbc1;
|
||||||
mod mbc2;
|
mod mbc2;
|
||||||
|
@ -21,11 +21,11 @@ const RAM_BANK_SIZE: usize = 8 * KB;
|
||||||
|
|
||||||
pub(super) trait Mbc: Send {
|
pub(super) trait Mbc: Send {
|
||||||
// addresses 0x0000 - 0x7FFF
|
// addresses 0x0000 - 0x7FFF
|
||||||
fn get(&self, address: Address) -> u8;
|
fn get(&self, address: RomAddress) -> u8;
|
||||||
// addresses 0xA000 - 0xBFFF
|
// addresses 0xA000 - 0xBFFF
|
||||||
fn get_ram(&self, address: Address) -> u8;
|
fn get_ram(&self, address: CartRamAddress) -> u8;
|
||||||
fn set(&mut self, address: Address, data: u8);
|
fn set(&mut self, address: RomAddress, data: u8);
|
||||||
fn set_ram(&mut self, address: Address, data: u8);
|
fn set_ram(&mut self, address: CartRamAddress, data: u8);
|
||||||
fn mbc_type(&self) -> String;
|
fn mbc_type(&self) -> String;
|
||||||
fn get_save_state(&self) -> MbcSaveState;
|
fn get_save_state(&self) -> MbcSaveState;
|
||||||
fn is_rumbling(&self) -> bool {
|
fn is_rumbling(&self) -> bool {
|
||||||
|
|
|
@ -4,8 +4,8 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE};
|
use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE};
|
||||||
use crate::processor::memory::{
|
use crate::processor::memory::{
|
||||||
|
addresses::{AddressMarker, CartRamAddress, RomAddress},
|
||||||
rom::{MaybeBufferedSram, MbcSaveState, SramSaveState},
|
rom::{MaybeBufferedSram, MbcSaveState, SramSaveState},
|
||||||
Address,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Serialize, Deserialize)]
|
||||||
|
@ -53,39 +53,32 @@ impl Mbc1 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_rom_addr(&self, address: Address) -> usize {
|
fn get_rom_addr(&self, address: RomAddress) -> usize {
|
||||||
(match address {
|
(match address {
|
||||||
0x0..0x4000 => match self.bank_mode {
|
RomAddress::Bank0(address) => match self.bank_mode {
|
||||||
BankingMode::Simple => address as usize,
|
BankingMode::Simple => address.inner() as usize,
|
||||||
BankingMode::Advanced => {
|
BankingMode::Advanced => {
|
||||||
(address as usize) + (self.upper_banks as usize * 512 * KB)
|
(address.inner() as usize) + (self.upper_banks as usize * 512 * KB)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
0x4000..0x8000 => {
|
RomAddress::MappedBank(address) => {
|
||||||
(address - 0x4000) as usize
|
(address.get_local() as usize)
|
||||||
+ (ROM_BANK_SIZE * self.rom_bank as usize)
|
+ (ROM_BANK_SIZE * self.rom_bank as usize)
|
||||||
+ (self.upper_banks as usize * 512 * KB)
|
+ (self.upper_banks as usize * 512 * KB)
|
||||||
}
|
}
|
||||||
|
|
||||||
0xA000..0xC000 => panic!("passed ram address to rom address function"),
|
|
||||||
_ => panic!("address {address} incompatible with MBC"),
|
|
||||||
} % self.rom_len)
|
} % self.rom_len)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_ram_addr(&self, address: Address) -> usize {
|
fn get_ram_addr(&self, address: CartRamAddress) -> usize {
|
||||||
match address {
|
match self.bank_mode {
|
||||||
0x0..0x8000 => panic!("passed rom address to ram address function"),
|
BankingMode::Simple => {
|
||||||
0xA000..0xC000 => match self.bank_mode {
|
(address.get_local() as usize) + (RAM_BANK_SIZE * self.ram_bank as usize)
|
||||||
BankingMode::Simple => {
|
}
|
||||||
(address - 0xA000) as usize + (RAM_BANK_SIZE * self.ram_bank as usize)
|
BankingMode::Advanced => {
|
||||||
}
|
(address.get_local() as usize)
|
||||||
BankingMode::Advanced => {
|
+ (RAM_BANK_SIZE * self.ram_bank as usize)
|
||||||
(address - 0xA000) as usize
|
+ (self.upper_banks as usize * 16 * KB)
|
||||||
+ (RAM_BANK_SIZE * self.ram_bank as usize)
|
}
|
||||||
+ (self.upper_banks as usize * 16 * KB)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => panic!("address {address} incompatible with MBC"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,11 +97,11 @@ impl Mbc1 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mbc for Mbc1 {
|
impl Mbc for Mbc1 {
|
||||||
fn get(&self, address: Address) -> u8 {
|
fn get(&self, address: RomAddress) -> u8 {
|
||||||
self.data[self.get_rom_addr(address)]
|
self.data[self.get_rom_addr(address)]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_ram(&self, address: Address) -> u8 {
|
fn get_ram(&self, address: CartRamAddress) -> u8 {
|
||||||
if self.ram_enabled && let Some(ram) = &self.ram {
|
if self.ram_enabled && let Some(ram) = &self.ram {
|
||||||
let addr = self.get_ram_addr(address) % ram.len();
|
let addr = self.get_ram_addr(address) % ram.len();
|
||||||
return ram.get(addr);
|
return ram.get(addr);
|
||||||
|
@ -116,7 +109,7 @@ impl Mbc for Mbc1 {
|
||||||
0xFF
|
0xFF
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_ram(&mut self, address: Address, data: u8) {
|
fn set_ram(&mut self, address: CartRamAddress, data: u8) {
|
||||||
let mut addr = self.get_ram_addr(address);
|
let mut addr = self.get_ram_addr(address);
|
||||||
if self.ram_enabled && let Some(ram) = &mut self.ram {
|
if self.ram_enabled && let Some(ram) = &mut self.ram {
|
||||||
addr %= ram.len();
|
addr %= ram.len();
|
||||||
|
@ -124,8 +117,8 @@ impl Mbc for Mbc1 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(&mut self, address: Address, data: u8) {
|
fn set(&mut self, address: RomAddress, data: u8) {
|
||||||
match address {
|
match address.inner() {
|
||||||
0x0..0x2000 => {
|
0x0..0x2000 => {
|
||||||
// enable/disable ram
|
// enable/disable ram
|
||||||
self.ram_enabled = (data & 0x0F) == 0xA;
|
self.ram_enabled = (data & 0x0F) == 0xA;
|
||||||
|
|
|
@ -3,8 +3,8 @@ use std::path::PathBuf;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::processor::memory::{
|
use crate::processor::memory::{
|
||||||
|
addresses::{AddressMarker, CartRamAddress, RomAddress},
|
||||||
rom::{MaybeBufferedSram, MbcSaveState, SramSaveState},
|
rom::{MaybeBufferedSram, MbcSaveState, SramSaveState},
|
||||||
Address,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{rom_banks, Mbc, ROM_BANK_SIZE};
|
use super::{rom_banks, Mbc, ROM_BANK_SIZE};
|
||||||
|
@ -51,28 +51,27 @@ impl Mbc2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mbc for Mbc2 {
|
impl Mbc for Mbc2 {
|
||||||
fn get(&self, address: Address) -> u8 {
|
fn get(&self, address: RomAddress) -> u8 {
|
||||||
match address {
|
match address {
|
||||||
0x0..0x4000 => self.data[address as usize],
|
RomAddress::Bank0(address) => self.data[address.inner() as usize],
|
||||||
0x4000..0x8000 => {
|
RomAddress::MappedBank(address) => {
|
||||||
self.data[((address as usize - 0x4000) + (0x4000 * self.rom_bank as usize))
|
self.data[((address.get_local() as usize) + (0x4000 * self.rom_bank as usize))
|
||||||
% self.rom_len]
|
% self.rom_len]
|
||||||
}
|
}
|
||||||
_ => panic!("passed wrong address to mbc"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_ram(&self, address: Address) -> u8 {
|
fn get_ram(&self, address: CartRamAddress) -> u8 {
|
||||||
if self.ram_enabled {
|
if self.ram_enabled {
|
||||||
0xF0 | (0x0F & self.ram.get((address - 0xA000) as usize % 512))
|
0xF0 | (0x0F & self.ram.get((address.get_local() as usize) % 512))
|
||||||
} else {
|
} else {
|
||||||
0xFF
|
0xFF
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(&mut self, address: Address, data: u8) {
|
fn set(&mut self, address: RomAddress, data: u8) {
|
||||||
if address < 0x4000 {
|
if let RomAddress::Bank0(_) = address {
|
||||||
if address & (1 << 8) == (1 << 8) {
|
if address.inner() & (1 << 8) == (1 << 8) {
|
||||||
// bit 8 is set - rom bank
|
// bit 8 is set - rom bank
|
||||||
self.rom_bank = data & 0xF;
|
self.rom_bank = data & 0xF;
|
||||||
if self.rom_bank == 0 {
|
if self.rom_bank == 0 {
|
||||||
|
@ -85,9 +84,9 @@ impl Mbc for Mbc2 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_ram(&mut self, address: Address, data: u8) {
|
fn set_ram(&mut self, address: CartRamAddress, data: u8) {
|
||||||
if self.ram_enabled {
|
if self.ram_enabled {
|
||||||
self.ram.set((address - 0xA000) as usize % 512, data);
|
self.ram.set((address.get_local() as usize) % 512, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,8 @@ use serde::{Deserialize, Serialize};
|
||||||
use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE};
|
use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE};
|
||||||
use crate::{
|
use crate::{
|
||||||
processor::memory::{
|
processor::memory::{
|
||||||
|
addresses::{AddressMarker, CartRamAddress, RomAddress},
|
||||||
rom::{MaybeBufferedSram, MbcSaveState, SramSaveState},
|
rom::{MaybeBufferedSram, MbcSaveState, SramSaveState},
|
||||||
Address,
|
|
||||||
},
|
},
|
||||||
util::set_or_clear_bit,
|
util::set_or_clear_bit,
|
||||||
};
|
};
|
||||||
|
@ -127,19 +127,18 @@ impl Mbc3 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_rom_addr(&self, address: Address) -> usize {
|
fn get_rom_addr(&self, address: RomAddress) -> usize {
|
||||||
(match address {
|
(match address {
|
||||||
0x0..0x4000 => address as usize,
|
RomAddress::Bank0(address) => address.inner() as usize,
|
||||||
0x4000..0x8000 => {
|
RomAddress::MappedBank(address) => {
|
||||||
let internal_addr = address as usize - 0x4000;
|
let internal_addr = address.get_local() as usize;
|
||||||
internal_addr + (ROM_BANK_SIZE * self.rom_bank as usize)
|
internal_addr + (ROM_BANK_SIZE * self.rom_bank as usize)
|
||||||
}
|
}
|
||||||
_ => panic!("address {address} incompatible with MBC"),
|
|
||||||
} % self.rom_size)
|
} % self.rom_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_ram_addr(&self, address: Address, ram_bank: usize) -> usize {
|
fn get_ram_addr(&self, address: CartRamAddress, ram_bank: usize) -> usize {
|
||||||
((address as usize - 0xA000) + (RAM_BANK_SIZE * ram_bank)) % self.ram_size
|
((address.get_local() as usize) + (RAM_BANK_SIZE * ram_bank)) % self.ram_size
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_save_state(state: Mbc3SaveState, data: Vec<u8>) -> Self {
|
pub fn from_save_state(state: Mbc3SaveState, data: Vec<u8>) -> Self {
|
||||||
|
@ -158,11 +157,11 @@ impl Mbc3 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mbc for Mbc3 {
|
impl Mbc for Mbc3 {
|
||||||
fn get(&self, address: Address) -> u8 {
|
fn get(&self, address: RomAddress) -> u8 {
|
||||||
self.data[self.get_rom_addr(address)]
|
self.data[self.get_rom_addr(address)]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_ram(&self, address: Address) -> u8 {
|
fn get_ram(&self, address: CartRamAddress) -> u8 {
|
||||||
if self.ram_enabled {
|
if self.ram_enabled {
|
||||||
match &self.ram_bank {
|
match &self.ram_bank {
|
||||||
RamBank::Ram(ram_bank) => {
|
RamBank::Ram(ram_bank) => {
|
||||||
|
@ -180,8 +179,8 @@ impl Mbc for Mbc3 {
|
||||||
0xFF
|
0xFF
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(&mut self, address: Address, data: u8) {
|
fn set(&mut self, address: RomAddress, data: u8) {
|
||||||
match address {
|
match address.inner() {
|
||||||
0x0..0x2000 => {
|
0x0..0x2000 => {
|
||||||
if data & 0xF == 0xA {
|
if data & 0xF == 0xA {
|
||||||
self.ram_enabled = true;
|
self.ram_enabled = true;
|
||||||
|
@ -218,12 +217,11 @@ impl Mbc for Mbc3 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => panic!("unsupported addr"),
|
_ => panic!("unsupported addr"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_ram(&mut self, address: Address, data: u8) {
|
fn set_ram(&mut self, address: CartRamAddress, data: u8) {
|
||||||
if self.ram_enabled {
|
if self.ram_enabled {
|
||||||
match &self.ram_bank {
|
match &self.ram_bank {
|
||||||
RamBank::Ram(ram_bank) => {
|
RamBank::Ram(ram_bank) => {
|
||||||
|
|
|
@ -4,8 +4,8 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
processor::memory::{
|
processor::memory::{
|
||||||
|
addresses::{AddressMarker, CartRamAddress, RomAddress},
|
||||||
rom::{MaybeBufferedSram, MbcSaveState, SramSaveState},
|
rom::{MaybeBufferedSram, MbcSaveState, SramSaveState},
|
||||||
Address,
|
|
||||||
},
|
},
|
||||||
util::get_bit,
|
util::get_bit,
|
||||||
};
|
};
|
||||||
|
@ -58,19 +58,18 @@ impl Mbc5 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_rom_addr(&self, address: Address) -> usize {
|
fn get_rom_addr(&self, address: RomAddress) -> usize {
|
||||||
(match address {
|
(match address {
|
||||||
0x0..0x4000 => address as usize,
|
RomAddress::Bank0(address) => address.inner() as usize,
|
||||||
0x4000..0x8000 => {
|
RomAddress::MappedBank(address) => {
|
||||||
let internal_addr = address as usize - 0x4000;
|
let internal_addr = address.get_local() as usize;
|
||||||
internal_addr + (ROM_BANK_SIZE * self.rom_bank as usize)
|
internal_addr + (ROM_BANK_SIZE * self.rom_bank as usize)
|
||||||
}
|
}
|
||||||
_ => panic!("address {address} incompatible with MBC"),
|
|
||||||
} % self.rom_size)
|
} % self.rom_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_ram_addr(&self, address: Address) -> usize {
|
fn get_ram_addr(&self, address: CartRamAddress) -> usize {
|
||||||
((address as usize - 0xA000) + (RAM_BANK_SIZE * self.ram_bank as usize)) % self.ram_size
|
((address.get_local() as usize) + (RAM_BANK_SIZE * self.ram_bank as usize)) % self.ram_size
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_save_state(state: Mbc5SaveState, data: Vec<u8>) -> Self {
|
pub fn from_save_state(state: Mbc5SaveState, data: Vec<u8>) -> Self {
|
||||||
|
@ -89,11 +88,11 @@ impl Mbc5 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mbc for Mbc5 {
|
impl Mbc for Mbc5 {
|
||||||
fn get(&self, address: Address) -> u8 {
|
fn get(&self, address: RomAddress) -> u8 {
|
||||||
self.data[self.get_rom_addr(address)]
|
self.data[self.get_rom_addr(address)]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_ram(&self, address: Address) -> u8 {
|
fn get_ram(&self, address: CartRamAddress) -> u8 {
|
||||||
if self.ram_enabled && let Some(ram) = &self.ram {
|
if self.ram_enabled && let Some(ram) = &self.ram {
|
||||||
ram.get(self.get_ram_addr(address))
|
ram.get(self.get_ram_addr(address))
|
||||||
} else {
|
} else {
|
||||||
|
@ -101,8 +100,8 @@ impl Mbc for Mbc5 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(&mut self, address: Address, data: u8) {
|
fn set(&mut self, address: RomAddress, data: u8) {
|
||||||
match address {
|
match address.inner() {
|
||||||
0x0..0x2000 => {
|
0x0..0x2000 => {
|
||||||
if (data & 0xF) == 0xA {
|
if (data & 0xF) == 0xA {
|
||||||
self.ram_enabled = true
|
self.ram_enabled = true
|
||||||
|
@ -121,11 +120,11 @@ impl Mbc for Mbc5 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0x6000..0x8000 => {}
|
0x6000..0x8000 => {}
|
||||||
_ => panic!("address {address} incompatible with MBC"),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_ram(&mut self, address: Address, data: u8) {
|
fn set_ram(&mut self, address: CartRamAddress, data: u8) {
|
||||||
let real_addr = self.get_ram_addr(address);
|
let real_addr = self.get_ram_addr(address);
|
||||||
if self.ram_enabled && let Some(ram) = &mut self.ram {
|
if self.ram_enabled && let Some(ram) = &mut self.ram {
|
||||||
ram.set(real_addr, data);
|
ram.set(real_addr, data);
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use super::Mbc;
|
use super::Mbc;
|
||||||
use crate::processor::memory::{rom::MbcSaveState, Address};
|
use crate::processor::memory::{
|
||||||
|
addresses::{AddressMarker, CartRamAddress, RomAddress},
|
||||||
|
rom::MbcSaveState,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct None {
|
pub struct None {
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
|
@ -12,17 +15,17 @@ impl None {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mbc for None {
|
impl Mbc for None {
|
||||||
fn get(&self, address: Address) -> u8 {
|
fn get(&self, address: RomAddress) -> u8 {
|
||||||
self.data[address as usize]
|
self.data[address.inner() as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_ram(&self, _address: Address) -> u8 {
|
fn get_ram(&self, _address: CartRamAddress) -> u8 {
|
||||||
0xFF
|
0xFF
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_ram(&mut self, _address: Address, _data: u8) {}
|
fn set_ram(&mut self, _address: CartRamAddress, _data: u8) {}
|
||||||
|
|
||||||
fn set(&mut self, _address: Address, _data: u8) {}
|
fn set(&mut self, _address: RomAddress, _data: u8) {}
|
||||||
|
|
||||||
fn mbc_type(&self) -> String {
|
fn mbc_type(&self) -> String {
|
||||||
String::from("None")
|
String::from("None")
|
||||||
|
|
|
@ -2,8 +2,8 @@ use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE};
|
||||||
use crate::{
|
use crate::{
|
||||||
connect::{CameraWrapperRef, PocketCamera as PocketCameraTrait},
|
connect::{CameraWrapperRef, PocketCamera as PocketCameraTrait},
|
||||||
processor::memory::{
|
processor::memory::{
|
||||||
|
addresses::{AddressMarker, CartRamAddress, RomAddress},
|
||||||
rom::{MaybeBufferedSram, MbcSaveState},
|
rom::{MaybeBufferedSram, MbcSaveState},
|
||||||
Address,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -60,19 +60,18 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_rom_addr(&self, address: Address) -> usize {
|
fn get_rom_addr(&self, address: RomAddress) -> usize {
|
||||||
(match address {
|
(match address {
|
||||||
0x0..0x4000 => address as usize,
|
RomAddress::Bank0(address) => address.inner() as usize,
|
||||||
0x4000..0x8000 => {
|
RomAddress::MappedBank(address) => {
|
||||||
let internal_addr = address as usize - 0x4000;
|
let internal_addr = address.get_local() as usize;
|
||||||
internal_addr + (ROM_BANK_SIZE * self.rom_bank as usize)
|
internal_addr + (ROM_BANK_SIZE * self.rom_bank as usize)
|
||||||
}
|
}
|
||||||
_ => panic!("address {address} incompatible with MBC"),
|
|
||||||
} % self.rom_size)
|
} % self.rom_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_ram_addr(&self, address: Address, bank: u8) -> usize {
|
fn get_ram_addr(&self, address: CartRamAddress, bank: u8) -> usize {
|
||||||
((address as usize - 0xA000) + (RAM_BANK_SIZE * bank as usize)) % self.ram_size
|
((address.get_local() as usize) + (RAM_BANK_SIZE * bank as usize)) % self.ram_size
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_save_state(
|
pub(crate) fn from_save_state(
|
||||||
|
@ -83,8 +82,8 @@ where
|
||||||
todo!();
|
todo!();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_cam_reg(&self, address: Address) -> u8 {
|
fn get_cam_reg(&self, address: CartRamAddress) -> u8 {
|
||||||
match address {
|
match address.inner() {
|
||||||
0xA000 => {
|
0xA000 => {
|
||||||
(if self.camera.lock().unwrap().is_capturing() {
|
(if self.camera.lock().unwrap().is_capturing() {
|
||||||
0x1
|
0x1
|
||||||
|
@ -92,13 +91,13 @@ where
|
||||||
0x0
|
0x0
|
||||||
}) | self.extra_bits_a000
|
}) | self.extra_bits_a000
|
||||||
}
|
}
|
||||||
0xA001..=0xA035 => self.camera_ram[(address - 0xA001) as usize],
|
0xA001..=0xA035 => self.camera_ram[(address.inner() - 0xA001) as usize],
|
||||||
_ => 0x00,
|
_ => 0x00,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_cam_reg(&mut self, address: Address, data: u8) {
|
fn set_cam_reg(&mut self, address: CartRamAddress, data: u8) {
|
||||||
match address {
|
match address.inner() {
|
||||||
0xA000 => {
|
0xA000 => {
|
||||||
if data & 0x1 == 0x1 {
|
if data & 0x1 == 0x1 {
|
||||||
self.camera.lock().unwrap().begin_capture();
|
self.camera.lock().unwrap().begin_capture();
|
||||||
|
@ -106,7 +105,7 @@ where
|
||||||
self.extra_bits_a000 = data & 0b110;
|
self.extra_bits_a000 = data & 0b110;
|
||||||
}
|
}
|
||||||
0xA001..=0xA035 => {
|
0xA001..=0xA035 => {
|
||||||
self.camera_ram[(address - 0xA001) as usize] = data;
|
self.camera_ram[(address.inner() - 0xA001) as usize] = data;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -127,11 +126,11 @@ impl<C> Mbc for PocketCamera<C>
|
||||||
where
|
where
|
||||||
C: PocketCameraTrait + Send,
|
C: PocketCameraTrait + Send,
|
||||||
{
|
{
|
||||||
fn get(&self, address: Address) -> u8 {
|
fn get(&self, address: RomAddress) -> u8 {
|
||||||
self.data[self.get_rom_addr(address)]
|
self.data[self.get_rom_addr(address)]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_ram(&self, address: Address) -> u8 {
|
fn get_ram(&self, address: CartRamAddress) -> u8 {
|
||||||
match self.ram_bank {
|
match self.ram_bank {
|
||||||
RamBank::Ram(bank) => {
|
RamBank::Ram(bank) => {
|
||||||
if let Some(ram) = &self.ram {
|
if let Some(ram) = &self.ram {
|
||||||
|
@ -144,9 +143,9 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set(&mut self, address: Address, data: u8) {
|
fn set(&mut self, address: RomAddress, data: u8) {
|
||||||
self.check_for_new_image();
|
self.check_for_new_image();
|
||||||
match address {
|
match address.inner() {
|
||||||
0x0..0x2000 => {
|
0x0..0x2000 => {
|
||||||
if (data & 0xF) == 0xA {
|
if (data & 0xF) == 0xA {
|
||||||
self.ram_enabled = true
|
self.ram_enabled = true
|
||||||
|
@ -167,11 +166,11 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0x6000..0x8000 => {}
|
0x6000..0x8000 => {}
|
||||||
_ => panic!("address {address} incompatible with MBC"),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_ram(&mut self, address: Address, data: u8) {
|
fn set_ram(&mut self, address: CartRamAddress, data: u8) {
|
||||||
self.check_for_new_image();
|
self.check_for_new_image();
|
||||||
match self.ram_bank {
|
match self.ram_bank {
|
||||||
RamBank::Ram(bank) => {
|
RamBank::Ram(bank) => {
|
||||||
|
|
Loading…
Reference in a new issue