address handling

This commit is contained in:
Alex Janka 2023-04-18 18:09:21 +10:00
parent 7448d18424
commit 828da3e01f
17 changed files with 506 additions and 259 deletions

View file

@ -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 connect::{

View file

@ -1,5 +1,6 @@
pub use self::rom::Rom;
use self::{
addresses::{Address, AddressMarker, IoAddress},
mmio::{
apu::ApuSaveState,
gpu::{Colour, GpuSaveState},
@ -11,17 +12,16 @@ use self::{
use crate::{
connect::{AudioOutput, CameraWrapperRef, JoypadState, PocketCamera, Renderer, SerialTarget},
processor::SplitRegister,
verbose_println, Cpu,
Cpu,
};
mod interrupts;
pub use interrupts::{Interrupt, Interrupts};
use serde::{Deserialize, Serialize};
pub(crate) mod addresses;
pub mod mmio;
pub(crate) mod rom;
pub(crate) type Address = u16;
pub struct Memory<ColourFormat, R, C>
where
ColourFormat: From<Colour> + Clone,
@ -124,73 +124,86 @@ 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 {
0x0..0x8000 => {
Address::Rom(address) => {
// rom access
// todo - switchable rom banks
if let Some(bootrom) = &self.bootrom && (address as usize) < bootrom.len() {
bootrom[address as usize]
if let Some(bootrom) = &self.bootrom && (address.inner() as usize) < bootrom.len() {
bootrom[address.inner() as usize]
} else {
self.rom.get(address)
}
}
0x8000..0xA000 => self.gpu.vram.get(address),
0xA000..0xC000 => {
// cart ram
self.rom.get_ram(address)
}
0xC000..0xE000 => self.ram[(address - 0xC000) as usize],
0xE000..0xFE00 => self.ram[(address - 0xE000) as usize],
0xFE00..0xFEA0 => self.gpu.oam.get(address),
0xFEA0..0xFF00 => 0xFF,
0xFF00..0xFF4C => self.get_io(address),
0xFF4C..0xFF80 => 0xFF,
0xFF80..0xFFFF => self.cpu_ram[(address - 0xFF80) as usize],
0xFFFF => self.interrupts.get_enable_register(),
Address::Vram(address) => self.gpu.vram.get(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::Oam(address) => self.gpu.oam.get(address),
Address::Prohibited(_) => 0xFF,
Address::Io(address) => self.get_io(address),
Address::Hram(address) => self.cpu_ram[address.get_local() as usize],
Address::InterruptEnable(_) => 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 {
0x0..0x8000 => {
// change this with MBC code...
Address::Rom(address) => {
self.rom.set(address, data);
if self.rom.can_rumble() {
// rumble
self.gpu.window.set_rumble(self.rom.is_rumbling())
}
}
0x8000..0xA000 => self.gpu.vram.set(address, data),
0xA000..0xC000 => self.rom.set_ram(address, data),
0xC000..0xE000 => self.ram[(address - 0xC000) as usize] = data,
0xE000..0xFE00 => self.ram[(address - 0xE000) as usize] = data,
0xFE00..0xFEA0 => self.gpu.oam.set(address, 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::Vram(address) => self.gpu.vram.set(address, data),
Address::CartRam(address) => self.rom.set_ram(address, data),
Address::WorkRam(address) => self.ram[address.get_local() as usize] = data,
Address::BankedWorkRam(address) => {
self.ram[(address.get_local() + 0x1000) as usize] = 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 {
// range: 0xFF00 - 0xFF4B inclusive
fn get_io(&self, address: IoAddress) -> u8 {
match address {
0xFF00 => self.joypad.as_register(),
IoAddress::Joypad => self.joypad.as_register(),
IoAddress::Serial(address) => match address.inner() {
0xFF01 => self.serial.get_queued(),
0xFF02 => self.serial.get_control(),
_ => unreachable!(),
},
IoAddress::Timer(address) => match address.inner() {
0xFF04 => self.timers.get_div(),
0xFF05 => self.timers.get_tima(),
0xFF06 => self.timers.get_tma(),
0xFF07 => self.timers.get_timer_control(),
0xFF0F => self.interrupts.get_flag_register(),
0xFF10..0xFF40 => self.apu.get_register(address),
_ => unreachable!(),
},
IoAddress::InterruptFlag => self.interrupts.get_flag_register(),
IoAddress::Audio(address) => self.apu.get_register(address),
IoAddress::WaveRam(address) => self.apu.get_wave_ram_register(address),
IoAddress::Video(address) => match address.inner() {
0xFF40 => self.gpu.get_lcdc(),
0xFF41 => self.gpu.get_lcd_status(),
0xFF42 => self.gpu.get_scy(),
@ -203,26 +216,31 @@ where
0xFF49 => self.gpu.get_obj_palette_1(),
0xFF4A => self.gpu.get_wy(),
0xFF4B => self.gpu.get_wx(),
0xFF03 | 0xFF08..0xFF0F => 0xFF,
0x0..0xFF00 | 0xFF4C..=0xFFFF => panic!("passed wrong address to get_io"),
_ => unreachable!(),
},
IoAddress::Unused(_) => 0xFF,
}
}
fn set_io(&mut self, address: Address, data: u8) {
// range: 0xFF00 - 0xFF4B inclusive
fn set_io(&mut self, address: IoAddress, data: u8) {
match address {
0xFF00 => {
// joypad
self.joypad.mmio_write(data);
}
IoAddress::Joypad => self.joypad.mmio_write(data),
IoAddress::Serial(address) => match address.inner() {
0xFF01 => self.serial.update_queued(data),
0xFF02 => self.serial.update_control(data),
_ => unreachable!(),
},
IoAddress::Timer(address) => match address.inner() {
0xFF04 => self.timers.update_div(),
0xFF05 => self.timers.update_tima(data),
0xFF06 => self.timers.update_tma(data),
0xFF07 => self.timers.update_timer_control(data),
0xFF0F => self.interrupts.set_flag_register(data),
0xFF10..0xFF40 => self.apu.mmio_write(address, data),
_ => unreachable!(),
},
IoAddress::InterruptFlag => self.interrupts.set_flag_register(data),
IoAddress::Audio(address) => self.apu.mmio_write(address, data),
IoAddress::WaveRam(address) => self.apu.mmio_write_wave_ram(address, data),
IoAddress::Video(address) => match address.inner() {
0xFF40 => self.gpu.update_lcdc(data),
0xFF41 => self.gpu.update_lcd_status(data),
0xFF42 => self.gpu.update_scy(data),
@ -245,11 +263,9 @@ where
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"),
_ => unreachable!(),
},
IoAddress::Unused(_) => {}
}
}

View 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()
}
}

View 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
}
}

View file

@ -4,7 +4,7 @@ use self::{
};
use crate::{
connect::AudioOutput,
processor::memory::Address,
processor::memory::addresses::{AddressMarker, AudioAddress, WaveRamAddress},
util::{get_bit, set_or_clear_bit},
};
use futures::executor;
@ -185,11 +185,11 @@ impl Apu {
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 {
self.make_register(addr)
} else {
match addr {
match addr.inner() {
0xFF26 | 0xFF11 | 0xFF16 | 0xFF1B | 0xFF20 | 0xFF30..0xFF40 => {
self.make_register(addr)
}
@ -198,8 +198,12 @@ impl Apu {
}
}
fn make_register(&self, addr: Address) -> u8 {
match addr {
pub(crate) fn get_wave_ram_register(&self, addr: WaveRamAddress) -> u8 {
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(),
0xFF11 => self.channels.one.get_length_timer_and_duty_cycle(),
0xFF12 => self.channels.one.get_volume_and_envelope(),
@ -250,15 +254,13 @@ impl Apu {
// write-only registers
0xFF13 | 0xFF18 | 0xFF1B | 0xFF1D | 0xFF20 => 0xFF,
// not registers
0xFF15 | 0xFF1F | 0xFF27..0xFF30 => 0xFF,
// wave ram
0xFF30..0xFF40 => self.channels.three.wave_ram.data[(addr - 0xFF30) as usize],
0x0..0xFF10 | 0xFF40..=0xFFFF => panic!("non-apu addr in apu"),
0xFF15 | 0xFF1F => 0xFF,
_ => unreachable!(),
}
}
pub fn mmio_write(&mut self, addr: Address, data: u8) {
match addr {
pub(crate) fn mmio_write(&mut self, addr: AudioAddress, data: u8) {
match addr.inner() {
0xFF10 => self.channels.one.update_sweep(data),
0xFF11 => self.channels.one.update_length_timer_and_duty_cycle(data),
0xFF12 => self.channels.one.update_volume_and_envelope(data),
@ -296,17 +298,19 @@ impl Apu {
0xFF26 => {
if !self.apu_enable {
for i in 0xFF10..0xFF20 {
self.mmio_write(i, 0x0);
self.mmio_write(i.try_into().unwrap(), 0x0);
}
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);
}
0xFF30..0xFF40 => self.channels.three.update_wave_ram(addr, data),
0xFF15 | 0xFF1F | 0xFF27..0xFF30 => {}
0x0..0xFF10 | 0xFF40..=0xFFFF => panic!("non-apu addr in apu"),
_ => unreachable!(),
}
}
pub(crate) fn mmio_write_wave_ram(&mut self, addr: WaveRamAddress, data: u8) {
self.channels.three.update_wave_ram(addr, data);
}
}

View file

@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use crate::{
processor::memory::Address,
processor::memory::addresses::WaveRamAddress,
util::{get_bit, set_or_clear_bit, Nibbles},
};
@ -461,8 +461,8 @@ impl WaveChannel {
set_or_clear_bit(0xFF, 6, self.length_enable)
}
pub(super) fn update_wave_ram(&mut self, addr: Address, data: u8) {
let real_addr = (addr - 0xFF30) as usize;
pub(super) fn update_wave_ram(&mut self, addr: WaveRamAddress, data: u8) {
let real_addr = addr.get_local() as usize;
if real_addr >= self.wave_ram.data.len() {
panic!("sent the wrong address to update_wave_ram");
}

View file

@ -332,17 +332,17 @@ where
let mut objs = vec![];
let effective_scanline = scanline + 16;
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
&& (y_pos + self.lcdc.obj_size.get_height()) > effective_scanline
{
// sprite is on line
let x_pos = self.oam.get(i + 1);
let mut tile_index = self.oam.get(i + 2);
let x_pos = self.oam.get((i + 1).try_into().unwrap());
let mut tile_index = self.oam.get((i + 2).try_into().unwrap());
if self.lcdc.obj_size == ObjSize::S8x16 {
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 {
x: x_pos,
y: y_pos,
@ -380,11 +380,12 @@ where
object_row = self.lcdc.obj_size.get_height() - (object_row + 1);
}
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 })
+ (tile_row as u16 * 2);
+ (tile_row as u16 * 2))
.unwrap();
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 {
let x_addr = if object.flags.x_flip { px_x } else { 7 - px_x };
let lsb = get_bit(lsbs, x_addr);
@ -437,14 +438,15 @@ where
let tilemap_column = (tile_line_x / 8) as u16;
let tile_px_x = tile_line_x % 8;
let tile_addr = self
let tile_addr = (self
.lcdc
.tile_area
.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 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 msb = get_bit(msbs, 7 - tile_px_x);
let (colour, is_zero) = self.bg_palette.map_bits(lsb, msb);

View file

@ -61,9 +61,9 @@ where
for tile_x in 0..16 {
let tile_num = (tile_y * 16) + tile_x;
let data_begin = area.get_addr(tile_num);
for px_y in 0..8 {
let lsbs = memory.get((px_y * 2) + data_begin);
let msbs = memory.get((px_y * 2) + data_begin + 1);
for px_y in 0..8_u16 {
let lsbs = memory.get((data_begin + (px_y * 2)).unwrap());
let msbs = memory.get((data_begin + (1 + (px_y * 2))).unwrap());
for px_x in 0..8 {
let real_px_y = (display_y * 8) + px_y as usize;
let real_px_x = (tile_x as usize * 8) + px_x as usize;

View file

@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use crate::{
processor::memory::Address,
processor::memory::addresses::{OamAddress, VramAddress},
util::{as_signed, get_bit},
};
@ -20,10 +20,10 @@ pub(super) enum TilemapArea {
}
impl TilemapArea {
pub(super) fn get_addr(&self, addr: u16) -> u16 {
pub(super) fn get_addr(&self, addr: u16) -> VramAddress {
match self {
TilemapArea::T9800 => 0x9800 + addr,
TilemapArea::T9C00 => 0x9C00 + addr,
TilemapArea::T9800 => (0x9800 + addr).try_into().unwrap(),
TilemapArea::T9C00 => (0x9C00 + addr).try_into().unwrap(),
}
}
}
@ -35,10 +35,13 @@ pub(super) enum TiledataArea {
}
impl TiledataArea {
pub(super) fn get_addr(&self, addr: u8) -> u16 {
pub(super) fn get_addr(&self, addr: u8) -> VramAddress {
match self {
TiledataArea::D8000 => 0x8000 + ((addr as u16) * 16),
TiledataArea::D9000 => 0x9000_u16.wrapping_add_signed((as_signed(addr) as i16) * 16),
TiledataArea::D8000 => (0x8000 + ((addr as u16) * 16)).try_into().unwrap(),
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 {
pub fn get(&self, address: Address) -> u8 {
self.data[(address - 0x8000) as usize]
pub(crate) fn get(&self, address: VramAddress) -> u8 {
self.data[address.get_local() as usize]
}
pub fn set(&mut self, address: Address, data: u8) {
self.data[(address - 0x8000) as usize] = data;
pub(crate) fn set(&mut self, address: VramAddress, data: u8) {
self.data[address.get_local() as usize] = data;
}
}
@ -247,12 +250,12 @@ pub struct Oam {
}
impl Oam {
pub fn get(&self, address: Address) -> u8 {
self.data[(address - 0xFE00) as usize]
pub(crate) fn get(&self, address: OamAddress) -> u8 {
self.data[address.get_local() as usize]
}
pub fn set(&mut self, address: Address, data: u8) {
self.data[(address - 0xFE00) as usize] = data;
pub(crate) fn set(&mut self, address: OamAddress, data: u8) {
self.data[address.get_local() as usize] = data;
}
}

View file

@ -1,9 +1,6 @@
use serde::{Deserialize, Serialize};
use crate::{
connect::{CameraWrapperRef, PocketCamera as PocketCameraTrait},
processor::memory::Address,
};
use crate::connect::{CameraWrapperRef, PocketCamera as PocketCameraTrait};
use std::{
fs::{File, OpenOptions},
io::{Read, Seek, SeekFrom, Write},
@ -17,6 +14,8 @@ use self::mbcs::{
PocketCamera, PocketCameraSaveState,
};
use super::addresses::{CartRamAddress, RomAddress};
mod mbcs;
struct MaybeBufferedSram {
@ -249,19 +248,19 @@ where
&self.title
}
pub(super) fn get(&self, address: Address) -> u8 {
pub(super) fn get(&self, address: RomAddress) -> u8 {
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)
}
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);
}
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);
}

View file

@ -1,4 +1,4 @@
use crate::processor::memory::Address;
use crate::processor::memory::addresses::{CartRamAddress, RomAddress};
mod mbc1;
mod mbc2;
@ -21,11 +21,11 @@ const RAM_BANK_SIZE: usize = 8 * KB;
pub(super) trait Mbc: Send {
// addresses 0x0000 - 0x7FFF
fn get(&self, address: Address) -> u8;
fn get(&self, address: RomAddress) -> u8;
// addresses 0xA000 - 0xBFFF
fn get_ram(&self, address: Address) -> u8;
fn set(&mut self, address: Address, data: u8);
fn set_ram(&mut self, address: Address, data: u8);
fn get_ram(&self, address: CartRamAddress) -> u8;
fn set(&mut self, address: RomAddress, data: u8);
fn set_ram(&mut self, address: CartRamAddress, data: u8);
fn mbc_type(&self) -> String;
fn get_save_state(&self) -> MbcSaveState;
fn is_rumbling(&self) -> bool {

View file

@ -4,8 +4,8 @@ use serde::{Deserialize, Serialize};
use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE};
use crate::processor::memory::{
addresses::{AddressMarker, CartRamAddress, RomAddress},
rom::{MaybeBufferedSram, MbcSaveState, SramSaveState},
Address,
};
#[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 {
0x0..0x4000 => match self.bank_mode {
BankingMode::Simple => address as usize,
RomAddress::Bank0(address) => match self.bank_mode {
BankingMode::Simple => address.inner() as usize,
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 => {
(address - 0x4000) as usize
RomAddress::MappedBank(address) => {
(address.get_local() as usize)
+ (ROM_BANK_SIZE * self.rom_bank as usize)
+ (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)
}
fn get_ram_addr(&self, address: Address) -> usize {
match address {
0x0..0x8000 => panic!("passed rom address to ram address function"),
0xA000..0xC000 => match self.bank_mode {
fn get_ram_addr(&self, address: CartRamAddress) -> usize {
match self.bank_mode {
BankingMode::Simple => {
(address - 0xA000) as usize + (RAM_BANK_SIZE * self.ram_bank as usize)
(address.get_local() as usize) + (RAM_BANK_SIZE * self.ram_bank as usize)
}
BankingMode::Advanced => {
(address - 0xA000) as usize
(address.get_local() as usize)
+ (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 {
fn get(&self, address: Address) -> u8 {
fn get(&self, address: RomAddress) -> u8 {
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 {
let addr = self.get_ram_addr(address) % ram.len();
return ram.get(addr);
@ -116,7 +109,7 @@ impl Mbc for Mbc1 {
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);
if self.ram_enabled && let Some(ram) = &mut self.ram {
addr %= ram.len();
@ -124,8 +117,8 @@ impl Mbc for Mbc1 {
}
}
fn set(&mut self, address: Address, data: u8) {
match address {
fn set(&mut self, address: RomAddress, data: u8) {
match address.inner() {
0x0..0x2000 => {
// enable/disable ram
self.ram_enabled = (data & 0x0F) == 0xA;

View file

@ -3,8 +3,8 @@ use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use crate::processor::memory::{
addresses::{AddressMarker, CartRamAddress, RomAddress},
rom::{MaybeBufferedSram, MbcSaveState, SramSaveState},
Address,
};
use super::{rom_banks, Mbc, ROM_BANK_SIZE};
@ -51,28 +51,27 @@ impl Mbc2 {
}
impl Mbc for Mbc2 {
fn get(&self, address: Address) -> u8 {
fn get(&self, address: RomAddress) -> u8 {
match address {
0x0..0x4000 => self.data[address as usize],
0x4000..0x8000 => {
self.data[((address as usize - 0x4000) + (0x4000 * self.rom_bank as usize))
RomAddress::Bank0(address) => self.data[address.inner() as usize],
RomAddress::MappedBank(address) => {
self.data[((address.get_local() as usize) + (0x4000 * self.rom_bank as usize))
% 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 {
0xF0 | (0x0F & self.ram.get((address - 0xA000) as usize % 512))
0xF0 | (0x0F & self.ram.get((address.get_local() as usize) % 512))
} else {
0xFF
}
}
fn set(&mut self, address: Address, data: u8) {
if address < 0x4000 {
if address & (1 << 8) == (1 << 8) {
fn set(&mut self, address: RomAddress, data: u8) {
if let RomAddress::Bank0(_) = address {
if address.inner() & (1 << 8) == (1 << 8) {
// bit 8 is set - rom bank
self.rom_bank = data & 0xF;
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 {
self.ram.set((address - 0xA000) as usize % 512, data);
self.ram.set((address.get_local() as usize) % 512, data);
}
}

View file

@ -3,8 +3,8 @@ use serde::{Deserialize, Serialize};
use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE};
use crate::{
processor::memory::{
addresses::{AddressMarker, CartRamAddress, RomAddress},
rom::{MaybeBufferedSram, MbcSaveState, SramSaveState},
Address,
},
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 {
0x0..0x4000 => address as usize,
0x4000..0x8000 => {
let internal_addr = address as usize - 0x4000;
RomAddress::Bank0(address) => address.inner() as usize,
RomAddress::MappedBank(address) => {
let internal_addr = address.get_local() as usize;
internal_addr + (ROM_BANK_SIZE * self.rom_bank as usize)
}
_ => panic!("address {address} incompatible with MBC"),
} % self.rom_size)
}
fn get_ram_addr(&self, address: Address, ram_bank: usize) -> usize {
((address as usize - 0xA000) + (RAM_BANK_SIZE * ram_bank)) % self.ram_size
fn get_ram_addr(&self, address: CartRamAddress, ram_bank: usize) -> usize {
((address.get_local() as usize) + (RAM_BANK_SIZE * ram_bank)) % self.ram_size
}
pub fn from_save_state(state: Mbc3SaveState, data: Vec<u8>) -> Self {
@ -158,11 +157,11 @@ impl Mbc3 {
}
impl Mbc for Mbc3 {
fn get(&self, address: Address) -> u8 {
fn get(&self, address: RomAddress) -> u8 {
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 {
match &self.ram_bank {
RamBank::Ram(ram_bank) => {
@ -180,8 +179,8 @@ impl Mbc for Mbc3 {
0xFF
}
fn set(&mut self, address: Address, data: u8) {
match address {
fn set(&mut self, address: RomAddress, data: u8) {
match address.inner() {
0x0..0x2000 => {
if data & 0xF == 0xA {
self.ram_enabled = true;
@ -218,12 +217,11 @@ impl Mbc for Mbc3 {
}
}
}
_ => 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 {
match &self.ram_bank {
RamBank::Ram(ram_bank) => {

View file

@ -4,8 +4,8 @@ use serde::{Deserialize, Serialize};
use crate::{
processor::memory::{
addresses::{AddressMarker, CartRamAddress, RomAddress},
rom::{MaybeBufferedSram, MbcSaveState, SramSaveState},
Address,
},
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 {
0x0..0x4000 => address as usize,
0x4000..0x8000 => {
let internal_addr = address as usize - 0x4000;
RomAddress::Bank0(address) => address.inner() as usize,
RomAddress::MappedBank(address) => {
let internal_addr = address.get_local() as usize;
internal_addr + (ROM_BANK_SIZE * self.rom_bank as usize)
}
_ => panic!("address {address} incompatible with MBC"),
} % self.rom_size)
}
fn get_ram_addr(&self, address: Address) -> usize {
((address as usize - 0xA000) + (RAM_BANK_SIZE * self.ram_bank as usize)) % self.ram_size
fn get_ram_addr(&self, address: CartRamAddress) -> usize {
((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 {
@ -89,11 +88,11 @@ impl Mbc5 {
}
impl Mbc for Mbc5 {
fn get(&self, address: Address) -> u8 {
fn get(&self, address: RomAddress) -> u8 {
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 {
ram.get(self.get_ram_addr(address))
} else {
@ -101,8 +100,8 @@ impl Mbc for Mbc5 {
}
}
fn set(&mut self, address: Address, data: u8) {
match address {
fn set(&mut self, address: RomAddress, data: u8) {
match address.inner() {
0x0..0x2000 => {
if (data & 0xF) == 0xA {
self.ram_enabled = true
@ -121,11 +120,11 @@ impl Mbc for Mbc5 {
}
}
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);
if self.ram_enabled && let Some(ram) = &mut self.ram {
ram.set(real_addr, data);

View file

@ -1,5 +1,8 @@
use super::Mbc;
use crate::processor::memory::{rom::MbcSaveState, Address};
use crate::processor::memory::{
addresses::{AddressMarker, CartRamAddress, RomAddress},
rom::MbcSaveState,
};
pub struct None {
data: Vec<u8>,
@ -12,17 +15,17 @@ impl None {
}
impl Mbc for None {
fn get(&self, address: Address) -> u8 {
self.data[address as usize]
fn get(&self, address: RomAddress) -> u8 {
self.data[address.inner() as usize]
}
fn get_ram(&self, _address: Address) -> u8 {
fn get_ram(&self, _address: CartRamAddress) -> u8 {
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 {
String::from("None")

View file

@ -2,8 +2,8 @@ use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE};
use crate::{
connect::{CameraWrapperRef, PocketCamera as PocketCameraTrait},
processor::memory::{
addresses::{AddressMarker, CartRamAddress, RomAddress},
rom::{MaybeBufferedSram, MbcSaveState},
Address,
},
};
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 {
0x0..0x4000 => address as usize,
0x4000..0x8000 => {
let internal_addr = address as usize - 0x4000;
RomAddress::Bank0(address) => address.inner() as usize,
RomAddress::MappedBank(address) => {
let internal_addr = address.get_local() as usize;
internal_addr + (ROM_BANK_SIZE * self.rom_bank as usize)
}
_ => panic!("address {address} incompatible with MBC"),
} % self.rom_size)
}
fn get_ram_addr(&self, address: Address, bank: u8) -> usize {
((address as usize - 0xA000) + (RAM_BANK_SIZE * bank as usize)) % self.ram_size
fn get_ram_addr(&self, address: CartRamAddress, bank: u8) -> usize {
((address.get_local() as usize) + (RAM_BANK_SIZE * bank as usize)) % self.ram_size
}
pub(crate) fn from_save_state(
@ -83,8 +82,8 @@ where
todo!();
}
fn get_cam_reg(&self, address: Address) -> u8 {
match address {
fn get_cam_reg(&self, address: CartRamAddress) -> u8 {
match address.inner() {
0xA000 => {
(if self.camera.lock().unwrap().is_capturing() {
0x1
@ -92,13 +91,13 @@ where
0x0
}) | self.extra_bits_a000
}
0xA001..=0xA035 => self.camera_ram[(address - 0xA001) as usize],
0xA001..=0xA035 => self.camera_ram[(address.inner() - 0xA001) as usize],
_ => 0x00,
}
}
fn set_cam_reg(&mut self, address: Address, data: u8) {
match address {
fn set_cam_reg(&mut self, address: CartRamAddress, data: u8) {
match address.inner() {
0xA000 => {
if data & 0x1 == 0x1 {
self.camera.lock().unwrap().begin_capture();
@ -106,7 +105,7 @@ where
self.extra_bits_a000 = data & 0b110;
}
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
C: PocketCameraTrait + Send,
{
fn get(&self, address: Address) -> u8 {
fn get(&self, address: RomAddress) -> u8 {
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 {
RamBank::Ram(bank) => {
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();
match address {
match address.inner() {
0x0..0x2000 => {
if (data & 0xF) == 0xA {
self.ram_enabled = true
@ -167,11 +166,11 @@ where
}
}
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();
match self.ram_bank {
RamBank::Ram(bank) => {