2019-08-07 07:50:33 +01:00
|
|
|
use std::fmt;
|
|
|
|
use std::ops::Add;
|
2019-07-15 05:30:52 +01:00
|
|
|
|
2020-01-16 18:06:22 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
2019-11-08 23:43:43 +00:00
|
|
|
use super::cartridge::Cartridge;
|
2019-12-29 21:03:57 +00:00
|
|
|
use super::gpu::{GpuState, VIDEO_RAM_SIZE};
|
2019-11-08 23:43:43 +00:00
|
|
|
use super::iodev::IoDevices;
|
2019-12-29 21:03:57 +00:00
|
|
|
use super::{Addr, Bus};
|
2019-06-25 00:10:09 +01:00
|
|
|
|
|
|
|
const WORK_RAM_SIZE: usize = 256 * 1024;
|
2019-07-28 23:28:22 +01:00
|
|
|
const INTERNAL_RAM_SIZE: usize = 32 * 1024;
|
2019-06-25 00:10:09 +01:00
|
|
|
|
2019-08-07 07:50:33 +01:00
|
|
|
pub const BIOS_ADDR: u32 = 0x0000_0000;
|
|
|
|
pub const EWRAM_ADDR: u32 = 0x0200_0000;
|
|
|
|
pub const IWRAM_ADDR: u32 = 0x0300_0000;
|
|
|
|
pub const IOMEM_ADDR: u32 = 0x0400_0000;
|
|
|
|
pub const PALRAM_ADDR: u32 = 0x0500_0000;
|
|
|
|
pub const VRAM_ADDR: u32 = 0x0600_0000;
|
|
|
|
pub const OAM_ADDR: u32 = 0x0700_0000;
|
2020-01-26 00:06:44 +00:00
|
|
|
pub const GAMEPAK_WS0_LO: u32 = 0x0800_0000;
|
|
|
|
pub const GAMEPAK_WS0_HI: u32 = 0x0900_0000;
|
|
|
|
pub const GAMEPAK_WS1_LO: u32 = 0x0A00_0000;
|
|
|
|
pub const GAMEPAK_WS1_HI: u32 = 0x0B00_0000;
|
|
|
|
pub const GAMEPAK_WS2_LO: u32 = 0x0C00_0000;
|
|
|
|
pub const GAMEPAK_WS2_HI: u32 = 0x0D00_0000;
|
|
|
|
pub const SRAM_LO: u32 = 0x0E00_0000;
|
|
|
|
pub const SRAM_HI: u32 = 0x0F00_0000;
|
2019-08-07 07:50:33 +01:00
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
|
|
pub enum MemoryAccessType {
|
|
|
|
NonSeq,
|
|
|
|
Seq,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for MemoryAccessType {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(
|
|
|
|
f,
|
|
|
|
"{}",
|
|
|
|
match self {
|
|
|
|
MemoryAccessType::NonSeq => "N",
|
|
|
|
MemoryAccessType::Seq => "S",
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Copy, Clone)]
|
|
|
|
pub enum MemoryAccessWidth {
|
|
|
|
MemoryAccess8,
|
|
|
|
MemoryAccess16,
|
|
|
|
MemoryAccess32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Add<MemoryAccessWidth> for MemoryAccessType {
|
|
|
|
type Output = MemoryAccess;
|
|
|
|
|
|
|
|
fn add(self, other: MemoryAccessWidth) -> Self::Output {
|
|
|
|
MemoryAccess(self, other)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
|
|
pub struct MemoryAccess(pub MemoryAccessType, pub MemoryAccessWidth);
|
|
|
|
|
|
|
|
impl fmt::Display for MemoryAccess {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "{}-Cycle ({:?})", self.0, self.1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-16 18:06:22 +00:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
2019-07-28 23:28:22 +01:00
|
|
|
pub struct BoxedMemory {
|
2019-08-02 15:58:56 +01:00
|
|
|
pub mem: Box<[u8]>,
|
2019-07-28 23:28:22 +01:00
|
|
|
}
|
2019-07-02 23:26:48 +01:00
|
|
|
|
|
|
|
impl BoxedMemory {
|
2019-11-08 22:55:09 +00:00
|
|
|
pub fn new(boxed_slice: Box<[u8]>) -> BoxedMemory {
|
|
|
|
BoxedMemory { mem: boxed_slice }
|
2019-07-02 23:26:48 +01:00
|
|
|
}
|
|
|
|
}
|
2019-06-25 00:10:09 +01:00
|
|
|
|
2019-07-01 15:45:29 +01:00
|
|
|
impl Bus for BoxedMemory {
|
2019-07-15 05:30:52 +01:00
|
|
|
fn read_8(&self, addr: Addr) -> u8 {
|
2020-01-16 18:06:22 +00:00
|
|
|
unsafe { *self.mem.get_unchecked(addr as usize) }
|
2019-07-15 05:30:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn write_8(&mut self, addr: Addr, value: u8) {
|
2020-01-16 18:06:22 +00:00
|
|
|
unsafe {
|
|
|
|
*self.mem.get_unchecked_mut(addr as usize) = value;
|
|
|
|
}
|
2019-07-15 05:30:52 +01:00
|
|
|
}
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
|
2020-01-16 18:06:22 +00:00
|
|
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
2019-07-03 23:56:50 +01:00
|
|
|
struct DummyBus([u8; 4]);
|
|
|
|
|
|
|
|
impl Bus for DummyBus {
|
2019-07-05 13:34:52 +01:00
|
|
|
fn read_8(&self, _addr: Addr) -> u8 {
|
2019-07-03 23:56:50 +01:00
|
|
|
0
|
|
|
|
}
|
|
|
|
|
2019-07-15 05:30:52 +01:00
|
|
|
fn write_8(&mut self, _addr: Addr, _value: u8) {}
|
2019-07-03 23:56:50 +01:00
|
|
|
}
|
|
|
|
|
2020-01-16 18:06:22 +00:00
|
|
|
#[derive(Serialize, Deserialize, Clone)]
|
2019-06-30 14:59:19 +01:00
|
|
|
pub struct SysBus {
|
2019-11-08 23:43:43 +00:00
|
|
|
pub io: IoDevices,
|
2019-08-05 07:44:27 +01:00
|
|
|
|
2019-07-01 15:45:29 +01:00
|
|
|
bios: BoxedMemory,
|
|
|
|
onboard_work_ram: BoxedMemory,
|
|
|
|
internal_work_ram: BoxedMemory,
|
2020-01-26 00:06:44 +00:00
|
|
|
cartridge: Cartridge,
|
2019-07-03 23:56:50 +01:00
|
|
|
dummy: DummyBus,
|
2019-11-16 16:09:37 +00:00
|
|
|
|
|
|
|
pub trace_access: bool,
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
|
2020-01-26 00:06:44 +00:00
|
|
|
use ansi_term::Colour;
|
|
|
|
|
2019-06-30 14:59:19 +01:00
|
|
|
impl SysBus {
|
2020-01-26 00:06:44 +00:00
|
|
|
pub fn new(io: IoDevices, bios_rom: Vec<u8>, cartridge: Cartridge) -> SysBus {
|
2019-07-01 15:45:29 +01:00
|
|
|
SysBus {
|
2019-08-05 07:44:27 +01:00
|
|
|
io: io,
|
|
|
|
|
2019-11-08 22:55:09 +00:00
|
|
|
bios: BoxedMemory::new(bios_rom.into_boxed_slice()),
|
|
|
|
onboard_work_ram: BoxedMemory::new(vec![0; WORK_RAM_SIZE].into_boxed_slice()),
|
|
|
|
internal_work_ram: BoxedMemory::new(vec![0; INTERNAL_RAM_SIZE].into_boxed_slice()),
|
2020-01-26 00:06:44 +00:00
|
|
|
cartridge: cartridge,
|
2019-07-03 23:56:50 +01:00
|
|
|
dummy: DummyBus([0; 4]),
|
2019-11-16 16:09:37 +00:00
|
|
|
|
|
|
|
trace_access: false,
|
2019-07-01 15:45:29 +01:00
|
|
|
}
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
|
2019-11-08 23:43:43 +00:00
|
|
|
fn map(&self, addr: Addr) -> (&dyn Bus, Addr) {
|
2019-08-07 07:50:33 +01:00
|
|
|
match addr & 0xff000000 {
|
2019-11-16 16:09:37 +00:00
|
|
|
BIOS_ADDR => {
|
2020-01-26 00:06:44 +00:00
|
|
|
if addr >= 0x4000 {
|
|
|
|
(&self.dummy, addr) // TODO return last fetched opcode
|
2019-11-16 16:09:37 +00:00
|
|
|
} else {
|
2020-01-26 00:06:44 +00:00
|
|
|
(&self.bios, addr)
|
2019-11-16 16:09:37 +00:00
|
|
|
}
|
|
|
|
}
|
2020-01-26 00:06:44 +00:00
|
|
|
EWRAM_ADDR => (&self.onboard_work_ram, addr & 0x3_ffff),
|
|
|
|
IWRAM_ADDR => (&self.internal_work_ram, addr & 0x7fff),
|
2019-11-08 23:43:43 +00:00
|
|
|
IOMEM_ADDR => (&self.io, {
|
2020-01-26 00:06:44 +00:00
|
|
|
if addr & 0xffff == 0x8000 {
|
2019-11-08 22:55:09 +00:00
|
|
|
0x800
|
|
|
|
} else {
|
2020-01-26 00:06:44 +00:00
|
|
|
addr & 0x7ff
|
2019-11-08 22:55:09 +00:00
|
|
|
}
|
|
|
|
}),
|
2020-01-26 00:06:44 +00:00
|
|
|
PALRAM_ADDR => (&self.io.gpu.palette_ram, addr & 0x3ff),
|
2019-12-29 21:03:57 +00:00
|
|
|
VRAM_ADDR => (&self.io.gpu.vram, {
|
2020-01-26 00:06:44 +00:00
|
|
|
let mut ofs = addr & ((VIDEO_RAM_SIZE as u32) - 1);
|
2019-11-08 22:55:09 +00:00
|
|
|
if ofs > 0x18000 {
|
|
|
|
ofs -= 0x8000;
|
|
|
|
}
|
|
|
|
ofs
|
|
|
|
}),
|
2020-01-26 00:06:44 +00:00
|
|
|
OAM_ADDR => (&self.io.gpu.oam, addr & 0x3ff),
|
|
|
|
GAMEPAK_WS0_LO | GAMEPAK_WS0_HI | GAMEPAK_WS1_LO | GAMEPAK_WS1_HI | GAMEPAK_WS2_LO => {
|
|
|
|
(&self.cartridge, addr)
|
|
|
|
}
|
|
|
|
GAMEPAK_WS2_HI => {
|
|
|
|
// println!(
|
|
|
|
// "[{}] Possible read form EEPROM",
|
|
|
|
// Colour::Yellow.bold().paint("warn")
|
|
|
|
// );
|
|
|
|
(&self.cartridge, addr)
|
|
|
|
}
|
|
|
|
SRAM_LO | SRAM_HI => (&self.cartridge, addr),
|
|
|
|
_ => {
|
|
|
|
println!(
|
|
|
|
"[{}] Trying to read address {:#x}",
|
|
|
|
Colour::Yellow.bold().paint("warn"),
|
|
|
|
addr
|
|
|
|
);
|
|
|
|
(&self.dummy, addr)
|
2019-11-16 16:09:37 +00:00
|
|
|
}
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-01 15:45:29 +01:00
|
|
|
/// TODO proc-macro for generating this function
|
2019-11-08 23:43:43 +00:00
|
|
|
fn map_mut(&mut self, addr: Addr) -> (&mut dyn Bus, Addr) {
|
2019-08-07 07:50:33 +01:00
|
|
|
match addr & 0xff000000 {
|
2020-01-26 00:06:44 +00:00
|
|
|
BIOS_ADDR => (&mut self.dummy, addr),
|
|
|
|
EWRAM_ADDR => (&mut self.onboard_work_ram, addr & 0x3_ffff),
|
|
|
|
IWRAM_ADDR => (&mut self.internal_work_ram, addr & 0x7fff),
|
2019-11-08 23:43:43 +00:00
|
|
|
IOMEM_ADDR => (&mut self.io, {
|
2020-01-26 00:06:44 +00:00
|
|
|
if addr & 0xffff == 0x8000 {
|
2019-11-08 22:55:09 +00:00
|
|
|
0x800
|
|
|
|
} else {
|
2020-01-26 00:06:44 +00:00
|
|
|
addr & 0x7ff
|
2019-11-08 22:55:09 +00:00
|
|
|
}
|
|
|
|
}),
|
2020-01-26 00:06:44 +00:00
|
|
|
PALRAM_ADDR => (&mut self.io.gpu.palette_ram, addr & 0x3ff),
|
2019-12-29 21:03:57 +00:00
|
|
|
VRAM_ADDR => (&mut self.io.gpu.vram, {
|
2020-01-26 00:06:44 +00:00
|
|
|
let mut ofs = addr & ((VIDEO_RAM_SIZE as u32) - 1);
|
2019-11-08 22:55:09 +00:00
|
|
|
if ofs > 0x18000 {
|
|
|
|
ofs -= 0x8000;
|
|
|
|
}
|
|
|
|
ofs
|
|
|
|
}),
|
2020-01-26 00:06:44 +00:00
|
|
|
OAM_ADDR => (&mut self.io.gpu.oam, addr & 0x3ff),
|
|
|
|
GAMEPAK_WS0_LO | GAMEPAK_WS0_HI => (&mut self.dummy, addr),
|
|
|
|
GAMEPAK_WS2_HI => {
|
|
|
|
// println!(
|
|
|
|
// "[{}] Possible write to EEPROM",
|
|
|
|
// Colour::Yellow.bold().paint("warn")
|
|
|
|
// );
|
|
|
|
(&mut self.dummy, addr)
|
|
|
|
}
|
|
|
|
SRAM_LO | SRAM_HI => (&mut self.cartridge, addr),
|
|
|
|
_ => {
|
|
|
|
println!(
|
|
|
|
"[{}] Trying to write {:#x}",
|
|
|
|
Colour::Yellow.bold().paint("warn"),
|
|
|
|
addr
|
|
|
|
);
|
|
|
|
(&mut self.dummy, addr)
|
2019-11-16 16:09:37 +00:00
|
|
|
}
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
}
|
2019-08-07 07:50:33 +01:00
|
|
|
|
|
|
|
pub fn get_cycles(&self, addr: Addr, access: MemoryAccess) -> usize {
|
|
|
|
let nonseq_cycles = [4, 3, 2, 8];
|
|
|
|
let seq_cycles = [2, 1];
|
|
|
|
|
|
|
|
let mut cycles = 0;
|
|
|
|
|
|
|
|
// TODO handle EWRAM accesses
|
|
|
|
match addr & 0xff000000 {
|
2019-08-08 17:46:56 +01:00
|
|
|
EWRAM_ADDR => match access.1 {
|
|
|
|
MemoryAccessWidth::MemoryAccess32 => cycles += 6,
|
|
|
|
_ => cycles += 3,
|
|
|
|
},
|
2019-08-07 07:50:33 +01:00
|
|
|
OAM_ADDR | VRAM_ADDR | PALRAM_ADDR => {
|
|
|
|
match access.1 {
|
|
|
|
MemoryAccessWidth::MemoryAccess32 => cycles += 2,
|
2019-08-08 17:46:56 +01:00
|
|
|
_ => cycles += 1,
|
2019-08-07 07:50:33 +01:00
|
|
|
}
|
2019-11-08 23:43:43 +00:00
|
|
|
if self.io.gpu.state == GpuState::HDraw {
|
2019-08-07 07:50:33 +01:00
|
|
|
cycles += 1;
|
|
|
|
}
|
|
|
|
}
|
2020-01-26 00:06:44 +00:00
|
|
|
GAMEPAK_WS0_LO | GAMEPAK_WS0_HI => match access.0 {
|
2019-08-08 17:46:56 +01:00
|
|
|
MemoryAccessType::NonSeq => match access.1 {
|
|
|
|
MemoryAccessWidth::MemoryAccess32 => {
|
2019-11-08 23:43:43 +00:00
|
|
|
cycles += nonseq_cycles[self.io.waitcnt.ws0_first_access() as usize];
|
|
|
|
cycles += seq_cycles[self.io.waitcnt.ws0_second_access() as usize];
|
2019-08-08 17:46:56 +01:00
|
|
|
}
|
|
|
|
_ => {
|
2019-11-08 23:43:43 +00:00
|
|
|
cycles += nonseq_cycles[self.io.waitcnt.ws0_first_access() as usize];
|
2019-08-07 07:50:33 +01:00
|
|
|
}
|
2019-08-08 17:46:56 +01:00
|
|
|
},
|
|
|
|
MemoryAccessType::Seq => {
|
2019-11-08 23:43:43 +00:00
|
|
|
cycles += seq_cycles[self.io.waitcnt.ws0_second_access() as usize];
|
2019-08-08 17:46:56 +01:00
|
|
|
if access.1 == MemoryAccessWidth::MemoryAccess32 {
|
2019-11-08 23:43:43 +00:00
|
|
|
cycles += seq_cycles[self.io.waitcnt.ws0_second_access() as usize];
|
2019-08-07 07:50:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2020-01-26 00:06:44 +00:00
|
|
|
GAMEPAK_WS1_LO | GAMEPAK_WS1_HI => match access.0 {
|
|
|
|
MemoryAccessType::NonSeq => match access.1 {
|
|
|
|
MemoryAccessWidth::MemoryAccess32 => {
|
|
|
|
cycles += nonseq_cycles[self.io.waitcnt.ws1_first_access() as usize];
|
|
|
|
cycles += seq_cycles[self.io.waitcnt.ws1_second_access() as usize];
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
cycles += nonseq_cycles[self.io.waitcnt.ws1_first_access() as usize];
|
|
|
|
}
|
|
|
|
},
|
|
|
|
MemoryAccessType::Seq => {
|
|
|
|
cycles += seq_cycles[self.io.waitcnt.ws1_second_access() as usize];
|
|
|
|
if access.1 == MemoryAccessWidth::MemoryAccess32 {
|
|
|
|
cycles += seq_cycles[self.io.waitcnt.ws1_second_access() as usize];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
GAMEPAK_WS2_LO | GAMEPAK_WS2_HI => match access.0 {
|
|
|
|
MemoryAccessType::NonSeq => match access.1 {
|
|
|
|
MemoryAccessWidth::MemoryAccess32 => {
|
|
|
|
cycles += nonseq_cycles[self.io.waitcnt.ws2_first_access() as usize];
|
|
|
|
cycles += seq_cycles[self.io.waitcnt.ws2_second_access() as usize];
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
cycles += nonseq_cycles[self.io.waitcnt.ws2_first_access() as usize];
|
|
|
|
}
|
|
|
|
},
|
|
|
|
MemoryAccessType::Seq => {
|
|
|
|
cycles += seq_cycles[self.io.waitcnt.ws2_second_access() as usize];
|
|
|
|
if access.1 == MemoryAccessWidth::MemoryAccess32 {
|
|
|
|
cycles += seq_cycles[self.io.waitcnt.ws2_second_access() as usize];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2019-08-07 07:50:33 +01:00
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
cycles
|
|
|
|
}
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Bus for SysBus {
|
|
|
|
fn read_32(&self, addr: Addr) -> u32 {
|
2020-01-26 00:06:44 +00:00
|
|
|
if addr & 3 != 0 {
|
|
|
|
println!("warn: Unaligned read32 at {:#X}", addr);
|
|
|
|
}
|
|
|
|
let (dev, addr) = self.map(addr & !3);
|
|
|
|
dev.read_32(addr)
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn read_16(&self, addr: Addr) -> u16 {
|
2020-01-26 00:06:44 +00:00
|
|
|
if addr & 1 != 0 {
|
|
|
|
println!("warn: Unaligned read16 at {:#X}", addr);
|
|
|
|
}
|
|
|
|
let (dev, addr) = self.map(addr & !1);
|
|
|
|
dev.read_16(addr)
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn read_8(&self, addr: Addr) -> u8 {
|
2019-11-08 22:55:09 +00:00
|
|
|
let (dev, addr) = self.map(addr);
|
2020-01-26 00:06:44 +00:00
|
|
|
dev.read_8(addr)
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
|
2019-07-15 05:30:52 +01:00
|
|
|
fn write_32(&mut self, addr: Addr, value: u32) {
|
2020-01-26 00:06:44 +00:00
|
|
|
if addr & 3 != 0 {
|
|
|
|
println!("warn: Unaligned write32 at {:#X} (value={:#X}", addr, value);
|
|
|
|
}
|
|
|
|
let (dev, addr) = self.map_mut(addr & !3);
|
|
|
|
dev.write_32(addr, value);
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
|
2019-07-15 05:30:52 +01:00
|
|
|
fn write_16(&mut self, addr: Addr, value: u16) {
|
2020-01-26 00:06:44 +00:00
|
|
|
if addr & 1 != 0 {
|
|
|
|
println!("warn: Unaligned write16 at {:#X} (value={:#X}", addr, value);
|
|
|
|
}
|
|
|
|
let (dev, addr) = self.map_mut(addr & !1);
|
|
|
|
dev.write_16(addr, value);
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
|
2019-07-15 05:30:52 +01:00
|
|
|
fn write_8(&mut self, addr: Addr, value: u8) {
|
2019-11-08 22:55:09 +00:00
|
|
|
let (dev, addr) = self.map_mut(addr);
|
2020-01-26 00:06:44 +00:00
|
|
|
dev.write_8(addr, value);
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
2019-06-25 00:10:09 +01:00
|
|
|
}
|