Implement all memory mappings. Reformat many files.
Former-commit-id: c0a62b610e62d2db2a4daf4aeef40068820daa52
This commit is contained in:
parent
22c175d9cc
commit
6b225d776d
|
@ -1,10 +1,10 @@
|
|||
use std::fmt;
|
||||
|
||||
use crate::arm7tdmi::{Addr, reg_string, REG_PC};
|
||||
use super::{
|
||||
ArmCond, ArmHalfwordTransferType, ArmInstruction, ArmInstructionFormat, ArmOpCode,
|
||||
ArmRegisterShift, ArmShiftType, ArmShiftedValue,
|
||||
};
|
||||
use crate::arm7tdmi::{reg_string, Addr, REG_PC};
|
||||
|
||||
impl fmt::Display for ArmCond {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::bit::BitIndex;
|
||||
|
||||
use crate::arm7tdmi::bus::{Bus, MemoryAccess, MemoryAccessType::*, MemoryAccessWidth::*};
|
||||
use crate::arm7tdmi::cpu::{Core, CpuExecResult, CpuPipelineAction};
|
||||
use crate::arm7tdmi::exception::Exception;
|
||||
use crate::arm7tdmi::bus::{Bus, MemoryAccess, MemoryAccessType::*, MemoryAccessWidth::*};
|
||||
use crate::arm7tdmi::{Addr, CpuError, CpuInstruction, CpuResult, CpuState, REG_PC};
|
||||
|
||||
use crate::sysbus::SysBus;
|
||||
|
@ -15,7 +15,11 @@ use super::{
|
|||
impl Core {
|
||||
pub fn exec_arm(&mut self, sysbus: &mut SysBus, insn: ArmInstruction) -> CpuExecResult {
|
||||
if !self.check_arm_cond(insn.cond) {
|
||||
self.add_cycles(self.pc + (self.word_size() as u32), sysbus, Seq + MemoryAccess32);
|
||||
self.add_cycles(
|
||||
insn.pc + (self.word_size() as u32),
|
||||
sysbus,
|
||||
Seq + MemoryAccess32,
|
||||
);
|
||||
self.add_cycle();
|
||||
return Ok(CpuPipelineAction::IncPC);
|
||||
}
|
||||
|
@ -48,7 +52,11 @@ impl Core {
|
|||
|
||||
// +2S
|
||||
self.add_cycles(self.pc, sysbus, Seq + MemoryAccess32);
|
||||
self.add_cycles(self.pc + (self.word_size() as u32), sysbus, Seq + MemoryAccess32);
|
||||
self.add_cycles(
|
||||
self.pc + (self.word_size() as u32),
|
||||
sysbus,
|
||||
Seq + MemoryAccess32,
|
||||
);
|
||||
|
||||
Ok(CpuPipelineAction::Flush)
|
||||
}
|
||||
|
@ -73,7 +81,11 @@ impl Core {
|
|||
|
||||
// +2S
|
||||
self.add_cycles(self.pc, sysbus, Seq + MemoryAccess32);
|
||||
self.add_cycles(self.pc + (self.word_size() as u32), sysbus, Seq + MemoryAccess32);
|
||||
self.add_cycles(
|
||||
self.pc + (self.word_size() as u32),
|
||||
sysbus,
|
||||
Seq + MemoryAccess32,
|
||||
);
|
||||
|
||||
Ok(CpuPipelineAction::Flush)
|
||||
}
|
||||
|
@ -208,7 +220,11 @@ impl Core {
|
|||
}
|
||||
|
||||
// +1S
|
||||
self.add_cycles(self.pc + (self.word_size() as u32), sysbus, Seq + MemoryAccess32);
|
||||
self.add_cycles(
|
||||
self.pc + (self.word_size() as u32),
|
||||
sysbus,
|
||||
Seq + MemoryAccess32,
|
||||
);
|
||||
Ok(pipeline_action)
|
||||
}
|
||||
|
||||
|
@ -219,12 +235,16 @@ impl Core {
|
|||
ArmShiftedValue::ShiftedRegister {
|
||||
reg,
|
||||
shift,
|
||||
added: Some(added)
|
||||
added: Some(added),
|
||||
} => {
|
||||
let abs = self.register_shift(reg, shift).unwrap();
|
||||
if added { abs } else { -abs }
|
||||
if added {
|
||||
abs
|
||||
} else {
|
||||
-abs
|
||||
}
|
||||
_ => panic!("bad barrel shift")
|
||||
}
|
||||
_ => panic!("bad barrel shift"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,7 +260,6 @@ impl Core {
|
|||
sysbus: &mut SysBus,
|
||||
insn: ArmInstruction,
|
||||
) -> CpuResult<CpuPipelineAction> {
|
||||
|
||||
if insn.write_back_flag() && insn.rd() == insn.rn() {
|
||||
return Err(CpuError::IllegalInstruction);
|
||||
}
|
||||
|
@ -273,7 +292,11 @@ impl Core {
|
|||
sysbus.read_32(addr)
|
||||
};
|
||||
// +1S
|
||||
self.add_cycles(self.pc + (self.word_size() as u32), sysbus, Seq + MemoryAccess32);
|
||||
self.add_cycles(
|
||||
self.pc + (self.word_size() as u32),
|
||||
sysbus,
|
||||
Seq + MemoryAccess32,
|
||||
);
|
||||
|
||||
self.set_reg(insn.rd(), data);
|
||||
|
||||
|
@ -284,7 +307,11 @@ impl Core {
|
|||
// +1S
|
||||
self.add_cycles(self.pc, sysbus, Seq + MemoryAccess32);
|
||||
// +1N
|
||||
self.add_cycles(self.pc + (self.word_size() as u32), sysbus, NonSeq + MemoryAccess32);
|
||||
self.add_cycles(
|
||||
self.pc + (self.word_size() as u32),
|
||||
sysbus,
|
||||
NonSeq + MemoryAccess32,
|
||||
);
|
||||
pipeline_action = CpuPipelineAction::Flush;
|
||||
}
|
||||
} else {
|
||||
|
@ -293,11 +320,11 @@ impl Core {
|
|||
if insn.transfer_size() == 1 {
|
||||
// +1N
|
||||
self.add_cycles(dest, sysbus, NonSeq + MemoryAccess8);
|
||||
sysbus.write_8(addr, value as u8);
|
||||
sysbus.write_8(addr, value as u8).expect("bus error");
|
||||
} else {
|
||||
// +1N
|
||||
self.add_cycles(dest, sysbus, NonSeq + MemoryAccess32);
|
||||
sysbus.write_32(addr, value);
|
||||
sysbus.write_32(addr, value).expect("bus error");
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -305,6 +332,6 @@ impl Core {
|
|||
self.set_reg(insn.rn(), effective_addr as u32)
|
||||
}
|
||||
|
||||
Ok(CpuPipelineAction::IncPC)
|
||||
Ok(pipeline_action)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
use super::Addr;
|
||||
use std::io;
|
||||
use std::ops::Add;
|
||||
use super::Addr;
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
|
||||
pub enum MemoryAccessType {
|
||||
NonSeq,
|
||||
Seq
|
||||
Seq,
|
||||
}
|
||||
|
||||
pub enum MemoryAccessWidth {
|
||||
MemoryAccess8,
|
||||
MemoryAccess16,
|
||||
MemoryAccess32
|
||||
MemoryAccess32,
|
||||
}
|
||||
|
||||
|
||||
impl Add<MemoryAccessWidth> for MemoryAccessType {
|
||||
type Output = MemoryAccess;
|
||||
|
||||
|
@ -22,17 +23,40 @@ impl Add<MemoryAccessWidth> for MemoryAccessType {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct MemoryAccess(MemoryAccessType, MemoryAccessWidth);
|
||||
pub struct MemoryAccess(pub MemoryAccessType, pub MemoryAccessWidth);
|
||||
|
||||
pub trait Bus {
|
||||
fn read_32(&self, addr: Addr) -> u32;
|
||||
fn read_16(&self, addr: Addr) -> u16;
|
||||
fn read_8(&self, addr: Addr) -> u8;
|
||||
fn write_32(&mut self, addr: Addr, value: u32) -> Result<(), io::Error>;
|
||||
fn write_16(&mut self, addr: Addr, value: u16) -> Result<(), io::Error>;
|
||||
fn write_8(&mut self, addr: Addr, value: u8) -> Result<(), io::Error>;
|
||||
fn read_32(&self, addr: Addr) -> u32 {
|
||||
self.get_bytes(addr, 4).read_u32::<LittleEndian>().unwrap()
|
||||
}
|
||||
|
||||
fn read_16(&self, addr: Addr) -> u16 {
|
||||
self.get_bytes(addr, 2).read_u16::<LittleEndian>().unwrap()
|
||||
}
|
||||
|
||||
fn read_8(&self, addr: Addr) -> u8 {
|
||||
self.get_bytes(addr, 1)[0]
|
||||
}
|
||||
|
||||
fn write_32(&mut self, addr: Addr, value: u32) -> Result<(), io::Error> {
|
||||
self.get_bytes_mut(addr, 4).write_u32::<LittleEndian>(value)
|
||||
}
|
||||
|
||||
fn write_16(&mut self, addr: Addr, value: u16) -> Result<(), io::Error> {
|
||||
self.get_bytes_mut(addr, 2).write_u16::<LittleEndian>(value)
|
||||
}
|
||||
|
||||
fn write_8(&mut self, addr: Addr, value: u8) -> Result<(), io::Error> {
|
||||
self.get_bytes_mut(addr, 1).write_u8(value)
|
||||
}
|
||||
/// Return a slice of bytes
|
||||
/// Will panic if requested range is out of bounds
|
||||
fn get_bytes(&self, addr: Addr, len: usize) -> &[u8];
|
||||
|
||||
/// Return a mutable slice of bytes
|
||||
/// Will panic if requested range is out of bounds
|
||||
fn get_bytes_mut(&mut self, addr: Addr, len: usize) -> &mut [u8];
|
||||
|
||||
fn get_bytes(&self, addr: Addr, size: usize) -> Option<&[u8]>;
|
||||
/// returns the number of cycles needed for this memory access
|
||||
fn get_cycles(&self, addr: Addr, access: MemoryAccess) -> usize;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
use std::ops::Add;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::ops::Add;
|
||||
|
||||
use ansi_term::{Colour, Style};
|
||||
|
||||
|
@ -8,10 +8,10 @@ use crate::sysbus::SysBus;
|
|||
|
||||
pub use super::exception::Exception;
|
||||
use super::{
|
||||
CpuState, CpuMode, reg_string, CpuResult, Addr,
|
||||
psr::RegPSR,
|
||||
arm::*,
|
||||
bus::{Bus, MemoryAccess, MemoryAccessType::*, MemoryAccessWidth::*},
|
||||
arm::*
|
||||
psr::RegPSR,
|
||||
reg_string, Addr, CpuMode, CpuResult, CpuState,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
@ -198,9 +198,7 @@ impl Core {
|
|||
let action = self.exec_arm(sysbus, d)?;
|
||||
Ok((Some(d), action))
|
||||
}
|
||||
None => {
|
||||
Ok((None, CpuPipelineAction::IncPC))
|
||||
},
|
||||
None => Ok((None, CpuPipelineAction::IncPC)),
|
||||
};
|
||||
|
||||
self.pipeline.fetched = Some((self.pc, new_fetched));
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
use std::fmt;
|
||||
|
||||
pub mod arm;
|
||||
use arm::{ArmInstruction, ArmDecodeError};
|
||||
use arm::{ArmDecodeError, ArmInstruction};
|
||||
|
||||
pub mod cpu;
|
||||
pub use cpu::*;
|
||||
pub mod bus;
|
||||
mod exception;
|
||||
mod psr;
|
||||
|
||||
|
||||
pub const REG_PC: usize = 15;
|
||||
pub const REG_LR: usize = 14;
|
||||
pub const REG_SP: usize = 13;
|
||||
|
|
|
@ -5,11 +5,17 @@ subcommands:
|
|||
- debug:
|
||||
about: debug the bios with the arm core emulation
|
||||
args:
|
||||
- BIOS:
|
||||
- bios:
|
||||
help: Sets the bios file to use
|
||||
required: false
|
||||
default_value: gba_bios.bin
|
||||
index: 1
|
||||
- game_rom:
|
||||
short: g
|
||||
long: game-rom
|
||||
takes_value: true
|
||||
help: Sets the game-rom file to use
|
||||
required: true
|
||||
- disass:
|
||||
about: disassemble an arm binary file
|
||||
args:
|
||||
|
|
|
@ -8,9 +8,9 @@ use clap::{App, ArgMatches};
|
|||
extern crate rustboyadvance_ng;
|
||||
|
||||
use rustboyadvance_ng::arm7tdmi;
|
||||
use rustboyadvance_ng::sysbus::SysBus;
|
||||
use rustboyadvance_ng::debugger::{Debugger, DebuggerError};
|
||||
use rustboyadvance_ng::disass::Disassembler;
|
||||
use rustboyadvance_ng::sysbus::SysBus;
|
||||
use rustboyadvance_ng::util::read_bin_file;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -18,7 +18,7 @@ pub enum GBAError {
|
|||
IO(io::Error),
|
||||
ArmDecodeError(arm7tdmi::arm::ArmDecodeError),
|
||||
CpuError(arm7tdmi::CpuError),
|
||||
DebuggerError(DebuggerError)
|
||||
DebuggerError(DebuggerError),
|
||||
}
|
||||
|
||||
pub type GBAResult<T> = Result<T, GBAError>;
|
||||
|
@ -59,11 +59,10 @@ fn run_disass(matches: &ArgMatches) -> GBAResult<()> {
|
|||
}
|
||||
|
||||
fn run_debug(matches: &ArgMatches) -> GBAResult<()> {
|
||||
let gba_bios_path = matches.value_of("BIOS").unwrap_or_default();
|
||||
println!("Loading BIOS: {}", gba_bios_path);
|
||||
let bios_bin = read_bin_file(gba_bios_path)?;
|
||||
let bios_bin = read_bin_file(matches.value_of("bios").unwrap_or_default())?;
|
||||
let rom_bin = read_bin_file(matches.value_of("game_rom").unwrap())?;
|
||||
|
||||
let sysbus = SysBus::new(bios_bin);
|
||||
let sysbus = SysBus::new(bios_bin, rom_bin);
|
||||
let mut core = arm7tdmi::cpu::Core::new();
|
||||
core.reset();
|
||||
core.set_verbose(true);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::arm7tdmi::{reg_string, REG_PC};
|
||||
use crate::arm7tdmi::bus::Bus;
|
||||
use crate::arm7tdmi::{reg_string, REG_PC};
|
||||
use crate::debugger::Debugger;
|
||||
use crate::disass::Disassembler;
|
||||
|
||||
|
@ -63,7 +63,7 @@ impl Command {
|
|||
insn.pc,
|
||||
Colour::Yellow.italic().paint(format!("{} ", insn))
|
||||
);
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
println!("{}: {}", "cpu encountered an error".red(), e);
|
||||
println!("cpu: {:x?}", debugger.cpu);
|
||||
|
@ -72,23 +72,11 @@ impl Command {
|
|||
};
|
||||
},
|
||||
HexDump(addr, nbytes) => {
|
||||
let bytes = match debugger.sysbus.get_bytes(addr, nbytes) {
|
||||
Some(bytes) => bytes,
|
||||
None => {
|
||||
println!("requested content out of bounds");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let bytes = debugger.sysbus.get_bytes(addr, nbytes);
|
||||
hexdump::hexdump(bytes);
|
||||
}
|
||||
Disass(addr, n) => {
|
||||
let bytes = match debugger.sysbus.get_bytes(addr, 4 * n) {
|
||||
Some(bytes) => bytes,
|
||||
None => {
|
||||
println!("requested content out of bounds");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let bytes = debugger.sysbus.get_bytes(addr, 4 * n);
|
||||
let disass = Disassembler::new(addr, bytes);
|
||||
for (_, line) in disass {
|
||||
println!("{}", line)
|
||||
|
|
|
@ -171,7 +171,7 @@ impl Debugger {
|
|||
if let Some(Command::Disass(addr, n)) = self.previous_command {
|
||||
(addr + (4 * n as u32), 10)
|
||||
} else {
|
||||
(self.cpu.get_reg(15), 10)
|
||||
(self.cpu.get_next_pc(), 10)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
|
181
src/sysbus.rs
181
src/sysbus.rs
|
@ -1,132 +1,163 @@
|
|||
use std::io;
|
||||
|
||||
use super::arm7tdmi::bus::{Bus, MemoryAccess, MemoryAccessWidth};
|
||||
use super::arm7tdmi::Addr;
|
||||
use super::arm7tdmi::bus::{Bus, MemoryAccess, MemoryAccessType, MemoryAccessWidth};
|
||||
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
|
||||
const VIDEO_RAM_SIZE: usize = 128 * 1024;
|
||||
const WORK_RAM_SIZE: usize = 256 * 1024;
|
||||
const INTERNAL_RAM: usize = 32 * 1024;
|
||||
const PALETTE_AM_SIZE: usize = 1 * 1024;
|
||||
const PALETTE_RAM_SIZE: usize = 1 * 1024;
|
||||
const OAM_SIZE: usize = 1 * 1024;
|
||||
const BIOS_SIZE: usize = 16 * 1024;
|
||||
const GAMEPAK_ROM_SIZE: usize = 32 * 1024 * 1024;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct BiosROM(Vec<u8>);
|
||||
|
||||
impl Bus for BiosROM {
|
||||
fn read_32(&self, addr: Addr) -> u32 {
|
||||
let addr = addr as usize;
|
||||
(&self.0[addr..addr + 4])
|
||||
.read_u32::<LittleEndian>()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn read_16(&self, addr: Addr) -> u16 {
|
||||
let addr = addr as usize;
|
||||
(&self.0[addr..addr + 4])
|
||||
.read_u16::<LittleEndian>()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn read_8(&self, addr: Addr) -> u8 {
|
||||
self.0[addr as usize]
|
||||
}
|
||||
|
||||
fn write_32(&mut self, addr: Addr, value: u32) -> Result<(), io::Error> {
|
||||
let mut wrt = io::Cursor::new(&mut self.0);
|
||||
wrt.set_position(addr as u64);
|
||||
wrt.write_u32::<LittleEndian>(value)
|
||||
}
|
||||
|
||||
fn write_16(&mut self, addr: Addr, value: u16) -> Result<(), io::Error> {
|
||||
let mut wrt = io::Cursor::new(&mut self.0);
|
||||
wrt.set_position(addr as u64);
|
||||
wrt.write_u16::<LittleEndian>(value)
|
||||
}
|
||||
|
||||
fn write_8(&mut self, addr: Addr, value: u8) -> Result<(), io::Error> {
|
||||
let mut wrt = io::Cursor::new(&mut self.0);
|
||||
wrt.write_u8(value)
|
||||
}
|
||||
|
||||
fn get_bytes(&self, addr: Addr, size: usize) -> Option<&[u8]> {
|
||||
let addr = addr as usize;
|
||||
if addr + size > self.0.len() {
|
||||
None
|
||||
} else {
|
||||
Some(&self.0[addr..addr + size])
|
||||
}
|
||||
}
|
||||
|
||||
fn get_cycles(&self, _addr: Addr, _access: MemoryAccess) -> usize {
|
||||
1
|
||||
}
|
||||
}
|
||||
struct BoxedMemory(Box<[u8]>, WaitState);
|
||||
|
||||
#[derive(Debug)]
|
||||
enum SysBusDevice {
|
||||
BiosROM(BiosROM)
|
||||
struct WaitState {
|
||||
access8: usize,
|
||||
access16: usize,
|
||||
access32: usize,
|
||||
}
|
||||
|
||||
impl WaitState {
|
||||
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 {
|
||||
fn get_bytes(&self, addr: Addr, size: usize) -> &[u8] {
|
||||
let addr = addr as usize;
|
||||
&self.0[addr..addr + size]
|
||||
}
|
||||
|
||||
fn get_bytes_mut(&mut self, addr: Addr, size: usize) -> &mut [u8] {
|
||||
let addr = addr as usize;
|
||||
&mut self.0[addr..addr + size]
|
||||
}
|
||||
|
||||
fn get_cycles(&self, _addr: Addr, access: MemoryAccess) -> usize {
|
||||
match access.1 {
|
||||
MemoryAccessWidth::MemoryAccess8 => self.1.access8,
|
||||
MemoryAccessWidth::MemoryAccess16 => self.1.access16,
|
||||
MemoryAccessWidth::MemoryAccess32 => self.1.access32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SysBus {
|
||||
bios: BiosROM
|
||||
bios: BoxedMemory,
|
||||
onboard_work_ram: BoxedMemory,
|
||||
internal_work_ram: BoxedMemory,
|
||||
/// Currently model the IOMem as regular buffer, later make it into something more sophisticated.
|
||||
ioregs: BoxedMemory,
|
||||
palette_ram: BoxedMemory,
|
||||
vram: BoxedMemory,
|
||||
oam: BoxedMemory,
|
||||
gamepak_flashrom: BoxedMemory,
|
||||
}
|
||||
|
||||
impl SysBus {
|
||||
pub fn new(bios_rom: Vec<u8>) -> SysBus {
|
||||
SysBus { bios: BiosROM(bios_rom) }
|
||||
pub fn new(bios_rom: Vec<u8>, game_rom: Vec<u8>) -> SysBus {
|
||||
SysBus {
|
||||
bios: BoxedMemory(bios_rom.into_boxed_slice(), Default::default()),
|
||||
onboard_work_ram: BoxedMemory(
|
||||
vec![0; WORK_RAM_SIZE].into_boxed_slice(),
|
||||
Default::default(),
|
||||
),
|
||||
internal_work_ram: BoxedMemory(
|
||||
vec![0; INTERNAL_RAM].into_boxed_slice(),
|
||||
Default::default(),
|
||||
),
|
||||
ioregs: BoxedMemory(vec![0; 1024].into_boxed_slice(), Default::default()),
|
||||
palette_ram: BoxedMemory(
|
||||
vec![0; PALETTE_RAM_SIZE].into_boxed_slice(),
|
||||
WaitState::new(1, 1, 2),
|
||||
),
|
||||
vram: BoxedMemory(
|
||||
vec![0; VIDEO_RAM_SIZE].into_boxed_slice(),
|
||||
WaitState::new(1, 1, 2),
|
||||
),
|
||||
oam: BoxedMemory(vec![0; OAM_SIZE].into_boxed_slice(), Default::default()),
|
||||
gamepak_flashrom: BoxedMemory(game_rom.into_boxed_slice(), WaitState::new(5, 5, 8)),
|
||||
}
|
||||
}
|
||||
|
||||
fn map(&self, addr: Addr) -> &impl Bus {
|
||||
match addr as usize {
|
||||
0...BIOS_SIZE => &self.bios,
|
||||
_ => panic!("unmapped address")
|
||||
0x0000_0000...0x0000_3fff => &self.bios,
|
||||
0x0200_0000...0x0203_ffff => &self.onboard_work_ram,
|
||||
0x0300_0000...0x0300_7fff => &self.internal_work_ram,
|
||||
0x0400_0000...0x0400_03fe => &self.ioregs,
|
||||
0x0500_0000...0x0500_03ff => &self.palette_ram,
|
||||
0x0600_0000...0x0601_7fff => &self.vram,
|
||||
0x0700_0000...0x0700_03ff => &self.oam,
|
||||
0x0800_0000...0x09ff_ffff => &self.gamepak_flashrom,
|
||||
_ => panic!("unmapped address @0x{:08x}", addr),
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO proc-macro for generating this function
|
||||
fn map_mut(&mut self, addr: Addr) -> &mut impl Bus {
|
||||
match addr as usize {
|
||||
0...BIOS_SIZE => &mut self.bios,
|
||||
_ => panic!("unmapped address")
|
||||
0x0000_0000...0x0000_3fff => &mut self.bios,
|
||||
0x0200_0000...0x0203_ffff => &mut self.onboard_work_ram,
|
||||
0x0300_0000...0x0300_7fff => &mut self.internal_work_ram,
|
||||
0x0400_0000...0x0400_03fe => &mut self.ioregs,
|
||||
0x0500_0000...0x0500_03ff => &mut self.palette_ram,
|
||||
0x0600_0000...0x0601_7fff => &mut self.vram,
|
||||
0x0700_0000...0x0700_03ff => &mut self.oam,
|
||||
0x0800_0000...0x09ff_ffff => &mut self.gamepak_flashrom,
|
||||
_ => panic!("unmapped address @0x{:08x}", addr),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Bus for SysBus {
|
||||
fn read_32(&self, addr: Addr) -> u32 {
|
||||
self.map(addr).read_32(addr)
|
||||
self.map(addr).read_32(addr & 0xff_ffff)
|
||||
}
|
||||
|
||||
fn read_16(&self, addr: Addr) -> u16 {
|
||||
self.map(addr).read_16(addr)
|
||||
self.map(addr).read_16(addr & 0xff_ffff)
|
||||
}
|
||||
|
||||
fn read_8(&self, addr: Addr) -> u8 {
|
||||
self.map(addr).read_8(addr)
|
||||
self.map(addr).read_8(addr & 0xff_ffff)
|
||||
}
|
||||
|
||||
fn write_32(&mut self, addr: Addr, value: u32) -> Result<(), io::Error> {
|
||||
self.map_mut(addr).write_32(addr, value)
|
||||
self.map_mut(addr).write_32(addr & 0xff_ffff, value)
|
||||
}
|
||||
|
||||
fn write_16(&mut self, addr: Addr, value: u16) -> Result<(), io::Error> {
|
||||
self.map_mut(addr).write_16(addr, value)
|
||||
self.map_mut(addr).write_16(addr & 0xff_ffff, value)
|
||||
}
|
||||
|
||||
fn write_8(&mut self, addr: Addr, value: u8) -> Result<(), io::Error> {
|
||||
self.map_mut(addr).write_8(addr, value)
|
||||
self.map_mut(addr).write_8(addr & 0xff_ffff, value)
|
||||
}
|
||||
|
||||
fn get_bytes(&self, addr: Addr, size: usize) -> &[u8] {
|
||||
self.map(addr).get_bytes(addr & 0xff_ffff, size)
|
||||
}
|
||||
|
||||
fn get_bytes(&self, addr: Addr, size: usize) -> Option<&[u8]> {
|
||||
self.map(addr).get_bytes(addr, size)
|
||||
fn get_bytes_mut(&mut self, addr: Addr, size: usize) -> &mut [u8] {
|
||||
self.map_mut(addr).get_bytes_mut(addr & 0xff_ffff, size)
|
||||
}
|
||||
|
||||
fn get_cycles(&self, addr: Addr, access: MemoryAccess) -> usize {
|
||||
self.map(addr).get_cycles(addr, access)
|
||||
self.map(addr).get_cycles(addr & 0xff_ffff, access)
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue