From 409ba28359e79a96d08d8d79a29d5be62d6c3c59 Mon Sep 17 00:00:00 2001 From: Mateusz Faderewski Date: Mon, 20 Feb 2023 18:17:09 +0100 Subject: [PATCH] [SC64][SW] Changed firmware version reporting --- docs/01_memory_map.md | 24 +++++----- docs/02_usb_commands.md | 44 +++++++++--------- docs/03_n64_commands.md | 7 ++- fw/rtl/mcu/mcu_top.sv | 8 ++-- fw/rtl/n64/n64_cfg.sv | 10 ++-- fw/rtl/n64/n64_scb.sv | 6 +-- sw/bootloader/src/sc64.c | 15 +++--- sw/controller/src/cfg.c | 13 ++---- sw/controller/src/cfg.h | 2 +- sw/controller/src/fpga.h | 2 +- sw/controller/src/usb.c | 48 +++++++++---------- sw/controller/src/version.c | 15 ++---- sw/controller/src/version.h | 8 +--- sw/pc/sc64.py | 92 ++++++++++++++++++++----------------- 14 files changed, 142 insertions(+), 152 deletions(-) diff --git a/docs/01_memory_map.md b/docs/01_memory_map.md index d00a635..2912706 100644 --- a/docs/01_memory_map.md +++ b/docs/01_memory_map.md @@ -5,7 +5,7 @@ - [SC64 registers](#sc64-registers) - [`0x1FFF_0000`: **STATUS/COMMAND**](#0x1fff_0000-statuscommand) - [`0x1FFF_0004`: **DATA0** and `0x1FFF_0008`: **DATA1**](#0x1fff_0004-data0-and-0x1fff_0008-data1) - - [`0x1FFF_000C`: **VERSION**](#0x1fff_000c-version) + - [`0x1FFF_000C`: **IDENTIFIER**](#0x1fff_000c-identifier) - [`0x1FFF_0010`: **KEY**](#0x1fff_0010-key) - [Command execution flow](#command-execution-flow) @@ -87,13 +87,13 @@ SC64 contains small register region used for communication between N64 and contr Protocol is command based with support for up to 256 diferrent commands and two 32-bit argument/result values per operation. Support for interrupts is provided but currently no command relies on it, 64DD IRQ is handled separately. -| name | address | size | access | usage | -| ------------------ | ------------- | ------- | ------ | -------------------------------- | -| **STATUS/COMMAND** | `0x1FFF_0000` | 4 bytes | RW | Command execution and status | -| **DATA0** | `0x1FFF_0004` | 4 bytes | RW | Command argument/result 0 | -| **DATA1** | `0x1FFF_0008` | 4 bytes | RW | Command argument/result 1 | -| **VERSION** | `0x1FFF_000C` | 4 bytes | RW | Hardware version and IRQ clear | -| **KEY** | `0x1FFF_0010` | 4 bytes | W | SC64 register access lock/unlock | +| name | address | size | access | usage | +| ------------------ | ------------- | ------- | ------ | ---------------------------------- | +| **STATUS/COMMAND** | `0x1FFF_0000` | 4 bytes | RW | Command execution and status | +| **DATA0** | `0x1FFF_0004` | 4 bytes | RW | Command argument/result 0 | +| **DATA1** | `0x1FFF_0008` | 4 bytes | RW | Command argument/result 1 | +| **IDENTIFIER** | `0x1FFF_000C` | 4 bytes | RW | Flashcart identifier and IRQ clear | +| **KEY** | `0x1FFF_0010` | 4 bytes | W | SC64 register access lock/unlock | --- @@ -121,11 +121,11 @@ Note: Result is valid only when command has executed and `CMD_BUSY` bit is reset --- -#### `0x1FFF_000C`: **VERSION** +#### `0x1FFF_000C`: **IDENTIFIER** -| name | bits | access | meaning | -| --------- | ------ | ------ | --------------------------------------------- | -| `VERSION` | [31:0] | RW | Hardware version (ASCII `SCv2`) and IRQ clear | +| name | bits | access | meaning | +| ------------ | ------ | ------ | ------------------------------------------------- | +| `IDENTIFIER` | [31:0] | RW | Flashcart identifier (ASCII `SCv2`) and IRQ clear | Note: Writing any value to this register will clear pending flashcart interrupt. diff --git a/docs/02_usb_commands.md b/docs/02_usb_commands.md index a4c71d9..0e26692 100644 --- a/docs/02_usb_commands.md +++ b/docs/02_usb_commands.md @@ -4,25 +4,25 @@ ## USB commands -| id | name | arg0 | arg1 | data | response | description | -| --- | ---------------------- | ------------ | ------------ | ---- | ---------------- | --------------------------------------------------- | -| `v` | **HW_VERSION_GET** | --- | --- | --- | hw_version | Get HW version | -| `V` | **API_VERSION_GET** | --- | --- | --- | api_version | Get USB command API version | -| `R` | **STATE_RESET** | --- | --- | --- | --- | Reset entire flashcart state | -| `B` | **CIC_PARAMS_SET** | cic_params_0 | cic_params_1 | --- | --- | Set CIC disable/mode/seed/checksum | -| `c` | **CONFIG_GET** | config_id | --- | --- | current_value | Get config option | -| `C` | **CONFIG_SET** | config_id | new_value | --- | --- | Set config option | -| `a` | **SETTING_GET** | setting_id | --- | --- | current_value | Get persistent setting | -| `A` | **SETTING_SET** | setting_id | new_value | --- | --- | Set persistent setting | -| `t` | **TIME_GET** | --- | --- | --- | time | Get current RTC value | -| `T` | **TIME_SET** | time_0 | time_1 | --- | --- | Set RTC value | -| `m` | **MEMORY_READ** | address | length | --- | data | Read data from specified memory address | -| `M` | **MEMORY_WRITE** | address | length | data | --- | Write data to specified memory address | -| `D` | **DD_SET_BLOCK_READY** | success | --- | --- | --- | Notify flashcart about 64DD block readiness | -| `U` | **USB_WRITE** | type | length | data | N/A | Send data to be received by app running on N64 | -| `f` | **FIRMWARE_BACKUP** | address | --- | --- | status/length | Backup firmware to specified memory address | -| `F` | **FIRMWARE_UPDATE** | address | length | --- | status | Update firmware from specified memory address | -| `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 | -| `?` | **DEBUG_GET** | --- | --- | --- | debug_data | Get internal FPGA debug info | -| `%` | **STACK_USAGE_GET** | --- | --- | --- | stack_usage | Get per task stack usage | +| id | name | arg0 | arg1 | data | response | description | +| --- | ---------------------- | ------------ | ------------ | ---- | ---------------- | ------------------------------------------------------------- | +| `v` | **IDENTIFIER_GET** | --- | --- | --- | identifier | Get flashcart identifier `SCv2` | +| `V` | **VERSION_GET** | --- | --- | --- | version | Get flashcart firmware version | +| `R` | **STATE_RESET** | --- | --- | --- | --- | Reset flashcart state (CIC params and config options) | +| `B` | **CIC_PARAMS_SET** | cic_params_0 | cic_params_1 | --- | --- | Set CIC emulation parameters (disable/seed/checksum) | +| `c` | **CONFIG_GET** | config_id | --- | --- | current_value | Get config option | +| `C` | **CONFIG_SET** | config_id | new_value | --- | --- | Set config option | +| `a` | **SETTING_GET** | setting_id | --- | --- | current_value | Get persistent setting option | +| `A` | **SETTING_SET** | setting_id | new_value | --- | --- | Set persistent setting option | +| `t` | **TIME_GET** | --- | --- | --- | time | Get current RTC value | +| `T` | **TIME_SET** | time_0 | time_1 | --- | --- | Set new RTC value | +| `m` | **MEMORY_READ** | address | length | --- | data | Read data from 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!) | +| `D` | **DD_SET_BLOCK_READY** | success | --- | --- | --- | Notify flashcart about 64DD block readiness | +| `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 | +| `F` | **FIRMWARE_UPDATE** | address | length | --- | status | Update firmware from specified memory address | +| `?` | **DEBUG_GET** | --- | --- | --- | debug_data | Get internal FPGA debug info | +| `%` | **STACK_USAGE_GET** | --- | --- | --- | stack_usage | Get per task stack usage | diff --git a/docs/03_n64_commands.md b/docs/03_n64_commands.md index 5417ba8..c12d6bc 100644 --- a/docs/03_n64_commands.md +++ b/docs/03_n64_commands.md @@ -6,14 +6,14 @@ | id | name | arg0 | arg1 | rsp0 | rsp1 | description | | --- | --------------------- | ---------- | ------------ | ---------------- | -------------- | -------------------------------------------------- | -| `v` | **HW_VERSION_GET** | --- | --- | hw_version | --- | Get HW version | -| `V` | **API_VERSION_GET** | --- | --- | api_version | --- | Get N64 command API version | +| `v` | **IDENTIFIER_GET** | --- | --- | identifier | --- | Get flashcart identifier `SCv2` | +| `V` | **VERSION_GET** | --- | --- | version | --- | Get flashcart firmware version | | `c` | **CONFIG_GET** | config_id | --- | --- | current_value | Get config option | | `C` | **CONFIG_SET** | config_id | new_value | --- | previous_value | Set config option and get previous value | | `c` | **SETTING_GET** | setting_id | --- | --- | current_value | Get persistent setting option | | `C` | **SETTING_SET** | setting_id | new_value | --- | --- | Set persistent setting option | | `t` | **TIME_GET** | --- | --- | time_0 | time_1 | Get current RTC value | -| `T` | **TIME_SET** | time_0 | time_1 | --- | --- | Set RTC value | +| `T` | **TIME_SET** | time_0 | time_1 | --- | --- | Set new RTC value | | `m` | **USB_READ** | pi_address | length | --- | --- | Receive data from USB to flashcart | | `M` | **USB_WRITE** | pi_address | length/type | --- | --- | Send data from from flashcart to USB | | `u` | **USB_READ_STATUS** | --- | --- | read_status/type | length | Get USB read status and type/length | @@ -27,4 +27,3 @@ | `K` | **FLASH_PROGRAM** | pi_address | length | --- | --- | Program flash with bytes loaded into data buffer | | `p` | **FLASH_WAIT_BUSY** | wait | --- | erase_block_size | --- | Wait until flash ready / get block erase size | | `P` | **FLASH_ERASE_BLOCK** | pi_address | --- | --- | --- | Start flash block erase | -| `?` | **DEBUG_GET** | --- | --- | debug_data_0 | debug_data_1 | Get internal FPGA debug info | diff --git a/fw/rtl/mcu/mcu_top.sv b/fw/rtl/mcu/mcu_top.sv index ddd1e7a..3b80958 100644 --- a/fw/rtl/mcu/mcu_top.sv +++ b/fw/rtl/mcu/mcu_top.sv @@ -332,7 +332,7 @@ module mcu_top ( REG_CFG_DATA_0, REG_CFG_DATA_1, REG_CFG_CMD, - REG_CFG_VERSION, + REG_CFG_IDENTIFIER, REG_FLASHRAM_SCR, REG_FLASH_SCR, REG_RTC_SCR, @@ -363,7 +363,7 @@ module mcu_top ( logic bootloader_skip; - assign n64_scb.cfg_version = 32'h53437632; + assign n64_scb.cfg_identifier = 32'h53437632; logic dd_bm_ack; @@ -460,8 +460,8 @@ module mcu_top ( }; end - REG_CFG_VERSION: begin - reg_rdata <= n64_scb.cfg_version; + REG_CFG_IDENTIFIER: begin + reg_rdata <= n64_scb.cfg_identifier; end REG_FLASHRAM_SCR: begin diff --git a/fw/rtl/n64/n64_cfg.sv b/fw/rtl/n64/n64_cfg.sv index e5b4212..0301250 100644 --- a/fw/rtl/n64/n64_cfg.sv +++ b/fw/rtl/n64/n64_cfg.sv @@ -16,8 +16,8 @@ module n64_cfg ( REG_DATA_0_L, REG_DATA_1_H, REG_DATA_1_L, - REG_VERSION_H, - REG_VERSION_L, + REG_IDENTIFIER_H, + REG_IDENTIFIER_L, REG_KEY_H, REG_KEY_L } e_reg; @@ -39,8 +39,8 @@ module n64_cfg ( REG_DATA_0_L: reg_bus.rdata = n64_scb.cfg_wdata[0][15:0]; REG_DATA_1_H: reg_bus.rdata = n64_scb.cfg_wdata[1][31:16]; REG_DATA_1_L: reg_bus.rdata = n64_scb.cfg_wdata[1][15:0]; - REG_VERSION_H: reg_bus.rdata = n64_scb.cfg_version[31:16]; - REG_VERSION_L: reg_bus.rdata = n64_scb.cfg_version[15:0]; + REG_IDENTIFIER_H: reg_bus.rdata = n64_scb.cfg_identifier[31:16]; + REG_IDENTIFIER_L: reg_bus.rdata = n64_scb.cfg_identifier[15:0]; REG_KEY_H: reg_bus.rdata = 16'd0; REG_KEY_L: reg_bus.rdata = 16'd0; endcase @@ -83,7 +83,7 @@ module n64_cfg ( REG_DATA_0_L: n64_scb.cfg_rdata[0][15:0] <= reg_bus.wdata; REG_DATA_1_H: n64_scb.cfg_rdata[1][31:16] <= reg_bus.wdata; REG_DATA_1_L: n64_scb.cfg_rdata[1][15:0] <= reg_bus.wdata; - REG_VERSION_H: irq <= 1'b0; + REG_IDENTIFIER_H: irq <= 1'b0; REG_KEY_H, REG_KEY_L: begin lock_sequence_counter <= lock_sequence_counter + 1'd1; if (reg_bus.wdata != 16'hFFFF) begin diff --git a/fw/rtl/n64/n64_scb.sv b/fw/rtl/n64/n64_scb.sv index 4e3c00d..106b70a 100644 --- a/fw/rtl/n64/n64_scb.sv +++ b/fw/rtl/n64/n64_scb.sv @@ -51,7 +51,7 @@ interface n64_scb (); logic [7:0] cfg_cmd; logic [31:0] cfg_rdata [0:1]; logic [31:0] cfg_wdata [0:1]; - logic [31:0] cfg_version; + logic [31:0] cfg_identifier; logic [15:0] save_count; @@ -94,7 +94,7 @@ interface n64_scb (); input cfg_cmd, input cfg_rdata, output cfg_wdata, - output cfg_version, + output cfg_identifier, input save_count, @@ -194,7 +194,7 @@ interface n64_scb (); output cfg_cmd, output cfg_rdata, input cfg_wdata, - input cfg_version + input cfg_identifier ); modport save_counter ( diff --git a/sw/bootloader/src/sc64.c b/sw/bootloader/src/sc64.c index 16104b1..c36fab3 100644 --- a/sw/bootloader/src/sc64.c +++ b/sw/bootloader/src/sc64.c @@ -5,7 +5,7 @@ typedef struct { io32_t SR_CMD; io32_t DATA[2]; - io32_t VERSION; + io32_t IDENTIFIER; io32_t KEY; } sc64_regs_t; @@ -16,7 +16,7 @@ typedef struct { #define SC64_SR_CMD_ERROR (1 << 30) #define SC64_SR_CPU_BUSY (1 << 31) -#define SC64_VERSION_2 (0x53437632) +#define SC64_V2_IDENTIFIER (0x53437632) #define SC64_KEY_RESET (0x00000000UL) #define SC64_KEY_UNLOCK_1 (0x5F554E4CUL) @@ -24,8 +24,8 @@ typedef struct { #define SC64_KEY_LOCK (0xFFFFFFFFUL) typedef enum { - SC64_CMD_HW_VERSION_GET = 'v', - SC64_CMD_API_VERSION_GET = 'V', + SC64_CMD_IDENTIFIER_GET = 'v', + SC64_CMD_VERSION_GET = 'V', SC64_CMD_CONFIG_GET = 'c', SC64_CMD_CONFIG_SET = 'C', SC64_CMD_SETTING_GET = 'a', @@ -45,7 +45,6 @@ typedef enum { SC64_CMD_FLASH_PROGRAM = 'K', SC64_CMD_FLASH_WAIT_BUSY = 'p', SC64_CMD_FLASH_ERASE_BLOCK = 'P', - SC64_CMD_DEBUG_GET = '?', } cmd_id_t; typedef enum { @@ -98,8 +97,8 @@ void sc64_lock (void) { } bool sc64_check_presence (void) { - uint32_t version = pi_io_read(&SC64_REGS->VERSION); - if (version == SC64_VERSION_2) { + uint32_t identifier = pi_io_read(&SC64_REGS->IDENTIFIER); + if (identifier == SC64_V2_IDENTIFIER) { sc64_wait_cpu_busy(); return true; } @@ -114,7 +113,7 @@ bool sc64_irq_pending (void) { } void sc64_irq_clear (void) { - pi_io_write(&SC64_REGS->VERSION, 0); + pi_io_write(&SC64_REGS->IDENTIFIER, 0); } uint32_t sc64_get_config (sc64_cfg_id_t id) { diff --git a/sw/controller/src/cfg.c b/sw/controller/src/cfg.c index b6a6729..1e1c8e8 100644 --- a/sw/controller/src/cfg.c +++ b/sw/controller/src/cfg.c @@ -221,8 +221,8 @@ static bool cfg_set_save_type (save_type_t save_type) { } -uint32_t cfg_get_version (void) { - return fpga_reg_get(REG_CFG_VERSION); +uint32_t cfg_get_identifier (void) { + return fpga_reg_get(REG_CFG_IDENTIFIER); } bool cfg_query (uint32_t *args) { @@ -468,11 +468,11 @@ void cfg_process (void) { switch (cmd) { case 'v': - args[0] = cfg_get_version(); + args[0] = cfg_get_identifier(); break; case 'V': - args[0] = version_api(API_N64); + args[0] = version_firmware(); break; case 'c': @@ -666,11 +666,6 @@ void cfg_process (void) { } break; - case '?': - args[0] = fpga_reg_get(REG_DEBUG_0); - args[1] = fpga_reg_get(REG_DEBUG_1); - break; - default: cfg_set_error(CFG_ERROR_UNKNOWN_CMD); return; diff --git a/sw/controller/src/cfg.h b/sw/controller/src/cfg.h index ef9efac..cebc36f 100644 --- a/sw/controller/src/cfg.h +++ b/sw/controller/src/cfg.h @@ -16,7 +16,7 @@ typedef enum { } save_type_t; -uint32_t cfg_get_version (void); +uint32_t cfg_get_identifier (void); bool cfg_query (uint32_t *args); bool cfg_update (uint32_t *args); bool cfg_query_setting (uint32_t *args); diff --git a/sw/controller/src/fpga.h b/sw/controller/src/fpga.h index 37d22f3..84521a5 100644 --- a/sw/controller/src/fpga.h +++ b/sw/controller/src/fpga.h @@ -28,7 +28,7 @@ typedef enum { REG_CFG_DATA_0, REG_CFG_DATA_1, REG_CFG_CMD, - REG_CFG_VERSION, + REG_CFG_IDENTIFIER, REG_FLASHRAM_SCR, REG_FLASH_SCR, REG_RTC_SCR, diff --git a/sw/controller/src/usb.c b/sw/controller/src/usb.c index f10385a..a694694 100644 --- a/sw/controller/src/usb.c +++ b/sw/controller/src/usb.c @@ -160,14 +160,14 @@ static void usb_rx_process (void) { p.rx_state = RX_STATE_IDLE; p.response_pending = true; p.response_info.data_length = 4; - p.response_info.data[0] = cfg_get_version(); + p.response_info.data[0] = cfg_get_identifier(); break; case 'V': p.rx_state = RX_STATE_IDLE; p.response_pending = true; p.response_info.data_length = 4; - p.response_info.data[0] = version_api(API_USB); + p.response_info.data[0] = version_firmware(); break; case 'R': @@ -247,12 +247,6 @@ static void usb_rx_process (void) { } break; - case 'D': - dd_set_block_ready(p.rx_args[0] == 0); - p.rx_state = RX_STATE_IDLE; - p.response_pending = true; - break; - case 'U': if ((p.read_length > 0) && usb_dma_ready()) { uint32_t length = (p.read_length > p.rx_args[1]) ? p.rx_args[1] : p.read_length; @@ -274,6 +268,28 @@ static void usb_rx_process (void) { } break; + case 'D': + dd_set_block_ready(p.rx_args[0] == 0); + p.rx_state = RX_STATE_IDLE; + p.response_pending = true; + break; + + case 'p': + if (p.rx_args[0]) { + flash_wait_busy(); + } + p.rx_state = RX_STATE_IDLE; + p.response_pending = true; + p.response_info.data_length = 4; + p.response_info.data[0] = FLASH_ERASE_BLOCK_SIZE; + break; + + case 'P': + p.response_error = flash_erase_block(p.rx_args[0]); + p.rx_state = RX_STATE_IDLE; + p.response_pending = true; + break; + case 'f': { bool rom_write_enable_restore = cfg_set_rom_write_enable(false); p.response_info.data[0] = update_backup(p.rx_args[0], &p.response_info.data[1]); @@ -300,22 +316,6 @@ static void usb_rx_process (void) { break; } - case 'p': - if (p.rx_args[0]) { - flash_wait_busy(); - } - p.rx_state = RX_STATE_IDLE; - p.response_pending = true; - p.response_info.data_length = 4; - p.response_info.data[0] = FLASH_ERASE_BLOCK_SIZE; - break; - - case 'P': - p.response_error = flash_erase_block(p.rx_args[0]); - p.rx_state = RX_STATE_IDLE; - p.response_pending = true; - break; - case '?': p.rx_state = RX_STATE_IDLE; p.response_pending = true; diff --git a/sw/controller/src/version.c b/sw/controller/src/version.c index 3186464..fb992ad 100644 --- a/sw/controller/src/version.c +++ b/sw/controller/src/version.c @@ -1,17 +1,10 @@ #include "version.h" -#define VERSION_API_USB (2) -#define VERSION_API_N64 (2) +#define VERSION_MAJOR (2) +#define VERSION_MINOR (12) -uint32_t version_api (version_api_type_t type) { - switch (type) { - case API_USB: - return VERSION_API_USB; - case API_N64: - return VERSION_API_N64; - default: - return 0; - } +uint32_t version_firmware (void) { + return ((VERSION_MAJOR << 16) | (VERSION_MINOR)); } diff --git a/sw/controller/src/version.h b/sw/controller/src/version.h index be0581d..b5a3c0a 100644 --- a/sw/controller/src/version.h +++ b/sw/controller/src/version.h @@ -5,13 +5,7 @@ #include -typedef enum { - API_USB, - API_N64, -} version_api_type_t; - - -uint32_t version_api (version_api_type_t type); +uint32_t version_firmware (void); #endif diff --git a/sw/pc/sc64.py b/sw/pc/sc64.py index e3eab5f..e15d2f8 100755 --- a/sw/pc/sc64.py +++ b/sw/pc/sc64.py @@ -200,6 +200,7 @@ class SC64: FLASH = 0x0400_0000 BUFFER = 0x0500_0000 EEPROM = 0x0500_2000 + END = 0x0500_297F FIRMWARE = 0x0200_0000 DDIPL = 0x03BC_0000 SAVE = 0x03FE_0000 @@ -321,7 +322,8 @@ class SC64: SCREENSHOT = 4 GDB = 0xDB - __MIN_SUPPORTED_API_VERSION = 2 + __SUPPORTED_MAJOR_VERSION = 2 + __SUPPORTED_MINOR_VERSION = 12 __isv_line_buffer: bytes = b'' __debug_header: Optional[bytes] = None @@ -329,21 +331,24 @@ class SC64: def __init__(self) -> None: self.__link = SC64Serial() - version = self.__link.execute_cmd(cmd=b'v') - if (version != b'SCv2'): - raise ConnectionException('Unknown SC64 HW version') + identifier = self.__link.execute_cmd(cmd=b'v') + if (identifier != b'SCv2'): + raise ConnectionException('Unknown SC64 v2 identifier') def __get_int(self, data: bytes) -> int: return int.from_bytes(data[:4], byteorder='big') - def check_api_version(self) -> None: + def check_firmware_version(self) -> None: try: - data = self.__link.execute_cmd(cmd=b'V') + version = self.__link.execute_cmd(cmd=b'V') + major = self.__get_int(version[0:2]) + minor = self.__get_int(version[2:4]) + if (major != self.__SUPPORTED_MAJOR_VERSION): + raise ConnectionException() + if (minor < self.__SUPPORTED_MINOR_VERSION): + raise ConnectionException() except ConnectionException: - raise ConnectionException('Outdated SC64 API, please update firmware') - version = self.__get_int(data) - if (version < self.__MIN_SUPPORTED_API_VERSION): - raise ConnectionException('Unsupported SC64 API version, please update firmware') + raise ConnectionException(f'Unsupported SC64 version ({major}.{minor}), please update firmware') def __set_config(self, config: __CfgId, value: int) -> None: try: @@ -461,8 +466,10 @@ class SC64: raise ValueError('Debug data size too big') self.__link.execute_cmd(cmd=b'U', args=[datatype, len(data)], data=data, response=False) - def download_memory(self) -> bytes: - return self.__read_memory(self.__Address.MEMORY, self.__Length.MEMORY) + def download_memory(self, address: int, length: int) -> bytes: + if ((address < 0) or (length < 0) or ((address + length) > self.__Address.END)): + raise ValueError('Invalid address or length') + return self.__read_memory(address, length) def upload_rom(self, data: bytes, use_shadow: bool=True) -> None: rom_length = len(data) @@ -582,7 +589,7 @@ class SC64: raise ConnectionException('Error while getting firmware backup') return self.__read_memory(address, length) - def set_cic_parameters(self, seed: Optional[int]=None, disabled=False) -> tuple[int, int, bool]: + def update_cic_parameters(self, seed: Optional[int]=None, disabled=False) -> tuple[int, int, bool]: if ((seed != None) and (seed < 0 or seed > 0xFF)): raise ValueError('CIC seed outside of allowed values') boot_mode = self.__get_config(self.__CfgId.BOOT_MODE) @@ -884,7 +891,7 @@ class SC64: current_image = 0 next_image = 0 - self.__set_config(self.__CfgId.ROM_WRITE_ENABLE, isv) + self.__set_config(self.__CfgId.ROM_WRITE_ENABLE, 1 if (isv != 0) else 0) self.__set_config(self.__CfgId.ISV_ADDRESS, isv) if (isv != 0): print(f'IS-Viewer64 support set to [ENABLED] at ROM offset [0x{(isv):08X}]') @@ -992,16 +999,24 @@ if __name__ == '__main__': disabled = bool(int(params[1])) if len(params) >= 2 else None return (seed, disabled) + def download_memory_type(argument: str): + params = argument.split(',') + if (len(params) < 2 or len(params) > 3): + raise argparse.ArgumentError() + address = int(params[0], 0) + length = int(params[1], 0) + file = params[2] if len(params) >= 3 else 'sc64dump.bin' + return (address, length, file) + parser = argparse.ArgumentParser(description='SC64 control software') parser.add_argument('--backup-firmware', metavar='file', help='backup SC64 firmware and write it to specified file') parser.add_argument('--update-firmware', metavar='file', help='update SC64 firmware from specified file') - parser.add_argument('--update-bootloader', metavar='file', help='update SC64 bootloader (not recommended, use --update-firmware instead)') parser.add_argument('--reset-state', action='store_true', help='reset SC64 internal state') parser.add_argument('--print-state', action='store_true', help='print SC64 internal state') - parser.add_argument('--led-enabled', metavar='{true,false}', help='disable or enable LED I/O activity blinking') + parser.add_argument('--led-blink', metavar='{yes,no}', help='disable or enable LED I/O activity blinking') parser.add_argument('--boot', type=SC64.BootMode, action=EnumAction, help='set boot mode') - 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('--tv', type=SC64.TVType, action=EnumAction, help='force TV type to set value, not used when direct boot mode is enabled') + parser.add_argument('--cic', type=SC64.CICSeed, action=EnumAction, help='force CIC seed to set value, not used when direct boot mode is enabled') parser.add_argument('--cic-params', metavar='seed,[disabled]', type=cic_params_type, help='set CIC emulation parameters') parser.add_argument('--rtc', action='store_true', help='update clock in SC64 to system time') 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)') @@ -1014,7 +1029,7 @@ if __name__ == '__main__': parser.add_argument('--isv', type=lambda x: int(x, 0), default=0, help='enable IS-Viewer64 support at provided ROM offset') parser.add_argument('--gdb', metavar='port', type=int, help='expose socket port for GDB debugging') parser.add_argument('--debug', action='store_true', help='run debug loop') - parser.add_argument('--download-memory', metavar='file', help='download whole memory space and write it to specified file') + parser.add_argument('--download-memory', metavar='address,length,[file]', type=download_memory_type, help='download specified memory region and write it to file') if (len(sys.argv) <= 1): parser.print_help() @@ -1039,13 +1054,7 @@ if __name__ == '__main__': sc64.update_firmware(f.read(), status_callback) print('done') - sc64.check_api_version() - - if (args.update_bootloader): - with open(args.update_bootloader, 'rb') as f: - print('Uploading Bootloader... ', end='', flush=True) - sc64.upload_bootloader(f.read()) - print('done') + sc64.check_firmware_version() if (args.reset_state): sc64.reset_state() @@ -1059,10 +1068,10 @@ if __name__ == '__main__': value = getattr(value, 'name') print(f' {key}: {value}') - if (args.led_enabled != None): - value = (args.led_enabled == 'true') - sc64.set_led_enable(value) - print(f'LED blinking set to [{"ENABLED" if value else "DISABLED"}]') + if (args.led_blink): + blink = (args.led_blink == 'yes') + sc64.set_led_enable(blink) + print(f'LED blinking set to [{"ENABLED" if blink else "DISABLED"}]') if (args.tv != None): sc64.set_tv_type(args.tv) @@ -1085,6 +1094,12 @@ if __name__ == '__main__': autodetected_save_type = sc64.autodetect_save_type(rom_data) print('done') + if (args.ddipl): + with open(args.ddipl, 'rb') as f: + print('Uploading 64DD IPL... ', end='', flush=True) + sc64.upload_ddipl(f.read()) + print('done') + if (args.save_type != None or autodetected_save_type != None): save_type = args.save_type if args.save_type != None else autodetected_save_type sc64.set_save_type(save_type) @@ -1096,26 +1111,20 @@ if __name__ == '__main__': sc64.upload_save(f.read()) print('done') - if (args.ddipl): - with open(args.ddipl, 'rb') as f: - print('Uploading 64DD IPL... ', end='', flush=True) - sc64.upload_ddipl(f.read()) - print('done') - if (args.boot != None): sc64.set_boot_mode(args.boot) print(f'Boot mode set to [{args.boot.name}]') if (args.cic_params != None): (seed, disabled) = args.cic_params - (seed, checksum, dd_mode) = sc64.set_cic_parameters(seed, disabled) + (seed, checksum, dd_mode) = sc64.update_cic_parameters(seed, disabled) print('CIC parameters set to [', end='') print(f'{"DISABLED" if disabled else "ENABLED"}, ', end='') print(f'{"DDIPL" if dd_mode else "ROM"}, ', end='') print(f'seed: 0x{seed:02X}, checksum: 0x{checksum:012X}', end='') print(']') else: - sc64.set_cic_parameters() + sc64.update_cic_parameters() if (args.debug or args.isv or args.disk or args.gdb): sc64.debug_loop(isv=args.isv, disks=args.disk, gdb_port=args.gdb) @@ -1126,10 +1135,11 @@ if __name__ == '__main__': f.write(sc64.download_save()) print('done') - if (args.download_memory): - with open(args.download_memory, 'wb') as f: + if (args.download_memory != None): + (address, length, file) = args.download_memory + with open(file, 'wb') as f: print('Downloading memory... ', end='', flush=True) - f.write(sc64.download_memory()) + f.write(sc64.download_memory(address, length)) print('done') except ValueError as e: print(f'\nValue error: {e}')