2023-04-18 18:09:21 +10:00
|
|
|
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>;
|
|
|
|
|
2023-04-20 16:45:45 +10:00
|
|
|
pub(crate) type VramDmaAddress = BoundedAddress<0xFF51, 0xFF56>;
|
|
|
|
pub(crate) type CgbPaletteAddress = BoundedAddress<0xFF68, 0xFF6C>;
|
|
|
|
|
2023-04-18 18:09:21 +10:00
|
|
|
pub(crate) enum RomAddress {
|
|
|
|
Bank0(Bank0Address),
|
|
|
|
MappedBank(MappedBankAddress),
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) enum IoAddress {
|
|
|
|
Joypad,
|
|
|
|
Serial(SerialAddress),
|
|
|
|
Timer(TimerAddress),
|
|
|
|
InterruptFlag,
|
|
|
|
Audio(AudioAddress),
|
|
|
|
WaveRam(WaveRamAddress),
|
|
|
|
Video(VideoAddress),
|
2023-04-20 12:16:04 +10:00
|
|
|
Cgb(CgbIoAddress),
|
2023-04-18 18:09:21 +10:00
|
|
|
Unused(u16),
|
|
|
|
}
|
|
|
|
|
2023-04-20 14:53:22 +10:00
|
|
|
pub(crate) enum CgbIoAddress {
|
2023-04-20 16:45:45 +10:00
|
|
|
PrepareSpeed,
|
|
|
|
VramBank,
|
|
|
|
VramDma(VramDmaAddress),
|
|
|
|
Infrared,
|
|
|
|
Palette(CgbPaletteAddress),
|
|
|
|
ObjPriority,
|
2023-04-20 14:53:22 +10:00
|
|
|
WramBank,
|
2023-04-20 16:45:45 +10:00
|
|
|
Pcm12,
|
|
|
|
Pcm34,
|
2023-04-20 14:53:22 +10:00
|
|
|
Unused(u16),
|
|
|
|
}
|
2023-04-20 12:16:04 +10:00
|
|
|
|
2023-04-18 18:09:21 +10:00
|
|
|
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 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(()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-20 12:16:04 +10:00
|
|
|
impl From<Address> for u16 {
|
|
|
|
fn from(value: Address) -> Self {
|
|
|
|
value.inner()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-18 18:09:21 +10:00
|
|
|
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())),
|
2023-04-20 12:16:04 +10:00
|
|
|
0xFF4D..0xFF78 => Ok(IoAddress::Cgb(self.try_into().unwrap())),
|
2023-04-20 14:53:22 +10:00
|
|
|
0x0..0xFF00 | 0xFFFF => Err(AddressError::OutOfBounds),
|
2023-04-18 18:09:21 +10:00
|
|
|
_ => Ok(IoAddress::Unused(self)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-20 12:16:04 +10:00
|
|
|
impl TryInto<CgbIoAddress> for u16 {
|
|
|
|
type Error = AddressError;
|
|
|
|
|
|
|
|
fn try_into(self) -> Result<CgbIoAddress, Self::Error> {
|
2023-04-20 14:53:22 +10:00
|
|
|
match self {
|
2023-04-20 16:45:45 +10:00
|
|
|
0xFF4D => Ok(CgbIoAddress::PrepareSpeed),
|
|
|
|
0xFF4F => Ok(CgbIoAddress::VramBank),
|
|
|
|
0xFF51..0xFF56 => Ok(CgbIoAddress::VramDma(self.try_into().unwrap())),
|
|
|
|
0xFF56 => Ok(CgbIoAddress::Infrared),
|
|
|
|
0xFF68..0xFF6C => Ok(CgbIoAddress::Palette(self.try_into().unwrap())),
|
|
|
|
0xFF6C => Ok(CgbIoAddress::ObjPriority),
|
2023-04-20 14:53:22 +10:00
|
|
|
0xFF70 => Ok(CgbIoAddress::WramBank),
|
2023-04-20 16:45:45 +10:00
|
|
|
0xFF76 => Ok(CgbIoAddress::Pcm12),
|
|
|
|
0xFF77 => Ok(CgbIoAddress::Pcm34),
|
2023-04-20 14:53:22 +10:00
|
|
|
0x0..0xFF4D | 0xFF78..=0xFFFF => Err(AddressError::OutOfBounds),
|
|
|
|
_ => Ok(CgbIoAddress::Unused(self)),
|
|
|
|
}
|
2023-04-20 12:16:04 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AddressMarker for RomAddress {
|
|
|
|
fn inner(&self) -> u16 {
|
|
|
|
match self {
|
|
|
|
RomAddress::Bank0(v) => v.inner(),
|
|
|
|
RomAddress::MappedBank(v) => v.inner(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-18 18:09:21 +10:00
|
|
|
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,
|
2023-04-20 12:16:04 +10:00
|
|
|
IoAddress::Cgb(v) => v.inner(),
|
2023-04-18 18:09:21 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-20 12:16:04 +10:00
|
|
|
impl AddressMarker for CgbIoAddress {
|
|
|
|
fn inner(&self) -> u16 {
|
2023-04-20 14:53:22 +10:00
|
|
|
match self {
|
2023-04-20 16:45:45 +10:00
|
|
|
CgbIoAddress::PrepareSpeed => 0xFF4D,
|
|
|
|
CgbIoAddress::VramBank => 0xFF4F,
|
|
|
|
CgbIoAddress::VramDma(v) => v.inner(),
|
|
|
|
CgbIoAddress::Infrared => 0xFF56,
|
|
|
|
CgbIoAddress::Palette(v) => v.inner(),
|
|
|
|
CgbIoAddress::ObjPriority => 0xFF6C,
|
2023-04-20 14:53:22 +10:00
|
|
|
CgbIoAddress::WramBank => 0xFF70,
|
2023-04-20 16:45:45 +10:00
|
|
|
CgbIoAddress::Pcm12 => 0xFF76,
|
|
|
|
CgbIoAddress::Pcm34 => 0xFF77,
|
|
|
|
CgbIoAddress::Unused(v) => *v,
|
2023-04-20 14:53:22 +10:00
|
|
|
}
|
2023-04-20 12:16:04 +10:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-18 18:09:21 +10:00
|
|
|
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 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()
|
|
|
|
}
|
|
|
|
}
|