sc64deployer done

This commit is contained in:
Mateusz Faderewski 2023-03-11 18:36:30 +01:00
parent 70d287c855
commit 8479c80ebe
6 changed files with 541 additions and 46 deletions

View File

@ -90,9 +90,9 @@ Second, program FPGA, microcontroller and bootloader:
4. Check in device manager which port number `COMx` is assigned to serial adapter 4. Check in device manager which port number `COMx` is assigned to serial adapter
5. Connect SC64 board to the PC with USB-C cable (***IMPORTANT:*** connect it to the same computer as serial adapter) 5. Connect SC64 board to the PC with USB-C cable (***IMPORTANT:*** connect it to the same computer as serial adapter)
6. Locate `primer.py` script in root folder 6. Locate `primer.py` script in root folder
7. Make sure these files are located in the same folder as `primer.py` script: `requirements.txt`, `sc64-firmware.bin` 7. Make sure these files are located in the same folder as `primer.py` script: `requirements.txt`, `sc64-firmware-{version}.bin`
8. Run `pip3 install -r requirements.txt` to install required python packages 8. Run `pip3 install -r requirements.txt` to install required python packages
9. Run `python3 primer.py COMx sc64-firmware.bin` (replace `COMx` with port located in step **4**) 9. Run `python3 primer.py COMx sc64-firmware-{version}.bin` (replace `COMx` with port located in step **4**)
10. Follow the instructions on the screen 10. Follow the instructions on the screen
11. Wait until programming process has finished (**DO NOT STOP PROGRAMMING PROCESS OR DISCONNECT SC64 BOARD FROM PC**, doing so might irrecoverably break programming through UART header and you would need to program FPGA and/or microcontroller with separate dedicated programming interfaces through *Tag-Connect* connector on the PCB) 11. Wait until programming process has finished (**DO NOT STOP PROGRAMMING PROCESS OR DISCONNECT SC64 BOARD FROM PC**, doing so might irrecoverably break programming through UART header and you would need to program FPGA and/or microcontroller with separate dedicated programming interfaces through *Tag-Connect* connector on the PCB)
@ -107,4 +107,4 @@ Congratulations! Your SC64 flashcart should be ready for use!
This issue can be attributed to incorrectly programmed FT232H EEPROM in the first programming step. This issue can be attributed to incorrectly programmed FT232H EEPROM in the first programming step.
Check again in `FT_PROG` application if device was configured properly. Check again in `FT_PROG` application if device was configured properly.
Once FPGA and microcontroller has been programmed successfully `primer.py` script needs to be run in special mode. Once FPGA and microcontroller has been programmed successfully `primer.py` script needs to be run in special mode.
Please use command `python3 primer.py COMx sc64-firmware.bin --bootloader-only` to try programming bootloader again. Please use command `python3 primer.py COMx sc64-firmware-{version}.bin --bootloader-only` to try programming bootloader again.

322
sw/deployer/src/disk.rs Normal file
View File

@ -0,0 +1,322 @@
use crate::sc64::Error;
use std::{
collections::HashMap,
fs::File,
io::{Read, Seek, SeekFrom, Write},
};
const BLOCKS_PER_TRACK: usize = 2;
const SECTORS_PER_BLOCK: usize = 85;
const SYSTEM_SECTOR_LENGTH: usize = 232;
const BAD_TRACKS_PER_ZONE: usize = 12;
#[derive(Clone, Copy, PartialEq)]
pub enum Format {
Retail,
Development,
}
struct SystemAreaInfo<'a> {
format: Format,
sector_length: usize,
sys_lba: &'a [usize],
bad_lba: &'a [usize],
}
const SYSTEM_AREA: [SystemAreaInfo; 2] = [
SystemAreaInfo {
format: Format::Retail,
sector_length: 232,
sys_lba: &[9, 8, 1, 0],
bad_lba: &[2, 3, 10, 11, 12, 16, 17, 18, 19, 20, 21, 22, 23],
},
SystemAreaInfo {
format: Format::Development,
sector_length: 192,
sys_lba: &[11, 10, 3, 2],
bad_lba: &[0, 1, 8, 9, 16, 17, 18, 19, 20, 21, 22, 23],
},
];
const ID_LBAS: [usize; 2] = [15, 14];
struct DiskZone {
head: usize,
sector_length: usize,
tracks: usize,
track_offset: usize,
}
macro_rules! zone {
($h:expr, $l:expr, $t:expr, $o:expr) => {
DiskZone {
head: $h,
sector_length: $l,
tracks: $t,
track_offset: $o,
}
};
}
const DISK_ZONES: [DiskZone; 16] = [
zone!(0, 232, 158, 0),
zone!(0, 216, 158, 158),
zone!(0, 208, 149, 316),
zone!(0, 192, 149, 465),
zone!(0, 176, 149, 614),
zone!(0, 160, 149, 763),
zone!(0, 144, 149, 912),
zone!(0, 128, 114, 1061),
zone!(1, 216, 158, 157),
zone!(1, 208, 158, 315),
zone!(1, 192, 149, 464),
zone!(1, 176, 149, 613),
zone!(1, 160, 149, 762),
zone!(1, 144, 149, 911),
zone!(1, 128, 149, 1060),
zone!(1, 112, 114, 1174),
];
const DISK_VZONE_TO_PZONE: [[usize; 16]; 7] = [
[0, 1, 2, 9, 8, 3, 4, 5, 6, 7, 15, 14, 13, 12, 11, 10],
[0, 1, 2, 3, 10, 9, 8, 4, 5, 6, 7, 15, 14, 13, 12, 11],
[0, 1, 2, 3, 4, 11, 10, 9, 8, 5, 6, 7, 15, 14, 13, 12],
[0, 1, 2, 3, 4, 5, 12, 11, 10, 9, 8, 6, 7, 15, 14, 13],
[0, 1, 2, 3, 4, 5, 6, 13, 12, 11, 10, 9, 8, 7, 15, 14],
[0, 1, 2, 3, 4, 5, 6, 7, 14, 13, 12, 11, 10, 9, 8, 15],
[0, 1, 2, 3, 4, 5, 6, 7, 15, 14, 13, 12, 11, 10, 9, 8],
];
struct Mapping {
lba: usize,
offset: usize,
length: usize,
writable: bool,
}
pub struct Disk {
file: File,
format: Format,
mapping: HashMap<usize, Mapping>,
}
impl Disk {
pub fn get_format(&self) -> &Format {
&self.format
}
pub fn get_lba(&self, track: u32, head: u32, block: u32) -> Option<usize> {
if head == 0 && track < 12 {
return Some((track << 1 | block ^ (track % 2)) as usize);
}
let location = track << 2 | head << 1 | block;
self.mapping
.get(&(location as usize))
.map(|block| block.lba)
}
pub fn read_block(
&mut self,
track: u32,
head: u32,
block: u32,
) -> Result<Option<Vec<u8>>, Error> {
let location = track << 2 | head << 1 | block;
if let Some(block) = self.mapping.get(&(location as usize)) {
let mut data = vec![0u8; block.length];
self.file.seek(SeekFrom::Start(block.offset as u64))?;
self.file.read_exact(&mut data)?;
return Ok(Some(data));
}
Ok(None)
}
pub fn write_block(
&mut self,
track: u32,
head: u32,
block: u32,
data: &[u8],
) -> Result<Option<()>, Error> {
let location = track << 2 | head << 1 | block;
if let Some(block) = self.mapping.get(&(location as usize)) {
if block.length == data.len() && block.writable {
self.file.seek(SeekFrom::Start(block.offset as u64))?;
self.file.write_all(data)?;
return Ok(Some(()));
}
}
Ok(None)
}
}
pub fn open(path: &str) -> Result<Disk, Error> {
let mut file = File::options().read(true).write(true).open(path)?;
let (format, mapping) = load_ndd(&mut file)?;
Ok(Disk {
file,
format,
mapping,
})
}
pub fn open_multiple(paths: &[String]) -> Result<Vec<Disk>, Error> {
let mut disks: Vec<Disk> = Vec::new();
for path in paths {
let disk = open(path)?;
disks.push(disk);
}
if !disks.windows(2).all(|d| d[0].format == d[1].format) {
return Err(Error::new("Disk format mismatch"));
}
Ok(disks)
}
fn load_ndd(file: &mut File) -> Result<(Format, HashMap<usize, Mapping>), Error> {
let mut disk_format: Option<Format> = None;
let mut disk_type: u8 = 0;
let mut sys_data = vec![0u8; SYSTEM_SECTOR_LENGTH];
let mut bad_lba: Vec<usize> = Vec::new();
for info in SYSTEM_AREA {
bad_lba.clear();
for &lba in info.sys_lba {
let data = load_sys_lba(file, lba)?;
if verify_sys_lba(&data, info.sector_length) {
if (data[4] != 0x10) || ((data[5] & 0xF0) != 0x10) {
bad_lba.push(lba);
} else {
disk_format = Some(info.format);
disk_type = data[5] & 0x0F;
sys_data = data[0..SYSTEM_SECTOR_LENGTH].to_vec();
}
} else {
bad_lba.push(lba);
}
}
if disk_format.is_some() {
bad_lba.append(&mut info.bad_lba.to_vec());
break;
}
}
if disk_format.is_none() {
return Err(Error::new("Provided 64DD disk file is not valid"));
}
if disk_type >= DISK_VZONE_TO_PZONE.len() as u8 {
return Err(Error::new("Unknown disk type"));
}
let mut id_lba_valid = false;
for lba in ID_LBAS {
let data = load_sys_lba(file, lba)?;
let valid = verify_sys_lba(&data, SYSTEM_SECTOR_LENGTH);
if !valid {
bad_lba.push(lba);
}
id_lba_valid |= valid;
}
if !id_lba_valid {
return Err(Error::new("No valid ID LBA found"));
}
let mut zone_bad_tracks: Vec<Vec<usize>> = Vec::new();
for (zone, info) in DISK_ZONES.iter().enumerate() {
let mut bad_tracks: Vec<usize> = Vec::new();
let start = if zone == 0 { 0 } else { sys_data[0x07 + zone] };
let stop = sys_data[0x07 + zone + 1];
for offset in start..stop {
bad_tracks.push(sys_data[0x20 + offset as usize] as usize);
}
for track in 0..(BAD_TRACKS_PER_ZONE - bad_tracks.len()) {
bad_tracks.push(info.tracks - track - 1);
}
zone_bad_tracks.push(bad_tracks);
}
let mut mapping = HashMap::new();
let mut current_lba: usize = 0;
let mut starting_block: usize = 0;
let mut file_offset: usize = 0;
for zone in DISK_VZONE_TO_PZONE[disk_type as usize] {
let DiskZone {
head,
sector_length,
tracks,
track_offset,
} = DISK_ZONES[zone];
let mut track = track_offset;
for zone_track in 0..tracks {
let current_zone_track = if head == 0 {
zone_track
} else {
(tracks - 1) - zone_track
};
if zone_bad_tracks[zone].contains(&current_zone_track) {
track = if head == 0 {
track + 1
} else {
track.saturating_sub(1)
};
continue;
}
for block in 0..BLOCKS_PER_TRACK {
let location = track << 2 | head << 1 | (starting_block ^ block);
let length = sector_length * SECTORS_PER_BLOCK;
if !bad_lba.contains(&current_lba) {
mapping.insert(
location,
Mapping {
lba: current_lba,
offset: file_offset,
length,
writable: true,
},
);
}
file_offset += length;
current_lba += 1;
}
track = if head == 0 {
track + 1
} else {
track.saturating_sub(1)
};
starting_block ^= 1;
}
}
Ok((disk_format.unwrap(), mapping))
}
fn load_sys_lba(file: &mut File, lba: usize) -> Result<Vec<u8>, Error> {
let length = SYSTEM_SECTOR_LENGTH * SECTORS_PER_BLOCK;
file.seek(SeekFrom::Start((lba * length) as u64))?;
let mut data = vec![0u8; length];
file.read_exact(&mut data)?;
Ok(data)
}
fn verify_sys_lba(data: &[u8], sector_length: usize) -> bool {
let sys_data = &data[0..sector_length];
for sector in 1..SECTORS_PER_BLOCK {
let offset = sector * sector_length;
let verify_data = &data[offset..(offset + sector_length)];
if sys_data != verify_data {
return false;
}
}
true
}

View File

@ -1,13 +1,13 @@
mod debug; mod debug;
mod disk;
mod n64; mod n64;
pub mod sc64; mod sc64;
use chrono::Local; use chrono::Local;
use clap::{Args, Parser, Subcommand, ValueEnum}; use clap::{Args, Parser, Subcommand, ValueEnum};
use clap_num::maybe_hex_range; use clap_num::maybe_hex_range;
use colored::Colorize; use colored::Colorize;
use panic_message::panic_message; use panic_message::panic_message;
use sc64::ServerEvent;
use std::{ use std::{
fs::File, fs::File,
io::{stdin, stdout, Read, Write}, io::{stdin, stdout, Read, Write},
@ -27,7 +27,7 @@ struct Cli {
command: Commands, command: Commands,
/// Connect to SC64 device on provided serial port /// Connect to SC64 device on provided serial port
#[arg(short, long, conflicts_with = "remote")] #[arg(short, long)]
port: Option<String>, port: Option<String>,
/// Connect to SC64 device on provided remote address /// Connect to SC64 device on provided remote address
@ -49,7 +49,7 @@ enum Commands {
command: DownloadCommands, command: DownloadCommands,
}, },
/// Upload ROM, 64DD IPL and run disk server /// Upload ROM (and save), 64DD IPL then run disk server
_64DD(_64DDArgs), _64DD(_64DDArgs),
/// Enter debug mode /// Enter debug mode
@ -58,7 +58,7 @@ enum Commands {
/// Dump data from arbitrary location in SC64 memory space /// Dump data from arbitrary location in SC64 memory space
Dump(DumpArgs), Dump(DumpArgs),
/// Print information about SC64 device /// Print information about connected SC64 device
Info, Info,
/// Update persistent settings on SC64 device /// Update persistent settings on SC64 device
@ -98,8 +98,8 @@ struct UploadArgs {
#[arg(short, long)] #[arg(short, long)]
no_shadow: bool, no_shadow: bool,
/// Force TV type (ignored when used in conjunction with direct boot mode) /// Force TV type
#[arg(long)] #[arg(long, conflicts_with = "direct")]
tv: Option<TvType>, tv: Option<TvType>,
} }
@ -117,19 +117,32 @@ struct DownloadArgs {
#[derive(Args)] #[derive(Args)]
struct _64DDArgs { struct _64DDArgs {
/// Path to the ROM file
#[arg(short, long)]
rom: Option<PathBuf>,
/// Path to the 64DD IPL file /// Path to the 64DD IPL file
ddipl: PathBuf, ddipl: PathBuf,
/// Path to the 64DD disk file (.ndd format, can be specified multiple times) /// Path to the 64DD disk file (.ndd format, can be specified multiple times)
#[arg(required = true)]
disk: Vec<PathBuf>, disk: Vec<PathBuf>,
/// Path to the ROM file
#[arg(short, long)]
rom: Option<PathBuf>,
/// Path to the save file
#[arg(short, long, requires = "rom")]
save: Option<PathBuf>,
/// Override autodetected save type
#[arg(short = 't', long, requires = "rom")]
save_type: Option<SaveType>,
/// Use direct boot mode (skip bootloader) /// Use direct boot mode (skip bootloader)
#[arg(short, long)] #[arg(short, long)]
direct: bool, direct: bool,
/// Force TV type
#[arg(long, conflicts_with = "direct")]
tv: Option<TvType>,
} }
#[derive(Args)] #[derive(Args)]
@ -308,10 +321,10 @@ fn handle_list_command() -> Result<(), sc64::Error> {
fn handle_upload_command(connection: Connection, args: &UploadArgs) -> Result<(), sc64::Error> { fn handle_upload_command(connection: Connection, args: &UploadArgs) -> Result<(), sc64::Error> {
let mut sc64 = init_sc64(connection, true)?; let mut sc64 = init_sc64(connection, true)?;
let (mut rom_file, rom_name, rom_length) = open_file(&args.rom)?;
sc64.reset_state()?; sc64.reset_state()?;
let (mut rom_file, rom_name, rom_length) = open_file(&args.rom)?;
log_wait(format!("Uploading ROM [{rom_name}]"), || { log_wait(format!("Uploading ROM [{rom_name}]"), || {
sc64.upload_rom(&mut rom_file, rom_length, args.no_shadow) sc64.upload_rom(&mut rom_file, rom_length, args.no_shadow)
})?; })?;
@ -325,7 +338,6 @@ fn handle_upload_command(connection: Connection, args: &UploadArgs) -> Result<()
}; };
save_type.into() save_type.into()
}; };
let save_type: sc64::SaveType = save.into(); let save_type: sc64::SaveType = save.into();
println!("Save type set to [{save_type}]"); println!("Save type set to [{save_type}]");
sc64.set_save_type(save_type)?; sc64.set_save_type(save_type)?;
@ -347,13 +359,9 @@ fn handle_upload_command(connection: Connection, args: &UploadArgs) -> Result<()
sc64.set_boot_mode(boot_mode)?; sc64.set_boot_mode(boot_mode)?;
if let Some(tv) = args.tv.clone() { if let Some(tv) = args.tv.clone() {
if args.direct { let tv_type: sc64::TvType = tv.into();
println!("TV type ignored due to direct boot mode being enabled"); println!("TV type set to [{tv_type}]");
} else { sc64.set_tv_type(tv_type)?;
let tv_type: sc64::TvType = tv.into();
println!("TV type set to [{tv_type}]");
sc64.set_tv_type(tv_type)?;
}
} }
sc64.calculate_cic_parameters()?; sc64.calculate_cic_parameters()?;
@ -381,26 +389,179 @@ fn handle_download_command(
} }
fn handle_64dd_command(connection: Connection, args: &_64DDArgs) -> Result<(), sc64::Error> { fn handle_64dd_command(connection: Connection, args: &_64DDArgs) -> Result<(), sc64::Error> {
let _ = (connection, args); const MAX_ROM_LENGTH: usize = 32 * 1024 * 1024;
// TODO: handle 64DD stuff let mut sc64 = init_sc64(connection, true)?;
// TODO: print BIG warning to not use this mode together with real 64DD println!(
"{} {} {}",
"[WARNING]:".bold().bright_yellow(),
"Do not use this mode when real 64DD accessory is connected to the N64.".bright_yellow(),
"This might permanently damage either 64DD or SC64.".bright_yellow()
);
println!("{}", "Sorry nothing".yellow()); sc64.reset_state()?;
println!("{}", "64DD emulation not implemented yet".red());
if let Some(rom) = &args.rom {
let (mut rom_file, rom_name, rom_length) = open_file(rom)?;
if rom_length > MAX_ROM_LENGTH {
return Err(sc64::Error::new("ROM file size too big for 64DD mode"));
}
log_wait(format!("Uploading ROM [{rom_name}]"), || {
sc64.upload_rom(&mut rom_file, rom_length, false)
})?;
let save: SaveType = if let Some(save_type) = args.save_type.clone() {
save_type
} else {
let (save_type, title) = n64::guess_save_type(&mut rom_file)?;
if let Some(title) = title {
println!("ROM title: {title}");
};
save_type.into()
};
let save_type: sc64::SaveType = save.into();
println!("Save type set to [{save_type}]");
sc64.set_save_type(save_type)?;
if args.save.is_some() {
let (mut save_file, save_name, save_length) = open_file(&args.save.as_ref().unwrap())?;
log_wait(format!("Uploading save [{save_name}]"), || {
sc64.upload_save(&mut save_file, save_length)
})?;
}
}
let (mut ddipl_file, ddipl_name, ddipl_length) = open_file(&args.ddipl)?;
log_wait(format!("Uploading DDIPL [{ddipl_name}]"), || {
sc64.upload_ddipl(&mut ddipl_file, ddipl_length)
})?;
let boot_mode = if args.rom.is_some() {
if args.direct {
sc64::BootMode::DirectRom
} else {
sc64::BootMode::Rom
}
} else {
if args.direct {
sc64::BootMode::DirectDdIpl
} else {
sc64::BootMode::DdIpl
}
};
println!("Boot mode set to [{boot_mode}]");
sc64.set_boot_mode(boot_mode)?;
if let Some(tv) = args.tv.clone() {
let tv_type: sc64::TvType = tv.into();
println!("TV type set to [{tv_type}]");
sc64.set_tv_type(tv_type)?;
}
sc64.calculate_cic_parameters()?;
let disk_paths: Vec<String> = args
.disk
.iter()
.map(|path| path.to_string_lossy().to_string())
.collect();
let disk_names: Vec<String> = args
.disk
.iter()
.map(|path| path.file_name().unwrap().to_string_lossy().to_string())
.collect();
let mut disks = disk::open_multiple(&disk_paths)?;
let mut selected_disk_index: usize = 0;
let mut selected_disk: Option<&mut disk::Disk> = None;
let drive_type = match disks[0].get_format() {
disk::Format::Retail => sc64::DdDriveType::Retail,
disk::Format::Development => sc64::DdDriveType::Development,
};
sc64.configure_64dd(sc64::DdMode::Full, drive_type)?;
println!(
"{}",
"Press button on the SC64 device to cycle through provided disks".bold()
);
let exit = setup_exit_flag();
while !exit.load(Ordering::Relaxed) {
if let Some(data_packet) = sc64.receive_data_packet()? {
match data_packet {
sc64::DataPacket::Disk(mut packet) => {
let track = packet.info.track;
let head = packet.info.head;
let block = packet.info.block;
if let Some(ref mut disk) = selected_disk {
let reply_packet = match packet.kind {
sc64::DiskPacketKind::Read => {
print!("[R]");
disk.read_block(track, head, block)?.map(|data| {
packet.info.set_data(&data);
packet
})
}
sc64::DiskPacketKind::Write => {
print!("[W]");
let data = &packet.info.data;
disk.write_block(track, head, block, data)?.map(|_| packet)
}
};
let lba = if let Some(lba) = disk.get_lba(track, head, block) {
format!("{lba}")
} else {
"Invalid".to_string()
};
let message = format!(" {track:4}:{head}:{block} / LBA: {lba}");
if reply_packet.is_some() {
println!("{}", message.green());
} else {
println!("{}", message.red());
}
sc64.reply_disk_packet(reply_packet)?;
} else {
sc64.reply_disk_packet(None)?;
}
}
sc64::DataPacket::Button => {
if selected_disk.is_some() {
sc64.set_64dd_disk_state(sc64::DdDiskState::Ejected)?;
selected_disk = None;
println!("64DD disk ejected [{}]", disk_names[selected_disk_index]);
} else {
selected_disk_index += 1;
if selected_disk_index >= disks.len() {
selected_disk_index = 0;
}
selected_disk = Some(&mut disks[selected_disk_index]);
sc64.set_64dd_disk_state(sc64::DdDiskState::Inserted)?;
println!("64DD disk inserted [{}]", disk_names[selected_disk_index]);
}
}
_ => {}
}
} else {
thread::sleep(Duration::from_millis(1));
}
}
Ok(()) Ok(())
} }
fn handle_debug_command(connection: Connection, args: &DebugArgs) -> Result<(), sc64::Error> { fn handle_debug_command(connection: Connection, args: &DebugArgs) -> Result<(), sc64::Error> {
let mut sc64 = init_sc64(connection, true)?;
let mut debug_handler = debug::new(args.gdb)?; let mut debug_handler = debug::new(args.gdb)?;
if let Some(port) = args.gdb { if let Some(port) = args.gdb {
println!("GDB TCP socket listening at [0.0.0.0:{port}]"); println!("GDB TCP socket listening at [0.0.0.0:{port}]");
} }
let mut sc64 = init_sc64(connection, true)?;
if args.isv.is_some() { if args.isv.is_some() {
sc64.configure_is_viewer_64(args.isv)?; sc64.configure_is_viewer_64(args.isv)?;
println!( println!(
@ -600,16 +761,16 @@ fn handle_server_command(connection: Connection, args: &ServerArgs) -> Result<()
}; };
sc64::run_server(port, args.address.clone(), |event| match event { sc64::run_server(port, args.address.clone(), |event| match event {
ServerEvent::Listening(address) => { sc64::ServerEvent::Listening(address) => {
println!("{}: Listening on address [{}]", "[Server]".bold(), address) println!("{}: Listening on address [{}]", "[Server]".bold(), address)
} }
ServerEvent::Connection(peer) => { sc64::ServerEvent::Connection(peer) => {
println!("{}: New connection from [{}]", "[Server]".bold(), peer); println!("{}: New connection from [{}]", "[Server]".bold(), peer);
} }
ServerEvent::Disconnected(peer) => { sc64::ServerEvent::Disconnected(peer) => {
println!("{}: Client disconnected [{}]", "[Server]".bold(), peer); println!("{}: Client disconnected [{}]", "[Server]".bold(), peer);
} }
ServerEvent::Err(error) => { sc64::ServerEvent::Err(error) => {
println!( println!(
"{}: Client disconnected with error: {}", "{}: Client disconnected with error: {}",
"[Server]".bold(), "[Server]".bold(),

View File

@ -10,7 +10,8 @@ pub use self::{
link::{list_local_devices, ServerEvent}, link::{list_local_devices, ServerEvent},
types::{ types::{
BootMode, ButtonMode, ButtonState, CicSeed, DataPacket, DdDiskState, DdDriveType, DdMode, BootMode, ButtonMode, ButtonState, CicSeed, DataPacket, DdDiskState, DdDriveType, DdMode,
DebugPacket, DiskPacket, FpgaDebugData, McuStackUsage, SaveType, Switch, TvType, DebugPacket, DiskPacket, DiskPacketKind, FpgaDebugData, McuStackUsage, SaveType, Switch,
TvType,
}, },
}; };
@ -550,6 +551,7 @@ impl SC64 {
self.command_config_set(Config::DdSdEnable(Switch::Off))?; self.command_config_set(Config::DdSdEnable(Switch::Off))?;
self.command_config_set(Config::DdDriveType(drive_type))?; self.command_config_set(Config::DdDriveType(drive_type))?;
self.command_config_set(Config::DdDiskState(DdDiskState::Ejected))?; self.command_config_set(Config::DdDiskState(DdDiskState::Ejected))?;
self.command_config_set(Config::ButtonMode(ButtonMode::UsbPacket))?;
Ok(()) Ok(())
} }
@ -587,11 +589,11 @@ impl SC64 {
pub fn reply_disk_packet(&mut self, disk_packet: Option<DiskPacket>) -> Result<(), Error> { pub fn reply_disk_packet(&mut self, disk_packet: Option<DiskPacket>) -> Result<(), Error> {
if let Some(packet) = disk_packet { if let Some(packet) = disk_packet {
match packet { match packet.kind {
DiskPacket::ReadBlock(disk_block) => { DiskPacketKind::Read => {
self.command_memory_write(disk_block.address, &disk_block.data)?; self.command_memory_write(packet.info.address, &packet.info.data)?;
} }
DiskPacket::WriteBlock(_) => {} DiskPacketKind::Write => {}
} }
self.command_dd_set_block_ready(false)?; self.command_dd_set_block_ready(false)?;
} else { } else {

View File

@ -628,9 +628,14 @@ impl TryFrom<Vec<u8>> for DebugPacket {
} }
} }
pub enum DiskPacket { pub enum DiskPacketKind {
ReadBlock(DiskBlock), Read,
WriteBlock(DiskBlock), Write,
}
pub struct DiskPacket {
pub kind: DiskPacketKind,
pub info: DiskBlock,
} }
impl TryFrom<Vec<u8>> for DiskPacket { impl TryFrom<Vec<u8>> for DiskPacket {
@ -650,8 +655,14 @@ impl TryFrom<Vec<u8>> for DiskPacket {
data: value[12..].to_vec(), data: value[12..].to_vec(),
}; };
Ok(match command { Ok(match command {
1 => Self::ReadBlock(disk_block), 1 => DiskPacket {
2 => Self::WriteBlock(disk_block), kind: DiskPacketKind::Read,
info: disk_block,
},
2 => DiskPacket {
kind: DiskPacketKind::Write,
info: disk_block,
},
_ => return Err(Error::new("Unknown disk packet command code")), _ => return Err(Error::new("Unknown disk packet command code")),
}) })
} }

1
sw/tools/.gitignore vendored
View File

@ -1 +0,0 @@
*.bin