core: Implement bios read protection when CPU is not executing from bios.

Fixes #109
Fixes #106
Fixes MegaMan email menu freezing and probably some more games


Former-commit-id: ed37520f2bc732b07334261dfe3d23cccf3fc04c
Former-commit-id: d7f206b0f405ffe09a3b36d90268f1d683a64cea
This commit is contained in:
Michel Heily 2020-10-17 17:00:02 -07:00
parent 1ca261e5c7
commit b003dc950f
6 changed files with 124 additions and 17 deletions

View file

@ -4,7 +4,7 @@ pub use super::exception::Exception;
use super::{arm::ArmCond, psr::RegPSR, Addr, CpuMode, CpuState};
use crate::util::Shared;
use crate::util::{Shared, WeakPointer};
use super::memory::{MemoryAccess, MemoryInterface};
use MemoryAccess::*;
@ -148,6 +148,10 @@ impl<I: MemoryInterface> Core<I> {
}
}
pub fn weak_ptr(&mut self) -> WeakPointer<Core<I>> {
WeakPointer::new(self as *mut Core<I>)
}
pub fn from_saved_state(bus: Shared<I>, state: SavedCpuState) -> Core<I> {
Core {
bus,

77
core/src/bios.rs Normal file
View file

@ -0,0 +1,77 @@
use super::arm7tdmi;
use super::bus::{Addr, Bus, DebugRead};
use super::util::WeakPointer;
use super::SysBus;
/// Struct representing the sytem ROM
#[derive(Clone)]
pub struct Bios {
/// Underlying memory
rom: Box<[u8]>,
/// Last read value
last_opcode: u32,
/// Arm pointer - used only to read the PC register
arm_core: WeakPointer<arm7tdmi::Core<SysBus>>,
}
impl Bios {
pub fn new(bios_rom: Box<[u8]>) -> Bios {
Bios {
rom: bios_rom,
last_opcode: 0xe129f000, // the opcode at [00DCh+8]
arm_core: WeakPointer::default(),
}
}
pub(super) fn connect_arm_core(&mut self, arm_ptr: WeakPointer<arm7tdmi::Core<SysBus>>) {
self.arm_core = arm_ptr;
}
#[inline]
fn read_allowed(&self) -> bool {
self.arm_core.pc < 0x4000
}
}
/// Impl of Bus trait for Bios
impl Bus for Bios {
#[inline]
fn read_32(&mut self, addr: Addr) -> u32 {
if self.read_allowed() {
let value = self.rom.read_32(addr);
// 32-bit read from bios is most probably an opcode fetch
self.last_opcode = value;
value
} else {
self.last_opcode
}
}
#[inline]
fn read_16(&mut self, addr: Addr) -> u16 {
if self.read_allowed() {
self.rom.read_16(addr) as u16
} else {
(self.last_opcode >> ((addr & 2) << 3)) as u16
}
}
#[inline]
fn read_8(&mut self, addr: Addr) -> u8 {
if self.read_allowed() {
self.rom.read_8(addr)
} else {
(self.last_opcode >> ((addr & 3) << 3)) as u8
}
}
#[inline]
fn write_8(&mut self, _addr: Addr, _value: u8) {
// The bios is RO
}
}
impl DebugRead for Bios {
fn debug_read_8(&mut self, addr: Addr) -> u8 {
self.rom[addr as usize]
}
}

View file

@ -113,9 +113,9 @@ impl Debugger {
GpioInfo => println!("GPIO: {:#?}", self.gba.sysbus.cartridge.get_gpio()),
Step(count) => {
for _ in 0..count {
self.gba.cpu.step();
self.gba.step_debugger();
while self.gba.cpu.last_executed.is_none() {
self.gba.cpu.step();
self.gba.step_debugger();
}
if let Some(last_executed) = &self.gba.cpu.last_executed {
let pc = last_executed.get_pc();

View file

@ -113,7 +113,7 @@ impl GameBoyAdvance {
interrupt_flags: interrupt_flags,
};
gba.sysbus.created();
gba.sysbus.init(gba.cpu.weak_ptr());
gba
}
@ -136,7 +136,7 @@ impl GameBoyAdvance {
io_devs.connect_irq(interrupts.clone());
io_devs.gpu.set_scheduler(scheduler.clone());
io_devs.sound.set_scheduler(scheduler.clone());
let sysbus = Shared::new(SysBus::new_with_memories(
let mut sysbus = Shared::new(SysBus::new_with_memories(
scheduler.clone(),
io_devs.clone(),
cartridge,
@ -144,11 +144,13 @@ impl GameBoyAdvance {
decoded.ewram,
decoded.iwram,
));
let arm7tdmi = Box::new(arm7tdmi::Core::from_saved_state(
let mut arm7tdmi = Box::new(arm7tdmi::Core::from_saved_state(
sysbus.clone(),
decoded.cpu_state,
));
sysbus.init(arm7tdmi.weak_ptr());
Ok(GameBoyAdvance {
cpu: arm7tdmi,
sysbus: sysbus,
@ -197,7 +199,7 @@ impl GameBoyAdvance {
self.sysbus.set_scheduler(self.scheduler.clone());
self.sysbus.set_io_devices(self.io_devs.clone());
self.sysbus.cartridge.update_from(decoded.cartridge);
self.sysbus.created();
self.sysbus.init(self.cpu.weak_ptr());
Ok(())
}
@ -354,14 +356,13 @@ impl GameBoyAdvance {
self.dma_step();
// Run the CPU
let _cycles = self.scheduler.measure_cycles(|| {
self.cpu_step();
});
self.cpu_step();
let breakpoint = self.check_breakpoint();
let mut _running = true;
while let Some((event, cycles_late)) = self.scheduler.pop_pending_event() {
self.handle_event(event, cycles_late, &mut running);
self.handle_event(event, cycles_late, &mut _running);
}
breakpoint

View file

@ -36,6 +36,7 @@ use std::fmt;
#[macro_use]
pub mod util;
pub mod arm7tdmi;
mod bios;
pub mod cartridge;
pub mod disass;
pub mod gpu;

View file

@ -1,6 +1,8 @@
use serde::{Deserialize, Serialize};
use super::arm7tdmi;
use super::arm7tdmi::memory::*;
use super::bios::Bios;
use super::bus::*;
use super::cartridge::Cartridge;
use super::dma::DmaNotifer;
@ -142,8 +144,9 @@ impl CycleLookupTables {
pub struct SysBus {
pub io: Shared<IoDevices>,
scheduler: Shared<Scheduler>,
arm_core: WeakPointer<arm7tdmi::Core<SysBus>>,
bios: BoxedMemory,
bios: Bios,
ewram: Box<[u8]>,
iwram: Box<[u8]>,
pub cartridge: Cartridge,
@ -171,9 +174,10 @@ impl SysBus {
SysBus {
io,
scheduler,
arm_core: WeakPointer::default(),
cartridge,
bios: bios_rom,
bios: Bios::new(bios_rom),
ewram,
iwram,
cycle_luts: luts,
@ -217,7 +221,9 @@ impl SysBus {
}
/// must be called whenever this object is instanciated
pub fn created(&mut self) {
pub fn init(&mut self, arm_core: WeakPointer<arm7tdmi::Core<SysBus>>) {
self.arm_core = arm_core.clone();
self.bios.connect_arm_core(arm_core.clone());
let ptr = SysBusPtr::new(self as *mut SysBus);
// HACK
self.io.set_sysbus_ptr(ptr.clone());
@ -261,7 +267,13 @@ impl Bus for SysBus {
#[inline]
fn read_32(&mut self, addr: Addr) -> u32 {
match addr & 0xff000000 {
BIOS_ADDR => self.bios.read_32(addr),
BIOS_ADDR => {
if addr <= 0x3ffc {
self.bios.read_32(addr)
} else {
0 // TODO open-bus
}
}
EWRAM_ADDR => self.ewram.read_32(addr & 0x3_fffc),
IWRAM_ADDR => self.iwram.read_32(addr & 0x7ffc),
IOMEM_ADDR => {
@ -288,7 +300,13 @@ impl Bus for SysBus {
#[inline]
fn read_16(&mut self, addr: Addr) -> u16 {
match addr & 0xff000000 {
BIOS_ADDR => self.bios.read_16(addr),
BIOS_ADDR => {
if addr <= 0x3ffe {
self.bios.read_16(addr)
} else {
0 // TODO open-bus
}
}
EWRAM_ADDR => self.ewram.read_16(addr & 0x3_fffe),
IWRAM_ADDR => self.iwram.read_16(addr & 0x7ffe),
IOMEM_ADDR => {
@ -315,7 +333,13 @@ impl Bus for SysBus {
#[inline]
fn read_8(&mut self, addr: Addr) -> u8 {
match addr & 0xff000000 {
BIOS_ADDR => self.bios.read_8(addr),
BIOS_ADDR => {
if addr <= 0x3fff {
self.bios.read_8(addr)
} else {
0 // TODO open-bus
}
}
EWRAM_ADDR => self.ewram.read_8(addr & 0x3_ffff),
IWRAM_ADDR => self.iwram.read_8(addr & 0x7fff),
IOMEM_ADDR => {