Compare commits
No commits in common. "b94ae4d5869dec715a84b9bbd9ac1e8020a47dba" and "1a54c2e6fc520bad5eab006d74b00b3ddece4f3f" have entirely different histories.
b94ae4d586
...
1a54c2e6fc
|
@ -28,15 +28,6 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "addr2line"
|
|
||||||
version = "0.24.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
|
|
||||||
dependencies = [
|
|
||||||
"gimli",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "adler2"
|
name = "adler2"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
|
@ -409,21 +400,6 @@ version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "backtrace"
|
|
||||||
version = "0.3.74"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
|
|
||||||
dependencies = [
|
|
||||||
"addr2line",
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"miniz_oxide",
|
|
||||||
"object",
|
|
||||||
"rustc-demangle",
|
|
||||||
"windows-targets 0.52.6",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bindgen"
|
name = "bindgen"
|
||||||
version = "0.70.1"
|
version = "0.70.1"
|
||||||
|
@ -1330,12 +1306,6 @@ dependencies = [
|
||||||
"windows 0.58.0",
|
"windows 0.58.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "gimli"
|
|
||||||
version = "0.31.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gl_generator"
|
name = "gl_generator"
|
||||||
version = "0.14.0"
|
version = "0.14.0"
|
||||||
|
@ -1754,7 +1724,6 @@ dependencies = [
|
||||||
"egui-winit",
|
"egui-winit",
|
||||||
"egui_extras",
|
"egui_extras",
|
||||||
"gilrs",
|
"gilrs",
|
||||||
"hex",
|
|
||||||
"image",
|
"image",
|
||||||
"itertools",
|
"itertools",
|
||||||
"num-derive",
|
"num-derive",
|
||||||
|
@ -1767,7 +1736,6 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thread-priority",
|
"thread-priority",
|
||||||
"tokio",
|
|
||||||
"wgpu",
|
"wgpu",
|
||||||
"windows 0.58.0",
|
"windows 0.58.0",
|
||||||
"winit",
|
"winit",
|
||||||
|
@ -1934,17 +1902,6 @@ dependencies = [
|
||||||
"simd-adler32",
|
"simd-adler32",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "mio"
|
|
||||||
version = "1.0.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"wasi",
|
|
||||||
"windows-sys 0.52.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "naga"
|
name = "naga"
|
||||||
version = "23.1.0"
|
version = "23.1.0"
|
||||||
|
@ -2319,15 +2276,6 @@ dependencies = [
|
||||||
"objc2-foundation",
|
"objc2-foundation",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "object"
|
|
||||||
version = "0.36.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "oboe"
|
name = "oboe"
|
||||||
version = "0.6.1"
|
version = "0.6.1"
|
||||||
|
@ -2748,12 +2696,6 @@ dependencies = [
|
||||||
"realfft",
|
"realfft",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustc-demangle"
|
|
||||||
version = "0.1.24"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "rustc-hash"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -2976,16 +2918,6 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "socket2"
|
|
||||||
version = "0.5.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"windows-sys 0.52.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spirv"
|
name = "spirv"
|
||||||
version = "0.3.0+sdk-1.3.268.0"
|
version = "0.3.0+sdk-1.3.268.0"
|
||||||
|
@ -3138,33 +3070,6 @@ dependencies = [
|
||||||
"zerovec",
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio"
|
|
||||||
version = "1.42.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551"
|
|
||||||
dependencies = [
|
|
||||||
"backtrace",
|
|
||||||
"bytes",
|
|
||||||
"libc",
|
|
||||||
"mio",
|
|
||||||
"pin-project-lite",
|
|
||||||
"socket2",
|
|
||||||
"tokio-macros",
|
|
||||||
"windows-sys 0.52.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio-macros"
|
|
||||||
version = "2.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.8.19"
|
version = "0.8.19"
|
||||||
|
|
|
@ -20,7 +20,6 @@ egui-toast = { git = "https://github.com/urholaukkarinen/egui-toast.git", rev =
|
||||||
egui-winit = "0.30"
|
egui-winit = "0.30"
|
||||||
egui-wgpu = { version = "0.30", features = ["winit"] }
|
egui-wgpu = { version = "0.30", features = ["winit"] }
|
||||||
gilrs = { version = "0.11", features = ["serde-serialize"] }
|
gilrs = { version = "0.11", features = ["serde-serialize"] }
|
||||||
hex = "0.4"
|
|
||||||
image = { version = "0.25", default-features = false, features = ["png"] }
|
image = { version = "0.25", default-features = false, features = ["png"] }
|
||||||
itertools = "0.13"
|
itertools = "0.13"
|
||||||
num-derive = "0.4"
|
num-derive = "0.4"
|
||||||
|
@ -33,7 +32,6 @@ rubato = "0.16"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
thread-priority = "1"
|
thread-priority = "1"
|
||||||
tokio = { version = "1", features = ["io-util", "macros", "net", "rt", "sync"] }
|
|
||||||
wgpu = "23"
|
wgpu = "23"
|
||||||
winit = { version = "0.30", features = ["serde"] }
|
winit = { version = "0.30", features = ["serde"] }
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ use crate::{
|
||||||
emulator::{EmulatorClient, EmulatorCommand, SimId},
|
emulator::{EmulatorClient, EmulatorCommand, SimId},
|
||||||
input::MappingProvider,
|
input::MappingProvider,
|
||||||
persistence::Persistence,
|
persistence::Persistence,
|
||||||
window::{AboutWindow, AppWindow, GameWindow, GdbServerWindow, InputWindow},
|
window::{AboutWindow, AppWindow, GameWindow, InputWindow},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn load_icon() -> anyhow::Result<IconData> {
|
fn load_icon() -> anyhow::Result<IconData> {
|
||||||
|
@ -183,10 +183,6 @@ impl ApplicationHandler<UserEvent> for Application {
|
||||||
let about = AboutWindow;
|
let about = AboutWindow;
|
||||||
self.open(event_loop, Box::new(about));
|
self.open(event_loop, Box::new(about));
|
||||||
}
|
}
|
||||||
UserEvent::OpenDebugger(sim_id) => {
|
|
||||||
let debugger = GdbServerWindow::new(sim_id, self.client.clone());
|
|
||||||
self.open(event_loop, Box::new(debugger));
|
|
||||||
}
|
|
||||||
UserEvent::OpenInput => {
|
UserEvent::OpenInput => {
|
||||||
let input = InputWindow::new(self.mappings.clone());
|
let input = InputWindow::new(self.mappings.clone());
|
||||||
self.open(event_loop, Box::new(input));
|
self.open(event_loop, Box::new(input));
|
||||||
|
@ -378,7 +374,6 @@ impl Drop for Viewport {
|
||||||
pub enum UserEvent {
|
pub enum UserEvent {
|
||||||
GamepadEvent(gilrs::Event),
|
GamepadEvent(gilrs::Event),
|
||||||
OpenAbout,
|
OpenAbout,
|
||||||
OpenDebugger(SimId),
|
|
||||||
OpenInput,
|
OpenInput,
|
||||||
OpenPlayer2,
|
OpenPlayer2,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt::Display,
|
|
||||||
fs::{self, File},
|
fs::{self, File},
|
||||||
io::{Read, Seek, SeekFrom, Write},
|
io::{Read, Seek, SeekFrom, Write},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
@ -46,14 +45,6 @@ impl SimId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Display for SimId {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.write_str(match self {
|
|
||||||
Self::Player1 => "Player 1",
|
|
||||||
Self::Player2 => "Player 2",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Cart {
|
struct Cart {
|
||||||
rom_path: PathBuf,
|
rom_path: PathBuf,
|
||||||
|
|
294
src/gdbserver.rs
294
src/gdbserver.rs
|
@ -1,294 +0,0 @@
|
||||||
use anyhow::{bail, Result};
|
|
||||||
use std::{
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
thread,
|
|
||||||
};
|
|
||||||
use tokio::{
|
|
||||||
io::{AsyncReadExt, AsyncWriteExt as _, BufReader, BufWriter},
|
|
||||||
net::{
|
|
||||||
tcp::{OwnedReadHalf, OwnedWriteHalf},
|
|
||||||
TcpListener, TcpStream,
|
|
||||||
},
|
|
||||||
select,
|
|
||||||
sync::oneshot,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::emulator::{EmulatorClient, EmulatorCommand, SimId};
|
|
||||||
|
|
||||||
pub struct GdbServer {
|
|
||||||
sim_id: SimId,
|
|
||||||
client: EmulatorClient,
|
|
||||||
status: Arc<Mutex<GdbServerStatus>>,
|
|
||||||
killer: Option<oneshot::Sender<()>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GdbServer {
|
|
||||||
pub fn new(sim_id: SimId, client: EmulatorClient) -> Self {
|
|
||||||
Self {
|
|
||||||
sim_id,
|
|
||||||
client,
|
|
||||||
status: Arc::new(Mutex::new(GdbServerStatus::Stopped)),
|
|
||||||
killer: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn status(&self) -> GdbServerStatus {
|
|
||||||
self.status.lock().unwrap().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start(&mut self, port: u16) {
|
|
||||||
let sim_id = self.sim_id;
|
|
||||||
let client = self.client.clone();
|
|
||||||
let status = self.status.clone();
|
|
||||||
let (tx, rx) = oneshot::channel();
|
|
||||||
self.killer = Some(tx);
|
|
||||||
thread::spawn(move || {
|
|
||||||
tokio::runtime::Builder::new_current_thread()
|
|
||||||
.enable_all()
|
|
||||||
.build()
|
|
||||||
.unwrap()
|
|
||||||
.block_on(async move {
|
|
||||||
select! {
|
|
||||||
_ = run_server(sim_id, client, port, &status) => {}
|
|
||||||
_ = rx => {
|
|
||||||
*status.lock().unwrap() = GdbServerStatus::Stopped;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn stop(&mut self) {
|
|
||||||
if let Some(killer) = self.killer.take() {
|
|
||||||
let _ = killer.send(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run_server(
|
|
||||||
sim_id: SimId,
|
|
||||||
client: EmulatorClient,
|
|
||||||
port: u16,
|
|
||||||
status: &Mutex<GdbServerStatus>,
|
|
||||||
) {
|
|
||||||
client.send_command(EmulatorCommand::Pause);
|
|
||||||
let Some(stream) = try_connect(port, status).await else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let connection = GdbConnection::new(sim_id, client, stream);
|
|
||||||
match connection.run().await {
|
|
||||||
Ok(()) => {
|
|
||||||
*status.lock().unwrap() = GdbServerStatus::Stopped;
|
|
||||||
}
|
|
||||||
Err(error) => {
|
|
||||||
*status.lock().unwrap() = GdbServerStatus::Error(error.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn try_connect(port: u16, status: &Mutex<GdbServerStatus>) -> Option<TcpStream> {
|
|
||||||
*status.lock().unwrap() = GdbServerStatus::Connecting;
|
|
||||||
let listener = match TcpListener::bind(("127.0.0.1", port)).await {
|
|
||||||
Ok(l) => l,
|
|
||||||
Err(err) => {
|
|
||||||
*status.lock().unwrap() = GdbServerStatus::Error(err.to_string());
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
match listener.accept().await {
|
|
||||||
Ok((stream, _)) => {
|
|
||||||
*status.lock().unwrap() = GdbServerStatus::Running;
|
|
||||||
Some(stream)
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
*status.lock().unwrap() = GdbServerStatus::Error(err.to_string());
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum GdbServerStatus {
|
|
||||||
Stopped,
|
|
||||||
Connecting,
|
|
||||||
Running,
|
|
||||||
Error(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GdbServerStatus {
|
|
||||||
pub fn running(&self) -> bool {
|
|
||||||
matches!(self, Self::Connecting | Self::Running)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct GdbConnection {
|
|
||||||
sim_id: SimId,
|
|
||||||
client: EmulatorClient,
|
|
||||||
stream_in: BufReader<OwnedReadHalf>,
|
|
||||||
stream_out: BufWriter<OwnedWriteHalf>,
|
|
||||||
ack_messages: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GdbConnection {
|
|
||||||
fn new(sim_id: SimId, client: EmulatorClient, stream: TcpStream) -> Self {
|
|
||||||
let (rx, tx) = stream.into_split();
|
|
||||||
Self {
|
|
||||||
sim_id,
|
|
||||||
client,
|
|
||||||
stream_in: BufReader::new(rx),
|
|
||||||
stream_out: BufWriter::new(tx),
|
|
||||||
ack_messages: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async fn run(mut self) -> Result<()> {
|
|
||||||
println!("Connected for {}", self.sim_id);
|
|
||||||
self.client.send_command(EmulatorCommand::Resume);
|
|
||||||
loop {
|
|
||||||
let message = self.read_message().await?;
|
|
||||||
println!("received {:?}", message);
|
|
||||||
|
|
||||||
let mut res = ResponseWriter::new(&mut self.stream_out);
|
|
||||||
res.init(self.ack_messages).await?;
|
|
||||||
|
|
||||||
let body = match &message {
|
|
||||||
Message::String(str) => str.as_str(),
|
|
||||||
Message::Signal => {
|
|
||||||
// TODO: handle this
|
|
||||||
res.send().await?;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if body == "QStartNoAckMode" {
|
|
||||||
self.ack_messages = false;
|
|
||||||
res.send_ok().await?;
|
|
||||||
} else if body.starts_with("qSupported:") {
|
|
||||||
res.write_str("multiprocess+;swbreak+;vContSupported+")
|
|
||||||
.await?;
|
|
||||||
res.send().await?;
|
|
||||||
} else if body == "QThreadSuffixSupported" || body == "QListThreadsInStopReply" {
|
|
||||||
res.send_ok().await?;
|
|
||||||
} else if body == "qHostInfo" || body == "qProcessInfo" {
|
|
||||||
res.write_str(&format!(
|
|
||||||
"triple:{};endian:little;ptrsize:4;",
|
|
||||||
hex::encode("v810-unknown-vb")
|
|
||||||
))
|
|
||||||
.await?;
|
|
||||||
res.send().await?;
|
|
||||||
} else if body == "k" {
|
|
||||||
return Ok(());
|
|
||||||
} else {
|
|
||||||
// unrecognized command
|
|
||||||
res.send().await?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn read_message(&mut self) -> Result<Message> {
|
|
||||||
let mut char = self.read_byte().await?;
|
|
||||||
while char == b'+' {
|
|
||||||
// just ignore positive acks
|
|
||||||
char = self.read_byte().await?;
|
|
||||||
}
|
|
||||||
if char == b'-' {
|
|
||||||
bail!("no support for negative acks");
|
|
||||||
}
|
|
||||||
if char == 0x03 {
|
|
||||||
// This is how the client "cancels an in-flight request"
|
|
||||||
return Ok(Message::Signal);
|
|
||||||
}
|
|
||||||
if char != b'$' {
|
|
||||||
// Messages are supposed to start with a dollar sign
|
|
||||||
bail!("malformed message");
|
|
||||||
}
|
|
||||||
|
|
||||||
// now read the body
|
|
||||||
let mut checksum = 0u8;
|
|
||||||
let mut body = vec![];
|
|
||||||
char = self.read_byte().await?;
|
|
||||||
while char != b'#' {
|
|
||||||
if char == b'}' {
|
|
||||||
// escape character
|
|
||||||
checksum = checksum.wrapping_add(char);
|
|
||||||
char = self.read_byte().await?;
|
|
||||||
checksum = checksum.wrapping_add(char);
|
|
||||||
body.push(char ^ 0x20);
|
|
||||||
} else {
|
|
||||||
checksum = checksum.wrapping_add(char);
|
|
||||||
body.push(char);
|
|
||||||
}
|
|
||||||
char = self.read_byte().await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut checksum_bytes = [b'0'; 2];
|
|
||||||
self.stream_in.read_exact(&mut checksum_bytes).await?;
|
|
||||||
let checksum_str = std::str::from_utf8(&checksum_bytes)?;
|
|
||||||
let real_checksum = u8::from_str_radix(checksum_str, 16)?;
|
|
||||||
if checksum != real_checksum {
|
|
||||||
bail!("invalid checksum");
|
|
||||||
}
|
|
||||||
|
|
||||||
let string = String::from_utf8(body)?;
|
|
||||||
Ok(Message::String(string))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn read_byte(&mut self) -> std::io::Result<u8> {
|
|
||||||
self.stream_in.read_u8().await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ResponseWriter<'a> {
|
|
||||||
inner: &'a mut BufWriter<OwnedWriteHalf>,
|
|
||||||
checksum: u8,
|
|
||||||
}
|
|
||||||
impl<'a> ResponseWriter<'a> {
|
|
||||||
fn new(inner: &'a mut BufWriter<OwnedWriteHalf>) -> Self {
|
|
||||||
Self { inner, checksum: 0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn init(&mut self, ack: bool) -> std::io::Result<()> {
|
|
||||||
if ack {
|
|
||||||
self.inner.write_u8(b'+').await?;
|
|
||||||
}
|
|
||||||
self.inner.write_u8(b'$').await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn write_str(&mut self, str: &str) -> std::io::Result<()> {
|
|
||||||
for byte in str.bytes() {
|
|
||||||
self.checksum = self.checksum.wrapping_add(byte);
|
|
||||||
}
|
|
||||||
self.inner.write_all(str.as_bytes()).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn write_hex_u8(&mut self, value: u8) -> std::io::Result<()> {
|
|
||||||
for digit in [(value >> 4), (value & 0xf)] {
|
|
||||||
let char = if digit > 9 {
|
|
||||||
b'a' + digit - 10
|
|
||||||
} else {
|
|
||||||
b'0' + digit
|
|
||||||
};
|
|
||||||
self.checksum = self.checksum.wrapping_add(char);
|
|
||||||
self.inner.write_u8(char).await?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send_ok(mut self) -> std::io::Result<()> {
|
|
||||||
self.write_str("OK").await?;
|
|
||||||
self.send().await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send(mut self) -> std::io::Result<()> {
|
|
||||||
let final_checksum = self.checksum;
|
|
||||||
self.inner.write_u8(b'#').await?;
|
|
||||||
self.write_hex_u8(final_checksum).await?;
|
|
||||||
println!("{:?}", std::str::from_utf8(self.inner.buffer()));
|
|
||||||
self.inner.flush().await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum Message {
|
|
||||||
String(String),
|
|
||||||
Signal,
|
|
||||||
}
|
|
|
@ -14,7 +14,6 @@ mod app;
|
||||||
mod audio;
|
mod audio;
|
||||||
mod controller;
|
mod controller;
|
||||||
mod emulator;
|
mod emulator;
|
||||||
mod gdbserver;
|
|
||||||
mod graphics;
|
mod graphics;
|
||||||
mod input;
|
mod input;
|
||||||
mod persistence;
|
mod persistence;
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
pub use about::AboutWindow;
|
pub use about::AboutWindow;
|
||||||
use egui::{Context, ViewportBuilder, ViewportId};
|
use egui::{Context, ViewportBuilder, ViewportId};
|
||||||
pub use game::GameWindow;
|
pub use game::GameWindow;
|
||||||
pub use gdb::GdbServerWindow;
|
|
||||||
pub use input::InputWindow;
|
pub use input::InputWindow;
|
||||||
use winit::event::KeyEvent;
|
use winit::event::KeyEvent;
|
||||||
|
|
||||||
mod about;
|
mod about;
|
||||||
mod game;
|
mod game;
|
||||||
mod game_screen;
|
mod game_screen;
|
||||||
mod gdb;
|
|
||||||
mod input;
|
mod input;
|
||||||
|
|
||||||
pub trait AppWindow {
|
pub trait AppWindow {
|
||||||
|
|
|
@ -121,14 +121,6 @@ impl GameWindow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.menu_button("Tools", |ui| {
|
|
||||||
if ui.button("GDB Server").clicked() {
|
|
||||||
self.proxy
|
|
||||||
.send_event(UserEvent::OpenDebugger(self.sim_id))
|
|
||||||
.unwrap();
|
|
||||||
ui.close_menu();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ui.menu_button("About", |ui| {
|
ui.menu_button("About", |ui| {
|
||||||
self.proxy.send_event(UserEvent::OpenAbout).unwrap();
|
self.proxy.send_event(UserEvent::OpenAbout).unwrap();
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
use egui::{Button, CentralPanel, TextEdit, ViewportBuilder, ViewportId};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
emulator::{EmulatorClient, SimId},
|
|
||||||
gdbserver::{GdbServer, GdbServerStatus},
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::AppWindow;
|
|
||||||
|
|
||||||
pub struct GdbServerWindow {
|
|
||||||
sim_id: SimId,
|
|
||||||
port_str: String,
|
|
||||||
server: GdbServer,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GdbServerWindow {
|
|
||||||
pub fn new(sim_id: SimId, client: EmulatorClient) -> Self {
|
|
||||||
Self {
|
|
||||||
sim_id,
|
|
||||||
port_str: (8080 + sim_id.to_index()).to_string(),
|
|
||||||
server: GdbServer::new(sim_id, client),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AppWindow for GdbServerWindow {
|
|
||||||
fn viewport_id(&self) -> ViewportId {
|
|
||||||
ViewportId::from_hash_of(format!("Debugger-{}", self.sim_id))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn initial_viewport(&self) -> ViewportBuilder {
|
|
||||||
ViewportBuilder::default()
|
|
||||||
.with_title(format!("GDB Server ({})", self.sim_id))
|
|
||||||
.with_inner_size((300.0, 200.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn show(&mut self, ctx: &egui::Context) {
|
|
||||||
let port_num: Option<u16> = self.port_str.parse().ok();
|
|
||||||
let status = self.server.status();
|
|
||||||
CentralPanel::default().show(ctx, |ui| {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
if port_num.is_none() {
|
|
||||||
let style = ui.style_mut();
|
|
||||||
let error = style.visuals.error_fg_color;
|
|
||||||
style.visuals.widgets.active.bg_stroke.color = error;
|
|
||||||
style.visuals.widgets.hovered.bg_stroke.color = error;
|
|
||||||
}
|
|
||||||
ui.label("Port");
|
|
||||||
let port_editor = TextEdit::singleline(&mut self.port_str).desired_width(100.0);
|
|
||||||
ui.add_enabled(!status.running(), port_editor);
|
|
||||||
});
|
|
||||||
|
|
||||||
if !status.running() {
|
|
||||||
let start_button = Button::new("Start");
|
|
||||||
if ui.add_enabled(port_num.is_some(), start_button).clicked() {
|
|
||||||
let port = port_num.unwrap();
|
|
||||||
self.server.start(port);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let stop_button = Button::new("Stop");
|
|
||||||
if ui.add(stop_button).clicked() {
|
|
||||||
self.server.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match &status {
|
|
||||||
GdbServerStatus::Stopped => {}
|
|
||||||
GdbServerStatus::Connecting => {
|
|
||||||
ui.label("Connecting...");
|
|
||||||
}
|
|
||||||
GdbServerStatus::Running => {
|
|
||||||
ui.label("Running");
|
|
||||||
}
|
|
||||||
GdbServerStatus::Error(message) => {
|
|
||||||
ui.label(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue