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 std::fmt;
|
||||||
|
|
||||||
use crate::arm7tdmi::{Addr, reg_string, REG_PC};
|
|
||||||
use super::{
|
use super::{
|
||||||
ArmCond, ArmHalfwordTransferType, ArmInstruction, ArmInstructionFormat, ArmOpCode,
|
ArmCond, ArmHalfwordTransferType, ArmInstruction, ArmInstructionFormat, ArmOpCode,
|
||||||
ArmRegisterShift, ArmShiftType, ArmShiftedValue,
|
ArmRegisterShift, ArmShiftType, ArmShiftedValue,
|
||||||
};
|
};
|
||||||
|
use crate::arm7tdmi::{reg_string, Addr, REG_PC};
|
||||||
|
|
||||||
impl fmt::Display for ArmCond {
|
impl fmt::Display for ArmCond {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::bit::BitIndex;
|
use crate::bit::BitIndex;
|
||||||
|
|
||||||
|
use crate::arm7tdmi::bus::{Bus, MemoryAccess, MemoryAccessType::*, MemoryAccessWidth::*};
|
||||||
use crate::arm7tdmi::cpu::{Core, CpuExecResult, CpuPipelineAction};
|
use crate::arm7tdmi::cpu::{Core, CpuExecResult, CpuPipelineAction};
|
||||||
use crate::arm7tdmi::exception::Exception;
|
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::arm7tdmi::{Addr, CpuError, CpuInstruction, CpuResult, CpuState, REG_PC};
|
||||||
|
|
||||||
use crate::sysbus::SysBus;
|
use crate::sysbus::SysBus;
|
||||||
|
@ -15,7 +15,11 @@ use super::{
|
||||||
impl Core {
|
impl Core {
|
||||||
pub fn exec_arm(&mut self, sysbus: &mut SysBus, insn: ArmInstruction) -> CpuExecResult {
|
pub fn exec_arm(&mut self, sysbus: &mut SysBus, insn: ArmInstruction) -> CpuExecResult {
|
||||||
if !self.check_arm_cond(insn.cond) {
|
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();
|
self.add_cycle();
|
||||||
return Ok(CpuPipelineAction::IncPC);
|
return Ok(CpuPipelineAction::IncPC);
|
||||||
}
|
}
|
||||||
|
@ -48,7 +52,11 @@ impl Core {
|
||||||
|
|
||||||
// +2S
|
// +2S
|
||||||
self.add_cycles(self.pc, sysbus, Seq + MemoryAccess32);
|
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)
|
Ok(CpuPipelineAction::Flush)
|
||||||
}
|
}
|
||||||
|
@ -73,7 +81,11 @@ impl Core {
|
||||||
|
|
||||||
// +2S
|
// +2S
|
||||||
self.add_cycles(self.pc, sysbus, Seq + MemoryAccess32);
|
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)
|
Ok(CpuPipelineAction::Flush)
|
||||||
}
|
}
|
||||||
|
@ -208,7 +220,11 @@ impl Core {
|
||||||
}
|
}
|
||||||
|
|
||||||
// +1S
|
// +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)
|
Ok(pipeline_action)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,12 +235,16 @@ impl Core {
|
||||||
ArmShiftedValue::ShiftedRegister {
|
ArmShiftedValue::ShiftedRegister {
|
||||||
reg,
|
reg,
|
||||||
shift,
|
shift,
|
||||||
added: Some(added)
|
added: Some(added),
|
||||||
} => {
|
} => {
|
||||||
let abs = self.register_shift(reg, shift).unwrap();
|
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,
|
sysbus: &mut SysBus,
|
||||||
insn: ArmInstruction,
|
insn: ArmInstruction,
|
||||||
) -> CpuResult<CpuPipelineAction> {
|
) -> CpuResult<CpuPipelineAction> {
|
||||||
|
|
||||||
if insn.write_back_flag() && insn.rd() == insn.rn() {
|
if insn.write_back_flag() && insn.rd() == insn.rn() {
|
||||||
return Err(CpuError::IllegalInstruction);
|
return Err(CpuError::IllegalInstruction);
|
||||||
}
|
}
|
||||||
|
@ -273,7 +292,11 @@ impl Core {
|
||||||
sysbus.read_32(addr)
|
sysbus.read_32(addr)
|
||||||
};
|
};
|
||||||
// +1S
|
// +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);
|
self.set_reg(insn.rd(), data);
|
||||||
|
|
||||||
|
@ -284,7 +307,11 @@ impl Core {
|
||||||
// +1S
|
// +1S
|
||||||
self.add_cycles(self.pc, sysbus, Seq + MemoryAccess32);
|
self.add_cycles(self.pc, sysbus, Seq + MemoryAccess32);
|
||||||
// +1N
|
// +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;
|
pipeline_action = CpuPipelineAction::Flush;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -293,11 +320,11 @@ impl Core {
|
||||||
if insn.transfer_size() == 1 {
|
if insn.transfer_size() == 1 {
|
||||||
// +1N
|
// +1N
|
||||||
self.add_cycles(dest, sysbus, NonSeq + MemoryAccess8);
|
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 {
|
} else {
|
||||||
// +1N
|
// +1N
|
||||||
self.add_cycles(dest, sysbus, NonSeq + MemoryAccess32);
|
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)
|
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::io;
|
||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
use super::Addr;
|
|
||||||
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
pub enum MemoryAccessType {
|
pub enum MemoryAccessType {
|
||||||
NonSeq,
|
NonSeq,
|
||||||
Seq
|
Seq,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum MemoryAccessWidth {
|
pub enum MemoryAccessWidth {
|
||||||
MemoryAccess8,
|
MemoryAccess8,
|
||||||
MemoryAccess16,
|
MemoryAccess16,
|
||||||
MemoryAccess32
|
MemoryAccess32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Add<MemoryAccessWidth> for MemoryAccessType {
|
impl Add<MemoryAccessWidth> for MemoryAccessType {
|
||||||
type Output = MemoryAccess;
|
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 {
|
pub trait Bus {
|
||||||
fn read_32(&self, addr: Addr) -> u32;
|
fn read_32(&self, addr: Addr) -> u32 {
|
||||||
fn read_16(&self, addr: Addr) -> u16;
|
self.get_bytes(addr, 4).read_u32::<LittleEndian>().unwrap()
|
||||||
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 read_16(&self, addr: Addr) -> u16 {
|
||||||
fn write_8(&mut self, addr: Addr, value: u8) -> Result<(), io::Error>;
|
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
|
/// returns the number of cycles needed for this memory access
|
||||||
fn get_cycles(&self, addr: Addr, access: MemoryAccess) -> usize;
|
fn get_cycles(&self, addr: Addr, access: MemoryAccess) -> usize;
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use std::ops::Add;
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::ops::Add;
|
||||||
|
|
||||||
use ansi_term::{Colour, Style};
|
use ansi_term::{Colour, Style};
|
||||||
|
|
||||||
|
@ -8,10 +8,10 @@ use crate::sysbus::SysBus;
|
||||||
|
|
||||||
pub use super::exception::Exception;
|
pub use super::exception::Exception;
|
||||||
use super::{
|
use super::{
|
||||||
CpuState, CpuMode, reg_string, CpuResult, Addr,
|
arm::*,
|
||||||
psr::RegPSR,
|
|
||||||
bus::{Bus, MemoryAccess, MemoryAccessType::*, MemoryAccessWidth::*},
|
bus::{Bus, MemoryAccess, MemoryAccessType::*, MemoryAccessWidth::*},
|
||||||
arm::*
|
psr::RegPSR,
|
||||||
|
reg_string, Addr, CpuMode, CpuResult, CpuState,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
|
@ -198,9 +198,7 @@ impl Core {
|
||||||
let action = self.exec_arm(sysbus, d)?;
|
let action = self.exec_arm(sysbus, d)?;
|
||||||
Ok((Some(d), action))
|
Ok((Some(d), action))
|
||||||
}
|
}
|
||||||
None => {
|
None => Ok((None, CpuPipelineAction::IncPC)),
|
||||||
Ok((None, CpuPipelineAction::IncPC))
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.pipeline.fetched = Some((self.pc, new_fetched));
|
self.pipeline.fetched = Some((self.pc, new_fetched));
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
pub mod arm;
|
pub mod arm;
|
||||||
use arm::{ArmInstruction, ArmDecodeError};
|
use arm::{ArmDecodeError, ArmInstruction};
|
||||||
|
|
||||||
pub mod cpu;
|
pub mod cpu;
|
||||||
|
pub use cpu::*;
|
||||||
pub mod bus;
|
pub mod bus;
|
||||||
mod exception;
|
mod exception;
|
||||||
mod psr;
|
mod psr;
|
||||||
|
|
||||||
|
|
||||||
pub const REG_PC: usize = 15;
|
pub const REG_PC: usize = 15;
|
||||||
pub const REG_LR: usize = 14;
|
pub const REG_LR: usize = 14;
|
||||||
pub const REG_SP: usize = 13;
|
pub const REG_SP: usize = 13;
|
||||||
|
|
|
@ -5,11 +5,17 @@ subcommands:
|
||||||
- debug:
|
- debug:
|
||||||
about: debug the bios with the arm core emulation
|
about: debug the bios with the arm core emulation
|
||||||
args:
|
args:
|
||||||
- BIOS:
|
- bios:
|
||||||
help: Sets the bios file to use
|
help: Sets the bios file to use
|
||||||
required: false
|
required: false
|
||||||
default_value: gba_bios.bin
|
default_value: gba_bios.bin
|
||||||
index: 1
|
index: 1
|
||||||
|
- game_rom:
|
||||||
|
short: g
|
||||||
|
long: game-rom
|
||||||
|
takes_value: true
|
||||||
|
help: Sets the game-rom file to use
|
||||||
|
required: true
|
||||||
- disass:
|
- disass:
|
||||||
about: disassemble an arm binary file
|
about: disassemble an arm binary file
|
||||||
args:
|
args:
|
||||||
|
|
|
@ -8,9 +8,9 @@ use clap::{App, ArgMatches};
|
||||||
extern crate rustboyadvance_ng;
|
extern crate rustboyadvance_ng;
|
||||||
|
|
||||||
use rustboyadvance_ng::arm7tdmi;
|
use rustboyadvance_ng::arm7tdmi;
|
||||||
use rustboyadvance_ng::sysbus::SysBus;
|
|
||||||
use rustboyadvance_ng::debugger::{Debugger, DebuggerError};
|
use rustboyadvance_ng::debugger::{Debugger, DebuggerError};
|
||||||
use rustboyadvance_ng::disass::Disassembler;
|
use rustboyadvance_ng::disass::Disassembler;
|
||||||
|
use rustboyadvance_ng::sysbus::SysBus;
|
||||||
use rustboyadvance_ng::util::read_bin_file;
|
use rustboyadvance_ng::util::read_bin_file;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -18,7 +18,7 @@ pub enum GBAError {
|
||||||
IO(io::Error),
|
IO(io::Error),
|
||||||
ArmDecodeError(arm7tdmi::arm::ArmDecodeError),
|
ArmDecodeError(arm7tdmi::arm::ArmDecodeError),
|
||||||
CpuError(arm7tdmi::CpuError),
|
CpuError(arm7tdmi::CpuError),
|
||||||
DebuggerError(DebuggerError)
|
DebuggerError(DebuggerError),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type GBAResult<T> = Result<T, GBAError>;
|
pub type GBAResult<T> = Result<T, GBAError>;
|
||||||
|
@ -59,11 +59,10 @@ fn run_disass(matches: &ArgMatches) -> GBAResult<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_debug(matches: &ArgMatches) -> GBAResult<()> {
|
fn run_debug(matches: &ArgMatches) -> GBAResult<()> {
|
||||||
let gba_bios_path = matches.value_of("BIOS").unwrap_or_default();
|
let bios_bin = read_bin_file(matches.value_of("bios").unwrap_or_default())?;
|
||||||
println!("Loading BIOS: {}", gba_bios_path);
|
let rom_bin = read_bin_file(matches.value_of("game_rom").unwrap())?;
|
||||||
let bios_bin = read_bin_file(gba_bios_path)?;
|
|
||||||
|
|
||||||
let sysbus = SysBus::new(bios_bin);
|
let sysbus = SysBus::new(bios_bin, rom_bin);
|
||||||
let mut core = arm7tdmi::cpu::Core::new();
|
let mut core = arm7tdmi::cpu::Core::new();
|
||||||
core.reset();
|
core.reset();
|
||||||
core.set_verbose(true);
|
core.set_verbose(true);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::arm7tdmi::{reg_string, REG_PC};
|
|
||||||
use crate::arm7tdmi::bus::Bus;
|
use crate::arm7tdmi::bus::Bus;
|
||||||
|
use crate::arm7tdmi::{reg_string, REG_PC};
|
||||||
use crate::debugger::Debugger;
|
use crate::debugger::Debugger;
|
||||||
use crate::disass::Disassembler;
|
use crate::disass::Disassembler;
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ impl Command {
|
||||||
insn.pc,
|
insn.pc,
|
||||||
Colour::Yellow.italic().paint(format!("{} ", insn))
|
Colour::Yellow.italic().paint(format!("{} ", insn))
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("{}: {}", "cpu encountered an error".red(), e);
|
println!("{}: {}", "cpu encountered an error".red(), e);
|
||||||
println!("cpu: {:x?}", debugger.cpu);
|
println!("cpu: {:x?}", debugger.cpu);
|
||||||
|
@ -72,23 +72,11 @@ impl Command {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
HexDump(addr, nbytes) => {
|
HexDump(addr, nbytes) => {
|
||||||
let bytes = match debugger.sysbus.get_bytes(addr, nbytes) {
|
let bytes = debugger.sysbus.get_bytes(addr, nbytes);
|
||||||
Some(bytes) => bytes,
|
|
||||||
None => {
|
|
||||||
println!("requested content out of bounds");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
hexdump::hexdump(bytes);
|
hexdump::hexdump(bytes);
|
||||||
}
|
}
|
||||||
Disass(addr, n) => {
|
Disass(addr, n) => {
|
||||||
let bytes = match debugger.sysbus.get_bytes(addr, 4 * n) {
|
let bytes = debugger.sysbus.get_bytes(addr, 4 * n);
|
||||||
Some(bytes) => bytes,
|
|
||||||
None => {
|
|
||||||
println!("requested content out of bounds");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let disass = Disassembler::new(addr, bytes);
|
let disass = Disassembler::new(addr, bytes);
|
||||||
for (_, line) in disass {
|
for (_, line) in disass {
|
||||||
println!("{}", line)
|
println!("{}", line)
|
||||||
|
|
|
@ -171,7 +171,7 @@ impl Debugger {
|
||||||
if let Some(Command::Disass(addr, n)) = self.previous_command {
|
if let Some(Command::Disass(addr, n)) = self.previous_command {
|
||||||
(addr + (4 * n as u32), 10)
|
(addr + (4 * n as u32), 10)
|
||||||
} else {
|
} 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 std::io;
|
||||||
|
|
||||||
|
use super::arm7tdmi::bus::{Bus, MemoryAccess, MemoryAccessWidth};
|
||||||
use super::arm7tdmi::Addr;
|
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 VIDEO_RAM_SIZE: usize = 128 * 1024;
|
||||||
const WORK_RAM_SIZE: usize = 256 * 1024;
|
const WORK_RAM_SIZE: usize = 256 * 1024;
|
||||||
const INTERNAL_RAM: usize = 32 * 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 OAM_SIZE: usize = 1 * 1024;
|
||||||
const BIOS_SIZE: usize = 16 * 1024;
|
|
||||||
const GAMEPAK_ROM_SIZE: usize = 32 * 1024 * 1024;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct BiosROM(Vec<u8>);
|
struct BoxedMemory(Box<[u8]>, WaitState);
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum SysBusDevice {
|
struct WaitState {
|
||||||
BiosROM(BiosROM)
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct SysBus {
|
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 {
|
impl SysBus {
|
||||||
pub fn new(bios_rom: Vec<u8>) -> SysBus {
|
pub fn new(bios_rom: Vec<u8>, game_rom: Vec<u8>) -> SysBus {
|
||||||
SysBus { bios: BiosROM(bios_rom) }
|
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 {
|
fn map(&self, addr: Addr) -> &impl Bus {
|
||||||
match addr as usize {
|
match addr as usize {
|
||||||
0...BIOS_SIZE => &self.bios,
|
0x0000_0000...0x0000_3fff => &self.bios,
|
||||||
_ => panic!("unmapped address")
|
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 {
|
fn map_mut(&mut self, addr: Addr) -> &mut impl Bus {
|
||||||
match addr as usize {
|
match addr as usize {
|
||||||
0...BIOS_SIZE => &mut self.bios,
|
0x0000_0000...0x0000_3fff => &mut self.bios,
|
||||||
_ => panic!("unmapped address")
|
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 {
|
impl Bus for SysBus {
|
||||||
fn read_32(&self, addr: Addr) -> u32 {
|
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 {
|
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 {
|
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> {
|
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> {
|
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> {
|
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]> {
|
fn get_bytes_mut(&mut self, addr: Addr, size: usize) -> &mut [u8] {
|
||||||
self.map(addr).get_bytes(addr, size)
|
self.map_mut(addr).get_bytes_mut(addr & 0xff_ffff, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_cycles(&self, addr: Addr, access: MemoryAccess) -> usize {
|
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