diff --git a/fw/rtl/mcu/mcu_top.sv b/fw/rtl/mcu/mcu_top.sv index 50bb33c..bf1b9a1 100644 --- a/fw/rtl/mcu/mcu_top.sv +++ b/fw/rtl/mcu/mcu_top.sv @@ -429,7 +429,8 @@ module mcu_top ( REG_CFG_SCR: begin reg_rdata <= { - 21'd0, + 20'd0, + n64_scb.rom_extended_enabled, n64_scb.eeprom_16k_mode, n64_scb.eeprom_enabled, n64_scb.ddipl_enabled, @@ -638,6 +639,7 @@ module mcu_top ( if (reset) begin mcu_int <= 1'b0; sd_scb.clock_mode <= 2'd0; + n64_scb.rom_extended_enabled <= 1'b0; n64_scb.eeprom_16k_mode <= 1'b0; n64_scb.eeprom_enabled <= 1'b0; n64_scb.dd_enabled <= 1'b0; @@ -695,6 +697,7 @@ module mcu_top ( REG_CFG_SCR: begin { + n64_scb.rom_extended_enabled, n64_scb.eeprom_16k_mode, n64_scb.eeprom_enabled, n64_scb.ddipl_enabled, @@ -706,7 +709,7 @@ module mcu_top ( n64_scb.rom_write_enabled, bootloader_skip, n64_scb.bootloader_enabled - } <= reg_wdata[10:0]; + } <= reg_wdata[11:0]; end REG_CFG_DATA_0: begin diff --git a/fw/rtl/n64/n64_pi.sv b/fw/rtl/n64/n64_pi.sv index 176f49b..1229583 100644 --- a/fw/rtl/n64/n64_pi.sv +++ b/fw/rtl/n64/n64_pi.sv @@ -214,10 +214,12 @@ module n64_pi ( end end - if (n64_pi_dq_in >= 16'h1400 && n64_pi_dq_in < 16'h14E0) begin - read_port <= PORT_MEM; - write_port <= PORT_NONE; - mem_offset <= (-32'h1400_0000) + FLASH_OFFSET; + if (n64_scb.rom_extended_enabled) begin + if (n64_pi_dq_in >= 16'h1400 && n64_pi_dq_in < 16'h14E0) begin + read_port <= PORT_MEM; + write_port <= PORT_NONE; + mem_offset <= (-32'h1400_0000) + FLASH_OFFSET; + end end if (n64_scb.cfg_unlock) begin diff --git a/fw/rtl/n64/n64_scb.sv b/fw/rtl/n64/n64_scb.sv index bdb9aa1..dae474f 100644 --- a/fw/rtl/n64/n64_scb.sv +++ b/fw/rtl/n64/n64_scb.sv @@ -6,6 +6,7 @@ interface n64_scb (); logic bootloader_enabled; logic rom_write_enabled; logic rom_shadow_enabled; + logic rom_extended_enabled; logic sram_enabled; logic sram_banked; logic flashram_enabled; @@ -57,6 +58,7 @@ interface n64_scb (); output bootloader_enabled, output rom_write_enabled, output rom_shadow_enabled, + output rom_extended_enabled, output sram_enabled, output sram_banked, output flashram_enabled, @@ -94,6 +96,7 @@ interface n64_scb (); input bootloader_enabled, input rom_write_enabled, input rom_shadow_enabled, + input rom_extended_enabled, input sram_enabled, input sram_banked, input flashram_enabled, diff --git a/sw/controller/src/cfg.c b/sw/controller/src/cfg.c index 487e68b..bb1ff5b 100644 --- a/sw/controller/src/cfg.c +++ b/sw/controller/src/cfg.c @@ -23,6 +23,7 @@ typedef enum { CFG_ID_DD_DISK_STATE, CFG_ID_BUTTON_STATE, CFG_ID_BUTTON_MODE, + CFG_ID_ROM_EXTENDED_ENABLE, } cfg_id_t; typedef enum { @@ -211,6 +212,9 @@ bool cfg_query (uint32_t *args) { case CFG_ID_BUTTON_MODE: args[1] = button_get_mode(); break; + case CFG_ID_ROM_EXTENDED_ENABLE: + args[1] = (scr & CFG_SCR_ROM_EXTENDED_ENABLED); + break; default: return true; } @@ -273,6 +277,9 @@ bool cfg_update (uint32_t *args) { case CFG_ID_BUTTON_MODE: button_set_mode(args[1]); break; + case CFG_ID_ROM_EXTENDED_ENABLE: + cfg_change_scr_bits(CFG_SCR_ROM_EXTENDED_ENABLED, args[1]); + break; default: return true; } diff --git a/sw/controller/src/fpga.h b/sw/controller/src/fpga.h index 79b3d64..d67f1ce 100644 --- a/sw/controller/src/fpga.h +++ b/sw/controller/src/fpga.h @@ -56,95 +56,98 @@ typedef enum { } fpga_reg_t; -#define FPGA_ID (0x64) +#define FPGA_ID (0x64) -#define FPGA_MAX_MEM_TRANSFER (1024) +#define FPGA_MAX_MEM_TRANSFER (1024) -#define USB_STATUS_RXNE (1 << 0) -#define USB_STATUS_TXE (1 << 1) +#define USB_STATUS_RXNE (1 << 0) +#define USB_STATUS_TXE (1 << 1) -#define MEM_SCR_START (1 << 0) -#define MEM_SCR_STOP (1 << 1) -#define MEM_SCR_DIRECTION (1 << 2) -#define MEM_SCR_BUSY (1 << 3) -#define MEM_SCR_LENGTH_BIT (4) +#define MEM_SCR_START (1 << 0) +#define MEM_SCR_STOP (1 << 1) +#define MEM_SCR_DIRECTION (1 << 2) +#define MEM_SCR_BUSY (1 << 3) +#define MEM_SCR_LENGTH_BIT (4) -#define STATUS_BUTTON (1 << 0) -#define STATUS_SD_INSERTED (1 << 1) -#define STATUS_CFG_PENDING (1 << 2) +#define STATUS_BUTTON (1 << 0) +#define STATUS_SD_INSERTED (1 << 1) +#define STATUS_CFG_PENDING (1 << 2) -#define USB_SCR_FIFO_FLUSH (1 << 0) -#define USB_SCR_RXNE (1 << 1) -#define USB_SCR_TXE (1 << 2) -#define USB_SCR_RESET_PENDING (1 << 3) -#define USB_SCR_RESET_ACK (1 << 4) -#define USB_SCR_WRITE_FLUSH (1 << 5) +#define USB_SCR_FIFO_FLUSH (1 << 0) +#define USB_SCR_RXNE (1 << 1) +#define USB_SCR_TXE (1 << 2) +#define USB_SCR_RESET_PENDING (1 << 3) +#define USB_SCR_RESET_ACK (1 << 4) +#define USB_SCR_WRITE_FLUSH (1 << 5) -#define DMA_SCR_START (1 << 0) -#define DMA_SCR_STOP (1 << 1) -#define DMA_SCR_DIRECTION (1 << 2) -#define DMA_SCR_BUSY (1 << 3) +#define DMA_SCR_START (1 << 0) +#define DMA_SCR_STOP (1 << 1) +#define DMA_SCR_DIRECTION (1 << 2) +#define DMA_SCR_BUSY (1 << 3) -#define CFG_SCR_BOOTLOADER_ENABLED (1 << 0) -#define CFG_SCR_BOOTLOADER_SKIP (1 << 1) -#define CFG_SCR_ROM_WRITE_ENABLED (1 << 2) -#define CFG_SCR_ROM_SHADOW_ENABLED (1 << 3) -#define CFG_SCR_SRAM_ENABLED (1 << 4) -#define CFG_SCR_SRAM_BANKED (1 << 5) -#define CFG_SCR_FLASHRAM_ENABLED (1 << 6) -#define CFG_SCR_DD_ENABLED (1 << 7) -#define CFG_SCR_DDIPL_ENABLED (1 << 8) -#define CFG_SCR_EEPROM_ENABLED (1 << 9) -#define CFG_SCR_EEPROM_16K (1 << 10) +#define CFG_SCR_BOOTLOADER_ENABLED (1 << 0) +#define CFG_SCR_BOOTLOADER_SKIP (1 << 1) +#define CFG_SCR_ROM_WRITE_ENABLED (1 << 2) +#define CFG_SCR_ROM_SHADOW_ENABLED (1 << 3) +#define CFG_SCR_SRAM_ENABLED (1 << 4) +#define CFG_SCR_SRAM_BANKED (1 << 5) +#define CFG_SCR_FLASHRAM_ENABLED (1 << 6) +#define CFG_SCR_DD_ENABLED (1 << 7) +#define CFG_SCR_DDIPL_ENABLED (1 << 8) +#define CFG_SCR_EEPROM_ENABLED (1 << 9) +#define CFG_SCR_EEPROM_16K (1 << 10) +#define CFG_SCR_ROM_EXTENDED_ENABLED (1 << 11) -#define CFG_CMD_DONE (1 << 0) -#define CFG_CMD_ERROR (1 << 1) -#define CFG_CMD_IRQ (1 << 2) +#define CFG_CMD_DONE (1 << 0) +#define CFG_CMD_ERROR (1 << 1) +#define CFG_CMD_IRQ (1 << 2) -#define FLASHRAM_SCR_DONE (1 << 0) -#define FLASHRAM_SCR_PENDING (1 << 1) -#define FLASHRAM_SCR_PAGE_BIT (2) -#define FLASHRAM_SCR_PAGE_MASK (0x3FF << FLASHRAM_SCR_PAGE_BIT) -#define FLASHRAM_SCR_SECTOR_OR_ALL (1 << 12) -#define FLASHRAM_SCR_WRITE_OR_ERASE (1 << 13) +#define FLASHRAM_SCR_DONE (1 << 0) +#define FLASHRAM_SCR_PENDING (1 << 1) +#define FLASHRAM_SCR_PAGE_BIT (2) +#define FLASHRAM_SCR_PAGE_MASK (0x3FF << FLASHRAM_SCR_PAGE_BIT) +#define FLASHRAM_SCR_SECTOR_OR_ALL (1 << 12) +#define FLASHRAM_SCR_WRITE_OR_ERASE (1 << 13) -#define FLASH_SCR_BUSY (1 << 0) +#define FLASH_SCR_BUSY (1 << 0) -#define RTC_SCR_PENDING (1 << 0) -#define RTC_SCR_DONE (1 << 1) -#define RTC_SCR_MAGIC (0x52544300) -#define RTC_SCR_MAGIC_MASK (0xFFFFFF00) +#define RTC_SCR_PENDING (1 << 0) +#define RTC_SCR_DONE (1 << 1) +#define RTC_SCR_MAGIC (0x52544300) +#define RTC_SCR_MAGIC_MASK (0xFFFFFF00) -#define SD_SCR_CLOCK_MODE_OFF (0 << 0) -#define SD_SCR_CLOCK_MODE_400KHZ (1 << 0) -#define SD_SCR_CLOCK_MODE_25MHZ (2 << 0) -#define SD_SCR_CLOCK_MODE_50MHZ (3 << 0) +#define SD_SCR_CLOCK_MODE_OFF (0 << 0) +#define SD_SCR_CLOCK_MODE_400KHZ (1 << 0) +#define SD_SCR_CLOCK_MODE_25MHZ (2 << 0) +#define SD_SCR_CLOCK_MODE_50MHZ (3 << 0) -#define DD_SCR_HARD_RESET (1 << 0) -#define DD_SCR_HARD_RESET_CLEAR (1 << 1) -#define DD_SCR_CMD_PENDING (1 << 2) -#define DD_SCR_CMD_READY (1 << 3) -#define DD_SCR_BM_PENDING (1 << 4) -#define DD_SCR_BM_READY (1 << 5) -#define DD_SCR_DISK_INSERTED (1 << 6) -#define DD_SCR_DISK_CHANGED (1 << 7) -#define DD_SCR_BM_START (1 << 8) -#define DD_SCR_BM_START_CLEAR (1 << 9) -#define DD_SCR_BM_STOP (1 << 10) -#define DD_SCR_BM_STOP_CLEAR (1 << 11) -#define DD_SCR_BM_TRANSFER_MODE (1 << 12) -#define DD_SCR_BM_TRANSFER_BLOCKS (1 << 13) -#define DD_SCR_BM_TRANSFER_DATA (1 << 14) -#define DD_SCR_BM_TRANSFER_C2 (1 << 15) -#define DD_SCR_BM_MICRO_ERROR (1 << 16) -#define DD_SCR_BM_ACK (1 << 17) -#define DD_SCR_BM_ACK_CLEAR (1 << 18) -#define DD_SCR_BM_CLEAR (1 << 19) +#define DD_SCR_HARD_RESET (1 << 0) +#define DD_SCR_HARD_RESET_CLEAR (1 << 1) +#define DD_SCR_CMD_PENDING (1 << 2) +#define DD_SCR_CMD_READY (1 << 3) +#define DD_SCR_BM_PENDING (1 << 4) +#define DD_SCR_BM_READY (1 << 5) +#define DD_SCR_DISK_INSERTED (1 << 6) +#define DD_SCR_DISK_CHANGED (1 << 7) +#define DD_SCR_BM_START (1 << 8) +#define DD_SCR_BM_START_CLEAR (1 << 9) +#define DD_SCR_BM_STOP (1 << 10) +#define DD_SCR_BM_STOP_CLEAR (1 << 11) +#define DD_SCR_BM_TRANSFER_MODE (1 << 12) +#define DD_SCR_BM_TRANSFER_BLOCKS (1 << 13) +#define DD_SCR_BM_TRANSFER_DATA (1 << 14) +#define DD_SCR_BM_TRANSFER_C2 (1 << 15) +#define DD_SCR_BM_MICRO_ERROR (1 << 16) +#define DD_SCR_BM_ACK (1 << 17) +#define DD_SCR_BM_ACK_CLEAR (1 << 18) +#define DD_SCR_BM_CLEAR (1 << 19) -#define DD_TRACK_MASK (0x0FFF) -#define DD_HEAD_MASK (0x1000) -#define DD_HEAD_TRACK_MASK (DD_HEAD_MASK | DD_TRACK_MASK) -#define DD_HEAD_TRACK_INDEX_LOCK (1 << 13) +#define DD_TRACK_MASK (0x0FFF) +#define DD_HEAD_MASK (0x1000) +#define DD_HEAD_TRACK_MASK (DD_HEAD_MASK | DD_TRACK_MASK) +#define DD_HEAD_TRACK_INDEX_LOCK (1 << 13) + +#define SWAP32(x) ((x & 0xFF) << 24 | (x & 0xFF00) << 8 | (x & 0xFF0000) >> 8 | (x & 0xFF000000) >> 24) uint8_t fpga_id_get (void); diff --git a/sw/controller/src/isv.c b/sw/controller/src/isv.c index 2282077..6463622 100644 --- a/sw/controller/src/isv.c +++ b/sw/controller/src/isv.c @@ -4,7 +4,8 @@ #include "usb.h" -#define ISV_READ_POINTER_ADDRESS (0x03FF0014) +#define ISV_READ_POINTER_ADDRESS (0x03FF0004) +#define ISV_WRITE_POINTER_ADDRESS (0x03FF0014) #define ISV_BUFFER_ADDRESS (0x03FF0020) #define ISV_BUFFER_SIZE ((64 * 1024) - 0x20) @@ -18,24 +19,28 @@ struct process { static struct process p; -static uint32_t isv_get_read_pointer (void) { - uint32_t read_pointer; - fpga_mem_read(ISV_READ_POINTER_ADDRESS, 4, (uint8_t *) (&read_pointer)); - return ( - (read_pointer & 0x000000FF) << 24 | - (read_pointer & 0x0000FF00) << 8 | - (read_pointer & 0x00FF0000) >> 8 | - (read_pointer & 0xFF000000) >> 24 - ); +static void isv_set_pointer (uint32_t address, uint32_t data) { + data = SWAP32(data); + fpga_mem_write(address, 4, (uint8_t *) (&data)); +} + +static uint32_t isv_get_pointer (uint32_t address) { + uint32_t data; + fpga_mem_read(address, 4, (uint8_t *) (&data)); + return SWAP32(data); +} + +static void isv_update_read_pointer (void) { + isv_set_pointer(ISV_READ_POINTER_ADDRESS, p.current_read_pointer); } void isv_set_enabled (bool enabled) { - uint32_t read_pointer = 0; if (enabled) { p.enabled = true; p.current_read_pointer = 0; - fpga_mem_write(ISV_READ_POINTER_ADDRESS, 4, (uint8_t *) (&read_pointer)); + isv_set_pointer(ISV_READ_POINTER_ADDRESS, 0); + isv_set_pointer(ISV_WRITE_POINTER_ADDRESS, 0); } else { p.enabled = false; } @@ -51,20 +56,26 @@ void isv_init (void) { void isv_process (void) { if (p.enabled) { - uint32_t read_pointer = isv_get_read_pointer(); - if (read_pointer < ISV_BUFFER_SIZE && read_pointer != p.current_read_pointer) { - bool wrap = read_pointer < p.current_read_pointer; + uint32_t write_pointer = isv_get_pointer(ISV_WRITE_POINTER_ADDRESS); - uint32_t length = ((wrap ? ISV_BUFFER_SIZE : read_pointer) - p.current_read_pointer); - uint32_t offset = ISV_BUFFER_ADDRESS + p.current_read_pointer; + if (p.current_read_pointer == write_pointer) { + return; + } + if (write_pointer >= ISV_BUFFER_SIZE) { + return; + } - usb_tx_info_t packet_info; - usb_create_packet(&packet_info, PACKET_CMD_ISV_OUTPUT); - packet_info.dma_length = length; - packet_info.dma_address = offset; - if (usb_enqueue_packet(&packet_info)) { - p.current_read_pointer = wrap ? 0 : read_pointer; - } + bool wrap = write_pointer < p.current_read_pointer; + uint32_t length = (wrap ? ISV_BUFFER_SIZE : write_pointer) - p.current_read_pointer; + uint32_t offset = ISV_BUFFER_ADDRESS + p.current_read_pointer; + + usb_tx_info_t packet_info; + usb_create_packet(&packet_info, PACKET_CMD_ISV_OUTPUT); + packet_info.dma_length = length; + packet_info.dma_address = offset; + packet_info.done_callback = isv_update_read_pointer; + if (usb_enqueue_packet(&packet_info)) { + p.current_read_pointer = wrap ? 0 : write_pointer; } } } diff --git a/sw/pc/sc64.py b/sw/pc/sc64.py index 19de8c4..4c727c2 100755 --- a/sw/pc/sc64.py +++ b/sw/pc/sc64.py @@ -199,6 +199,8 @@ class SC64: FIRMWARE = 0x0200_0000 DDIPL = 0x03BC_0000 SAVE = 0x03FE_0000 + EXTENDED = 0x0400_0000 + BOOTLOADER = 0x04E0_0000 SHADOW = 0x04FE_0000 class __Length(IntEnum): @@ -208,6 +210,8 @@ class SC64: EEPROM = (2 * 1024) DDIPL = (4 * 1024 * 1024) SAVE = (128 * 1024) + EXTENDED = (14 * 1024 * 1024) + BOOTLOADER = (1920 * 1024) SHADOW = (128 * 1024) class __SaveLength(IntEnum): @@ -233,6 +237,7 @@ class SC64: DD_DISK_STATE = 11 BUTTON_STATE = 12 BUTTON_MODE = 13 + ROM_EXTENDED_ENABLE = 14 class __UpdateError(IntEnum): OK = 0 @@ -283,7 +288,7 @@ class SC64: EEPROM_16K = 2 SRAM = 3 FLASHRAM = 4 - SRAM_X3 = 5 + SRAM_3X = 5 class CICSeed(IntEnum): ALECK = 0xAC @@ -333,11 +338,20 @@ class SC64: if ((address + length) > (self.__Address.FLASH + self.__Length.FLASH)): raise ValueError('Flash erase address or length outside of possible range') erase_block_size = self.__get_config(self.__CfgId.FLASH_ERASE_BLOCK) - if ((address % erase_block_size != 0) or (length % erase_block_size != 0)): - raise ValueError('Flash erase address or length not aligned to block size') + if (address % erase_block_size != 0): + raise ValueError('Flash erase address not aligned to block size') for offset in range(address, address + length, erase_block_size): self.__set_config(self.__CfgId.FLASH_ERASE_BLOCK, offset) + def __program_flash(self, address: int, data: bytes): + program_chunk_size = (128 * 1024) + if (self.__read_memory(address, len(data)) != data): + self.__erase_flash_region(address, len(data)) + for offset in range(0, len(data), program_chunk_size): + self.__write_memory(address + offset, data[offset:offset + program_chunk_size]) + if (self.__read_memory(address, len(data)) != data): + raise ConnectionException('Flash memory program failure') + def reset_state(self) -> None: self.__set_config(self.__CfgId.ROM_WRITE_ENABLE, False) self.__set_config(self.__CfgId.ROM_SHADOW_ENABLE, False) @@ -350,6 +364,7 @@ class SC64: self.__set_config(self.__CfgId.DD_DRIVE_TYPE, self.__DDDriveType.RETAIL) self.__set_config(self.__CfgId.DD_DISK_STATE, self.__DDDiskState.EJECTED) self.__set_config(self.__CfgId.BUTTON_MODE, self.__ButtonMode.NONE) + self.__set_config(self.__CfgId.ROM_EXTENDED_ENABLE, False) self.set_cic_parameters() def get_state(self): @@ -368,24 +383,29 @@ class SC64: 'dd_disk_state': self.__DDDiskState(self.__get_config(self.__CfgId.DD_DISK_STATE)), 'button_state': bool(self.__get_config(self.__CfgId.BUTTON_STATE)), 'button_mode': self.__ButtonMode(self.__get_config(self.__CfgId.BUTTON_MODE)), + 'rom_extended_enable': bool(self.__get_config(self.__CfgId.ROM_EXTENDED_ENABLE)), } - def upload_rom(self, data: bytes, use_shadow: bool=True): + def download_memory(self) -> bytes: + return self.__read_memory(self.__Address.SDRAM, self.__Length.SDRAM + self.__Length.FLASH) + + def upload_rom(self, data: bytes, use_shadow: bool=True) -> None: rom_length = len(data) - if (rom_length > self.__Length.SDRAM): + if (rom_length > (self.__Length.SDRAM + self.__Length.EXTENDED)): raise ValueError('ROM size too big') - sdram_length = rom_length + sdram_length = self.__Length.SDRAM shadow_enabled = use_shadow and rom_length > (self.__Length.SDRAM - self.__Length.SHADOW) + extended_enabled = rom_length > self.__Length.SDRAM if (shadow_enabled): sdram_length = (self.__Length.SDRAM - self.__Length.SHADOW) - shadow_length = rom_length - sdram_length - if (self.__read_memory(self.__Address.SHADOW, shadow_length) != data[sdram_length:]): - self.__erase_flash_region(self.__Address.SHADOW, self.__Length.SHADOW) - self.__write_memory(self.__Address.SHADOW, data[sdram_length:]) - if (self.__read_memory(self.__Address.SHADOW, shadow_length) != data[sdram_length:]): - raise ConnectionException('Shadow ROM program failure') - self.__write_memory(self.__Address.SDRAM, data[:sdram_length]) + shadow_data = data[sdram_length:sdram_length + self.__Length.SHADOW] + self.__program_flash(self.__Address.SHADOW, shadow_data) self.__set_config(self.__CfgId.ROM_SHADOW_ENABLE, shadow_enabled) + if (extended_enabled): + extended_data = data[self.__Length.SDRAM:] + self.__program_flash(self.__Address.EXTENDED, extended_data) + self.__set_config(self.__CfgId.ROM_EXTENDED_ENABLE, extended_enabled) + self.__write_memory(self.__Address.SDRAM, data[:sdram_length]) def upload_ddipl(self, data: bytes) -> None: if (len(data) > self.__Length.DDIPL): @@ -615,8 +635,8 @@ if __name__ == '__main__': parser.add_argument('--tv', type=SC64.TVType, action=EnumAction, help='force TV type to set value') parser.add_argument('--cic', type=SC64.CICSeed, action=EnumAction, help='force CIC seed to set value') parser.add_argument('--rtc', action='store_true', help='update clock in SC64 to system time') - parser.add_argument('--rom', help='upload ROM from specified file') parser.add_argument('--no-shadow', action='store_false', help='do not put last 128 kB of ROM inside flash memory (can corrupt non EEPROM saves)') + parser.add_argument('--rom', help='upload ROM from specified file') parser.add_argument('--save-type', type=SC64.SaveType, action=EnumAction, help='set save type') parser.add_argument('--save', help='upload save from specified file') parser.add_argument('--backup-save', help='download save and write it to specified file') @@ -624,6 +644,7 @@ if __name__ == '__main__': parser.add_argument('--disk', action='append', help='path to 64DD disk (.ndd format), can be specified multiple times') parser.add_argument('--isv', action='store_true', help='enable IS-Viewer64 support') parser.add_argument('--debug', action='store_true', help='run debug loop (required for 64DD and IS-Viewer64)') + parser.add_argument('--download-memory', help='download whole memory space and write it to specified file') if (len(sys.argv) <= 1): parser.print_help() @@ -706,6 +727,12 @@ if __name__ == '__main__': print('Downloading save... ', end='', flush=True) f.write(sc64.download_save()) print('done') + + if (args.download_memory): + with open(args.download_memory, 'wb+') as f: + print('Downloading memory... ', end='', flush=True) + f.write(sc64.download_memory()) + print('done') except ValueError as e: print(f'\nValue error: {e}') except ConnectionException as e: