From 55fa0836abe79fb534827226107f0cfa86554dde Mon Sep 17 00:00:00 2001 From: Logan McNaughton <848146+loganmc10@users.noreply.github.com> Date: Tue, 10 Nov 2026 15:41:42 +0000 Subject: [PATCH] UNFLoader improvements (#1372) --- src/device.rs | 3 ++ src/device/cart/sc64.rs | 53 ++++++++++++++++----------- src/ui/usb.rs | 80 ++++++++++++++--------------------------- 3 files changed, 62 insertions(+), 74 deletions(-) diff --git a/src/device.rs b/src/device.rs index e8535af9..03700bae 100644 --- a/src/device.rs +++ b/src/device.rs @@ -427,6 +427,9 @@ impl Device { buffer: vec![0; 8192], writeback_sector: vec![0; 256], usb_buffer: vec![], + usb_read_type: 0, + usb_read_size: 0, + usb_next_read: 0, }, flashram: cart::sram::Flashram { mode: cart::sram::FlashramMode::ReadArray, diff --git a/src/device/cart/sc64.rs b/src/device/cart/sc64.rs index bc3f3b71..3f971697 100644 --- a/src/device/cart/sc64.rs +++ b/src/device/cart/sc64.rs @@ -19,6 +19,7 @@ pub const SC64_CFG_COUNT: usize = 15; const SC64_BUFFER_MASK: usize = 0x1FFF; const SC64_EEPROM_MASK: usize = 0xFFF; +const USB_PACKET_DELAY: u64 = 93750 * 2; // ~2ms of COUNT (CPU cycles at 93.75 MHz) #[derive(Clone, serde::Serialize, serde::Deserialize)] pub struct Sc64 { @@ -29,6 +30,9 @@ pub struct Sc64 { pub sector: u32, pub writeback_sector: Vec, pub usb_buffer: Vec, + pub usb_read_type: u32, + pub usb_read_size: u32, // non-zero while a received packet is waiting to be read by 'm' + pub usb_next_read: u64, // COUNT at which the next received packet becomes visible } fn format_sdcard(device: &mut device::Device) { @@ -217,24 +221,23 @@ pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u device.cart.sc64.regs[SC64_DATA0_REG] = 0; } 'u' => { - // used to notify the game that there is data to read - if let Some(cart_rx) = device.ui.usb.cart_rx.as_mut() { - match cart_rx.try_recv() { - Ok(data) => { - device.cart.sc64.regs[SC64_DATA0_REG] = data.data_type & 0xFF; // read_status/type - device.cart.sc64.regs[SC64_DATA1_REG] = - data.data_size & 0xFFFFFF; // length - device.cart.sc64.usb_buffer = data.data; // store the data to be read - } - Err(_err) => { - device.cart.sc64.regs[SC64_DATA0_REG] = 0; // read_status/type - device.cart.sc64.regs[SC64_DATA1_REG] = 0; // length - } - } - } else { - device.cart.sc64.regs[SC64_DATA0_REG] = 0; // read_status/type - device.cart.sc64.regs[SC64_DATA1_REG] = 0; // length + // used to notify the game that there is data to read. + // Status reads don't consume the packet, only 'm' does. + // Packets from the PC arrive with some USB latency in between, so don't make + // the next one visible right away (games may rely on this, NetLib examples do) + let count = device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG]; + if device.cart.sc64.usb_read_size == 0 + && (count >= device.cart.sc64.usb_next_read + || device.cart.sc64.usb_next_read - count > USB_PACKET_DELAY) + && let Some(cart_rx) = device.ui.usb.cart_rx.as_mut() + && let Ok(data) = cart_rx.try_recv() + { + device.cart.sc64.usb_read_type = data.data_type & 0xFF; + device.cart.sc64.usb_read_size = data.data_size & 0xFFFFFF; + device.cart.sc64.usb_buffer = data.data; // store the data to be read } + device.cart.sc64.regs[SC64_DATA0_REG] = device.cart.sc64.usb_read_type; // read_status/type + device.cart.sc64.regs[SC64_DATA1_REG] = device.cart.sc64.usb_read_size; // length } 'M' => { // Send data from from flashcart to USB @@ -299,7 +302,8 @@ pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u 'm' => { // Receive data from USB to flashcart let address = device.cart.sc64.regs[SC64_DATA0_REG] as u64 & 0x1FFFFFFF; - let length = device.cart.sc64.regs[SC64_DATA1_REG] as usize; + let length = device.cart.sc64.regs[SC64_DATA1_REG] as usize & 0xFFFFFF; + let usb_buffer = std::mem::take(&mut device.cart.sc64.usb_buffer); let mut i = 0; @@ -309,8 +313,7 @@ pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u .rdram .mem .get_mut((address as usize + i) ^ device.byte_swap) - .unwrap_or(&mut 0) = - *device.cart.sc64.usb_buffer.get(i).unwrap_or(&0); + .unwrap_or(&mut 0) = *usb_buffer.get(i).unwrap_or(&0); i += 1; } } else if address >= device::memory::MM_CART_ROM as u64 @@ -319,13 +322,21 @@ pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u while i < length { device.ui.storage.saves.romsave.data.insert( ((address as usize + i) & device::cart::rom::CART_MASK) as u32, - *device.cart.sc64.usb_buffer.get(i).unwrap_or(&0), + *usb_buffer.get(i).unwrap_or(&0), ); i += 1; } } else { panic!("Unknown address {address:#x} for SC64 m command"); } + // packet consumed + if device.cart.sc64.usb_read_size != 0 { + device.cart.sc64.usb_read_type = 0; + device.cart.sc64.usb_read_size = 0; + device.cart.sc64.usb_next_read = device.cpu.cop0.regs + [device::cop0::COP0_COUNT_REG] + + USB_PACKET_DELAY; + } } 'w' => { // SD card writeback pending diff --git a/src/ui/usb.rs b/src/ui/usb.rs index 4f2b8e98..d73806f3 100644 --- a/src/ui/usb.rs +++ b/src/ui/usb.rs @@ -35,69 +35,38 @@ async fn handle_connection( ) { let (mut incoming, mut outgoing) = conn.into_split(); - let mut shutdown_rx_clone = shutdown_rx.clone(); - while usb_rx.try_recv().is_ok() {} // drain stale USB messages - tokio::spawn(async move { + let mut reader = tokio::spawn(async move { let mut incoming_buffer = vec![0u8; 4096]; - let mut data_type: Option = None; - let mut data_size: Option = None; let mut usb_buffer: Vec = vec![]; loop { - tokio::select! { - result = incoming.read(&mut incoming_buffer) => { - match result { - Ok(0) => { + match incoming.read(&mut incoming_buffer).await { + Ok(0) | Err(_) => break, + Ok(n) => { + usb_buffer.extend_from_slice(&incoming_buffer[0..n]); + // A single read can contain several packets, handle all complete ones + while usb_buffer.len() >= 8 { + let d_type = u32::from_be_bytes(usb_buffer[0..4].try_into().unwrap()); + let d_size = u32::from_be_bytes(usb_buffer[4..8].try_into().unwrap()); + let length = d_size as usize; + if usb_buffer.len() < 8 + length { break; } - Ok(n) => { - usb_buffer.extend_from_slice(&incoming_buffer[0..n]); - if data_type.is_none() { - if usb_buffer.len() < 4 { - continue; - } else { - data_type = Some(u32::from_be_bytes(usb_buffer[0..4].try_into().unwrap())); - usb_buffer.drain(0..4); - } + let data: Vec = usb_buffer.drain(..8 + length).skip(8).collect(); + match d_type { + DATATYPE_TCPTEST => respond_to_handshake(&usb_tx, data), + DATATYPE_ROMUPLOAD => {} // The ROM is booted by the user in the emulator, ignore uploads + _ => { + let _ = cart_tx.send(UsbData { + data, + data_type: d_type, + data_size: d_size, + }); } - if data_type.is_some() && data_size.is_none() { - if usb_buffer.len() < 4 { - continue; - } else { - data_size = Some(u32::from_be_bytes(usb_buffer[0..4].try_into().unwrap())); - usb_buffer.drain(0..4); - } - } - if let Some(d_type) = data_type && let Some(d_size) = data_size { - let length = d_size as usize; - if usb_buffer.len() >= length { - let usb_data = UsbData { - data: usb_buffer[0..length].to_vec(), - data_type: d_type, - data_size: d_size, - }; - usb_buffer.drain(0..length); - if usb_data.data_type == DATATYPE_TCPTEST { - respond_to_handshake(&usb_tx,usb_data.data); - } else if usb_data.data_type == DATATYPE_ROMUPLOAD { - panic!("ROM upload not supported"); - } else { - cart_tx.send(usb_data).unwrap(); - } - data_type = None; - data_size = None; - } - } - } - Err(_e) => { - break; } } } - _ = shutdown_rx.changed() => { - break; - } } } }); @@ -120,11 +89,16 @@ async fn handle_connection( } } } - _ = shutdown_rx_clone.changed() => { + _ = &mut reader => { + // client disconnected + break; + } + _ = shutdown_rx.changed() => { break; } } } + reader.abort(); } pub fn init() -> (