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 |
| `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 |
| `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_ERASE_BLOCK** | address | --- | --- | --- | Start flash block erase |
| `f` | **FIRMWARE_BACKUP** | address | --- | --- | status/length | Backup firmware to specified memory address |

View File

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

View File

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

View File

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

View File

@ -2,23 +2,25 @@
#include "fpga.h"
#include "sd.h"
#include "timer.h"
#include "usb.h"
#include "writeback.h"
#define SAVE_MAX_SECTOR_COUNT (256)
#define EEPROM_ADDRESS (0x05002000)
#define SRAM_FLASHRAM_ADDRESS (0x03FE0000)
#define EEPROM_4K_SECTOR_COUNT (1)
#define EEPROM_16K_SECTOR_COUNT (4)
#define SRAM_SECTOR_COUNT (64)
#define FLASHRAM_SECTOR_COUNT (256)
#define SRAM_BANKED_SECTOR_COUNT (192)
#define EEPROM_4K_LENGTH (512)
#define EEPROM_16K_LENGTH (2048)
#define SRAM_LENGTH (32 * 1024)
#define FLASHRAM_LENGTH (128 * 1024)
#define SRAM_BANKED_LENGTH (3 * 32 * 1024)
#define WRITEBACK_DELAY_TICKS (100)
struct process {
bool enabled;
bool pending;
writeback_mode_t mode;
uint16_t last_save_count;
uint32_t sectors[SAVE_MAX_SECTOR_COUNT];
};
@ -27,41 +29,75 @@ struct process {
static struct process p;
static void writeback_save_to_sd (void) {
uint32_t address;
uint32_t count;
static save_type_t writeback_get_address_length (uint32_t *address, uint32_t *length) {
save_type_t save = cfg_get_save_type();
switch (cfg_get_save_type()) {
switch (save) {
case SAVE_TYPE_EEPROM_4K:
address = EEPROM_ADDRESS;
count = EEPROM_4K_SECTOR_COUNT;
*address = EEPROM_ADDRESS;
*length = EEPROM_4K_LENGTH;
break;
case SAVE_TYPE_EEPROM_16K:
address = EEPROM_ADDRESS;
count = EEPROM_16K_SECTOR_COUNT;
*address = EEPROM_ADDRESS;
*length = EEPROM_16K_LENGTH;
break;
case SAVE_TYPE_SRAM:
address = SRAM_FLASHRAM_ADDRESS;
count = SRAM_SECTOR_COUNT;
*address = SRAM_FLASHRAM_ADDRESS;
*length = SRAM_LENGTH;
break;
case SAVE_TYPE_FLASHRAM:
address = SRAM_FLASHRAM_ADDRESS;
count = FLASHRAM_SECTOR_COUNT;
*address = SRAM_FLASHRAM_ADDRESS;
*length = FLASHRAM_LENGTH;
break;
case SAVE_TYPE_SRAM_BANKED:
address = SRAM_FLASHRAM_ADDRESS;
count = SRAM_BANKED_SECTOR_COUNT;
*address = SRAM_FLASHRAM_ADDRESS;
*length = SRAM_BANKED_LENGTH;
break;
default:
writeback_disable();
return;
*address = 0;
*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();
}
}
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) {
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.pending = false;
p.mode = mode;
p.last_save_count = fpga_reg_get(REG_SAVE_COUNT);
}
@ -85,13 +122,14 @@ void writeback_disable (void) {
void writeback_init (void) {
p.enabled = false;
p.pending = false;
p.mode = WRITEBACK_SD;
for (int i = 0; i < SAVE_MAX_SECTOR_COUNT; i++) {
p.sectors[i] = 0;
}
}
void writeback_process (void) {
if (p.enabled && !sd_card_is_inserted()) {
if (p.enabled && (p.mode == WRITEBACK_SD) && !sd_card_is_inserted()) {
writeback_disable();
}
@ -105,7 +143,19 @@ void writeback_process (void) {
}
if (p.pending && (timer_get(TIMER_ID_WRITEBACK) == 0)) {
p.pending = false;
writeback_save_to_sd();
switch (p.mode) {
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)
typedef enum {
WRITEBACK_SD,
WRITEBACK_USB,
} writeback_mode_t;
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_init (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 {
pub fn process_user_input(&self) -> Option<sc64::DebugPacket> {
if let Ok(line) = self.line_rx.try_recv() {
@ -151,6 +153,10 @@ impl Handler {
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];
if let Err(error) = file.read_exact(&mut data) {
println!("Couldn't read file contents: {error}");
@ -202,9 +208,9 @@ impl Handler {
match File::create(filename) {
Ok(mut file) => {
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) => {
println!("Error during raw binary file creation: {error}");
@ -267,6 +273,37 @@ impl Handler {
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 {
format!(
"{prefix}-{}.{extension}",
@ -314,7 +351,7 @@ fn stdin_thread(line_tx: Sender<String>) {
loop {
let mut line = String::new();
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;
}
}

View File

@ -146,6 +146,10 @@ struct _64DDArgs {
#[derive(Args)]
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)
#[arg(long, value_name = "offset", value_parser = |s: &str| maybe_hex_range::<u32>(s, 0x00000004, 0x03FF0000))]
isv: Option<u32>,
@ -581,6 +585,7 @@ fn handle_debug_command(connection: Connection, args: &DebugArgs) -> Result<(),
.bright_blue()
);
}
sc64.set_save_writeback(true)?;
println!("{}: Started", "[Debug]".bold());
@ -594,6 +599,9 @@ fn handle_debug_command(connection: Connection, args: &DebugArgs) -> Result<(),
sc64::DataPacket::Debug(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() {
@ -603,6 +611,7 @@ fn handle_debug_command(connection: Connection, args: &DebugArgs) -> Result<(),
}
}
sc64.set_save_writeback(false)?;
if args.isv.is_some() {
sc64.configure_is_viewer_64(None)?;
println!("{}: Stopped listening", "[IS-Viewer 64]".bold());

View File

@ -12,8 +12,8 @@ pub use self::{
server::ServerEvent,
types::{
BootMode, ButtonMode, ButtonState, CicSeed, DataPacket, DdDiskState, DdDriveType, DdMode,
DebugPacket, DiskPacket, DiskPacketKind, FpgaDebugData, McuStackUsage, SaveType, Switch,
TvType,
DebugPacket, DiskPacket, DiskPacketKind, FpgaDebugData, McuStackUsage, SaveType,
SaveWriteback, Switch, TvType,
},
};
@ -272,6 +272,15 @@ impl SC64 {
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> {
let data = self.link.execute_command(&Command {
id: b'p',
@ -582,6 +591,16 @@ impl SC64 {
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> {
if let Some(packet) = self.link.receive_packet()? {
return Ok(Some(packet.try_into()?));

View File

@ -580,6 +580,7 @@ pub enum DataPacket {
Debug(DebugPacket),
Disk(DiskPacket),
IsViewer64(String),
SaveWriteback(SaveWriteback),
UpdateStatus(UpdateStatus),
}
@ -591,6 +592,7 @@ impl TryFrom<Packet> for DataPacket {
b'U' => Self::Debug(value.data.try_into()?),
b'D' => Self::Disk(value.data.try_into()?),
b'I' => Self::IsViewer64(EUC_JP.decode(&value.data).0.into()),
b'S' => Self::SaveWriteback(value.data.try_into()?),
b'F' => {
if value.data.len() != 4 {
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 {
Ok,
ErrToken,
@ -782,7 +803,10 @@ impl TryFrom<Vec<u8>> for FpgaDebugData {
impl Display for FpgaDebugData {
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 {
f.write_str(" RW")?;
}