diff --git a/src/emulator.rs b/src/emulator.rs index 4f84103..0250cf4 100644 --- a/src/emulator.rs +++ b/src/emulator.rs @@ -3,6 +3,7 @@ use std::{ fmt::Display, fs::{self, File}, io::{Read, Seek, SeekFrom, Write}, + ops::Range, path::{Path, PathBuf}, sync::{ atomic::{AtomicBool, AtomicUsize, Ordering}, @@ -397,6 +398,13 @@ impl Emulator { let value = sim.read_register(register); 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) => { self.audio_on[SimId::Player1.to_index()].store(p1, Ordering::Release); self.audio_on[SimId::Player2.to_index()].store(p2, Ordering::Release); @@ -455,6 +463,7 @@ pub enum EmulatorCommand { Pause, Resume, ReadRegister(SimId, VBRegister, oneshot::Sender), + ReadMemory(SimId, Range, Vec, oneshot::Sender>), SetAudioEnabled(bool, bool), Link, Unlink, diff --git a/src/emulator/shrooms_vb_core.rs b/src/emulator/shrooms_vb_core.rs index 507de03..0fffe62 100644 --- a/src/emulator/shrooms_vb_core.rs +++ b/src/emulator/shrooms_vb_core.rs @@ -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 bitflags::bitflags; @@ -101,6 +101,8 @@ extern "C" { fn vb_get_user_data(sim: *mut VB) -> *mut c_void; #[link_name = "vbInit"] 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"] fn vb_reset(sim: *mut VB); #[link_name = "vbSetCartRAM"] @@ -314,6 +316,13 @@ impl Sim { VBRegister::PC => unsafe { vb_get_program_counter(self.sim) }, } } + + pub fn read_memory(&mut self, addresses: Range, into: &mut Vec) { + for address in addresses { + let byte = unsafe { vb_read(self.sim, address, VBDataType::U8) }; + into.push(byte as u8); + } + } } impl Drop for Sim { diff --git a/src/gdbserver.rs b/src/gdbserver.rs index 056ae08..09d0dbe 100644 --- a/src/gdbserver.rs +++ b/src/gdbserver.rs @@ -135,6 +135,7 @@ struct GdbConnection { ack_messages: bool, request_buf: Vec, response_buf: Option>, + memory_buf: Option>, } impl GdbConnection { @@ -148,6 +149,7 @@ impl GdbConnection { ack_messages: true, request_buf: vec![], response_buf: None, + memory_buf: None, } } async fn run(mut self) -> Result<()> { @@ -155,7 +157,7 @@ impl GdbConnection { self.client.send_command(EmulatorCommand::Pause); loop { 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 { self.client.send_command(EmulatorCommand::Pause); @@ -173,7 +175,7 @@ impl GdbConnection { } else if req.match_str("qSupported:") { let res = self .response() - .write_str("multiprocess+;swbreak+;vContSupported+"); + .write_str("multiprocess+;swbreak+;vContSupported+;PacketSize=10000"); self.send(res).await?; } else if req.match_str("QThreadSuffixSupported") || req.match_str("QListThreadsInStopReply") @@ -199,8 +201,7 @@ impl GdbConnection { REGISTERS.get(register) }; let Some(reg_info) = get_reg_info() else { - let res = self.response(); - self.send(res).await?; + self.send_empty().await?; continue; }; let res = self.response().write_str(®_info.to_description()); @@ -239,39 +240,83 @@ impl GdbConnection { rx.recv().ok() }; let Some(value) = read_register() else { - let res = self.response(); - self.send(res).await?; + self.send_empty().await?; continue; }; let res = self.response().write_hex(value); self.send(res).await?; } else if req.match_str("m") { - let mut read_params = || { + let mut read_memory = || { let start = req.match_hex::()?; if !req.match_str(",") { return None; }; let size = req.match_hex::()?; - 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 res = self.response(); - self.send(res).await?; + let Some(memory) = read_memory() else { + self.send_empty().await?; continue; }; let mut res = self.response(); - for i in 0..size { - res = res.write_hex((start + i) as u8); + for byte in &memory { + 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::()?; + if !req.match_str(",") { + return None; + }; + let size = req.match_hex::()?; + 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?; } else { // unrecognized command - let res = self.response(); - self.send(res).await?; + self.send_empty().await?; } } } + async fn send_empty(&mut self) -> std::io::Result<()> { + let res = self.response(); + self.send(res).await + } + fn response(&mut self) -> Response { Response::new( self.response_buf.take().unwrap_or_default(), @@ -281,7 +326,10 @@ impl GdbConnection { async fn send(&mut self, res: Response) -> std::io::Result<()> { 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.response_buf = Some(buffer); self.stream_out.flush().await diff --git a/src/gdbserver/response.rs b/src/gdbserver/response.rs index b7c684e..e35bcc7 100644 --- a/src/gdbserver/response.rs +++ b/src/gdbserver/response.rs @@ -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() { - self.buffer.push(*byte); - self.checksum = self.checksum.wrapping_add(*byte); + me = me.write_byte(*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 } pub fn write_hex(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)] { let char = if digit > 9 { b'a' + digit - 10