Add WAITCNT, and refactor cycle calculation
Former-commit-id: e1ee5c9ce1f1db549fddd80907467da51e63b676
This commit is contained in:
parent
fe071bf1ec
commit
d86cc87c79
|
@ -1,51 +1,5 @@
|
||||||
use std::fmt;
|
|
||||||
use std::ops::Add;
|
|
||||||
|
|
||||||
use super::Addr;
|
use super::Addr;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
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)]
|
|
||||||
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)]
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Bus {
|
pub trait Bus {
|
||||||
fn read_32(&self, addr: Addr) -> u32;
|
fn read_32(&self, addr: Addr) -> u32;
|
||||||
fn read_16(&self, addr: Addr) -> u16;
|
fn read_16(&self, addr: Addr) -> u16;
|
||||||
|
@ -54,9 +8,6 @@ pub trait Bus {
|
||||||
fn write_16(&mut self, addr: Addr, value: u16);
|
fn write_16(&mut self, addr: Addr, value: u16);
|
||||||
fn write_8(&mut self, addr: Addr, value: u8);
|
fn write_8(&mut self, addr: Addr, value: u8);
|
||||||
|
|
||||||
/// returns the number of cycles needed for this memory access
|
|
||||||
fn get_cycles(&self, addr: Addr, access: MemoryAccess) -> usize;
|
|
||||||
|
|
||||||
fn get_bytes(&self, range: std::ops::Range<u32>) -> Vec<u8> {
|
fn get_bytes(&self, range: std::ops::Range<u32>) -> Vec<u8> {
|
||||||
let mut bytes = Vec::new();
|
let mut bytes = Vec::new();
|
||||||
for b in range {
|
for b in range {
|
||||||
|
|
|
@ -2,11 +2,8 @@ use std::str::from_utf8;
|
||||||
|
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
use super::arm7tdmi::{
|
use super::arm7tdmi::{bus::Bus, Addr};
|
||||||
bus::{Bus, MemoryAccess, MemoryAccessWidth},
|
|
||||||
Addr,
|
|
||||||
};
|
|
||||||
use super::sysbus::WaitState;
|
|
||||||
use crate::util::read_bin_file;
|
use crate::util::read_bin_file;
|
||||||
|
|
||||||
/// From GBATEK
|
/// From GBATEK
|
||||||
|
@ -72,7 +69,6 @@ impl CartridgeHeader {
|
||||||
pub struct Cartridge {
|
pub struct Cartridge {
|
||||||
pub header: CartridgeHeader,
|
pub header: CartridgeHeader,
|
||||||
bytes: Box<[u8]>,
|
bytes: Box<[u8]>,
|
||||||
ws: WaitState,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cartridge {
|
impl Cartridge {
|
||||||
|
@ -88,7 +84,6 @@ impl Cartridge {
|
||||||
Ok(Cartridge {
|
Ok(Cartridge {
|
||||||
header: header,
|
header: header,
|
||||||
bytes: rom_bin.into_boxed_slice(),
|
bytes: rom_bin.into_boxed_slice(),
|
||||||
ws: WaitState::new(5, 5, 8),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,12 +120,4 @@ impl Bus for Cartridge {
|
||||||
fn write_8(&mut self, addr: Addr, value: u8) {
|
fn write_8(&mut self, addr: Addr, value: u8) {
|
||||||
(&mut self.bytes[addr as usize..]).write_u8(value).unwrap()
|
(&mut self.bytes[addr as usize..]).write_u8(value).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_cycles(&self, _addr: Addr, access: MemoryAccess) -> usize {
|
|
||||||
match access.1 {
|
|
||||||
MemoryAccessWidth::MemoryAccess8 => self.ws.access8,
|
|
||||||
MemoryAccessWidth::MemoryAccess16 => self.ws.access16,
|
|
||||||
MemoryAccessWidth::MemoryAccess32 => self.ws.access32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::arm7tdmi::{Addr, Bus, MemoryAccess};
|
use super::arm7tdmi::{Addr, Bus};
|
||||||
use super::gba::IoDevices;
|
use super::gba::IoDevices;
|
||||||
|
use super::sysbus::BoxedMemory;
|
||||||
use super::keypad;
|
use super::keypad;
|
||||||
|
|
||||||
pub mod consts {
|
pub mod consts {
|
||||||
|
@ -124,17 +125,21 @@ use consts::*;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct IoRegs {
|
pub struct IoRegs {
|
||||||
|
mem: BoxedMemory,
|
||||||
pub io: Rc<RefCell<IoDevices>>,
|
pub io: Rc<RefCell<IoDevices>>,
|
||||||
pub keyinput: u16,
|
pub keyinput: u16,
|
||||||
pub post_boot_flag: bool,
|
pub post_boot_flag: bool,
|
||||||
|
pub waitcnt: WaitControl, // TODO also implement 4000800
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IoRegs {
|
impl IoRegs {
|
||||||
pub fn new(io: Rc<RefCell<IoDevices>>) -> IoRegs {
|
pub fn new(io: Rc<RefCell<IoDevices>>) -> IoRegs {
|
||||||
IoRegs {
|
IoRegs {
|
||||||
|
mem: BoxedMemory::new(vec![0; 0x400].into_boxed_slice(), 0x3ff),
|
||||||
io: io,
|
io: io,
|
||||||
post_boot_flag: false,
|
post_boot_flag: false,
|
||||||
keyinput: keypad::KEYINPUT_ALL_RELEASED,
|
keyinput: keypad::KEYINPUT_ALL_RELEASED,
|
||||||
|
waitcnt: WaitControl(0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,12 +183,13 @@ impl Bus for IoRegs {
|
||||||
REG_TM3CNT_L => io.timers[3].timer_data,
|
REG_TM3CNT_L => io.timers[3].timer_data,
|
||||||
REG_TM3CNT_H => io.timers[3].timer_ctl.0,
|
REG_TM3CNT_H => io.timers[3].timer_ctl.0,
|
||||||
|
|
||||||
|
REG_WAITCNT => self.waitcnt.0,
|
||||||
|
|
||||||
REG_POSTFLG => self.post_boot_flag as u16,
|
REG_POSTFLG => self.post_boot_flag as u16,
|
||||||
REG_HALTCNT => 0,
|
REG_HALTCNT => 0,
|
||||||
REG_KEYINPUT => self.keyinput as u16,
|
REG_KEYINPUT => self.keyinput as u16,
|
||||||
_ => {
|
_ => {
|
||||||
println!("tried to read register {:#x}", addr + IO_BASE);
|
self.mem.read_16(addr)
|
||||||
0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,10 +259,12 @@ impl Bus for IoRegs {
|
||||||
}
|
}
|
||||||
REG_TM3CNT_H => io.timers[3].timer_ctl.0 = value,
|
REG_TM3CNT_H => io.timers[3].timer_ctl.0 = value,
|
||||||
|
|
||||||
|
REG_WAITCNT => self.waitcnt.0 = value,
|
||||||
|
|
||||||
REG_POSTFLG => self.post_boot_flag = value != 0,
|
REG_POSTFLG => self.post_boot_flag = value != 0,
|
||||||
REG_HALTCNT => {}
|
REG_HALTCNT => {}
|
||||||
_ => {
|
_ => {
|
||||||
println!("tried to write register {:#x}", addr + IO_BASE);
|
self.mem.write_16(addr, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,9 +273,21 @@ impl Bus for IoRegs {
|
||||||
let t = self.read_16(addr);
|
let t = self.read_16(addr);
|
||||||
self.write_16(addr, (t & 0xff) | ((value as u16) << 8));
|
self.write_16(addr, (t & 0xff) | ((value as u16) << 8));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/// returns the number of cycles needed for this memory access
|
|
||||||
fn get_cycles(&self, _addr: Addr, _access: MemoryAccess) -> usize {
|
bitfield! {
|
||||||
1
|
#[derive(Default, Copy, Clone, PartialEq)]
|
||||||
}
|
pub struct WaitControl(u16);
|
||||||
|
impl Debug;
|
||||||
|
u16;
|
||||||
|
sram_wait_control, _: 1, 0;
|
||||||
|
pub ws0_first_access, _: 3, 2;
|
||||||
|
pub ws0_second_access, _: 4, 4;
|
||||||
|
pub ws1_first_access, _: 6, 5;
|
||||||
|
pub ws1_second_access, _: 7, 7;
|
||||||
|
pub ws2_first_access, _: 9, 8;
|
||||||
|
pub ws2_second_access, _: 10, 10;
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
PHI_terminal_output, _: 12, 11;
|
||||||
|
prefetch, _: 14;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::fmt;
|
||||||
|
use std::ops::Add;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
use super::arm7tdmi::bus::{Bus, MemoryAccess, MemoryAccessWidth};
|
use super::arm7tdmi::bus::Bus;
|
||||||
use super::arm7tdmi::Addr;
|
use super::arm7tdmi::Addr;
|
||||||
use super::gba::IoDevices;
|
use super::gba::IoDevices;
|
||||||
|
use super::gpu::GpuState;
|
||||||
use super::{cartridge::Cartridge, ioregs::IoRegs};
|
use super::{cartridge::Cartridge, ioregs::IoRegs};
|
||||||
|
|
||||||
const VIDEO_RAM_SIZE: usize = 128 * 1024;
|
const VIDEO_RAM_SIZE: usize = 128 * 1024;
|
||||||
|
@ -14,10 +17,63 @@ const INTERNAL_RAM_SIZE: usize = 32 * 1024;
|
||||||
const PALETTE_RAM_SIZE: usize = 1 * 1024;
|
const PALETTE_RAM_SIZE: usize = 1 * 1024;
|
||||||
const OAM_SIZE: usize = 1 * 1024;
|
const OAM_SIZE: usize = 1 * 1024;
|
||||||
|
|
||||||
|
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;
|
||||||
|
pub const GAMEPAK_WS0_ADDR: u32 = 0x0800_0000;
|
||||||
|
pub const GAMEPAK_WS1_ADDR: u32 = 0x0A00_0000;
|
||||||
|
pub const GAMEPAK_WS2_ADDR: u32 = 0x0C00_0000;
|
||||||
|
|
||||||
|
#[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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BoxedMemory {
|
pub struct BoxedMemory {
|
||||||
pub mem: Box<[u8]>,
|
pub mem: Box<[u8]>,
|
||||||
ws: WaitState,
|
|
||||||
mask: u32,
|
mask: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,40 +82,8 @@ impl BoxedMemory {
|
||||||
BoxedMemory {
|
BoxedMemory {
|
||||||
mem: boxed_slice,
|
mem: boxed_slice,
|
||||||
mask: mask,
|
mask: mask,
|
||||||
ws: WaitState::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_with_waitstate(boxed_slice: Box<[u8]>, mask: u32, ws: WaitState) -> BoxedMemory {
|
|
||||||
BoxedMemory {
|
|
||||||
mem: boxed_slice,
|
|
||||||
mask: mask,
|
|
||||||
ws: ws,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct WaitState {
|
|
||||||
pub access8: usize,
|
|
||||||
pub access16: usize,
|
|
||||||
pub access32: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl WaitState {
|
|
||||||
pub fn new(access8: usize, access16: usize, access32: usize) -> WaitState {
|
|
||||||
WaitState {
|
|
||||||
access8,
|
|
||||||
access16,
|
|
||||||
access32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for WaitState {
|
|
||||||
fn default() -> WaitState {
|
|
||||||
WaitState::new(1, 1, 1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bus for BoxedMemory {
|
impl Bus for BoxedMemory {
|
||||||
|
@ -96,14 +120,6 @@ impl Bus for BoxedMemory {
|
||||||
.write_u8(value)
|
.write_u8(value)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_cycles(&self, _addr: Addr, access: MemoryAccess) -> usize {
|
|
||||||
match access.1 {
|
|
||||||
MemoryAccessWidth::MemoryAccess8 => self.ws.access8,
|
|
||||||
MemoryAccessWidth::MemoryAccess16 => self.ws.access16,
|
|
||||||
MemoryAccessWidth::MemoryAccess32 => self.ws.access32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -127,10 +143,6 @@ impl Bus for DummyBus {
|
||||||
fn write_16(&mut self, _addr: Addr, _value: u16) {}
|
fn write_16(&mut self, _addr: Addr, _value: u16) {}
|
||||||
|
|
||||||
fn write_8(&mut self, _addr: Addr, _value: u8) {}
|
fn write_8(&mut self, _addr: Addr, _value: u8) {}
|
||||||
|
|
||||||
fn get_cycles(&self, _addr: Addr, _access: MemoryAccess) -> usize {
|
|
||||||
1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -160,25 +172,22 @@ impl SysBus {
|
||||||
io: io,
|
io: io,
|
||||||
|
|
||||||
bios: BoxedMemory::new(bios_rom.into_boxed_slice(), 0xff_ffff),
|
bios: BoxedMemory::new(bios_rom.into_boxed_slice(), 0xff_ffff),
|
||||||
onboard_work_ram: BoxedMemory::new_with_waitstate(
|
onboard_work_ram: BoxedMemory::new(
|
||||||
vec![0; WORK_RAM_SIZE].into_boxed_slice(),
|
vec![0; WORK_RAM_SIZE].into_boxed_slice(),
|
||||||
(WORK_RAM_SIZE as u32) - 1,
|
(WORK_RAM_SIZE as u32) - 1,
|
||||||
WaitState::new(3, 3, 6),
|
|
||||||
),
|
),
|
||||||
internal_work_ram: BoxedMemory::new(
|
internal_work_ram: BoxedMemory::new(
|
||||||
vec![0; INTERNAL_RAM_SIZE].into_boxed_slice(),
|
vec![0; INTERNAL_RAM_SIZE].into_boxed_slice(),
|
||||||
0x7fff,
|
0x7fff,
|
||||||
),
|
),
|
||||||
ioregs: ioregs,
|
ioregs: ioregs,
|
||||||
palette_ram: BoxedMemory::new_with_waitstate(
|
palette_ram: BoxedMemory::new(
|
||||||
vec![0; PALETTE_RAM_SIZE].into_boxed_slice(),
|
vec![0; PALETTE_RAM_SIZE].into_boxed_slice(),
|
||||||
(PALETTE_RAM_SIZE as u32) - 1,
|
(PALETTE_RAM_SIZE as u32) - 1,
|
||||||
WaitState::new(1, 1, 2),
|
|
||||||
),
|
),
|
||||||
vram: BoxedMemory::new_with_waitstate(
|
vram: BoxedMemory::new(
|
||||||
vec![0; VIDEO_RAM_SIZE].into_boxed_slice(),
|
vec![0; VIDEO_RAM_SIZE].into_boxed_slice(),
|
||||||
(VIDEO_RAM_SIZE as u32) - 1,
|
(VIDEO_RAM_SIZE as u32) - 1,
|
||||||
WaitState::new(1, 1, 2),
|
|
||||||
),
|
),
|
||||||
oam: BoxedMemory::new(vec![0; OAM_SIZE].into_boxed_slice(), (OAM_SIZE as u32) - 1),
|
oam: BoxedMemory::new(vec![0; OAM_SIZE].into_boxed_slice(), (OAM_SIZE as u32) - 1),
|
||||||
gamepak: gamepak,
|
gamepak: gamepak,
|
||||||
|
@ -187,33 +196,86 @@ impl SysBus {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn map(&self, addr: Addr) -> &Bus {
|
fn map(&self, addr: Addr) -> &Bus {
|
||||||
match (addr & 0xff000000) as usize {
|
match addr & 0xff000000 {
|
||||||
0x00000000 => &self.bios,
|
BIOS_ADDR => &self.bios,
|
||||||
0x02000000 => &self.onboard_work_ram,
|
EWRAM_ADDR => &self.onboard_work_ram,
|
||||||
0x03000000 => &self.internal_work_ram,
|
IWRAM_ADDR => &self.internal_work_ram,
|
||||||
0x04000000 => &self.ioregs,
|
IOMEM_ADDR => &self.ioregs,
|
||||||
0x05000000 => &self.palette_ram,
|
PALRAM_ADDR => &self.palette_ram,
|
||||||
0x06000000 => &self.vram,
|
VRAM_ADDR => &self.vram,
|
||||||
0x07000000 => &self.oam,
|
OAM_ADDR => &self.oam,
|
||||||
0x08000000 => &self.gamepak,
|
GAMEPAK_WS0_ADDR | GAMEPAK_WS1_ADDR | GAMEPAK_WS2_ADDR => &self.gamepak,
|
||||||
_ => &self.dummy,
|
_ => &self.dummy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO proc-macro for generating this function
|
/// TODO proc-macro for generating this function
|
||||||
fn map_mut(&mut self, addr: Addr) -> &mut Bus {
|
fn map_mut(&mut self, addr: Addr) -> &mut Bus {
|
||||||
match (addr & 0xff000000) as usize {
|
match addr & 0xff000000 {
|
||||||
0x00000000 => &mut self.bios,
|
BIOS_ADDR => &mut self.bios,
|
||||||
0x02000000 => &mut self.onboard_work_ram,
|
EWRAM_ADDR => &mut self.onboard_work_ram,
|
||||||
0x03000000 => &mut self.internal_work_ram,
|
IWRAM_ADDR => &mut self.internal_work_ram,
|
||||||
0x04000000 => &mut self.ioregs,
|
IOMEM_ADDR => &mut self.ioregs,
|
||||||
0x05000000 => &mut self.palette_ram,
|
PALRAM_ADDR => &mut self.palette_ram,
|
||||||
0x06000000 => &mut self.vram,
|
VRAM_ADDR => &mut self.vram,
|
||||||
0x07000000 => &mut self.oam,
|
OAM_ADDR => &mut self.oam,
|
||||||
0x08000000 => &mut self.gamepak,
|
GAMEPAK_WS0_ADDR | GAMEPAK_WS1_ADDR | GAMEPAK_WS2_ADDR => &mut self.gamepak,
|
||||||
_ => &mut self.dummy,
|
_ => &mut self.dummy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
EWRAM_ADDR => {
|
||||||
|
match access.1 {
|
||||||
|
MemoryAccessWidth::MemoryAccess32 => cycles += 6,
|
||||||
|
_ => cycles += 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OAM_ADDR | VRAM_ADDR | PALRAM_ADDR => {
|
||||||
|
match access.1 {
|
||||||
|
MemoryAccessWidth::MemoryAccess32 => cycles += 2,
|
||||||
|
_ => cycles += 1
|
||||||
|
}
|
||||||
|
if self.io.borrow().gpu.state == GpuState::HDraw {
|
||||||
|
cycles += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GAMEPAK_WS0_ADDR => {
|
||||||
|
match access.0 {
|
||||||
|
MemoryAccessType::NonSeq => {
|
||||||
|
match access.1 {
|
||||||
|
MemoryAccessWidth::MemoryAccess32 => {
|
||||||
|
cycles += nonseq_cycles[self.ioregs.waitcnt.ws0_first_access() as usize];
|
||||||
|
cycles += seq_cycles[self.ioregs.waitcnt.ws0_second_access() as usize];
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
cycles += nonseq_cycles[self.ioregs.waitcnt.ws0_first_access() as usize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MemoryAccessType::Seq => {
|
||||||
|
cycles += seq_cycles[self.ioregs.waitcnt.ws0_second_access() as usize];
|
||||||
|
if access.1 == MemoryAccessWidth::MemoryAccess32 {
|
||||||
|
cycles += seq_cycles[self.ioregs.waitcnt.ws0_second_access() as usize];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
GAMEPAK_WS1_ADDR | GAMEPAK_WS2_ADDR => {
|
||||||
|
panic!("unimplemented - need to refactor code with a nice macro :(")
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
cycles
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bus for SysBus {
|
impl Bus for SysBus {
|
||||||
|
@ -240,8 +302,4 @@ impl Bus for SysBus {
|
||||||
fn write_8(&mut self, addr: Addr, value: u8) {
|
fn write_8(&mut self, addr: Addr, value: u8) {
|
||||||
self.map_mut(addr).write_8(addr & 0xff_ffff, value)
|
self.map_mut(addr).write_8(addr & 0xff_ffff, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_cycles(&self, addr: Addr, access: MemoryAccess) -> usize {
|
|
||||||
self.map(addr).get_cycles(addr & 0xff_ffff, access)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue