Implement reading from real memory

This commit is contained in:
Simon Gellis 2025-01-02 01:10:19 -05:00
parent 82c3104ab9
commit be8cdd958a
4 changed files with 101 additions and 21 deletions

View File

@ -3,6 +3,7 @@ use std::{
fmt::Display, fmt::Display,
fs::{self, File}, fs::{self, File},
io::{Read, Seek, SeekFrom, Write}, io::{Read, Seek, SeekFrom, Write},
ops::Range,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::{ sync::{
atomic::{AtomicBool, AtomicUsize, Ordering}, atomic::{AtomicBool, AtomicUsize, Ordering},
@ -397,6 +398,13 @@ impl Emulator {
let value = sim.read_register(register); let value = sim.read_register(register);
let _ = done.send(value); let _ = done.send(value);
} }
EmulatorCommand::ReadMemory(sim_id, addresses, mut buffer, done) => {
let Some(sim) = self.sims.get_mut(sim_id.to_index()) else {
return;
};
sim.read_memory(addresses, &mut buffer);
let _ = done.send(buffer);
}
EmulatorCommand::SetAudioEnabled(p1, p2) => { EmulatorCommand::SetAudioEnabled(p1, p2) => {
self.audio_on[SimId::Player1.to_index()].store(p1, Ordering::Release); self.audio_on[SimId::Player1.to_index()].store(p1, Ordering::Release);
self.audio_on[SimId::Player2.to_index()].store(p2, Ordering::Release); self.audio_on[SimId::Player2.to_index()].store(p2, Ordering::Release);
@ -455,6 +463,7 @@ pub enum EmulatorCommand {
Pause, Pause,
Resume, Resume,
ReadRegister(SimId, VBRegister, oneshot::Sender<u32>), ReadRegister(SimId, VBRegister, oneshot::Sender<u32>),
ReadMemory(SimId, Range<u32>, Vec<u8>, oneshot::Sender<Vec<u8>>),
SetAudioEnabled(bool, bool), SetAudioEnabled(bool, bool),
Link, Link,
Unlink, Unlink,

View File

@ -1,4 +1,4 @@
use std::{ffi::c_void, ptr, slice}; use std::{ffi::c_void, ops::Range, ptr, slice};
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use bitflags::bitflags; use bitflags::bitflags;
@ -101,6 +101,8 @@ extern "C" {
fn vb_get_user_data(sim: *mut VB) -> *mut c_void; fn vb_get_user_data(sim: *mut VB) -> *mut c_void;
#[link_name = "vbInit"] #[link_name = "vbInit"]
fn vb_init(sim: *mut VB) -> *mut VB; fn vb_init(sim: *mut VB) -> *mut VB;
#[link_name = "vbRead"]
fn vb_read(sim: *mut VB, address: u32, typ_: VBDataType) -> i32;
#[link_name = "vbReset"] #[link_name = "vbReset"]
fn vb_reset(sim: *mut VB); fn vb_reset(sim: *mut VB);
#[link_name = "vbSetCartRAM"] #[link_name = "vbSetCartRAM"]
@ -314,6 +316,13 @@ impl Sim {
VBRegister::PC => unsafe { vb_get_program_counter(self.sim) }, VBRegister::PC => unsafe { vb_get_program_counter(self.sim) },
} }
} }
pub fn read_memory(&mut self, addresses: Range<u32>, into: &mut Vec<u8>) {
for address in addresses {
let byte = unsafe { vb_read(self.sim, address, VBDataType::U8) };
into.push(byte as u8);
}
}
} }
impl Drop for Sim { impl Drop for Sim {

View File

@ -135,6 +135,7 @@ struct GdbConnection {
ack_messages: bool, ack_messages: bool,
request_buf: Vec<u8>, request_buf: Vec<u8>,
response_buf: Option<Vec<u8>>, response_buf: Option<Vec<u8>>,
memory_buf: Option<Vec<u8>>,
} }
impl GdbConnection { impl GdbConnection {
@ -148,6 +149,7 @@ impl GdbConnection {
ack_messages: true, ack_messages: true,
request_buf: vec![], request_buf: vec![],
response_buf: None, response_buf: None,
memory_buf: None,
} }
} }
async fn run(mut self) -> Result<()> { async fn run(mut self) -> Result<()> {
@ -155,7 +157,7 @@ impl GdbConnection {
self.client.send_command(EmulatorCommand::Pause); self.client.send_command(EmulatorCommand::Pause);
loop { loop {
let mut req = Request::read(&mut self.stream_in, &mut self.request_buf).await?; let mut req = Request::read(&mut self.stream_in, &mut self.request_buf).await?;
println!("received {:?}", req); println!("received {:02x?}", req);
if req.kind == RequestKind::Signal { if req.kind == RequestKind::Signal {
self.client.send_command(EmulatorCommand::Pause); self.client.send_command(EmulatorCommand::Pause);
@ -173,7 +175,7 @@ impl GdbConnection {
} else if req.match_str("qSupported:") { } else if req.match_str("qSupported:") {
let res = self let res = self
.response() .response()
.write_str("multiprocess+;swbreak+;vContSupported+"); .write_str("multiprocess+;swbreak+;vContSupported+;PacketSize=10000");
self.send(res).await?; self.send(res).await?;
} else if req.match_str("QThreadSuffixSupported") } else if req.match_str("QThreadSuffixSupported")
|| req.match_str("QListThreadsInStopReply") || req.match_str("QListThreadsInStopReply")
@ -199,8 +201,7 @@ impl GdbConnection {
REGISTERS.get(register) REGISTERS.get(register)
}; };
let Some(reg_info) = get_reg_info() else { let Some(reg_info) = get_reg_info() else {
let res = self.response(); self.send_empty().await?;
self.send(res).await?;
continue; continue;
}; };
let res = self.response().write_str(&reg_info.to_description()); let res = self.response().write_str(&reg_info.to_description());
@ -239,39 +240,83 @@ impl GdbConnection {
rx.recv().ok() rx.recv().ok()
}; };
let Some(value) = read_register() else { let Some(value) = read_register() else {
let res = self.response(); self.send_empty().await?;
self.send(res).await?;
continue; continue;
}; };
let res = self.response().write_hex(value); let res = self.response().write_hex(value);
self.send(res).await?; self.send(res).await?;
} else if req.match_str("m") { } else if req.match_str("m") {
let mut read_params = || { let mut read_memory = || {
let start = req.match_hex::<u32>()?; let start = req.match_hex::<u32>()?;
if !req.match_str(",") { if !req.match_str(",") {
return None; return None;
}; };
let size = req.match_hex::<u32>()?; let size = req.match_hex::<u32>()?;
Some((start, size)) let mut buf = self.memory_buf.take().unwrap_or_default();
buf.clear();
let (tx, rx) = ::oneshot::channel();
self.client.send_command(EmulatorCommand::ReadMemory(
self.sim_id,
start..(start + size),
buf,
tx,
));
rx.recv().ok()
}; };
let Some((start, size)) = read_params() else { let Some(memory) = read_memory() else {
let res = self.response(); self.send_empty().await?;
self.send(res).await?;
continue; continue;
}; };
let mut res = self.response(); let mut res = self.response();
for i in 0..size { for byte in &memory {
res = res.write_hex((start + i) as u8); res = res.write_hex(*byte);
} }
self.memory_buf = Some(memory);
self.send(res).await?;
} else if req.match_str("x") {
let mut read_memory = || {
let start = req.match_hex::<u32>()?;
if !req.match_str(",") {
return None;
};
let size = req.match_hex::<u32>()?;
let mut buf = self.memory_buf.take().unwrap_or_default();
buf.clear();
let (tx, rx) = ::oneshot::channel();
self.client.send_command(EmulatorCommand::ReadMemory(
self.sim_id,
start..(start + size),
buf,
tx,
));
rx.recv().ok()
};
let Some(memory) = read_memory() else {
self.send_empty().await?;
continue;
};
let mut res = self.response();
if memory.is_empty() {
res = res.write_str("OK");
} else {
for byte in &memory {
res = res.write_byte(*byte);
}
}
self.memory_buf = Some(memory);
self.send(res).await?; self.send(res).await?;
} else { } else {
// unrecognized command // unrecognized command
let res = self.response(); self.send_empty().await?;
self.send(res).await?;
} }
} }
} }
async fn send_empty(&mut self) -> std::io::Result<()> {
let res = self.response();
self.send(res).await
}
fn response(&mut self) -> Response { fn response(&mut self) -> Response {
Response::new( Response::new(
self.response_buf.take().unwrap_or_default(), self.response_buf.take().unwrap_or_default(),
@ -281,7 +326,10 @@ impl GdbConnection {
async fn send(&mut self, res: Response) -> std::io::Result<()> { async fn send(&mut self, res: Response) -> std::io::Result<()> {
let buffer = res.finish(); let buffer = res.finish();
println!("{:?}", std::str::from_utf8(&buffer)); match std::str::from_utf8(&buffer) {
Ok(text) => println!("response: {text}"),
Err(_) => println!("response: {buffer:02x?}"),
}
self.stream_out.write_all(&buffer).await?; self.stream_out.write_all(&buffer).await?;
self.response_buf = Some(buffer); self.response_buf = Some(buffer);
self.stream_out.flush().await self.stream_out.flush().await

View File

@ -18,16 +18,30 @@ impl Response {
} }
} }
pub fn write_str(mut self, str: &str) -> Self { pub fn write_str(self, str: &str) -> Self {
let mut me = self;
for byte in str.as_bytes() { for byte in str.as_bytes() {
self.buffer.push(*byte); me = me.write_byte(*byte);
self.checksum = self.checksum.wrapping_add(*byte); }
me
}
pub fn write_byte(mut self, byte: u8) -> Self {
if byte == b'}' || byte == b'#' || byte == b'$' || byte == b'*' {
self.buffer.push(b'}');
self.checksum = self.checksum.wrapping_add(b'}');
let escaped = byte ^ 0x20;
self.buffer.push(escaped);
self.checksum = self.checksum.wrapping_add(escaped);
} else {
self.buffer.push(byte);
self.checksum = self.checksum.wrapping_add(byte);
} }
self self
} }
pub fn write_hex<T: ToBytes>(mut self, value: T) -> Self { pub fn write_hex<T: ToBytes>(mut self, value: T) -> Self {
for byte in value.to_be_bytes().as_ref() { for byte in value.to_le_bytes().as_ref() {
for digit in [(byte >> 4), (byte & 0xf)] { for digit in [(byte >> 4), (byte & 0xf)] {
let char = if digit > 9 { let char = if digit > 9 {
b'a' + digit - 10 b'a' + digit - 10