Implement all memory mappings. Reformat many files.

Former-commit-id: c0a62b610e62d2db2a4daf4aeef40068820daa52
This commit is contained in:
Michel Heily 2019-07-01 17:45:29 +03:00
parent 22c175d9cc
commit 6b225d776d
11 changed files with 219 additions and 146 deletions

View file

@ -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 {

View file

@ -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);
}
@ -40,7 +44,7 @@ impl Core {
if insn.link_flag() {
self.set_reg(14, self.pc & !0b1);
}
// +1N
self.add_cycles(self.pc, sysbus, NonSeq + MemoryAccess32);
@ -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)
}
@ -68,12 +76,16 @@ impl Core {
// +1N
self.add_cycles(self.pc, sysbus, NonSeq + MemoryAccess32);
self.pc = rn & !1;
// +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)
}
@ -159,7 +171,7 @@ impl Core {
}
/// Logical/Arithmetic ALU operations
///
///
/// Cycles: 1S+x+y (from GBATEK)
/// Add x=1I cycles if Op2 shifted-by-register. Add y=1S+1N cycles if Rd=R15.
fn exec_data_processing(
@ -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);
}
@ -258,7 +277,7 @@ impl Core {
let effective_addr = (addr as i32).wrapping_add(offset) as Addr;
addr = if insn.pre_index_flag() {
effective_addr
} else {
} else {
addr
};
@ -273,8 +292,12 @@ 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);
// +1I
@ -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)
}
}

View file

@ -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;
}
}

View file

@ -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));

View file

@ -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;

View file

@ -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:

View file

@ -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);

View file

@ -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;
@ -59,11 +59,11 @@ impl Command {
match debugger.cpu.step_debugger(&mut debugger.sysbus) {
Ok(insn) => {
println!(
"@0x{:08x}:\n\t{}",
insn.pc,
Colour::Yellow.italic().paint(format!("{} ", insn))
);
},
"@0x{:08x}:\n\t{}",
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)

View file

@ -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)
}
}
_ => {

View file

@ -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>);
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()
}
#[derive(Debug)]
struct WaitState {
access8: usize,
access16: usize,
access32: usize,
}
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])
impl WaitState {
fn new(access8: usize, access16: usize, access32: usize) -> WaitState {
WaitState {
access8,
access16,
access32,
}
}
fn get_cycles(&self, _addr: Addr, _access: MemoryAccess) -> usize {
1
}
}
#[derive(Debug)]
enum SysBusDevice {
BiosROM(BiosROM)
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) }
}
fn map(&self, addr: Addr) -> & impl Bus {
match addr as usize {
0...BIOS_SIZE => &self.bios,
_ => panic!("unmapped address")
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 {
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)
}
}

View file

@ -7,4 +7,4 @@ pub fn read_bin_file(filename: &str) -> io::Result<Vec<u8>> {
let mut file = File::open(filename)?;
file.read_to_end(&mut buf)?;
Ok(buf)
}
}