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:
parent
1ca261e5c7
commit
b003dc950f
|
@ -4,7 +4,7 @@ pub use super::exception::Exception;
|
||||||
|
|
||||||
use super::{arm::ArmCond, psr::RegPSR, Addr, CpuMode, CpuState};
|
use super::{arm::ArmCond, psr::RegPSR, Addr, CpuMode, CpuState};
|
||||||
|
|
||||||
use crate::util::Shared;
|
use crate::util::{Shared, WeakPointer};
|
||||||
|
|
||||||
use super::memory::{MemoryAccess, MemoryInterface};
|
use super::memory::{MemoryAccess, MemoryInterface};
|
||||||
use MemoryAccess::*;
|
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> {
|
pub fn from_saved_state(bus: Shared<I>, state: SavedCpuState) -> Core<I> {
|
||||||
Core {
|
Core {
|
||||||
bus,
|
bus,
|
||||||
|
|
77
core/src/bios.rs
Normal file
77
core/src/bios.rs
Normal 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]
|
||||||
|
}
|
||||||
|
}
|
|
@ -113,9 +113,9 @@ impl Debugger {
|
||||||
GpioInfo => println!("GPIO: {:#?}", self.gba.sysbus.cartridge.get_gpio()),
|
GpioInfo => println!("GPIO: {:#?}", self.gba.sysbus.cartridge.get_gpio()),
|
||||||
Step(count) => {
|
Step(count) => {
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
self.gba.cpu.step();
|
self.gba.step_debugger();
|
||||||
while self.gba.cpu.last_executed.is_none() {
|
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 {
|
if let Some(last_executed) = &self.gba.cpu.last_executed {
|
||||||
let pc = last_executed.get_pc();
|
let pc = last_executed.get_pc();
|
||||||
|
|
|
@ -113,7 +113,7 @@ impl GameBoyAdvance {
|
||||||
interrupt_flags: interrupt_flags,
|
interrupt_flags: interrupt_flags,
|
||||||
};
|
};
|
||||||
|
|
||||||
gba.sysbus.created();
|
gba.sysbus.init(gba.cpu.weak_ptr());
|
||||||
|
|
||||||
gba
|
gba
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,7 @@ impl GameBoyAdvance {
|
||||||
io_devs.connect_irq(interrupts.clone());
|
io_devs.connect_irq(interrupts.clone());
|
||||||
io_devs.gpu.set_scheduler(scheduler.clone());
|
io_devs.gpu.set_scheduler(scheduler.clone());
|
||||||
io_devs.sound.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(),
|
scheduler.clone(),
|
||||||
io_devs.clone(),
|
io_devs.clone(),
|
||||||
cartridge,
|
cartridge,
|
||||||
|
@ -144,11 +144,13 @@ impl GameBoyAdvance {
|
||||||
decoded.ewram,
|
decoded.ewram,
|
||||||
decoded.iwram,
|
decoded.iwram,
|
||||||
));
|
));
|
||||||
let arm7tdmi = Box::new(arm7tdmi::Core::from_saved_state(
|
let mut arm7tdmi = Box::new(arm7tdmi::Core::from_saved_state(
|
||||||
sysbus.clone(),
|
sysbus.clone(),
|
||||||
decoded.cpu_state,
|
decoded.cpu_state,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
sysbus.init(arm7tdmi.weak_ptr());
|
||||||
|
|
||||||
Ok(GameBoyAdvance {
|
Ok(GameBoyAdvance {
|
||||||
cpu: arm7tdmi,
|
cpu: arm7tdmi,
|
||||||
sysbus: sysbus,
|
sysbus: sysbus,
|
||||||
|
@ -197,7 +199,7 @@ impl GameBoyAdvance {
|
||||||
self.sysbus.set_scheduler(self.scheduler.clone());
|
self.sysbus.set_scheduler(self.scheduler.clone());
|
||||||
self.sysbus.set_io_devices(self.io_devs.clone());
|
self.sysbus.set_io_devices(self.io_devs.clone());
|
||||||
self.sysbus.cartridge.update_from(decoded.cartridge);
|
self.sysbus.cartridge.update_from(decoded.cartridge);
|
||||||
self.sysbus.created();
|
self.sysbus.init(self.cpu.weak_ptr());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -354,14 +356,13 @@ impl GameBoyAdvance {
|
||||||
self.dma_step();
|
self.dma_step();
|
||||||
|
|
||||||
// Run the CPU
|
// Run the CPU
|
||||||
let _cycles = self.scheduler.measure_cycles(|| {
|
|
||||||
self.cpu_step();
|
self.cpu_step();
|
||||||
});
|
|
||||||
|
|
||||||
let breakpoint = self.check_breakpoint();
|
let breakpoint = self.check_breakpoint();
|
||||||
|
|
||||||
|
let mut _running = true;
|
||||||
while let Some((event, cycles_late)) = self.scheduler.pop_pending_event() {
|
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
|
breakpoint
|
||||||
|
|
|
@ -36,6 +36,7 @@ use std::fmt;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod arm7tdmi;
|
pub mod arm7tdmi;
|
||||||
|
mod bios;
|
||||||
pub mod cartridge;
|
pub mod cartridge;
|
||||||
pub mod disass;
|
pub mod disass;
|
||||||
pub mod gpu;
|
pub mod gpu;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::arm7tdmi;
|
||||||
use super::arm7tdmi::memory::*;
|
use super::arm7tdmi::memory::*;
|
||||||
|
use super::bios::Bios;
|
||||||
use super::bus::*;
|
use super::bus::*;
|
||||||
use super::cartridge::Cartridge;
|
use super::cartridge::Cartridge;
|
||||||
use super::dma::DmaNotifer;
|
use super::dma::DmaNotifer;
|
||||||
|
@ -142,8 +144,9 @@ impl CycleLookupTables {
|
||||||
pub struct SysBus {
|
pub struct SysBus {
|
||||||
pub io: Shared<IoDevices>,
|
pub io: Shared<IoDevices>,
|
||||||
scheduler: Shared<Scheduler>,
|
scheduler: Shared<Scheduler>,
|
||||||
|
arm_core: WeakPointer<arm7tdmi::Core<SysBus>>,
|
||||||
|
|
||||||
bios: BoxedMemory,
|
bios: Bios,
|
||||||
ewram: Box<[u8]>,
|
ewram: Box<[u8]>,
|
||||||
iwram: Box<[u8]>,
|
iwram: Box<[u8]>,
|
||||||
pub cartridge: Cartridge,
|
pub cartridge: Cartridge,
|
||||||
|
@ -171,9 +174,10 @@ impl SysBus {
|
||||||
SysBus {
|
SysBus {
|
||||||
io,
|
io,
|
||||||
scheduler,
|
scheduler,
|
||||||
|
arm_core: WeakPointer::default(),
|
||||||
cartridge,
|
cartridge,
|
||||||
|
|
||||||
bios: bios_rom,
|
bios: Bios::new(bios_rom),
|
||||||
ewram,
|
ewram,
|
||||||
iwram,
|
iwram,
|
||||||
cycle_luts: luts,
|
cycle_luts: luts,
|
||||||
|
@ -217,7 +221,9 @@ impl SysBus {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// must be called whenever this object is instanciated
|
/// 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);
|
let ptr = SysBusPtr::new(self as *mut SysBus);
|
||||||
// HACK
|
// HACK
|
||||||
self.io.set_sysbus_ptr(ptr.clone());
|
self.io.set_sysbus_ptr(ptr.clone());
|
||||||
|
@ -261,7 +267,13 @@ impl Bus for SysBus {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_32(&mut self, addr: Addr) -> u32 {
|
fn read_32(&mut self, addr: Addr) -> u32 {
|
||||||
match addr & 0xff000000 {
|
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),
|
EWRAM_ADDR => self.ewram.read_32(addr & 0x3_fffc),
|
||||||
IWRAM_ADDR => self.iwram.read_32(addr & 0x7ffc),
|
IWRAM_ADDR => self.iwram.read_32(addr & 0x7ffc),
|
||||||
IOMEM_ADDR => {
|
IOMEM_ADDR => {
|
||||||
|
@ -288,7 +300,13 @@ impl Bus for SysBus {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_16(&mut self, addr: Addr) -> u16 {
|
fn read_16(&mut self, addr: Addr) -> u16 {
|
||||||
match addr & 0xff000000 {
|
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),
|
EWRAM_ADDR => self.ewram.read_16(addr & 0x3_fffe),
|
||||||
IWRAM_ADDR => self.iwram.read_16(addr & 0x7ffe),
|
IWRAM_ADDR => self.iwram.read_16(addr & 0x7ffe),
|
||||||
IOMEM_ADDR => {
|
IOMEM_ADDR => {
|
||||||
|
@ -315,7 +333,13 @@ impl Bus for SysBus {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_8(&mut self, addr: Addr) -> u8 {
|
fn read_8(&mut self, addr: Addr) -> u8 {
|
||||||
match addr & 0xff000000 {
|
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),
|
EWRAM_ADDR => self.ewram.read_8(addr & 0x3_ffff),
|
||||||
IWRAM_ADDR => self.iwram.read_8(addr & 0x7fff),
|
IWRAM_ADDR => self.iwram.read_8(addr & 0x7fff),
|
||||||
IOMEM_ADDR => {
|
IOMEM_ADDR => {
|
||||||
|
|
Reference in a new issue