UNFLoader improvements (#1372)

This commit is contained in:
Logan McNaughton authored and GitHub committed 2026-11-10 15:41:42 +00:00
1 parent 19b03c96a7
commit 55fa0836ab
3 files changed
+62 -74

No files matched your search

+3
View File
@@ -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,
+32 -21
View File
@@ -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<u32>,
pub usb_buffer: Vec<u8>,
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
+27 -53
View File
@@ -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<u32> = None;
let mut data_size: Option<u32> = None;
let mut usb_buffer: Vec<u8> = 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<u8> = 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() -> (