mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-11-22 05:59:15 +01:00
Added save writeback through USB
This commit is contained in:
parent
b3891f2c75
commit
009f41d87f
@ -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 |
|
||||
|
@ -628,7 +628,7 @@ void cfg_process (void) {
|
||||
return;
|
||||
}
|
||||
writeback_load_sector_table(args[0]);
|
||||
writeback_enable();
|
||||
writeback_enable(WRITEBACK_SD);
|
||||
break;
|
||||
|
||||
case 'K':
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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:
|
||||
*address = 0;
|
||||
*length = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
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, count, sd_write_sectors)) {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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()?));
|
||||
|
@ -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")?;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user