Added save writeback through USB

This commit is contained in:
Mateusz Faderewski 2023-03-31 21:32:54 +02:00
parent b3891f2c75
commit 009f41d87f
10 changed files with 188 additions and 34 deletions

View File

@ -20,6 +20,7 @@
| `M` | **MEMORY_WRITE** | address | length | data | --- | Write data to specified memory address | | `M` | **MEMORY_WRITE** | address | length | data | --- | Write data to specified memory address |
| `U` | **USB_WRITE** | type | length | data | N/A | Send data to be received by app running on N64 (no response!) | | `U` | **USB_WRITE** | type | length | data | N/A | Send data to be received by app running on N64 (no response!) |
| `D` | **DD_SET_BLOCK_READY** | success | --- | --- | --- | Notify flashcart about 64DD block readiness | | `D` | **DD_SET_BLOCK_READY** | success | --- | --- | --- | Notify flashcart about 64DD block readiness |
| `W` | **WRITEBACK_ENABLE** | --- | --- | --- | --- | Enable save writeback through USB packet |
| `p` | **FLASH_WAIT_BUSY** | wait | --- | --- | erase_block_size | Wait until flash ready / Get flash block erase size | | `p` | **FLASH_WAIT_BUSY** | wait | --- | --- | erase_block_size | Wait until flash ready / Get flash block erase size |
| `P` | **FLASH_ERASE_BLOCK** | address | --- | --- | --- | Start flash block erase | | `P` | **FLASH_ERASE_BLOCK** | address | --- | --- | --- | Start flash block erase |
| `f` | **FIRMWARE_BACKUP** | address | --- | --- | status/length | Backup firmware to specified memory address | | `f` | **FIRMWARE_BACKUP** | address | --- | --- | status/length | Backup firmware to specified memory address |

View File

@ -628,7 +628,7 @@ void cfg_process (void) {
return; return;
} }
writeback_load_sector_table(args[0]); writeback_load_sector_table(args[0]);
writeback_enable(); writeback_enable(WRITEBACK_SD);
break; break;
case 'K': case 'K':

View File

@ -8,6 +8,7 @@
#include "update.h" #include "update.h"
#include "usb.h" #include "usb.h"
#include "version.h" #include "version.h"
#include "writeback.h"
#define BOOTLOADER_ADDRESS (0x04E00000UL) #define BOOTLOADER_ADDRESS (0x04E00000UL)
@ -307,6 +308,12 @@ static void usb_rx_process (void) {
p.response_pending = true; p.response_pending = true;
break; break;
case 'W':
writeback_enable(WRITEBACK_USB);
p.rx_state = RX_STATE_IDLE;
p.response_pending = true;
break;
case 'p': case 'p':
if (p.rx_args[0]) { if (p.rx_args[0]) {
flash_wait_busy(); flash_wait_busy();

View File

@ -11,6 +11,7 @@ typedef enum packet_cmd {
PACKET_CMD_DD_REQUEST = 'D', PACKET_CMD_DD_REQUEST = 'D',
PACKET_CMD_DEBUG_OUTPUT = 'U', PACKET_CMD_DEBUG_OUTPUT = 'U',
PACKET_CMD_ISV_OUTPUT = 'I', PACKET_CMD_ISV_OUTPUT = 'I',
PACKET_CMD_SAVE_WRITEBACK = 'S',
PACKET_CMD_UPDATE_STATUS = 'F', PACKET_CMD_UPDATE_STATUS = 'F',
} usb_packet_cmd_e; } usb_packet_cmd_e;

View File

@ -2,23 +2,25 @@
#include "fpga.h" #include "fpga.h"
#include "sd.h" #include "sd.h"
#include "timer.h" #include "timer.h"
#include "usb.h"
#include "writeback.h" #include "writeback.h"
#define SAVE_MAX_SECTOR_COUNT (256) #define SAVE_MAX_SECTOR_COUNT (256)
#define EEPROM_ADDRESS (0x05002000) #define EEPROM_ADDRESS (0x05002000)
#define SRAM_FLASHRAM_ADDRESS (0x03FE0000) #define SRAM_FLASHRAM_ADDRESS (0x03FE0000)
#define EEPROM_4K_SECTOR_COUNT (1) #define EEPROM_4K_LENGTH (512)
#define EEPROM_16K_SECTOR_COUNT (4) #define EEPROM_16K_LENGTH (2048)
#define SRAM_SECTOR_COUNT (64) #define SRAM_LENGTH (32 * 1024)
#define FLASHRAM_SECTOR_COUNT (256) #define FLASHRAM_LENGTH (128 * 1024)
#define SRAM_BANKED_SECTOR_COUNT (192) #define SRAM_BANKED_LENGTH (3 * 32 * 1024)
#define WRITEBACK_DELAY_TICKS (100) #define WRITEBACK_DELAY_TICKS (100)
struct process { struct process {
bool enabled; bool enabled;
bool pending; bool pending;
writeback_mode_t mode;
uint16_t last_save_count; uint16_t last_save_count;
uint32_t sectors[SAVE_MAX_SECTOR_COUNT]; uint32_t sectors[SAVE_MAX_SECTOR_COUNT];
}; };
@ -27,41 +29,75 @@ struct process {
static struct process p; static struct process p;
static void writeback_save_to_sd (void) { static save_type_t writeback_get_address_length (uint32_t *address, uint32_t *length) {
uint32_t address; save_type_t save = cfg_get_save_type();
uint32_t count;
switch (cfg_get_save_type()) { switch (save) {
case SAVE_TYPE_EEPROM_4K: case SAVE_TYPE_EEPROM_4K:
address = EEPROM_ADDRESS; *address = EEPROM_ADDRESS;
count = EEPROM_4K_SECTOR_COUNT; *length = EEPROM_4K_LENGTH;
break; break;
case SAVE_TYPE_EEPROM_16K: case SAVE_TYPE_EEPROM_16K:
address = EEPROM_ADDRESS; *address = EEPROM_ADDRESS;
count = EEPROM_16K_SECTOR_COUNT; *length = EEPROM_16K_LENGTH;
break; break;
case SAVE_TYPE_SRAM: case SAVE_TYPE_SRAM:
address = SRAM_FLASHRAM_ADDRESS; *address = SRAM_FLASHRAM_ADDRESS;
count = SRAM_SECTOR_COUNT; *length = SRAM_LENGTH;
break; break;
case SAVE_TYPE_FLASHRAM: case SAVE_TYPE_FLASHRAM:
address = SRAM_FLASHRAM_ADDRESS; *address = SRAM_FLASHRAM_ADDRESS;
count = FLASHRAM_SECTOR_COUNT; *length = FLASHRAM_LENGTH;
break; break;
case SAVE_TYPE_SRAM_BANKED: case SAVE_TYPE_SRAM_BANKED:
address = SRAM_FLASHRAM_ADDRESS; *address = SRAM_FLASHRAM_ADDRESS;
count = SRAM_BANKED_SECTOR_COUNT; *length = SRAM_BANKED_LENGTH;
break; break;
default: default:
writeback_disable(); *address = 0;
return; *length = 0;
break;
} }
if(sd_optimize_sectors(address, p.sectors, count, sd_write_sectors)) { return save;
}
static void writeback_save_to_sd (void) {
save_type_t save;
uint32_t address;
uint32_t length;
save = writeback_get_address_length(&address, &length);
if (save == SAVE_TYPE_NONE) {
writeback_disable();
return;
}
if(sd_optimize_sectors(address, p.sectors, length / SD_SECTOR_SIZE, sd_write_sectors)) {
writeback_disable(); writeback_disable();
} }
} }
static bool writeback_save_to_usb (void) {
save_type_t save;
uint32_t address;
uint32_t length;
save = writeback_get_address_length(&address, &length);
if (save == SAVE_TYPE_NONE) {
writeback_disable();
return true;
}
usb_tx_info_t packet_info;
usb_create_packet(&packet_info, PACKET_CMD_SAVE_WRITEBACK);
packet_info.data_length = 4;
packet_info.data[0] = save;
packet_info.dma_length = length;
packet_info.dma_address = address;
return usb_enqueue_packet(&packet_info);
}
void writeback_load_sector_table (uint32_t address) { void writeback_load_sector_table (uint32_t address) {
fpga_mem_read(address, sizeof(p.sectors), (uint8_t *) (p.sectors)); fpga_mem_read(address, sizeof(p.sectors), (uint8_t *) (p.sectors));
@ -70,9 +106,10 @@ void writeback_load_sector_table (uint32_t address) {
} }
} }
void writeback_enable (void) { void writeback_enable (writeback_mode_t mode) {
p.enabled = true; p.enabled = true;
p.pending = false; p.pending = false;
p.mode = mode;
p.last_save_count = fpga_reg_get(REG_SAVE_COUNT); p.last_save_count = fpga_reg_get(REG_SAVE_COUNT);
} }
@ -85,13 +122,14 @@ void writeback_disable (void) {
void writeback_init (void) { void writeback_init (void) {
p.enabled = false; p.enabled = false;
p.pending = false; p.pending = false;
p.mode = WRITEBACK_SD;
for (int i = 0; i < SAVE_MAX_SECTOR_COUNT; i++) { for (int i = 0; i < SAVE_MAX_SECTOR_COUNT; i++) {
p.sectors[i] = 0; p.sectors[i] = 0;
} }
} }
void writeback_process (void) { void writeback_process (void) {
if (p.enabled && !sd_card_is_inserted()) { if (p.enabled && (p.mode == WRITEBACK_SD) && !sd_card_is_inserted()) {
writeback_disable(); writeback_disable();
} }
@ -105,7 +143,19 @@ void writeback_process (void) {
} }
if (p.pending && (timer_get(TIMER_ID_WRITEBACK) == 0)) { if (p.pending && (timer_get(TIMER_ID_WRITEBACK) == 0)) {
p.pending = false; switch (p.mode) {
writeback_save_to_sd(); case WRITEBACK_SD:
writeback_save_to_sd();
p.pending = false;
break;
case WRITEBACK_USB:
if (writeback_save_to_usb()) {
p.pending = false;
}
break;
default:
writeback_disable();
break;
}
} }
} }

View File

@ -9,8 +9,14 @@
#define WRITEBACK_SECTOR_TABLE_SIZE (1024) #define WRITEBACK_SECTOR_TABLE_SIZE (1024)
typedef enum {
WRITEBACK_SD,
WRITEBACK_USB,
} writeback_mode_t;
void writeback_load_sector_table (uint32_t address); void writeback_load_sector_table (uint32_t address);
void writeback_enable (void); void writeback_enable (writeback_mode_t mode);
void writeback_disable (void); void writeback_disable (void);
void writeback_init (void); void writeback_init (void);
void writeback_process (void); void writeback_process (void);

View File

@ -115,6 +115,8 @@ impl TryFrom<Vec<u8>> for ScreenshotMetadata {
} }
} }
const MAX_FILE_LENGTH: u64 = 8 * 1024 * 1024;
impl Handler { impl Handler {
pub fn process_user_input(&self) -> Option<sc64::DebugPacket> { pub fn process_user_input(&self) -> Option<sc64::DebugPacket> {
if let Ok(line) = self.line_rx.try_recv() { if let Ok(line) = self.line_rx.try_recv() {
@ -151,6 +153,10 @@ impl Handler {
return None; return None;
} }
}; };
if length > MAX_FILE_LENGTH {
println!("File too big ({length} bytes), exceeding max size of {MAX_FILE_LENGTH} bytes");
return None;
}
let mut data = vec![0u8; length as usize]; let mut data = vec![0u8; length as usize];
if let Err(error) = file.read_exact(&mut data) { if let Err(error) = file.read_exact(&mut data) {
println!("Couldn't read file contents: {error}"); println!("Couldn't read file contents: {error}");
@ -202,9 +208,9 @@ impl Handler {
match File::create(filename) { match File::create(filename) {
Ok(mut file) => { Ok(mut file) => {
if let Err(error) = file.write_all(data) { if let Err(error) = file.write_all(data) {
println!("Error during raw binary save: {error}"); println!("Error during raw binary write: {error}");
} }
println!("Wrote {} bytes to [{}]", data.len(), filename); println!("Wrote [{}] bytes to [{}]", data.len(), filename);
} }
Err(error) => { Err(error) => {
println!("Error during raw binary file creation: {error}"); println!("Error during raw binary file creation: {error}");
@ -267,6 +273,37 @@ impl Handler {
self.gdb_tx.send(data.to_vec()).ok(); self.gdb_tx.send(data.to_vec()).ok();
} }
pub fn handle_save_writeback(
&self,
save_writeback: sc64::SaveWriteback,
path: &Option<PathBuf>,
) {
let filename = &if let Some(path) = path {
path.to_string_lossy().to_string()
} else {
self.generate_filename(
"save",
match save_writeback.save {
sc64::SaveType::Eeprom4k | sc64::SaveType::Eeprom16k => "eep",
sc64::SaveType::Sram | sc64::SaveType::SramBanked => "srm",
sc64::SaveType::Flashram => "fla",
_ => "sav",
},
)
};
match File::create(filename) {
Ok(mut file) => {
if let Err(error) = file.write_all(&save_writeback.data) {
println!("Error during save write: {error}");
}
println!("Wrote [{}] save to [{}]", save_writeback.save, filename);
}
Err(error) => {
println!("Error during save writeback file creation: {error}");
}
}
}
fn generate_filename(&self, prefix: &str, extension: &str) -> String { fn generate_filename(&self, prefix: &str, extension: &str) -> String {
format!( format!(
"{prefix}-{}.{extension}", "{prefix}-{}.{extension}",
@ -314,7 +351,7 @@ fn stdin_thread(line_tx: Sender<String>) {
loop { loop {
let mut line = String::new(); let mut line = String::new();
if stdin().read_line(&mut line).is_ok() { if stdin().read_line(&mut line).is_ok() {
if line_tx.send(line).is_err() { if line_tx.send(line.trim_end().to_string()).is_err() {
return; return;
} }
} }

View File

@ -146,6 +146,10 @@ struct _64DDArgs {
#[derive(Args)] #[derive(Args)]
struct DebugArgs { struct DebugArgs {
/// Path to the save file to use by the save writeback mechanism
#[arg(short, long)]
save: Option<PathBuf>,
/// Enable IS-Viewer64 and set listening address at ROM offset (in most cases it's fixed at 0x03FF0000) /// Enable IS-Viewer64 and set listening address at ROM offset (in most cases it's fixed at 0x03FF0000)
#[arg(long, value_name = "offset", value_parser = |s: &str| maybe_hex_range::<u32>(s, 0x00000004, 0x03FF0000))] #[arg(long, value_name = "offset", value_parser = |s: &str| maybe_hex_range::<u32>(s, 0x00000004, 0x03FF0000))]
isv: Option<u32>, isv: Option<u32>,
@ -581,6 +585,7 @@ fn handle_debug_command(connection: Connection, args: &DebugArgs) -> Result<(),
.bright_blue() .bright_blue()
); );
} }
sc64.set_save_writeback(true)?;
println!("{}: Started", "[Debug]".bold()); println!("{}: Started", "[Debug]".bold());
@ -594,6 +599,9 @@ fn handle_debug_command(connection: Connection, args: &DebugArgs) -> Result<(),
sc64::DataPacket::Debug(debug_packet) => { sc64::DataPacket::Debug(debug_packet) => {
debug_handler.handle_debug_packet(debug_packet) debug_handler.handle_debug_packet(debug_packet)
} }
sc64::DataPacket::SaveWriteback(save_writeback) => {
debug_handler.handle_save_writeback(save_writeback, &args.save);
}
_ => {} _ => {}
} }
} else if let Some(gdb_packet) = debug_handler.receive_gdb_packet() { } else if let Some(gdb_packet) = debug_handler.receive_gdb_packet() {
@ -603,6 +611,7 @@ fn handle_debug_command(connection: Connection, args: &DebugArgs) -> Result<(),
} }
} }
sc64.set_save_writeback(false)?;
if args.isv.is_some() { if args.isv.is_some() {
sc64.configure_is_viewer_64(None)?; sc64.configure_is_viewer_64(None)?;
println!("{}: Stopped listening", "[IS-Viewer 64]".bold()); println!("{}: Stopped listening", "[IS-Viewer 64]".bold());

View File

@ -12,8 +12,8 @@ pub use self::{
server::ServerEvent, server::ServerEvent,
types::{ types::{
BootMode, ButtonMode, ButtonState, CicSeed, DataPacket, DdDiskState, DdDriveType, DdMode, BootMode, ButtonMode, ButtonState, CicSeed, DataPacket, DdDiskState, DdDriveType, DdMode,
DebugPacket, DiskPacket, DiskPacketKind, FpgaDebugData, McuStackUsage, SaveType, Switch, DebugPacket, DiskPacket, DiskPacketKind, FpgaDebugData, McuStackUsage, SaveType,
TvType, SaveWriteback, Switch, TvType,
}, },
}; };
@ -272,6 +272,15 @@ impl SC64 {
Ok(()) Ok(())
} }
fn command_writeback_enable(&mut self) -> Result<(), Error> {
self.link.execute_command(&Command {
id: b'W',
args: [0, 0],
data: &[],
})?;
Ok(())
}
fn command_flash_wait_busy(&mut self, wait: bool) -> Result<u32, Error> { fn command_flash_wait_busy(&mut self, wait: bool) -> Result<u32, Error> {
let data = self.link.execute_command(&Command { let data = self.link.execute_command(&Command {
id: b'p', id: b'p',
@ -582,6 +591,16 @@ impl SC64 {
Ok(()) Ok(())
} }
pub fn set_save_writeback(&mut self, enabled: bool) -> Result<(), Error> {
if enabled {
self.command_writeback_enable()?;
} else {
let save_type = get_config!(self, SaveType)?;
self.set_save_type(save_type)?;
}
Ok(())
}
pub fn receive_data_packet(&mut self) -> Result<Option<DataPacket>, Error> { pub fn receive_data_packet(&mut self) -> Result<Option<DataPacket>, Error> {
if let Some(packet) = self.link.receive_packet()? { if let Some(packet) = self.link.receive_packet()? {
return Ok(Some(packet.try_into()?)); return Ok(Some(packet.try_into()?));

View File

@ -580,6 +580,7 @@ pub enum DataPacket {
Debug(DebugPacket), Debug(DebugPacket),
Disk(DiskPacket), Disk(DiskPacket),
IsViewer64(String), IsViewer64(String),
SaveWriteback(SaveWriteback),
UpdateStatus(UpdateStatus), UpdateStatus(UpdateStatus),
} }
@ -591,6 +592,7 @@ impl TryFrom<Packet> for DataPacket {
b'U' => Self::Debug(value.data.try_into()?), b'U' => Self::Debug(value.data.try_into()?),
b'D' => Self::Disk(value.data.try_into()?), b'D' => Self::Disk(value.data.try_into()?),
b'I' => Self::IsViewer64(EUC_JP.decode(&value.data).0.into()), b'I' => Self::IsViewer64(EUC_JP.decode(&value.data).0.into()),
b'S' => Self::SaveWriteback(value.data.try_into()?),
b'F' => { b'F' => {
if value.data.len() != 4 { if value.data.len() != 4 {
return Err(Error::new( return Err(Error::new(
@ -682,6 +684,25 @@ impl DiskBlock {
} }
} }
pub struct SaveWriteback {
pub save: SaveType,
pub data: Vec<u8>,
}
impl TryFrom<Vec<u8>> for SaveWriteback {
type Error = Error;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
if value.len() < 4 {
return Err(Error::new(
"Couldn't extract save info from save writeback packet",
));
}
let save: SaveType = u32::from_be_bytes(value[0..4].try_into().unwrap()).try_into()?;
let data = value[4..].to_vec();
Ok(SaveWriteback { save, data })
}
}
pub enum FirmwareStatus { pub enum FirmwareStatus {
Ok, Ok,
ErrToken, ErrToken,
@ -782,7 +803,10 @@ impl TryFrom<Vec<u8>> for FpgaDebugData {
impl Display for FpgaDebugData { impl Display for FpgaDebugData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("Last PI address: 0x{:08X}", self.last_pi_address))?; f.write_fmt(format_args!(
"Last PI address: 0x{:08X}",
self.last_pi_address
))?;
if self.read_fifo_wait { if self.read_fifo_wait {
f.write_str(" RW")?; f.write_str(" RW")?;
} }