2019-06-30 14:59:19 +01:00
|
|
|
use std::io;
|
|
|
|
|
2019-07-15 05:30:52 +01:00
|
|
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
|
|
|
|
2019-07-06 13:53:36 +01:00
|
|
|
use super::{cartridge::Cartridge, ioregs::IoRegs};
|
2019-07-02 23:40:08 +01:00
|
|
|
|
2019-07-01 15:45:29 +01:00
|
|
|
use super::arm7tdmi::bus::{Bus, MemoryAccess, MemoryAccessWidth};
|
2019-06-30 14:59:19 +01:00
|
|
|
use super::arm7tdmi::Addr;
|
2019-06-25 00:10:09 +01:00
|
|
|
|
|
|
|
const VIDEO_RAM_SIZE: usize = 128 * 1024;
|
|
|
|
const WORK_RAM_SIZE: usize = 256 * 1024;
|
2019-07-28 23:28:22 +01:00
|
|
|
const INTERNAL_RAM_SIZE: usize = 32 * 1024;
|
2019-07-01 15:45:29 +01:00
|
|
|
const PALETTE_RAM_SIZE: usize = 1 * 1024;
|
2019-06-25 00:10:09 +01:00
|
|
|
const OAM_SIZE: usize = 1 * 1024;
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
2019-07-28 23:28:22 +01:00
|
|
|
pub struct BoxedMemory {
|
|
|
|
mem: Box<[u8]>,
|
|
|
|
ws: WaitState,
|
|
|
|
mask: u32,
|
|
|
|
}
|
2019-07-02 23:26:48 +01:00
|
|
|
|
|
|
|
impl BoxedMemory {
|
2019-07-28 23:28:22 +01:00
|
|
|
pub fn new(boxed_slice: Box<[u8]>, mask: u32) -> BoxedMemory {
|
|
|
|
BoxedMemory {
|
|
|
|
mem: boxed_slice,
|
|
|
|
mask: mask,
|
|
|
|
ws: WaitState::default(),
|
|
|
|
}
|
2019-07-02 23:26:48 +01:00
|
|
|
}
|
|
|
|
|
2019-07-28 23:28:22 +01:00
|
|
|
pub fn new_with_waitstate(boxed_slice: Box<[u8]>, mask: u32, ws: WaitState) -> BoxedMemory {
|
|
|
|
BoxedMemory {
|
|
|
|
mem: boxed_slice,
|
|
|
|
mask: mask,
|
|
|
|
ws: ws,
|
|
|
|
}
|
2019-07-02 23:26:48 +01:00
|
|
|
}
|
|
|
|
}
|
2019-06-25 00:10:09 +01:00
|
|
|
|
2019-07-01 15:45:29 +01:00
|
|
|
#[derive(Debug)]
|
2019-07-02 23:26:48 +01:00
|
|
|
pub struct WaitState {
|
|
|
|
pub access8: usize,
|
|
|
|
pub access16: usize,
|
|
|
|
pub access32: usize,
|
2019-07-01 15:45:29 +01:00
|
|
|
}
|
2019-06-25 11:28:02 +01:00
|
|
|
|
2019-07-01 15:45:29 +01:00
|
|
|
impl WaitState {
|
2019-07-02 23:40:08 +01:00
|
|
|
pub fn new(access8: usize, access16: usize, access32: usize) -> WaitState {
|
2019-07-01 15:45:29 +01:00
|
|
|
WaitState {
|
|
|
|
access8,
|
|
|
|
access16,
|
|
|
|
access32,
|
|
|
|
}
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
2019-07-01 15:45:29 +01:00
|
|
|
}
|
2019-06-30 14:59:19 +01:00
|
|
|
|
2019-07-01 15:45:29 +01:00
|
|
|
impl Default for WaitState {
|
|
|
|
fn default() -> WaitState {
|
|
|
|
WaitState::new(1, 1, 1)
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
2019-07-01 15:45:29 +01:00
|
|
|
}
|
2019-06-30 14:59:19 +01:00
|
|
|
|
2019-07-01 15:45:29 +01:00
|
|
|
impl Bus for BoxedMemory {
|
2019-07-15 05:30:52 +01:00
|
|
|
fn read_32(&self, addr: Addr) -> u32 {
|
2019-07-28 23:28:22 +01:00
|
|
|
(&self.mem[(addr & self.mask) as usize..])
|
2019-07-15 05:30:52 +01:00
|
|
|
.read_u32::<LittleEndian>()
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn read_16(&self, addr: Addr) -> u16 {
|
2019-07-28 23:28:22 +01:00
|
|
|
(&self.mem[(addr & self.mask) as usize..])
|
2019-07-15 05:30:52 +01:00
|
|
|
.read_u16::<LittleEndian>()
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn read_8(&self, addr: Addr) -> u8 {
|
2019-07-28 23:28:22 +01:00
|
|
|
(&self.mem[(addr & self.mask) as usize..])[0]
|
2019-07-15 05:30:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn write_32(&mut self, addr: Addr, value: u32) {
|
2019-07-28 23:28:22 +01:00
|
|
|
(&mut self.mem[(addr & self.mask) as usize..])
|
2019-07-15 05:30:52 +01:00
|
|
|
.write_u32::<LittleEndian>(value)
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_16(&mut self, addr: Addr, value: u16) {
|
2019-07-28 23:28:22 +01:00
|
|
|
(&mut self.mem[(addr & self.mask) as usize..])
|
2019-07-15 05:30:52 +01:00
|
|
|
.write_u16::<LittleEndian>(value)
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn write_8(&mut self, addr: Addr, value: u8) {
|
2019-07-28 23:28:22 +01:00
|
|
|
(&mut self.mem[(addr & self.mask) as usize..])
|
|
|
|
.write_u8(value)
|
|
|
|
.unwrap()
|
2019-07-15 05:30:52 +01:00
|
|
|
}
|
|
|
|
|
2019-07-02 14:57:35 +01:00
|
|
|
fn get_bytes(&self, addr: Addr) -> &[u8] {
|
2019-07-28 23:28:22 +01:00
|
|
|
&self.mem[(addr & self.mask) as usize..]
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
|
2019-07-02 14:57:35 +01:00
|
|
|
fn get_bytes_mut(&mut self, addr: Addr) -> &mut [u8] {
|
2019-07-28 23:28:22 +01:00
|
|
|
&mut self.mem[(addr & self.mask) as usize..]
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
|
2019-07-01 15:45:29 +01:00
|
|
|
fn get_cycles(&self, _addr: Addr, access: MemoryAccess) -> usize {
|
|
|
|
match access.1 {
|
2019-07-28 23:28:22 +01:00
|
|
|
MemoryAccessWidth::MemoryAccess8 => self.ws.access8,
|
|
|
|
MemoryAccessWidth::MemoryAccess16 => self.ws.access16,
|
|
|
|
MemoryAccessWidth::MemoryAccess32 => self.ws.access32,
|
2019-06-25 11:28:02 +01:00
|
|
|
}
|
|
|
|
}
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
|
2019-07-03 23:56:50 +01:00
|
|
|
#[derive(Debug)]
|
|
|
|
struct DummyBus([u8; 4]);
|
|
|
|
|
|
|
|
impl Bus for DummyBus {
|
2019-07-05 13:34:52 +01:00
|
|
|
fn read_32(&self, _addr: Addr) -> u32 {
|
2019-07-03 23:56:50 +01:00
|
|
|
0
|
|
|
|
}
|
|
|
|
|
2019-07-05 13:34:52 +01:00
|
|
|
fn read_16(&self, _addr: Addr) -> u16 {
|
2019-07-03 23:56:50 +01:00
|
|
|
0
|
|
|
|
}
|
|
|
|
|
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_32(&mut self, _addr: Addr, _value: u32) {}
|
2019-07-03 23:56:50 +01:00
|
|
|
|
2019-07-15 05:30:52 +01:00
|
|
|
fn write_16(&mut self, _addr: Addr, _value: u16) {}
|
|
|
|
|
|
|
|
fn write_8(&mut self, _addr: Addr, _value: u8) {}
|
2019-07-03 23:56:50 +01:00
|
|
|
|
2019-07-05 13:34:52 +01:00
|
|
|
fn get_bytes(&self, _addr: Addr) -> &[u8] {
|
2019-07-03 23:56:50 +01:00
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
|
2019-07-05 13:34:52 +01:00
|
|
|
fn get_bytes_mut(&mut self, _addr: Addr) -> &mut [u8] {
|
2019-07-03 23:56:50 +01:00
|
|
|
&mut self.0
|
|
|
|
}
|
|
|
|
|
2019-07-05 13:34:52 +01:00
|
|
|
fn get_cycles(&self, _addr: Addr, _access: MemoryAccess) -> usize {
|
2019-07-03 23:56:50 +01:00
|
|
|
1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-30 14:59:19 +01:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct SysBus {
|
2019-07-01 15:45:29 +01:00
|
|
|
bios: BoxedMemory,
|
|
|
|
onboard_work_ram: BoxedMemory,
|
|
|
|
internal_work_ram: BoxedMemory,
|
|
|
|
/// Currently model the IOMem as regular buffer, later make it into something more sophisticated.
|
2019-07-06 13:53:36 +01:00
|
|
|
pub ioregs: IoRegs,
|
2019-07-01 15:45:29 +01:00
|
|
|
palette_ram: BoxedMemory,
|
|
|
|
vram: BoxedMemory,
|
|
|
|
oam: BoxedMemory,
|
2019-07-02 23:40:08 +01:00
|
|
|
gamepak: Cartridge,
|
2019-07-03 23:56:50 +01:00
|
|
|
dummy: DummyBus,
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl SysBus {
|
2019-07-02 23:40:08 +01:00
|
|
|
pub fn new(bios_rom: Vec<u8>, gamepak: Cartridge) -> SysBus {
|
2019-07-01 15:45:29 +01:00
|
|
|
SysBus {
|
2019-07-28 23:28:22 +01:00
|
|
|
bios: BoxedMemory::new(bios_rom.into_boxed_slice(), 0xff_ffff),
|
2019-07-03 09:30:00 +01:00
|
|
|
onboard_work_ram: BoxedMemory::new_with_waitstate(
|
|
|
|
vec![0; WORK_RAM_SIZE].into_boxed_slice(),
|
2019-07-28 23:28:22 +01:00
|
|
|
(WORK_RAM_SIZE as u32) - 1,
|
2019-07-03 09:30:00 +01:00
|
|
|
WaitState::new(3, 3, 6),
|
|
|
|
),
|
2019-07-28 23:28:22 +01:00
|
|
|
internal_work_ram: BoxedMemory::new(
|
|
|
|
vec![0; INTERNAL_RAM_SIZE].into_boxed_slice(),
|
|
|
|
0x7fff,
|
|
|
|
),
|
2019-07-06 13:53:36 +01:00
|
|
|
ioregs: IoRegs::default(),
|
2019-07-02 23:40:08 +01:00
|
|
|
palette_ram: BoxedMemory::new_with_waitstate(
|
2019-07-01 15:45:29 +01:00
|
|
|
vec![0; PALETTE_RAM_SIZE].into_boxed_slice(),
|
2019-07-28 23:28:22 +01:00
|
|
|
(PALETTE_RAM_SIZE as u32) - 1,
|
2019-07-01 15:45:29 +01:00
|
|
|
WaitState::new(1, 1, 2),
|
|
|
|
),
|
2019-07-02 23:40:08 +01:00
|
|
|
vram: BoxedMemory::new_with_waitstate(
|
2019-07-01 15:45:29 +01:00
|
|
|
vec![0; VIDEO_RAM_SIZE].into_boxed_slice(),
|
2019-07-28 23:28:22 +01:00
|
|
|
(VIDEO_RAM_SIZE as u32) - 1,
|
2019-07-01 15:45:29 +01:00
|
|
|
WaitState::new(1, 1, 2),
|
|
|
|
),
|
2019-07-28 23:28:22 +01:00
|
|
|
oam: BoxedMemory::new(vec![0; OAM_SIZE].into_boxed_slice(), (OAM_SIZE as u32) - 1),
|
2019-07-02 23:40:08 +01:00
|
|
|
gamepak: gamepak,
|
2019-07-03 23:56:50 +01:00
|
|
|
dummy: DummyBus([0; 4]),
|
2019-07-01 15:45:29 +01:00
|
|
|
}
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
|
2019-07-02 23:40:08 +01:00
|
|
|
fn map(&self, addr: Addr) -> &Bus {
|
2019-06-30 14:59:19 +01:00
|
|
|
match addr as usize {
|
2019-07-01 15:45:29 +01:00
|
|
|
0x0000_0000...0x0000_3fff => &self.bios,
|
2019-07-28 23:28:22 +01:00
|
|
|
0x0200_0000...0x02ff_ffff => &self.onboard_work_ram,
|
|
|
|
0x0300_0000...0x03ff_ffff => &self.internal_work_ram,
|
2019-07-01 15:45:29 +01:00
|
|
|
0x0400_0000...0x0400_03fe => &self.ioregs,
|
2019-07-28 23:28:22 +01:00
|
|
|
0x0500_0000...0x05ff_ffff => &self.palette_ram,
|
|
|
|
0x0600_0000...0x06ff_ffff => &self.vram,
|
2019-07-01 15:45:29 +01:00
|
|
|
0x0700_0000...0x0700_03ff => &self.oam,
|
2019-07-02 23:40:08 +01:00
|
|
|
0x0800_0000...0x09ff_ffff => &self.gamepak,
|
2019-07-15 05:30:52 +01:00
|
|
|
_ => &self.dummy,
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-01 15:45:29 +01:00
|
|
|
/// TODO proc-macro for generating this function
|
2019-07-02 23:22:36 +01:00
|
|
|
fn map_mut(&mut self, addr: Addr) -> &mut Bus {
|
2019-06-30 14:59:19 +01:00
|
|
|
match addr as usize {
|
2019-07-01 15:45:29 +01:00
|
|
|
0x0000_0000...0x0000_3fff => &mut self.bios,
|
2019-07-28 23:28:22 +01:00
|
|
|
0x0200_0000...0x02ff_ffff => &mut self.onboard_work_ram,
|
|
|
|
0x0300_0000...0x03ff_ffff => &mut self.internal_work_ram,
|
2019-07-01 15:45:29 +01:00
|
|
|
0x0400_0000...0x0400_03fe => &mut self.ioregs,
|
2019-07-28 23:28:22 +01:00
|
|
|
0x0500_0000...0x05ff_ffff => &mut self.palette_ram,
|
|
|
|
0x0600_0000...0x06ff_ffff => &mut self.vram,
|
2019-07-01 15:45:29 +01:00
|
|
|
0x0700_0000...0x0700_03ff => &mut self.oam,
|
2019-07-02 23:40:08 +01:00
|
|
|
0x0800_0000...0x09ff_ffff => &mut self.gamepak,
|
2019-07-15 05:30:52 +01:00
|
|
|
_ => &mut self.dummy,
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Bus for SysBus {
|
|
|
|
fn read_32(&self, addr: Addr) -> u32 {
|
2019-07-01 15:45:29 +01:00
|
|
|
self.map(addr).read_32(addr & 0xff_ffff)
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn read_16(&self, addr: Addr) -> u16 {
|
2019-07-01 15:45:29 +01:00
|
|
|
self.map(addr).read_16(addr & 0xff_ffff)
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn read_8(&self, addr: Addr) -> u8 {
|
2019-07-01 15:45:29 +01:00
|
|
|
self.map(addr).read_8(addr & 0xff_ffff)
|
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) {
|
2019-07-01 15:45:29 +01:00
|
|
|
self.map_mut(addr).write_32(addr & 0xff_ffff, 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) {
|
2019-07-01 15:45:29 +01:00
|
|
|
self.map_mut(addr).write_16(addr & 0xff_ffff, 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-07-01 15:45:29 +01:00
|
|
|
self.map_mut(addr).write_8(addr & 0xff_ffff, value)
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
|
2019-07-02 14:57:35 +01:00
|
|
|
fn get_bytes(&self, addr: Addr) -> &[u8] {
|
|
|
|
self.map(addr).get_bytes(addr & 0xff_ffff)
|
2019-07-01 15:45:29 +01:00
|
|
|
}
|
2019-06-30 14:59:19 +01:00
|
|
|
|
2019-07-02 14:57:35 +01:00
|
|
|
fn get_bytes_mut(&mut self, addr: Addr) -> &mut [u8] {
|
|
|
|
self.map_mut(addr).get_bytes_mut(addr & 0xff_ffff)
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn get_cycles(&self, addr: Addr, access: MemoryAccess) -> usize {
|
2019-07-01 15:45:29 +01:00
|
|
|
self.map(addr).get_cycles(addr & 0xff_ffff, access)
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
2019-06-25 00:10:09 +01:00
|
|
|
}
|