This repository has been archived on 2024-06-01. You can view files and clone it, but cannot push or open issues or pull requests.
rustboyadvance-ng/core/src/gdb.rs
Michel Heily 28fb9ffa70 chore: Refactor into crates
Been wanting to do this for a long time.
This does impose performance issues when building with the vanilla
release profile, as cargo does not perform optimizations across crate
boundary (we care mostly about inlining).
so I added release-lto profile

Also fixed some broken stuff I found across the project, meh


Former-commit-id: 06d03263cc6245313f3ea22c715479ab6da7c4d4
Former-commit-id: f93abd10c67ea8a3b8072b47462be5eca4f3e02b
2022-09-04 23:54:44 +03:00

165 lines
4.7 KiB
Rust

use super::arm7tdmi::CpuState;
use super::core::GameBoyAdvance;
use super::interrupt::*;
use super::iodev::IoDevices;
use super::sysbus::SysBus;
use super::Bus;
use byteorder::{LittleEndian, ReadBytesExt};
use gdbstub::{Access, Target, TargetState};
use std::io::Cursor;
use gdbstub;
use gdbstub::GdbStub;
use std::fmt;
use std::net::TcpListener;
use std::net::ToSocketAddrs;
pub fn spawn_and_run_gdb_server<A: ToSocketAddrs + fmt::Display>(
#[allow(unused)] target: &mut GameBoyAdvance,
#[allow(unused)] addr: A,
) -> Result<(), Box<dyn std::error::Error>> {
#[cfg(feature = "gdb")]
{
info!("spawning gdbserver, listening on {}", addr);
let sock = TcpListener::bind(addr)?;
let (stream, addr) = sock.accept()?;
info!("got connection from {}", addr);
let mut gdb = GdbStub::new(stream);
let result = match gdb.run(target) {
Ok(state) => {
info!("Disconnected from GDB. Target state: {:?}", state);
Ok(())
}
Err(gdbstub::Error::TargetError(e)) => Err(e),
Err(e) => return Err(e.into()),
};
info!("Debugger session ended, result={:?}", result);
}
#[cfg(not(feature = "gdb"))]
{
error!("failed. please compile me with 'gdb' feature")
}
Ok(())
}
impl Target for GameBoyAdvance {
type Usize = u32;
type Error = ();
fn step(
&mut self,
mut _log_mem_access: impl FnMut(Access<u32>),
) -> Result<TargetState, Self::Error> {
static mut S_TOTAL_CYCLES: usize = 0;
let io = unsafe {
let ptr = &mut *self.sysbus as *mut SysBus;
&mut (*ptr).io as &mut IoDevices
};
// clear any pending DMAs
let mut irqs = IrqBitmask(0);
while io.dmac.is_active() {
io.dmac.perform_work(&mut self.sysbus, &mut irqs);
}
io.intc.request_irqs(irqs);
// run the CPU, ignore haltcnt
let cycles = self.step_cpu(io);
io.timers.update(cycles, &mut self.sysbus, &mut irqs);
unsafe {
S_TOTAL_CYCLES += cycles;
}
if self.cycles_to_next_event <= unsafe { S_TOTAL_CYCLES } {
let mut cycles_to_next_event = std::usize::MAX;
io.gpu.update(
unsafe { S_TOTAL_CYCLES },
&mut self.sysbus,
&mut irqs,
&mut cycles_to_next_event,
&self.video_device,
);
io.sound.update(
unsafe { S_TOTAL_CYCLES },
&mut cycles_to_next_event,
&self.audio_device,
);
self.cycles_to_next_event = cycles_to_next_event;
unsafe {
S_TOTAL_CYCLES = 0;
};
} else {
self.cycles_to_next_event -= unsafe { S_TOTAL_CYCLES };
}
io.intc.request_irqs(irqs);
Ok(TargetState::Running)
}
fn read_pc(&mut self) -> u32 {
self.cpu.get_next_pc()
}
// read the specified memory addresses from the target
fn read_addrs(&mut self, addr: std::ops::Range<u32>, mut push_byte: impl FnMut(u8)) {
for addr in addr {
push_byte(self.sysbus.read_8(addr))
}
}
// write data to the specified memory addresses
fn write_addrs(&mut self, mut get_addr_val: impl FnMut() -> Option<(u32, u8)>) {
while let Some((addr, val)) = get_addr_val() {
self.sysbus.write_8(addr, val);
}
}
fn read_registers(&mut self, mut push_reg: impl FnMut(&[u8])) {
// general purpose registers
for i in 0..15 {
push_reg(&self.cpu.get_reg(i).to_le_bytes());
}
push_reg(&self.cpu.get_next_pc().to_le_bytes());
// Floating point registers, unused
for _ in 0..25 {
push_reg(&[0, 0, 0, 0]);
}
push_reg(&self.cpu.cpsr.get().to_le_bytes());
}
fn write_registers(&mut self, regs: &[u8]) {
let mut rdr = Cursor::new(regs);
for i in 0..15 {
self.cpu.set_reg(i, rdr.read_u32::<LittleEndian>().unwrap());
}
let new_pc = rdr.read_u32::<LittleEndian>().unwrap();
self.cpu.set_reg(15, new_pc);
self.cpu.cpsr.set(rdr.read_u32::<LittleEndian>().unwrap());
match self.cpu.cpsr.state() {
CpuState::ARM => self.cpu.reload_pipeline32(&mut self.sysbus),
CpuState::THUMB => self.cpu.reload_pipeline16(&mut self.sysbus),
};
}
fn target_description_xml() -> Option<&'static str> {
Some(
r#"
<target version="1.0">
<architecture>armv4t</architecture>
</target>"#,
)
}
}