aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAstatin <[email protected]>2025-06-29 02:27:13 +0200
committerAstatin <[email protected]>2025-06-29 02:27:13 +0200
commit4e46a48e042ca0d4d6814624e91108afeb5e7636 (patch)
tree46afde2ab9315026b4c1588c6919e297f60c6f21
parent4cb95575feedda5e2006d706a9e85db6569043ca (diff)
Add serial over tcp support
-rw-r--r--http.rombin0 -> 389 bytes
-rw-r--r--src/desktop/serial.rs174
-rw-r--r--src/main.rs20
-rw-r--r--testbin0 -> 339 bytes
4 files changed, 181 insertions, 13 deletions
diff --git a/http.rom b/http.rom
new file mode 100644
index 0000000..79b6dd2
--- /dev/null
+++ b/http.rom
Binary files differ
diff --git a/src/desktop/serial.rs b/src/desktop/serial.rs
index 29fad9d..3eac454 100644
--- a/src/desktop/serial.rs
+++ b/src/desktop/serial.rs
@@ -1,7 +1,7 @@
use std::fs::File;
use std::io::{Read, Write};
use std::sync::mpsc::{self, Receiver, Sender};
-use std::sync::{Arc, Mutex};
+use std::net::{TcpListener, TcpStream};
use std::thread;
use crate::io::Serial;
@@ -25,11 +25,6 @@ impl Serial for UnconnectedSerial {
}
}
-enum FIFOMessage {
- Request(u8),
- Response(u8),
-}
-
pub struct FIFOSerial {
transfer_requested: bool,
current_transfer: bool,
@@ -37,13 +32,14 @@ pub struct FIFOSerial {
external_clock: bool,
next_byte_transfer_cycle: u128,
+ no_response: bool,
input: Receiver<u8>,
output: Sender<u8>,
}
impl FIFOSerial {
- pub fn new(input_path: String, output_path: String) -> FIFOSerial {
+ pub fn new(input_path: String, output_path: String, no_response: bool) -> FIFOSerial {
let (tx, input) = mpsc::channel::<u8>();
thread::spawn(move || {
let mut input_f = File::open(input_path).unwrap();
@@ -69,6 +65,8 @@ impl FIFOSerial {
current_transfer: false,
current_data: 0,
+ no_response,
+
external_clock: false,
next_byte_transfer_cycle: 0,
@@ -84,6 +82,152 @@ impl Serial for FIFOSerial {
}
fn read_control(&self) -> u8 {
+ (if self.external_clock { 0 } else { 0x01 }) |
+ (if self.transfer_requested { 0x80 } else { 0 })
+ }
+
+ fn write_data(&mut self, data: u8) {
+ self.current_data = data;
+ }
+
+ fn write_control(&mut self, control: u8) {
+ self.external_clock = (control & 0b01) == 0;
+ self.transfer_requested = (control & 0x80) != 0;
+ }
+
+ fn update_serial(&mut self, cycles: u128) -> bool {
+ if let Ok(x) = self.input.try_recv() {
+ if self.current_transfer {
+ self.current_data = x;
+ self.external_clock = false;
+ self.transfer_requested = false;
+ self.next_byte_transfer_cycle = cycles + ((CPU_CLOCK_SPEED as u128) / 1024);
+ self.current_transfer = false;
+ } else {
+ if !self.no_response {
+ self.output.send(self.current_data).unwrap();
+ }
+ self.current_data = x;
+ self.external_clock = true;
+ self.transfer_requested = false;
+ }
+ true
+ } else if !self.external_clock && !self.current_transfer
+ && self.transfer_requested {
+ if cycles > self.next_byte_transfer_cycle {
+ self.output.send(self.current_data).unwrap();
+ self.current_transfer = true;
+ if self.no_response {
+ self.current_data = 0;
+ self.transfer_requested = false;
+ self.next_byte_transfer_cycle = cycles + ((CPU_CLOCK_SPEED as u128) / 1024);
+ self.current_transfer = false;
+ }
+ }
+ false
+ } else {
+ false
+ }
+ }
+}
+
+pub struct TcpSerial {
+ transfer_requested: bool,
+ current_transfer: bool,
+ current_data: u8,
+
+ external_clock: bool,
+ next_byte_transfer_cycle: u128,
+ next_byte_receive_cycle: u128,
+
+ no_response: bool,
+
+ input: Receiver<u8>,
+ output: Sender<u8>,
+}
+
+impl TcpSerial {
+ pub fn handle_stream(mut stream: TcpStream, tx: Sender<u8>, rx: Receiver<u8>) {
+ let mut stream_clone = stream.try_clone().unwrap();
+ thread::spawn(move || {
+ loop {
+ let mut byte = [0];
+
+ stream_clone.read(&mut byte).unwrap();
+
+ tx.send(byte[0]).unwrap();
+ }
+ });
+
+ thread::spawn(move || {
+ for b in rx.iter() {
+ stream.write(&[b]).unwrap();
+ }
+ });
+ }
+
+ pub fn new_listener(port: u16, no_response: bool) -> Self {
+ let (tx, input) = mpsc::channel::<u8>();
+ let (output, rx) = mpsc::channel::<u8>();
+ thread::spawn(move || {
+ match TcpListener::bind(("0.0.0.0", port)).unwrap().accept() {
+ Ok((socket, addr)) => {
+ println!("Connection on {:?}", addr);
+ Self::handle_stream(socket, tx, rx);
+ }
+ _ => ()
+ };
+ });
+
+ Self {
+ transfer_requested: false,
+ current_transfer: false,
+ current_data: 0,
+
+ no_response,
+
+ external_clock: false,
+ next_byte_transfer_cycle: 0,
+ next_byte_receive_cycle: 0,
+
+ input,
+ output,
+ }
+ }
+
+ pub fn connect(addr: String, no_response: bool) -> Self {
+ let (tx, input) = mpsc::channel::<u8>();
+ let (output, rx) = mpsc::channel::<u8>();
+ thread::spawn(move || {
+ if let Ok(socket) = TcpStream::connect(&addr) {
+ println!("Connected to {:?}", addr);
+ Self::handle_stream(socket, tx, rx);
+ }
+ });
+
+ Self {
+ transfer_requested: false,
+ current_transfer: false,
+ current_data: 0,
+
+ no_response,
+
+ external_clock: false,
+ next_byte_transfer_cycle: 0,
+ next_byte_receive_cycle: 0,
+
+ input,
+ output,
+ }
+ }
+}
+
+impl Serial for TcpSerial {
+ fn read_data(&self) -> u8 {
+ self.current_data
+ }
+
+ fn read_control(&self) -> u8 {
(if self.external_clock { 0 } else { 0x01 }) |
(if self.transfer_requested { 0x80 } else { 0 })
}
@@ -98,6 +242,9 @@ impl Serial for FIFOSerial {
}
fn update_serial(&mut self, cycles: u128) -> bool {
+ if cycles < self.next_byte_transfer_cycle {
+ return false;
+ }
if let Ok(x) = self.input.try_recv() {
if self.current_transfer {
self.current_data = x;
@@ -106,19 +253,26 @@ impl Serial for FIFOSerial {
self.next_byte_transfer_cycle = cycles + ((CPU_CLOCK_SPEED as u128) / 1024);
self.current_transfer = false;
} else {
- self.output.send(self.current_data).unwrap();
- println!("recv {:02x}, send back {:02x}", x, self.current_data);
+ if !self.no_response || self.transfer_requested {
+ self.output.send(self.current_data).unwrap();
+ }
self.current_data = x;
self.external_clock = true;
self.transfer_requested = false;
}
+ self.next_byte_transfer_cycle = cycles + ((CPU_CLOCK_SPEED as u128) / 16384);
true
} else if !self.external_clock && !self.current_transfer
&& self.transfer_requested {
if cycles > self.next_byte_transfer_cycle {
self.output.send(self.current_data).unwrap();
- println!("send {:02x}", self.current_data);
self.current_transfer = true;
+ if self.no_response {
+ self.current_data = 0;
+ self.transfer_requested = false;
+ self.next_byte_transfer_cycle = cycles + ((CPU_CLOCK_SPEED as u128) / 1024);
+ self.current_transfer = false;
+ }
}
false
} else {
diff --git a/src/main.rs b/src/main.rs
index d4cdb34..4399548 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -65,7 +65,19 @@ struct Cli {
/// Window title
#[arg(long, default_value = "Gameboy Emulator")]
- title: String
+ title: String,
+
+ /// Serial tcp listen port
+ #[arg(short='L', long)]
+ listen: Option<u16>,
+
+ /// Serial tcp connect port
+ #[arg(short, long)]
+ connect: Option<String>,
+
+ /// Don't send (or expect) a byte as a response to a serial transfer
+ #[arg(long, default_value_t = false)]
+ no_response: bool,
}
fn main() {
@@ -75,8 +87,10 @@ fn main() {
let window = desktop::window::DesktopWindow::new(cli.title).unwrap();
- let serial: Box<dyn Serial> = match (cli.fifo_input, cli.fifo_output) {
- (Some(fifo_input), Some(fifo_output)) => Box::new(desktop::serial::FIFOSerial::new(fifo_input, fifo_output)),
+ let serial: Box<dyn Serial> = match (cli.fifo_input, cli.fifo_output, cli.listen, cli.connect) {
+ (_, _, Some(port), _) => Box::new(desktop::serial::TcpSerial::new_listener(port, cli.no_response)),
+ (_, _, _, Some(addr)) => Box::new(desktop::serial::TcpSerial::connect(addr, cli.no_response)),
+ (Some(fifo_input), Some(fifo_output), _, _) => Box::new(desktop::serial::FIFOSerial::new(fifo_input, fifo_output, cli.no_response)),
_ => Box::new(desktop::serial::UnconnectedSerial {})
};
diff --git a/test b/test
new file mode 100644
index 0000000..eb8bb6c
--- /dev/null
+++ b/test
Binary files differ