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,37 +240,81 @@ 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
 | 
				
			||||||
 | 
					                self.send_empty().await?;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async fn send_empty(&mut self) -> std::io::Result<()> {
 | 
				
			||||||
        let res = self.response();
 | 
					        let res = self.response();
 | 
				
			||||||
                self.send(res).await?;
 | 
					        self.send(res).await
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn response(&mut self) -> Response {
 | 
					    fn response(&mut self) -> Response {
 | 
				
			||||||
| 
						 | 
					@ -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