diff --git a/src/emulator.rs b/src/emulator.rs index b0d3d61..3dadfe1 100644 --- a/src/emulator.rs +++ b/src/emulator.rs @@ -550,6 +550,13 @@ impl Emulator { sim.read_memory(start, length, &mut buffer); let _ = done.send(buffer); } + EmulatorCommand::WriteMemory(sim_id, start, buffer, done) => { + let Some(sim) = self.sims.get_mut(sim_id.to_index()) else { + return; + }; + sim.write_memory(start, &buffer); + let _ = done.send(buffer); + } EmulatorCommand::AddBreakpoint(sim_id, address) => { let Some(sim) = self.sims.get_mut(sim_id.to_index()) else { return; @@ -639,6 +646,7 @@ pub enum EmulatorCommand { ReadRegister(SimId, VBRegister, oneshot::Sender), WriteRegister(SimId, VBRegister, u32), ReadMemory(SimId, u32, usize, Vec, oneshot::Sender>), + WriteMemory(SimId, u32, Vec, oneshot::Sender>), AddBreakpoint(SimId, u32), RemoveBreakpoint(SimId, u32), AddWatchpoint(SimId, u32, usize, VBWatchpointType), diff --git a/src/emulator/shrooms_vb_core.rs b/src/emulator/shrooms_vb_core.rs index 773114f..c6952cd 100644 --- a/src/emulator/shrooms_vb_core.rs +++ b/src/emulator/shrooms_vb_core.rs @@ -166,6 +166,8 @@ extern "C" { fn vb_set_write_callback(sim: *mut VB, callback: Option) -> Option; #[link_name = "vbSizeOf"] fn vb_size_of() -> usize; + #[link_name = "vbWrite"] + fn vb_write(sim: *mut VB, address: u32, _type: VBDataType, value: i32) -> i32; } extern "C" fn on_frame(sim: *mut VB) -> c_int { @@ -476,6 +478,14 @@ impl Sim { } } + pub fn write_memory(&mut self, start: u32, buffer: &[u8]) { + let mut address = start; + for byte in buffer { + unsafe { vb_write(self.sim, address, VBDataType::U8, *byte as i32) }; + address = address.wrapping_add(1); + } + } + pub fn add_breakpoint(&mut self, address: u32) { let data = self.get_state(); if let Err(index) = data.breakpoints.binary_search(&address) { diff --git a/src/gdbserver.rs b/src/gdbserver.rs index 35ca275..b646990 100644 --- a/src/gdbserver.rs +++ b/src/gdbserver.rs @@ -346,22 +346,9 @@ impl GdbConnection { } } else if let Some(op) = req.match_some_str(["m", "x"]) { let mut read_memory = || { - let start = req.match_hex::()?; - if !req.match_str(",") { - return None; - }; - let length = req.match_hex::()?; - + let (start, length) = parse_memory_range(&mut req)?; let mut buf = self.memory_buf.take().unwrap_or_default(); buf.clear(); - - // The v810 has a 32-bit address space. - // Addresses wrap within that space, but we don't need to implement that for 64-bit addresses. - // Just refuse to return any info for addresses above 0xffffffff. - let Ok(start) = u32::try_from(start) else { - return Some(buf); - }; - let length = length.min((u32::MAX - start) as usize + 1); if length == 0 { return Some(buf); } @@ -395,6 +382,38 @@ impl GdbConnection { } else { self.response() } + } else if let Some(op) = req.match_some_str(["M", "X"]) { + let mut write_memory = || { + let (start, length) = parse_memory_range(&mut req)?; + if length == 0 { + return Some(()); + } + if !req.match_str(":") { + return None; + } + let mut buf = self.memory_buf.take().unwrap_or_default(); + buf.resize(length, 0); + let successful_read = if op == "M" { + req.match_hex_bytes(&mut buf) + } else { + req.match_bytes(&mut buf) + }; + if !successful_read { + self.memory_buf = Some(buf); + return None; + } + let (tx, rx) = ::oneshot::channel(); + self.client + .send_command(EmulatorCommand::WriteMemory(self.sim_id, start, buf, tx)); + let buf = rx.recv().ok()?; + self.memory_buf = Some(buf); + Some(()) + }; + if write_memory().is_some() { + self.response().write_str("OK") + } else { + self.response() + } } else if req.match_str("Z") { let mut parse_request = || { let type_ = req.match_hex::()?; @@ -483,6 +502,23 @@ impl Drop for GdbConnection { } } +// parse a memory range into a start and a length. +fn parse_memory_range(req: &mut Request<'_>) -> Option<(u32, usize)> { + let start = req.match_hex::()?; + if !req.match_str(",") { + return None; + }; + let length = req.match_hex::()?; + let Ok(start) = u32::try_from(start) else { + // The v810 has a 32-bit address space. + // Addresses wrap within that space, but we don't need to implement that for 64-bit addresses. + // Just refuse to return any info for addresses above 0xffffffff. + return Some((0, 0)); + }; + let length = length.min((u32::MAX - start) as usize + 1); + Some((start, length)) +} + fn debug_stop_reason_string(reason: Option) -> String { let mut result = String::new(); result += if reason.is_some() { "T05;" } else { "T00;" }; diff --git a/src/gdbserver/request.rs b/src/gdbserver/request.rs index d0459b3..86bf600 100644 --- a/src/gdbserver/request.rs +++ b/src/gdbserver/request.rs @@ -74,6 +74,15 @@ impl Request<'_> { self.buffer = self.buffer.split_at(buffer.len()).1; true } + + pub fn match_bytes(&mut self, buffer: &mut [u8]) -> bool { + if self.buffer.len() < buffer.len() { + return false; + } + buffer.copy_from_slice(&self.buffer[0..buffer.len()]); + self.buffer = self.buffer.split_at(buffer.len()).1; + true + } } pub struct RequestSource {