diff --git a/src/core/arm7tdmi/bus.rs b/src/core/arm7tdmi/bus.rs index 71d82e5..eff414f 100644 --- a/src/core/arm7tdmi/bus.rs +++ b/src/core/arm7tdmi/bus.rs @@ -1,51 +1,5 @@ -use std::fmt; -use std::ops::Add; - 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 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 { fn read_32(&self, addr: Addr) -> u32; 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_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) -> Vec { let mut bytes = Vec::new(); for b in range { diff --git a/src/core/cartridge.rs b/src/core/cartridge.rs index 6934f5d..68e091d 100644 --- a/src/core/cartridge.rs +++ b/src/core/cartridge.rs @@ -2,11 +2,8 @@ use std::str::from_utf8; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; -use super::arm7tdmi::{ - bus::{Bus, MemoryAccess, MemoryAccessWidth}, - Addr, -}; -use super::sysbus::WaitState; +use super::arm7tdmi::{bus::Bus, Addr}; + use crate::util::read_bin_file; /// From GBATEK @@ -72,7 +69,6 @@ impl CartridgeHeader { pub struct Cartridge { pub header: CartridgeHeader, bytes: Box<[u8]>, - ws: WaitState, } impl Cartridge { @@ -88,7 +84,6 @@ impl Cartridge { Ok(Cartridge { header: header, 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) { (&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, - } - } } diff --git a/src/core/ioregs.rs b/src/core/ioregs.rs index fdebd83..a168a3c 100644 --- a/src/core/ioregs.rs +++ b/src/core/ioregs.rs @@ -1,8 +1,9 @@ use std::cell::RefCell; use std::rc::Rc; -use super::arm7tdmi::{Addr, Bus, MemoryAccess}; +use super::arm7tdmi::{Addr, Bus}; use super::gba::IoDevices; +use super::sysbus::BoxedMemory; use super::keypad; pub mod consts { @@ -124,17 +125,21 @@ use consts::*; #[derive(Debug)] pub struct IoRegs { + mem: BoxedMemory, pub io: Rc>, pub keyinput: u16, pub post_boot_flag: bool, + pub waitcnt: WaitControl, // TODO also implement 4000800 } impl IoRegs { pub fn new(io: Rc>) -> IoRegs { IoRegs { + mem: BoxedMemory::new(vec![0; 0x400].into_boxed_slice(), 0x3ff), io: io, post_boot_flag: false, 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_H => io.timers[3].timer_ctl.0, + REG_WAITCNT => self.waitcnt.0, + REG_POSTFLG => self.post_boot_flag as u16, REG_HALTCNT => 0, REG_KEYINPUT => self.keyinput as u16, _ => { - println!("tried to read register {:#x}", addr + IO_BASE); - 0 + self.mem.read_16(addr) } } } @@ -253,10 +259,12 @@ impl Bus for IoRegs { } 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_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); 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 { - 1 - } +} + +bitfield! { + #[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; } diff --git a/src/core/sysbus.rs b/src/core/sysbus.rs index 93c41d3..0ef4358 100644 --- a/src/core/sysbus.rs +++ b/src/core/sysbus.rs @@ -1,11 +1,14 @@ use std::cell::RefCell; +use std::fmt; +use std::ops::Add; use std::rc::Rc; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; -use super::arm7tdmi::bus::{Bus, MemoryAccess, MemoryAccessWidth}; +use super::arm7tdmi::bus::Bus; use super::arm7tdmi::Addr; use super::gba::IoDevices; +use super::gpu::GpuState; use super::{cartridge::Cartridge, ioregs::IoRegs}; 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 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 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)] pub struct BoxedMemory { pub mem: Box<[u8]>, - ws: WaitState, mask: u32, } @@ -26,40 +82,8 @@ impl BoxedMemory { BoxedMemory { mem: boxed_slice, 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 { @@ -96,14 +120,6 @@ impl Bus for BoxedMemory { .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, - } - } } #[derive(Debug)] @@ -127,10 +143,6 @@ impl Bus for DummyBus { fn write_16(&mut self, _addr: Addr, _value: u16) {} fn write_8(&mut self, _addr: Addr, _value: u8) {} - - fn get_cycles(&self, _addr: Addr, _access: MemoryAccess) -> usize { - 1 - } } #[derive(Debug)] @@ -160,25 +172,22 @@ impl SysBus { io: io, 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(), (WORK_RAM_SIZE as u32) - 1, - WaitState::new(3, 3, 6), ), internal_work_ram: BoxedMemory::new( vec![0; INTERNAL_RAM_SIZE].into_boxed_slice(), 0x7fff, ), ioregs: ioregs, - palette_ram: BoxedMemory::new_with_waitstate( + palette_ram: BoxedMemory::new( vec![0; PALETTE_RAM_SIZE].into_boxed_slice(), (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(), (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), gamepak: gamepak, @@ -187,33 +196,86 @@ impl SysBus { } fn map(&self, addr: Addr) -> &Bus { - match (addr & 0xff000000) as usize { - 0x00000000 => &self.bios, - 0x02000000 => &self.onboard_work_ram, - 0x03000000 => &self.internal_work_ram, - 0x04000000 => &self.ioregs, - 0x05000000 => &self.palette_ram, - 0x06000000 => &self.vram, - 0x07000000 => &self.oam, - 0x08000000 => &self.gamepak, + match addr & 0xff000000 { + BIOS_ADDR => &self.bios, + EWRAM_ADDR => &self.onboard_work_ram, + IWRAM_ADDR => &self.internal_work_ram, + IOMEM_ADDR => &self.ioregs, + PALRAM_ADDR => &self.palette_ram, + VRAM_ADDR => &self.vram, + OAM_ADDR => &self.oam, + GAMEPAK_WS0_ADDR | GAMEPAK_WS1_ADDR | GAMEPAK_WS2_ADDR => &self.gamepak, _ => &self.dummy, } } /// TODO proc-macro for generating this function fn map_mut(&mut self, addr: Addr) -> &mut Bus { - match (addr & 0xff000000) as usize { - 0x00000000 => &mut self.bios, - 0x02000000 => &mut self.onboard_work_ram, - 0x03000000 => &mut self.internal_work_ram, - 0x04000000 => &mut self.ioregs, - 0x05000000 => &mut self.palette_ram, - 0x06000000 => &mut self.vram, - 0x07000000 => &mut self.oam, - 0x08000000 => &mut self.gamepak, + match addr & 0xff000000 { + BIOS_ADDR => &mut self.bios, + EWRAM_ADDR => &mut self.onboard_work_ram, + IWRAM_ADDR => &mut self.internal_work_ram, + IOMEM_ADDR => &mut self.ioregs, + PALRAM_ADDR => &mut self.palette_ram, + VRAM_ADDR => &mut self.vram, + OAM_ADDR => &mut self.oam, + GAMEPAK_WS0_ADDR | GAMEPAK_WS1_ADDR | GAMEPAK_WS2_ADDR => &mut self.gamepak, _ => &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 { @@ -240,8 +302,4 @@ impl Bus for SysBus { fn write_8(&mut self, addr: Addr, value: u8) { 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) - } }