Implement reading from real memory
This commit is contained in:
parent
82c3104ab9
commit
be8cdd958a
|
@ -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,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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(®_info.to_description());
|
let res = self.response().write_str(®_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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue