Let emulator tell debugger to stop
This commit is contained in:
		
							parent
							
								
									a5b5f8e80f
								
							
						
					
					
						commit
						11df670ff4
					
				| 
						 | 
					@ -151,6 +151,7 @@ pub struct Emulator {
 | 
				
			||||||
    linked: Arc<AtomicBool>,
 | 
					    linked: Arc<AtomicBool>,
 | 
				
			||||||
    renderers: HashMap<SimId, TextureSink>,
 | 
					    renderers: HashMap<SimId, TextureSink>,
 | 
				
			||||||
    messages: HashMap<SimId, mpsc::Sender<Toast>>,
 | 
					    messages: HashMap<SimId, mpsc::Sender<Toast>>,
 | 
				
			||||||
 | 
					    debuggers: HashMap<SimId, DebugInfo>,
 | 
				
			||||||
    eye_contents: Vec<u8>,
 | 
					    eye_contents: Vec<u8>,
 | 
				
			||||||
    audio_samples: Vec<f32>,
 | 
					    audio_samples: Vec<f32>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -174,6 +175,7 @@ impl Emulator {
 | 
				
			||||||
            linked,
 | 
					            linked,
 | 
				
			||||||
            renderers: HashMap::new(),
 | 
					            renderers: HashMap::new(),
 | 
				
			||||||
            messages: HashMap::new(),
 | 
					            messages: HashMap::new(),
 | 
				
			||||||
 | 
					            debuggers: HashMap::new(),
 | 
				
			||||||
            eye_contents: vec![0u8; 384 * 224 * 2],
 | 
					            eye_contents: vec![0u8; 384 * 224 * 2],
 | 
				
			||||||
            audio_samples: vec![0.0; EXPECTED_FRAME_SIZE],
 | 
					            audio_samples: vec![0.0; EXPECTED_FRAME_SIZE],
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
| 
						 | 
					@ -288,6 +290,48 @@ impl Emulator {
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn start_debugging(&mut self, sim_id: SimId, sender: DebugSender) {
 | 
				
			||||||
 | 
					        let debug = DebugInfo {
 | 
				
			||||||
 | 
					            sender,
 | 
				
			||||||
 | 
					            stop_reason: Some(DebugStopReason::Trapped),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        self.debuggers.insert(sim_id, debug);
 | 
				
			||||||
 | 
					        self.state
 | 
				
			||||||
 | 
					            .store(EmulatorState::Debugging, Ordering::Release);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn stop_debugging(&mut self, sim_id: SimId) {
 | 
				
			||||||
 | 
					        self.debuggers.remove(&sim_id);
 | 
				
			||||||
 | 
					        if self.debuggers.is_empty() {
 | 
				
			||||||
 | 
					            self.state.store(EmulatorState::Running, Ordering::Release);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn debug_interrupt(&mut self, sim_id: SimId) {
 | 
				
			||||||
 | 
					        let Some(debugger) = self.debuggers.get_mut(&sim_id) else {
 | 
				
			||||||
 | 
					            self.stop_debugging(sim_id);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        if !matches!(debugger.stop_reason, Some(DebugStopReason::Trapped)) {
 | 
				
			||||||
 | 
					            debugger.stop_reason = Some(DebugStopReason::Trapped);
 | 
				
			||||||
 | 
					            if debugger
 | 
				
			||||||
 | 
					                .sender
 | 
				
			||||||
 | 
					                .send(DebugEvent::Stopped(DebugStopReason::Trapped))
 | 
				
			||||||
 | 
					                .is_err()
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                self.stop_debugging(sim_id);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn debug_continue(&mut self, sim_id: SimId) {
 | 
				
			||||||
 | 
					        let Some(debugger) = self.debuggers.get_mut(&sim_id) else {
 | 
				
			||||||
 | 
					            self.stop_debugging(sim_id);
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        debugger.stop_reason = None;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn run(&mut self) {
 | 
					    pub fn run(&mut self) {
 | 
				
			||||||
        loop {
 | 
					        loop {
 | 
				
			||||||
            let idle = self.tick();
 | 
					            let idle = self.tick();
 | 
				
			||||||
| 
						 | 
					@ -320,8 +364,14 @@ impl Emulator {
 | 
				
			||||||
        let p1_state = self.sim_state[SimId::Player1.to_index()].load(Ordering::Acquire);
 | 
					        let p1_state = self.sim_state[SimId::Player1.to_index()].load(Ordering::Acquire);
 | 
				
			||||||
        let p2_state = self.sim_state[SimId::Player2.to_index()].load(Ordering::Acquire);
 | 
					        let p2_state = self.sim_state[SimId::Player2.to_index()].load(Ordering::Acquire);
 | 
				
			||||||
        let state = self.state.load(Ordering::Acquire);
 | 
					        let state = self.state.load(Ordering::Acquire);
 | 
				
			||||||
        let p1_running = state == EmulatorState::Running && p1_state == SimState::Ready;
 | 
					        // Don't emulate if the state is "paused", or if any sim is paused in the debugger
 | 
				
			||||||
        let p2_running = state == EmulatorState::Running && p2_state == SimState::Ready;
 | 
					        let running = match state {
 | 
				
			||||||
 | 
					            EmulatorState::Paused => false,
 | 
				
			||||||
 | 
					            EmulatorState::Running => true,
 | 
				
			||||||
 | 
					            EmulatorState::Debugging => self.debuggers.values().all(|d| d.stop_reason.is_none()),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        let p1_running = running && p1_state == SimState::Ready;
 | 
				
			||||||
 | 
					        let p2_running = running && p2_state == SimState::Ready;
 | 
				
			||||||
        let mut idle = !p1_running && !p2_running;
 | 
					        let mut idle = !p1_running && !p2_running;
 | 
				
			||||||
        if p1_running && p2_running {
 | 
					        if p1_running && p2_running {
 | 
				
			||||||
            Sim::emulate_many(&mut self.sims);
 | 
					            Sim::emulate_many(&mut self.sims);
 | 
				
			||||||
| 
						 | 
					@ -405,6 +455,18 @@ impl Emulator {
 | 
				
			||||||
            EmulatorCommand::Resume => {
 | 
					            EmulatorCommand::Resume => {
 | 
				
			||||||
                self.resume_sims();
 | 
					                self.resume_sims();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            EmulatorCommand::StartDebugging(sim_id, debugger) => {
 | 
				
			||||||
 | 
					                self.start_debugging(sim_id, debugger);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            EmulatorCommand::StopDebugging(sim_id) => {
 | 
				
			||||||
 | 
					                self.stop_debugging(sim_id);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            EmulatorCommand::DebugInterrupt(sim_id) => {
 | 
				
			||||||
 | 
					                self.debug_interrupt(sim_id);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            EmulatorCommand::DebugContinue(sim_id) => {
 | 
				
			||||||
 | 
					                self.debug_continue(sim_id);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            EmulatorCommand::ReadRegister(sim_id, register, done) => {
 | 
					            EmulatorCommand::ReadRegister(sim_id, register, done) => {
 | 
				
			||||||
                let Some(sim) = self.sims.get_mut(sim_id.to_index()) else {
 | 
					                let Some(sim) = self.sims.get_mut(sim_id.to_index()) else {
 | 
				
			||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
| 
						 | 
					@ -476,6 +538,10 @@ pub enum EmulatorCommand {
 | 
				
			||||||
    StopSecondSim,
 | 
					    StopSecondSim,
 | 
				
			||||||
    Pause,
 | 
					    Pause,
 | 
				
			||||||
    Resume,
 | 
					    Resume,
 | 
				
			||||||
 | 
					    StartDebugging(SimId, DebugSender),
 | 
				
			||||||
 | 
					    StopDebugging(SimId),
 | 
				
			||||||
 | 
					    DebugInterrupt(SimId),
 | 
				
			||||||
 | 
					    DebugContinue(SimId),
 | 
				
			||||||
    ReadRegister(SimId, VBRegister, oneshot::Sender<u32>),
 | 
					    ReadRegister(SimId, VBRegister, oneshot::Sender<u32>),
 | 
				
			||||||
    ReadMemory(SimId, Range<u32>, Vec<u8>, oneshot::Sender<Vec<u8>>),
 | 
					    ReadMemory(SimId, Range<u32>, Vec<u8>, oneshot::Sender<Vec<u8>>),
 | 
				
			||||||
    SetAudioEnabled(bool, bool),
 | 
					    SetAudioEnabled(bool, bool),
 | 
				
			||||||
| 
						 | 
					@ -499,6 +565,24 @@ pub enum SimState {
 | 
				
			||||||
pub enum EmulatorState {
 | 
					pub enum EmulatorState {
 | 
				
			||||||
    Paused,
 | 
					    Paused,
 | 
				
			||||||
    Running,
 | 
					    Running,
 | 
				
			||||||
 | 
					    Debugging,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type DebugSender = tokio::sync::mpsc::UnboundedSender<DebugEvent>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 | 
				
			||||||
 | 
					pub enum DebugStopReason {
 | 
				
			||||||
 | 
					    // The debugger told us to pause
 | 
				
			||||||
 | 
					    Trapped,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct DebugInfo {
 | 
				
			||||||
 | 
					    sender: DebugSender,
 | 
				
			||||||
 | 
					    stop_reason: Option<DebugStopReason>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub enum DebugEvent {
 | 
				
			||||||
 | 
					    Stopped(DebugStopReason),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Clone)]
 | 
					#[derive(Clone)]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										248
									
								
								src/gdbserver.rs
								
								
								
								
							
							
						
						
									
										248
									
								
								src/gdbserver.rs
								
								
								
								
							| 
						 | 
					@ -1,22 +1,19 @@
 | 
				
			||||||
use anyhow::{bail, Result};
 | 
					use anyhow::{bail, Result};
 | 
				
			||||||
use registers::REGISTERS;
 | 
					use registers::REGISTERS;
 | 
				
			||||||
use request::{Request, RequestKind};
 | 
					use request::{Request, RequestKind, RequestSource};
 | 
				
			||||||
use response::Response;
 | 
					use response::Response;
 | 
				
			||||||
use std::{
 | 
					use std::{
 | 
				
			||||||
    sync::{Arc, Mutex},
 | 
					    sync::{Arc, Mutex},
 | 
				
			||||||
    thread,
 | 
					    thread,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use tokio::{
 | 
					use tokio::{
 | 
				
			||||||
    io::{AsyncWriteExt as _, BufReader, BufWriter},
 | 
					    io::{AsyncWriteExt as _, BufReader},
 | 
				
			||||||
    net::{
 | 
					    net::{TcpListener, TcpStream},
 | 
				
			||||||
        tcp::{OwnedReadHalf, OwnedWriteHalf},
 | 
					 | 
				
			||||||
        TcpListener, TcpStream,
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    select,
 | 
					    select,
 | 
				
			||||||
    sync::oneshot,
 | 
					    sync::{mpsc, oneshot},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::emulator::{EmulatorClient, EmulatorCommand, SimId};
 | 
					use crate::emulator::{DebugEvent, DebugStopReason, EmulatorClient, EmulatorCommand, SimId};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod registers;
 | 
					mod registers;
 | 
				
			||||||
mod request;
 | 
					mod request;
 | 
				
			||||||
| 
						 | 
					@ -81,8 +78,8 @@ async fn run_server(
 | 
				
			||||||
    let Some(stream) = try_connect(port, status).await else {
 | 
					    let Some(stream) = try_connect(port, status).await else {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    let connection = GdbConnection::new(sim_id, client, stream);
 | 
					    let mut connection = GdbConnection::new(sim_id, client);
 | 
				
			||||||
    match connection.run().await {
 | 
					    match connection.run(stream).await {
 | 
				
			||||||
        Ok(()) => {
 | 
					        Ok(()) => {
 | 
				
			||||||
            *status.lock().unwrap() = GdbServerStatus::Stopped;
 | 
					            *status.lock().unwrap() = GdbServerStatus::Stopped;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -130,122 +127,151 @@ impl GdbServerStatus {
 | 
				
			||||||
struct GdbConnection {
 | 
					struct GdbConnection {
 | 
				
			||||||
    sim_id: SimId,
 | 
					    sim_id: SimId,
 | 
				
			||||||
    client: EmulatorClient,
 | 
					    client: EmulatorClient,
 | 
				
			||||||
    stream_in: BufReader<OwnedReadHalf>,
 | 
					 | 
				
			||||||
    stream_out: BufWriter<OwnedWriteHalf>,
 | 
					 | 
				
			||||||
    ack_messages: bool,
 | 
					    ack_messages: bool,
 | 
				
			||||||
    request_buf: Vec<u8>,
 | 
					    stop_reason: Option<DebugStopReason>,
 | 
				
			||||||
    response_buf: Option<Vec<u8>>,
 | 
					    response_buf: Option<Vec<u8>>,
 | 
				
			||||||
    memory_buf: Option<Vec<u8>>,
 | 
					    memory_buf: Option<Vec<u8>>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl GdbConnection {
 | 
					impl GdbConnection {
 | 
				
			||||||
    fn new(sim_id: SimId, client: EmulatorClient, stream: TcpStream) -> Self {
 | 
					    fn new(sim_id: SimId, client: EmulatorClient) -> Self {
 | 
				
			||||||
        let (rx, tx) = stream.into_split();
 | 
					 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            sim_id,
 | 
					            sim_id,
 | 
				
			||||||
            client,
 | 
					            client,
 | 
				
			||||||
            stream_in: BufReader::new(rx),
 | 
					 | 
				
			||||||
            stream_out: BufWriter::new(tx),
 | 
					 | 
				
			||||||
            ack_messages: true,
 | 
					            ack_messages: true,
 | 
				
			||||||
            request_buf: vec![],
 | 
					            stop_reason: None,
 | 
				
			||||||
            response_buf: None,
 | 
					            response_buf: None,
 | 
				
			||||||
            memory_buf: None,
 | 
					            memory_buf: None,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    async fn run(mut self) -> Result<()> {
 | 
					    async fn run(&mut self, stream: TcpStream) -> Result<()> {
 | 
				
			||||||
        println!("Connected for {}", self.sim_id);
 | 
					        let (debug_sink, mut debug_source) = mpsc::unbounded_channel();
 | 
				
			||||||
        self.client.send_command(EmulatorCommand::Pause);
 | 
					        let (rx, mut tx) = stream.into_split();
 | 
				
			||||||
 | 
					        let mut request_source = RequestSource::new(BufReader::new(rx));
 | 
				
			||||||
 | 
					        self.client
 | 
				
			||||||
 | 
					            .send_command(EmulatorCommand::StartDebugging(self.sim_id, debug_sink));
 | 
				
			||||||
        loop {
 | 
					        loop {
 | 
				
			||||||
            let mut req = Request::read(&mut self.stream_in, &mut self.request_buf).await?;
 | 
					            let response = select! {
 | 
				
			||||||
 | 
					                maybe_event = debug_source.recv() => {
 | 
				
			||||||
 | 
					                    let Some(event) = maybe_event else {
 | 
				
			||||||
 | 
					                        // debugger has stopped running
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					                    self.handle_event(event)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                maybe_request = request_source.recv() => {
 | 
				
			||||||
 | 
					                    let req = maybe_request?;
 | 
				
			||||||
 | 
					                    self.handle_request(req)?
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            if let Some(res) = response {
 | 
				
			||||||
 | 
					                let buffer = res.finish();
 | 
				
			||||||
 | 
					                match std::str::from_utf8(&buffer) {
 | 
				
			||||||
 | 
					                    Ok(text) => println!("response: {text}"),
 | 
				
			||||||
 | 
					                    Err(_) => println!("response: {buffer:02x?}"),
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                tx.write_all(&buffer).await?;
 | 
				
			||||||
 | 
					                self.response_buf = Some(buffer);
 | 
				
			||||||
 | 
					                tx.flush().await?;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Ok(())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn handle_event(&mut self, event: DebugEvent) -> Option<Response> {
 | 
				
			||||||
 | 
					        let res = match event {
 | 
				
			||||||
 | 
					            DebugEvent::Stopped(reason) => {
 | 
				
			||||||
 | 
					                if self.stop_reason.is_some_and(|r| r == reason) {
 | 
				
			||||||
 | 
					                    return None;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                self.stop_reason = Some(reason);
 | 
				
			||||||
 | 
					                self.response()
 | 
				
			||||||
 | 
					                    .write_str(debug_stop_reason_string(self.stop_reason))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        Some(res)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn handle_request(&mut self, mut req: Request<'_>) -> Result<Option<Response>> {
 | 
				
			||||||
        println!("received {:02x?}", req);
 | 
					        println!("received {:02x?}", req);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if req.kind == RequestKind::Signal {
 | 
					        if req.kind == RequestKind::Signal {
 | 
				
			||||||
                self.client.send_command(EmulatorCommand::Pause);
 | 
					            self.client
 | 
				
			||||||
                let res = self
 | 
					                .send_command(EmulatorCommand::DebugInterrupt(self.sim_id));
 | 
				
			||||||
                    .response()
 | 
					            return Ok(None); // we'll send a message when the emulator reports it has stopped
 | 
				
			||||||
                    .write_str("T05;thread:p1.t1;threads:p1.t1;reason:trap;");
 | 
					 | 
				
			||||||
                self.send(res).await?;
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if req.match_str("QStartNoAckMode") {
 | 
					        let res = if req.match_str("QStartNoAckMode") {
 | 
				
			||||||
            let res = self.response().write_str("OK");
 | 
					            let res = self.response().write_str("OK");
 | 
				
			||||||
                self.send(res).await?;
 | 
					 | 
				
			||||||
            self.ack_messages = false;
 | 
					            self.ack_messages = false;
 | 
				
			||||||
 | 
					            res
 | 
				
			||||||
        } else if req.match_str("qSupported:") {
 | 
					        } else if req.match_str("qSupported:") {
 | 
				
			||||||
                let res = self
 | 
					            self.response()
 | 
				
			||||||
                    .response()
 | 
					                .write_str("multiprocess+;swbreak+;vContSupported+;PacketSize=10000")
 | 
				
			||||||
                    .write_str("multiprocess+;swbreak+;vContSupported+;PacketSize=10000");
 | 
					        } else if req
 | 
				
			||||||
                self.send(res).await?;
 | 
					            .match_some_str([
 | 
				
			||||||
            } else if req.match_str("QThreadSuffixSupported")
 | 
					                "QThreadSuffixSupported",
 | 
				
			||||||
                || req.match_str("QListThreadsInStopReply")
 | 
					                "QListThreadsInStopReply",
 | 
				
			||||||
                || req.match_str("QEnableErrorStrings")
 | 
					                "QEnableErrorStrings",
 | 
				
			||||||
 | 
					            ])
 | 
				
			||||||
 | 
					            .is_some()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
                let res = self.response().write_str("OK");
 | 
					            self.response().write_str("OK")
 | 
				
			||||||
                self.send(res).await?;
 | 
					 | 
				
			||||||
        } else if req.match_str("qHostInfo") {
 | 
					        } else if req.match_str("qHostInfo") {
 | 
				
			||||||
                let res = self.response().write_str(&format!(
 | 
					            self.response().write_str(&format!(
 | 
				
			||||||
                "triple:{};endian:little;ptrsize:4;",
 | 
					                "triple:{};endian:little;ptrsize:4;",
 | 
				
			||||||
                hex::encode("v810-unknown-vb")
 | 
					                hex::encode("v810-unknown-vb")
 | 
				
			||||||
                ));
 | 
					            ))
 | 
				
			||||||
                self.send(res).await?;
 | 
					 | 
				
			||||||
        } else if req.match_str("qProcessInfo") {
 | 
					        } else if req.match_str("qProcessInfo") {
 | 
				
			||||||
                let res = self.response().write_str(&format!(
 | 
					            self.response().write_str(&format!(
 | 
				
			||||||
                "pid:1;triple:{};endian:little;ptrsize:4;",
 | 
					                "pid:1;triple:{};endian:little;ptrsize:4;",
 | 
				
			||||||
                hex::encode("v810-unknown-vb")
 | 
					                hex::encode("v810-unknown-vb")
 | 
				
			||||||
                ));
 | 
					            ))
 | 
				
			||||||
                self.send(res).await?;
 | 
					 | 
				
			||||||
        } else if req.match_str("qRegisterInfo") {
 | 
					        } else if req.match_str("qRegisterInfo") {
 | 
				
			||||||
            let mut get_reg_info = || {
 | 
					            let mut get_reg_info = || {
 | 
				
			||||||
                let register = req.match_hex::<usize>()?;
 | 
					                let register = req.match_hex::<usize>()?;
 | 
				
			||||||
                REGISTERS.get(register)
 | 
					                REGISTERS.get(register)
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
                let Some(reg_info) = get_reg_info() else {
 | 
					            if let Some(reg_info) = get_reg_info() {
 | 
				
			||||||
                    self.send_empty().await?;
 | 
					                self.response().write_str(®_info.to_description())
 | 
				
			||||||
                    continue;
 | 
					            } else {
 | 
				
			||||||
                };
 | 
					                self.response()
 | 
				
			||||||
                let res = self.response().write_str(®_info.to_description());
 | 
					            }
 | 
				
			||||||
                self.send(res).await?;
 | 
					 | 
				
			||||||
        } else if req.match_str("vCont?") {
 | 
					        } else if req.match_str("vCont?") {
 | 
				
			||||||
                let res = self.response().write_str("vCont;c;");
 | 
					            self.response().write_str("vCont;c;")
 | 
				
			||||||
                self.send(res).await?;
 | 
					 | 
				
			||||||
        } else if req.match_str("qC") {
 | 
					        } else if req.match_str("qC") {
 | 
				
			||||||
            // The v810 has no threads, so report that the "current thread" is 1.
 | 
					            // The v810 has no threads, so report that the "current thread" is 1.
 | 
				
			||||||
                let res = self.response().write_str("QCp1.t1");
 | 
					            self.response().write_str("QCp1.t1")
 | 
				
			||||||
                self.send(res).await?;
 | 
					 | 
				
			||||||
        } else if req.match_str("qfThreadInfo") {
 | 
					        } else if req.match_str("qfThreadInfo") {
 | 
				
			||||||
                let res = self.response().write_str("mp1.t1");
 | 
					            self.response().write_str("mp1.t1")
 | 
				
			||||||
                self.send(res).await?;
 | 
					 | 
				
			||||||
        } else if req.match_str("qsThreadInfo") {
 | 
					        } else if req.match_str("qsThreadInfo") {
 | 
				
			||||||
                let res = self.response().write_str("l");
 | 
					            self.response().write_str("l")
 | 
				
			||||||
                self.send(res).await?;
 | 
					 | 
				
			||||||
        } else if req.match_str("k") {
 | 
					        } else if req.match_str("k") {
 | 
				
			||||||
            bail!("debug process was killed");
 | 
					            bail!("debug process was killed");
 | 
				
			||||||
        } else if req.match_str("?") {
 | 
					        } else if req.match_str("?") {
 | 
				
			||||||
                let res = self.response().write_str("T00;thread:p1.t1;threads:p1.t1;");
 | 
					            self.response()
 | 
				
			||||||
                self.send(res).await?;
 | 
					                .write_str(debug_stop_reason_string(self.stop_reason))
 | 
				
			||||||
            } else if req.match_str("c") || req.match_str("vCont;c:") {
 | 
					        } else if req.match_some_str(["c", "vCont;c:"]).is_some() {
 | 
				
			||||||
                self.client.send_command(EmulatorCommand::Resume);
 | 
					            self.client
 | 
				
			||||||
 | 
					                .send_command(EmulatorCommand::DebugContinue(self.sim_id));
 | 
				
			||||||
 | 
					            self.stop_reason = None;
 | 
				
			||||||
            // Don't send a response until we hit a breakpoint or get interrupted
 | 
					            // Don't send a response until we hit a breakpoint or get interrupted
 | 
				
			||||||
 | 
					            return Ok(None);
 | 
				
			||||||
        } else if req.match_str("p") {
 | 
					        } else if req.match_str("p") {
 | 
				
			||||||
            let mut read_register = || {
 | 
					            let mut read_register = || {
 | 
				
			||||||
                let register_index = req.match_hex::<usize>()?;
 | 
					                let register_index = req.match_hex::<usize>()?;
 | 
				
			||||||
                let register = REGISTERS.get(register_index)?.to_vb_register();
 | 
					                let register = REGISTERS.get(register_index)?.to_vb_register();
 | 
				
			||||||
                let (tx, rx) = ::oneshot::channel();
 | 
					                let (tx, rx) = ::oneshot::channel();
 | 
				
			||||||
                    self.client.send_command(EmulatorCommand::ReadRegister(
 | 
					                self.client
 | 
				
			||||||
                        self.sim_id,
 | 
					                    .send_command(EmulatorCommand::ReadRegister(self.sim_id, register, tx));
 | 
				
			||||||
                        register,
 | 
					 | 
				
			||||||
                        tx,
 | 
					 | 
				
			||||||
                    ));
 | 
					 | 
				
			||||||
                rx.recv().ok()
 | 
					                rx.recv().ok()
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
                let Some(value) = read_register() else {
 | 
					            if let Some(value) = read_register() {
 | 
				
			||||||
                    self.send_empty().await?;
 | 
					                self.response().write_hex(value)
 | 
				
			||||||
                    continue;
 | 
					            } else {
 | 
				
			||||||
                };
 | 
					                self.response()
 | 
				
			||||||
                let res = self.response().write_hex(value);
 | 
					            }
 | 
				
			||||||
                self.send(res).await?;
 | 
					        } else if let Some(op) = req.match_some_str(["m", "x"]) {
 | 
				
			||||||
            } else if req.match_str("m") {
 | 
					 | 
				
			||||||
            let mut read_memory = || {
 | 
					            let mut read_memory = || {
 | 
				
			||||||
                let start = req.match_hex::<u32>()?;
 | 
					                let start = req.match_hex::<u32>()?;
 | 
				
			||||||
                if !req.match_str(",") {
 | 
					                if !req.match_str(",") {
 | 
				
			||||||
| 
						 | 
					@ -263,58 +289,29 @@ impl GdbConnection {
 | 
				
			||||||
                ));
 | 
					                ));
 | 
				
			||||||
                rx.recv().ok()
 | 
					                rx.recv().ok()
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
                let Some(memory) = read_memory() else {
 | 
					            if let Some(memory) = read_memory() {
 | 
				
			||||||
                    self.send_empty().await?;
 | 
					 | 
				
			||||||
                    continue;
 | 
					 | 
				
			||||||
                };
 | 
					 | 
				
			||||||
                let mut res = self.response();
 | 
					                let mut res = self.response();
 | 
				
			||||||
 | 
					                if op == "m" {
 | 
				
			||||||
 | 
					                    // send the hex-encoded byte stream
 | 
				
			||||||
                    for byte in &memory {
 | 
					                    for byte in &memory {
 | 
				
			||||||
                        res = res.write_hex(*byte);
 | 
					                        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 {
 | 
					                } else {
 | 
				
			||||||
 | 
					                    // send the raw byte stream
 | 
				
			||||||
                    for byte in &memory {
 | 
					                    for byte in &memory {
 | 
				
			||||||
                        res = res.write_byte(*byte);
 | 
					                        res = res.write_byte(*byte);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                self.memory_buf = Some(memory);
 | 
					                self.memory_buf = Some(memory);
 | 
				
			||||||
                self.send(res).await?;
 | 
					                res
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                self.response()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            // unrecognized command
 | 
					            // unrecognized command
 | 
				
			||||||
                self.send_empty().await?;
 | 
					            self.response()
 | 
				
			||||||
            }
 | 
					        };
 | 
				
			||||||
        }
 | 
					        Ok(Some(res))
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    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 {
 | 
				
			||||||
| 
						 | 
					@ -323,15 +320,18 @@ impl GdbConnection {
 | 
				
			||||||
            self.ack_messages,
 | 
					            self.ack_messages,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async fn send(&mut self, res: Response) -> std::io::Result<()> {
 | 
					impl Drop for GdbConnection {
 | 
				
			||||||
        let buffer = res.finish();
 | 
					    fn drop(&mut self) {
 | 
				
			||||||
        match std::str::from_utf8(&buffer) {
 | 
					        self.client
 | 
				
			||||||
            Ok(text) => println!("response: {text}"),
 | 
					            .send_command(EmulatorCommand::StopDebugging(self.sim_id));
 | 
				
			||||||
            Err(_) => println!("response: {buffer:02x?}"),
 | 
					    }
 | 
				
			||||||
        }
 | 
					}
 | 
				
			||||||
        self.stream_out.write_all(&buffer).await?;
 | 
					
 | 
				
			||||||
        self.response_buf = Some(buffer);
 | 
					fn debug_stop_reason_string(reason: Option<DebugStopReason>) -> &'static str {
 | 
				
			||||||
        self.stream_out.flush().await
 | 
					    match reason {
 | 
				
			||||||
 | 
					        Some(DebugStopReason::Trapped) => "T05;thread:p1.t1;threads:p1.t1;reason:trap;",
 | 
				
			||||||
 | 
					        None => "T00;thread:p1.t1;threads:p1.t1;",
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -35,66 +35,7 @@ impl std::fmt::Debug for Request<'_> {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'a> Request<'a> {
 | 
					impl Request<'_> {
 | 
				
			||||||
    pub async fn read<R: AsyncRead + Unpin>(
 | 
					 | 
				
			||||||
        reader: &mut R,
 | 
					 | 
				
			||||||
        buffer: &'a mut Vec<u8>,
 | 
					 | 
				
			||||||
    ) -> Result<Self> {
 | 
					 | 
				
			||||||
        buffer.clear();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut char = reader.read_u8().await?;
 | 
					 | 
				
			||||||
        while char == b'+' {
 | 
					 | 
				
			||||||
            // just ignore positive acks
 | 
					 | 
				
			||||||
            char = reader.read_u8().await?;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if char == b'-' {
 | 
					 | 
				
			||||||
            bail!("no support for negative acks");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if char == 0x03 {
 | 
					 | 
				
			||||||
            // This is how the client "cancels an in-flight request"
 | 
					 | 
				
			||||||
            buffer.push(char);
 | 
					 | 
				
			||||||
            return Ok(Self {
 | 
					 | 
				
			||||||
                kind: RequestKind::Signal,
 | 
					 | 
				
			||||||
                buffer,
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if char != b'$' {
 | 
					 | 
				
			||||||
            // Messages are supposed to start with a dollar sign
 | 
					 | 
				
			||||||
            bail!("malformed message");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // now read the body
 | 
					 | 
				
			||||||
        let mut checksum = 0u8;
 | 
					 | 
				
			||||||
        char = reader.read_u8().await?;
 | 
					 | 
				
			||||||
        while char != b'#' {
 | 
					 | 
				
			||||||
            if char == b'}' {
 | 
					 | 
				
			||||||
                // escape character
 | 
					 | 
				
			||||||
                checksum = checksum.wrapping_add(char);
 | 
					 | 
				
			||||||
                char = reader.read_u8().await?;
 | 
					 | 
				
			||||||
                checksum = checksum.wrapping_add(char);
 | 
					 | 
				
			||||||
                buffer.push(char ^ 0x20);
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                checksum = checksum.wrapping_add(char);
 | 
					 | 
				
			||||||
                buffer.push(char);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            char = reader.read_u8().await?;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut checksum_bytes = [b'0'; 2];
 | 
					 | 
				
			||||||
        reader.read_exact(&mut checksum_bytes).await?;
 | 
					 | 
				
			||||||
        let (real_checksum, 2) = u8::from_radix_16(&checksum_bytes) else {
 | 
					 | 
				
			||||||
            bail!("invalid checksum");
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        if checksum != real_checksum {
 | 
					 | 
				
			||||||
            bail!("mismatched checksum");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Ok(Self {
 | 
					 | 
				
			||||||
            kind: RequestKind::Command,
 | 
					 | 
				
			||||||
            buffer,
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn match_str(&mut self, prefix: &str) -> bool {
 | 
					    pub fn match_str(&mut self, prefix: &str) -> bool {
 | 
				
			||||||
        if let Some(new_buffer) = self.buffer.strip_prefix(prefix.as_bytes()) {
 | 
					        if let Some(new_buffer) = self.buffer.strip_prefix(prefix.as_bytes()) {
 | 
				
			||||||
            self.buffer = new_buffer;
 | 
					            self.buffer = new_buffer;
 | 
				
			||||||
| 
						 | 
					@ -103,6 +44,13 @@ impl<'a> Request<'a> {
 | 
				
			||||||
        false
 | 
					        false
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn match_some_str<'a, I: IntoIterator<Item = &'a str>>(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        prefixes: I,
 | 
				
			||||||
 | 
					    ) -> Option<&'a str> {
 | 
				
			||||||
 | 
					        prefixes.into_iter().find(|&prefix| self.match_str(prefix))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn match_hex<I: FromRadix16>(&mut self) -> Option<I> {
 | 
					    pub fn match_hex<I: FromRadix16>(&mut self) -> Option<I> {
 | 
				
			||||||
        match I::from_radix_16(self.buffer) {
 | 
					        match I::from_radix_16(self.buffer) {
 | 
				
			||||||
            (_, 0) => None,
 | 
					            (_, 0) => None,
 | 
				
			||||||
| 
						 | 
					@ -113,3 +61,119 @@ impl<'a> Request<'a> {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct RequestSource<R> {
 | 
				
			||||||
 | 
					    reader: R,
 | 
				
			||||||
 | 
					    buffer: Vec<u8>,
 | 
				
			||||||
 | 
					    state: RequestReadState,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<R: AsyncRead + Unpin> RequestSource<R> {
 | 
				
			||||||
 | 
					    pub fn new(reader: R) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            reader,
 | 
				
			||||||
 | 
					            buffer: vec![],
 | 
				
			||||||
 | 
					            state: RequestReadState::Header,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub async fn recv(&mut self) -> Result<Request<'_>> {
 | 
				
			||||||
 | 
					        let mut char = self.reader.read_u8().await?;
 | 
				
			||||||
 | 
					        if matches!(self.state, RequestReadState::Start) {
 | 
				
			||||||
 | 
					            self.buffer.clear();
 | 
				
			||||||
 | 
					            self.state = RequestReadState::Header;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if matches!(self.state, RequestReadState::Header) {
 | 
				
			||||||
 | 
					            // Just ignore positive acks
 | 
				
			||||||
 | 
					            while char == b'+' {
 | 
				
			||||||
 | 
					                char = self.reader.read_u8().await?;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if char == b'-' {
 | 
				
			||||||
 | 
					                bail!("no support for negative acks");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if char == 0x03 {
 | 
				
			||||||
 | 
					                // This is how the client "cancels an in-flight request"
 | 
				
			||||||
 | 
					                self.buffer.push(char);
 | 
				
			||||||
 | 
					                self.state = RequestReadState::Start;
 | 
				
			||||||
 | 
					                return Ok(Request {
 | 
				
			||||||
 | 
					                    kind: RequestKind::Signal,
 | 
				
			||||||
 | 
					                    buffer: &self.buffer,
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if char != b'$' {
 | 
				
			||||||
 | 
					                // Messages are supposed to start with a dollar sign
 | 
				
			||||||
 | 
					                bail!("malformed message");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            self.state = RequestReadState::Body {
 | 
				
			||||||
 | 
					                checksum: 0,
 | 
				
			||||||
 | 
					                escaping: false,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            char = self.reader.read_u8().await?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        while let RequestReadState::Body { checksum, escaping } = &mut self.state {
 | 
				
			||||||
 | 
					            if char == b'#' && !*escaping {
 | 
				
			||||||
 | 
					                self.state = RequestReadState::Checksum {
 | 
				
			||||||
 | 
					                    expected: *checksum,
 | 
				
			||||||
 | 
					                    actual: 0,
 | 
				
			||||||
 | 
					                    digits: 0,
 | 
				
			||||||
 | 
					                };
 | 
				
			||||||
 | 
					                char = self.reader.read_u8().await?;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            *checksum = checksum.wrapping_add(char);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if *escaping {
 | 
				
			||||||
 | 
					                // escaped character
 | 
				
			||||||
 | 
					                self.buffer.push(char ^ 0x20);
 | 
				
			||||||
 | 
					                *escaping = false;
 | 
				
			||||||
 | 
					            } else if char == b'}' {
 | 
				
			||||||
 | 
					                // next character will be escaped
 | 
				
			||||||
 | 
					                *escaping = true;
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                self.buffer.push(char);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            char = self.reader.read_u8().await?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        while let RequestReadState::Checksum {
 | 
				
			||||||
 | 
					            expected,
 | 
				
			||||||
 | 
					            actual,
 | 
				
			||||||
 | 
					            digits,
 | 
				
			||||||
 | 
					        } = &mut self.state
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            let digit = match char {
 | 
				
			||||||
 | 
					                b'0'..=b'9' => char - b'0',
 | 
				
			||||||
 | 
					                b'a'..=b'f' => char - b'a' + 10,
 | 
				
			||||||
 | 
					                b'A'..=b'F' => char - b'A' + 10,
 | 
				
			||||||
 | 
					                _ => bail!("invalid checksum"),
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            *actual = (*actual << 4) + digit;
 | 
				
			||||||
 | 
					            *digits += 1;
 | 
				
			||||||
 | 
					            if *digits == 2 {
 | 
				
			||||||
 | 
					                if *expected != *actual {
 | 
				
			||||||
 | 
					                    bail!("mismatched checksum");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                self.state = RequestReadState::Start;
 | 
				
			||||||
 | 
					                return Ok(Request {
 | 
				
			||||||
 | 
					                    kind: RequestKind::Command,
 | 
				
			||||||
 | 
					                    buffer: &self.buffer,
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            char = self.reader.read_u8().await?;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        unreachable!();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum RequestReadState {
 | 
				
			||||||
 | 
					    Start,
 | 
				
			||||||
 | 
					    Header,
 | 
				
			||||||
 | 
					    Body {
 | 
				
			||||||
 | 
					        checksum: u8,
 | 
				
			||||||
 | 
					        escaping: bool,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    Checksum {
 | 
				
			||||||
 | 
					        expected: u8,
 | 
				
			||||||
 | 
					        actual: u8,
 | 
				
			||||||
 | 
					        digits: u8,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -83,20 +83,18 @@ impl GameWindow {
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        ui.menu_button("Emulation", |ui| {
 | 
					        ui.menu_button("Emulation", |ui| {
 | 
				
			||||||
            let state = self.client.emulator_state();
 | 
					            let state = self.client.emulator_state();
 | 
				
			||||||
            let can_interact = self.client.sim_state(self.sim_id) == SimState::Ready;
 | 
					            let is_ready = self.client.sim_state(self.sim_id) == SimState::Ready;
 | 
				
			||||||
 | 
					            let can_pause = is_ready && state == EmulatorState::Running;
 | 
				
			||||||
            if state == EmulatorState::Running {
 | 
					            if state == EmulatorState::Running {
 | 
				
			||||||
                if ui.add_enabled(can_interact, Button::new("Pause")).clicked() {
 | 
					                if ui.add_enabled(is_ready, Button::new("Pause")).clicked() {
 | 
				
			||||||
                    self.client.send_command(EmulatorCommand::Pause);
 | 
					                    self.client.send_command(EmulatorCommand::Pause);
 | 
				
			||||||
                    ui.close_menu();
 | 
					                    ui.close_menu();
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            } else if ui
 | 
					            } else if ui.add_enabled(can_pause, Button::new("Resume")).clicked() {
 | 
				
			||||||
                .add_enabled(can_interact, Button::new("Resume"))
 | 
					 | 
				
			||||||
                .clicked()
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                self.client.send_command(EmulatorCommand::Resume);
 | 
					                self.client.send_command(EmulatorCommand::Resume);
 | 
				
			||||||
                ui.close_menu();
 | 
					                ui.close_menu();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if ui.add_enabled(can_interact, Button::new("Reset")).clicked() {
 | 
					            if ui.add_enabled(is_ready, Button::new("Reset")).clicked() {
 | 
				
			||||||
                self.client
 | 
					                self.client
 | 
				
			||||||
                    .send_command(EmulatorCommand::Reset(self.sim_id));
 | 
					                    .send_command(EmulatorCommand::Reset(self.sim_id));
 | 
				
			||||||
                ui.close_menu();
 | 
					                ui.close_menu();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue