diff --git a/docs/00_quick_startup_guide.md b/docs/00_quick_startup_guide.md index 3b0f40a..7bcb35f 100644 --- a/docs/00_quick_startup_guide.md +++ b/docs/00_quick_startup_guide.md @@ -1,11 +1,11 @@ - [First time setup](#first-time-setup) +- [Firmware backup/update](#firmware-backupupdate) - [Internal flashcart state](#internal-flashcart-state) - [Uploading game/save](#uploading-gamesave) - [Downloading save](#downloading-save) - [Running 64DD games](#running-64dd-games) - [Direct boot option](#direct-boot-option) - [Debug terminal](#debug-terminal) -- [Firmware backup/update](#firmware-backupupdate) --- @@ -20,6 +20,16 @@ --- +## Firmware backup/update + +Keeping SC64 firmware up to date is highly recommended. `sc64.py` script is tightly coupled with specific firmware versions and will error out when it detects unsupported firmware version. + +To download and backup current version of SC64 firmware run `python3 sc64.py --backup-firmware sc64_backup_package.bin` + +To update SC64 firmware run `python3 sc64.py --update-firmware sc64_update_package.bin` + +--- + ## Internal flashcart state SC64 holds some configuration after script has exit. To reset it simply run: `python3 sc64.py --reset-state`. Internal flashcart state can be checked by running: `python3 sc64.py --print-state` @@ -31,7 +41,7 @@ SC64 holds some configuration after script has exit. To reset it simply run: `py `python3 sc64.py --boot rom --rom path_to_rom.n64 --save-type eeprom-4k --save path_to_save.sav` Replace `path_to_rom.n64` / `eeprom-4k` / `path_to_save.sav` with appropriate values for desired game. Check included help in script to check available save types. -Arguments `--save-type` and/or `--save` can be ommited if game doesn't require any save. +Arguments `--save-type` and/or `--save` can be omitted if game doesn't require any save. --- @@ -45,7 +55,7 @@ Replace `path_to_save.sav` with appropriate value. Specifying save type isn't re ## Running 64DD games -64DD games require DDIPL ROM and disk images. To run disk game type `python3 sc64.py --boot ddipl --ddipl path_to_ddipl.n64 --disk path_to_disk_1.ndd --disk path_to_disk_2.ndd --debug`. +64DD games require DDIPL ROM and disk images. To run disk game type `python3 sc64.py --boot ddipl --ddipl path_to_ddipl.n64 --disk path_to_disk_1.ndd --disk path_to_disk_2.ndd`. Replace `path_to_ddipl.n64` / `path_to_disk_x.ndd` with appropriate values. Argument `--disk` can be specified multiple times. Only `.ndd` disk format is supported currently. To change inserted disk press button on the back of SC64 flashcart. @@ -53,19 +63,11 @@ Replace `path_to_ddipl.n64` / `path_to_disk_x.ndd` with appropriate values. Argu ## Direct boot option -If booting game through included bootloader isn't a desired option then flashcart can be put in special mode that ommits this step. -Run `python3 sc64.py --boot direct --rom path_to_rom.n64` to disable bootloader during boot and console reset. By default only games with 6102/7101 CIC chips will boot correctly. To change this use `--cic-params 0,0,0x3F,0x12345678ABCD` argument with appropriate values. Refer to included help in script for values meaning. This option is useful only for very specific cases (e.g. running SC64 on top of GameShark). +If booting game through included bootloader isn't a desired option then flashcart can be put in special mode that omits this step. +Run `python3 sc64.py --boot direct-rom --rom path_to_rom.n64` to disable bootloader during boot and console reset. By default `sc64.py` script will try to guess CIC seed and calculate checksum. To change seed or disable CIC use `--cic-params 0x3F,0` argument with appropriate values. Refer to included help in script for values meaning. This option is useful only for very specific cases (e.g. testing custom IPL3 or running SC64 on top of GameShark). --- ## Debug terminal -`sc64.py` supports UNFLoader protocol and has same functionality implemented as desktop program. Use argument `--debug` to activate it. - ---- - -## Firmware backup/update - -To download and backup current version of SC64 firmware run `python3 sc64.py --backup-firmware sc64_firmware_backup.bin`. - -To update SC64 firmware run `python3 sc64.py --update-firmware sc64_firmware.bin` +`sc64.py` supports UNFLoader protocol and has same functionality implemented as aforementioned program. Use argument `--debug` to activate it. diff --git a/docs/04_config_options.md b/docs/04_config_options.md index 301e51c..c9739ab 100644 --- a/docs/04_config_options.md +++ b/docs/04_config_options.md @@ -113,10 +113,12 @@ type: *enum* | default: `0` - `0` - Load menu from SD card - `1` - Attempt to boot application loaded in ROM section - `2` - Attempt to boot 64DD IPL -- `3` - Direct mode, skips bootloader entirely +- `3` - Direct ROM mode, skips bootloader entirely +- `4` - Direct 64DD IPL mode, skips bootloader entirely Use this setting to change boot behavior. -Value `3` (direct boot) will always keep **BOOTLOADER_SWITCH** option disabled. +Value `3` or `4` (direct boot) will always keep **BOOTLOADER_SWITCH** option disabled. +Value `4` will set CIC emulation to 64DD mode --- @@ -145,7 +147,7 @@ type: *word* | default: `0xFFFF` Use this setting to force specific CIC seed. By setting value `0xFFFF` bootloader will try to guess needed values from loaded ROM IPL3. -This setting is not used when **BOOT_MODE** is set to `3` (direct boot). +This setting is not used when **BOOT_MODE** is set to `3` or `4` (direct boot). --- @@ -160,7 +162,7 @@ type: *enum* | default: `3` Use this setting to force specific TV type. By setting value `3` bootloader will try to guess TV type from loaded ROM header. -This setting is not used when **BOOT_MODE** is set to `3` (direct boot). +This setting is not used when **BOOT_MODE** is set to `3` or `4` (direct boot). --- @@ -238,7 +240,7 @@ Use this setting to enable PI access for extended ROM data located inside flash ## Supported persistent setting options -These options are similar to config options but state is persisted through power cycles. +These options are similar to config options but state is persisted through power cycles. Setting are kept in RTC backup memory and require battery to be installed for correct operation. | id | name | type | description | | --- | -------------- | ------ | --------------------------------------------- | diff --git a/sw/bootloader/src/boot.c b/sw/bootloader/src/boot.c index 6cc5214..5e29156 100644 --- a/sw/bootloader/src/boot.c +++ b/sw/bootloader/src/boot.c @@ -1,6 +1,7 @@ #include "boot.h" #include "crc32.h" #include "io.h" +#include "vr4300.h" extern uint32_t ipl2 __attribute__((section(".data"))); @@ -15,7 +16,7 @@ static const ipl3_crc32_t ipl3_crc32[] = { { .crc32 = 0x587BD543, .seed = 0xAC }, // 5101 { .crc32 = 0x6170A4A1, .seed = 0x3F }, // 6101 { .crc32 = 0x009E9EA3, .seed = 0x3F }, // 7102 - { .crc32 = 0x90BB6CB5, .seed = 0x3F }, // x102 + { .crc32 = 0x90BB6CB5, .seed = 0x3F }, // 6102/7101 { .crc32 = 0x0B050EE0, .seed = 0x78 }, // x103 { .crc32 = 0x98BC2C86, .seed = 0x91 }, // x105 { .crc32 = 0xACC8580A, .seed = 0x85 }, // x106 @@ -83,14 +84,24 @@ static bool boot_get_cic_seed (boot_info_t *info) { } void boot (boot_info_t *info, bool detect_tv_type, bool detect_cic_seed) { - if (detect_tv_type && !boot_get_tv_type(info)) { - info->tv_type = OS_INFO->tv_type; + if (detect_tv_type) { + if (!boot_get_tv_type(info)) { + info->tv_type = OS_INFO->tv_type; + } } - if (detect_cic_seed && !boot_get_cic_seed(info)) { - info->cic_seed = 0x3F; + if (detect_cic_seed) { + if (!boot_get_cic_seed(info)) { + info->cic_seed = 0x3F; + } } + asm volatile ( + "li $t1, %[status] \n" + "mtc0 $t1, $12 \n" :: + [status] "i" (C0_SR_CU1 | C0_SR_CU0 | C0_SR_FR) + ); + OS_INFO->mem_size_6105 = OS_INFO->mem_size; while (!(cpu_io_read(&SP->SR) & SP_SR_HALT)); @@ -106,8 +117,21 @@ void boot (boot_info_t *info, bool detect_tv_type, bool detect_cic_seed) { cpu_io_write(&AI->MADDR, 0); cpu_io_write(&AI->LEN, 0); - io32_t *base = boot_get_device_base(info); + while (cpu_io_read(&SP->SR) & SP_SR_DMA_BUSY); + uint32_t *ipl2_src = &ipl2; + io32_t *ipl2_dst = SP_MEM->IMEM; + + for (int i = 0; i < 8; i++) { + cpu_io_write(&ipl2_dst[i], ipl2_src[i]); + } + + cpu_io_write(&PI->DOM[0].LAT, 0xFF); + cpu_io_write(&PI->DOM[0].PWD, 0xFF); + cpu_io_write(&PI->DOM[0].PGS, 0x0F); + cpu_io_write(&PI->DOM[0].RLS, 0x03); + + io32_t *base = boot_get_device_base(info); uint32_t pi_config = pi_io_read(base); cpu_io_write(&PI->DOM[0].LAT, pi_config & 0xFF); @@ -119,13 +143,6 @@ void boot (boot_info_t *info, bool detect_tv_type, bool detect_cic_seed) { while (cpu_io_read(&DPC->SR) & DPC_SR_PIPE_BUSY); } - uint32_t *ipl2_src = &ipl2; - io32_t *ipl2_dst = SP_MEM->IMEM; - - for (int i = 0; i < 8; i++) { - cpu_io_write(&ipl2_dst[i], ipl2_src[i]); - } - io32_t *ipl3_src = base; io32_t *ipl3_dst = SP_MEM->DMEM; @@ -146,7 +163,7 @@ void boot (boot_info_t *info, bool detect_tv_type, bool detect_cic_seed) { tv_type = (info->tv_type & 0x03); reset_type = (info->reset_type & 0x01); cic_seed = (info->cic_seed & 0xFF); - version = 1; + version = (info->tv_type == BOOT_TV_TYPE_PAL) ? 6 : 1; stack_pointer = (void *) UNCACHED(&SP_MEM->IMEM[1020]); asm volatile ( diff --git a/sw/bootloader/src/sc64.h b/sw/bootloader/src/sc64.h index 887ea83..ae47463 100644 --- a/sw/bootloader/src/sc64.h +++ b/sw/bootloader/src/sc64.h @@ -49,7 +49,8 @@ typedef enum { BOOT_MODE_MENU = 0, BOOT_MODE_ROM = 1, BOOT_MODE_DDIPL = 2, - BOOT_MODE_DIRECT = 3 + BOOT_MODE_DIRECT_ROM = 3, + BOOT_MODE_DIRECT_DDIPL = 4, } sc64_boot_mode_t; typedef enum { diff --git a/sw/controller/src/cfg.c b/sw/controller/src/cfg.c index b7e36b2..b6a6729 100644 --- a/sw/controller/src/cfg.c +++ b/sw/controller/src/cfg.c @@ -1,5 +1,6 @@ #include "button.h" #include "cfg.h" +#include "cic.h" #include "dd.h" #include "flash.h" #include "fpga.h" @@ -47,8 +48,9 @@ typedef enum { typedef enum { BOOT_MODE_MENU = 0, BOOT_MODE_ROM = 1, - BOOT_MODE_DD = 2, - BOOT_MODE_DIRECT = 3 + BOOT_MODE_DDIPL = 2, + BOOT_MODE_DIRECT_ROM = 3, + BOOT_MODE_DIRECT_DDIPL = 4, } boot_mode_t; typedef enum { @@ -315,11 +317,15 @@ bool cfg_update (uint32_t *args) { return isv_set_address(args[1]); break; case CFG_ID_BOOT_MODE: - if (args[1] > BOOT_MODE_DIRECT) { + if (args[1] > BOOT_MODE_DIRECT_DDIPL) { return true; } p.boot_mode = args[1]; - cfg_change_scr_bits(CFG_SCR_BOOTLOADER_SKIP, (args[1] == BOOT_MODE_DIRECT)); + cfg_change_scr_bits(CFG_SCR_BOOTLOADER_SKIP, + (args[1] == BOOT_MODE_DIRECT_ROM) || + (args[1] == BOOT_MODE_DIRECT_DDIPL) + ); + cic_set_dd_mode(args[1] == BOOT_MODE_DIRECT_DDIPL); break; case CFG_ID_SAVE_TYPE: return cfg_set_save_type((save_type_t) (args[1])); diff --git a/sw/controller/src/cic.c b/sw/controller/src/cic.c index 3c7b6ba..5961946 100644 --- a/sw/controller/src/cic.c +++ b/sw/controller/src/cic.c @@ -24,7 +24,6 @@ // SOFTWARE. -#include #include "cic.h" #include "hw.h" #include "led.h" @@ -336,8 +335,7 @@ void cic_reset_parameters (void) { } void cic_set_parameters (uint32_t *args) { - cic_disabled = (args[0] >> 24) & (1 << 1); - cic_dd_mode = (args[0] >> 24) & (1 << 0); + cic_disabled = (args[0] >> 24) & (1 << 0); cic_seed = (args[0] >> 16) & 0xFF; cic_checksum[0] = (args[0] >> 8) & 0xFF; cic_checksum[1] = args[0] & 0xFF; @@ -347,6 +345,10 @@ void cic_set_parameters (uint32_t *args) { cic_checksum[5] = args[1] & 0xFF; } +void cic_set_dd_mode (bool enabled) { + cic_dd_mode = enabled; +} + void cic_hw_init (void) { hw_gpio_irq_setup(GPIO_ID_N64_RESET, GPIO_IRQ_FALLING, cic_irq_reset_falling); hw_gpio_irq_setup(GPIO_ID_N64_RESET, GPIO_IRQ_RISING, cic_irq_reset_rising); diff --git a/sw/controller/src/cic.h b/sw/controller/src/cic.h index d5a70e8..98fe6ae 100644 --- a/sw/controller/src/cic.h +++ b/sw/controller/src/cic.h @@ -2,11 +2,13 @@ #define CIC_H__ +#include #include void cic_reset_parameters (void); void cic_set_parameters (uint32_t *args); +void cic_set_dd_mode (bool enabled); void cic_hw_init (void); void cic_task (void); diff --git a/sw/controller/src/version.c b/sw/controller/src/version.c index a3a6667..3186464 100644 --- a/sw/controller/src/version.c +++ b/sw/controller/src/version.c @@ -1,8 +1,8 @@ #include "version.h" -#define VERSION_API_USB (1) -#define VERSION_API_N64 (1) +#define VERSION_API_USB (2) +#define VERSION_API_N64 (2) uint32_t version_api (version_api_type_t type) { diff --git a/sw/pc/sc64.py b/sw/pc/sc64.py index 67f88c2..e3eab5f 100755 --- a/sw/pc/sc64.py +++ b/sw/pc/sc64.py @@ -7,6 +7,7 @@ import serial import socket import sys import time +from binascii import crc32 from datetime import datetime from dd64 import BadBlockError, DD64Image from enum import Enum, IntEnum @@ -286,7 +287,8 @@ class SC64: MENU = 0 ROM = 1 DDIPL = 2 - DIRECT = 3 + DIRECT_ROM = 3 + DIRECT_DDIPL = 4 class SaveType(IntEnum): NONE = 0 @@ -319,7 +321,7 @@ class SC64: SCREENSHOT = 4 GDB = 0xDB - __MIN_SUPPORTED_API_VERSION = 1 + __MIN_SUPPORTED_API_VERSION = 2 __isv_line_buffer: bytes = b'' __debug_header: Optional[bytes] = None @@ -546,19 +548,6 @@ class SC64: def set_save_type(self, type: SaveType) -> None: self.__set_config(self.__CfgId.SAVE_TYPE, type) - def set_cic_parameters(self, disabled=False, dd_mode: bool=False, seed: Optional[int]=None, checksum: Optional[int]=None) -> None: - # TODO: guess seed and calculate checksum if not provided - seed = seed if seed else 0x3F - checksum = checksum if checksum else 0xA536C0F1D859 - if (seed < 0 or seed > 0xFF): - raise ValueError('CIC seed outside of allowed values') - mode = (1 << 1) if disabled else 0 - mode |= (1 << 0) if dd_mode else 0 - data = bytes([mode, seed]) - data = [*data, *checksum.to_bytes(6, byteorder='big')] - self.__link.execute_cmd(cmd=b'B', args=[self.__get_int(data[0:4]), self.__get_int(data[4:8])]) - return (seed, checksum) - def set_led_enable(self, enabled: bool) -> None: self.__set_setting(self.__SettingId.LED_ENABLE, enabled) @@ -593,6 +582,168 @@ 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]: + 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) + address = self.__Address.BOOTLOADER + if (boot_mode == self.BootMode.DIRECT_ROM): + address = self.__Address.SDRAM + elif (boot_mode == self.BootMode.DIRECT_DDIPL): + address = self.__Address.DDIPL + ipl3 = self.__read_memory(address, 4096)[0x40:0x1000] + seed = seed if (seed != None) else self.__guess_ipl3_seed(ipl3) + checksum = self.__calculate_ipl3_checksum(ipl3, seed) + data = [(1 << 0) if disabled else 0, seed, *checksum.to_bytes(6, byteorder='big')] + self.__link.execute_cmd(cmd=b'B', args=[self.__get_int(data[0:4]), self.__get_int(data[4:8])]) + return (seed, checksum, boot_mode == self.BootMode.DIRECT_DDIPL) + + def __guess_ipl3_seed(self, ipl3: bytes) -> int: + checksum = crc32(ipl3) + seed_mapping = { + 0x587BD543: 0xAC, # 5101 + 0x6170A4A1: 0x3F, # 6101 + 0x009E9EA3: 0x3F, # 7102 + 0x90BB6CB5: 0x3F, # 6102/7101 + 0x0B050EE0: 0x78, # x103 + 0x98BC2C86: 0x91, # x105 + 0xACC8580A: 0x85, # x106 + 0x0E018159: 0xDD, # 5167 + 0x10C68B18: 0xDD, # NDXJ0 + 0xBC605D0A: 0xDD, # NDDJ0 + 0x502C4466: 0xDD, # NDDJ1 + 0x0C965795: 0xDD, # NDDJ2 + 0x8FEBA21E: 0xDE, # NDDE0 + } + return seed_mapping[checksum] if checksum in seed_mapping else 0x3F + + def __calculate_ipl3_checksum(self, ipl3: bytes, seed: int) -> int: + _CHECKSUM_MAGIC = 0x6C078965 + + _add = lambda a1, a2: ((a1 + a2) & 0xFFFFFFFF) + _sub = lambda a1, a2: ((a1 - a2) & 0xFFFFFFFF) + _mul = lambda a1, a2: ((a1 * a2) & 0xFFFFFFFF) + _xor = lambda a1, a2: ((a1 ^ a2) & 0xFFFFFFFF) + _lsh = lambda a, s: (((a & 0xFFFFFFFF) << s) & 0xFFFFFFFF) + _rsh = lambda a, s: (((a & 0xFFFFFFFF) >> s) & 0xFFFFFFFF) + + def _get(offset: int) -> int: + offset *= 4 + return int.from_bytes(ipl3[offset:(offset + 4)], byteorder='big') + + def _checksum(a0: int, a1: int, a2: int) -> int: + prod = (a0 * (a2 if (a1 == 0) else a1)) + hi = ((prod >> 32) & 0xFFFFFFFF) + lo = (prod & 0xFFFFFFFF) + diff = ((hi - lo) & 0xFFFFFFFF) + return ((a0 if (diff == 0) else diff) & 0xFFFFFFFF) + + if (seed < 0x00 or seed > 0xFF): + raise ValueError('Invalid seed') + + buffer = [_xor(_add(_mul(_CHECKSUM_MAGIC, seed), 1), _get(0))] * 16 + + for i in range(1, 1009): + data_prev = data_curr if (i > 1) else _get(0) + data_curr = _get(i - 1) + data_next = _get(i) + + buffer[0] = _add(buffer[0], _checksum(_sub(1007, i), data_curr, i)) + buffer[1] = _checksum(buffer[1], data_curr, i) + buffer[2] = _xor(buffer[2], data_curr) + buffer[3] = _add(buffer[3], _checksum(_add(data_curr, 5), _CHECKSUM_MAGIC, i)) + + shift = (data_prev & 0x1F) + data_left = _lsh(data_curr, (32 - shift)) + data_right = _rsh(data_curr, shift) + b4_shifted = (data_left | data_right) + buffer[4] = _add(buffer[4], b4_shifted) + + shift = _rsh(data_prev, 27) + data_left = _lsh(data_curr, shift) + data_right = _rsh(data_curr, (32 - shift)) + b5_shifted = (data_left | data_right) + buffer[5] = _add(buffer[5], b5_shifted) + + if (data_curr < buffer[6]): + buffer[6] = _xor(_add(buffer[3], buffer[6]), _add(data_curr, i)) + else: + buffer[6] = _xor(_add(buffer[4], data_curr), buffer[6]) + + shift = (data_prev & 0x1F) + data_left = _lsh(data_curr, shift) + data_right = _rsh(data_curr, (32 - shift)) + buffer[7] = _checksum(buffer[7], (data_left | data_right), i) + + shift = _rsh(data_prev, 27) + data_left = _lsh(data_curr, (32 - shift)) + data_right = _rsh(data_curr, shift) + buffer[8] = _checksum(buffer[8], (data_left | data_right), i) + + if (data_prev < data_curr): + buffer[9] = _checksum(buffer[9], data_curr, i) + else: + buffer[9] = _add(buffer[9], data_curr) + + if (i == 1008): + break + + buffer[10] = _checksum(_add(buffer[10], data_curr), data_next, i) + buffer[11] = _checksum(_xor(buffer[11], data_curr), data_next, i) + buffer[12] = _add(buffer[12], _xor(buffer[8], data_curr)) + + shift = (data_curr & 0x1F) + data_left = _lsh(data_curr, (32 - shift)) + data_right = _rsh(data_curr, shift) + tmp = (data_left | data_right) + shift = (data_next & 0x1F) + data_left = _lsh(data_next, (32 - shift)) + data_right = _rsh(data_next, shift) + buffer[13] = _add(buffer[13], _add(tmp, (data_left | data_right))) + + shift = (data_curr & 0x1F) + data_left = _lsh(data_next, (32 - shift)) + data_right = _rsh(data_next, shift) + sum = _checksum(buffer[14], b4_shifted, i) + buffer[14] = _checksum(sum, (data_left | data_right), i) + + shift = _rsh(data_curr, 27) + data_left = _lsh(data_next, shift) + data_right = _rsh(data_next, (32 - shift)) + sum = _checksum(buffer[15], b5_shifted, i) + buffer[15] = _checksum(sum, (data_left | data_right), i) + + final_buffer = [buffer[0]] * 4 + + for i in range(16): + data = buffer[i] + + shift = (data & 0x1F) + data_left = _lsh(data, (32 - shift)) + data_right = _rsh(data, shift) + b0_shifted = _add(final_buffer[0], (data_left | data_right)) + final_buffer[0] = b0_shifted + + if (data < b0_shifted): + final_buffer[1] = _add(final_buffer[1], data) + else: + final_buffer[1] = _checksum(final_buffer[1], data, i) + + if (_rsh((data & 0x02), 1) == (data & 0x01)): + final_buffer[2] = _add(final_buffer[2], data) + else: + final_buffer[2] = _checksum(final_buffer[2], data, i) + + if (data & 0x01): + final_buffer[3] = _xor(final_buffer[3], data) + else: + final_buffer[3] = _checksum(final_buffer[3], data, i) + + sum = _checksum(final_buffer[0], final_buffer[1], 16) + xor = _xor(final_buffer[3], final_buffer[2]) + + return ((sum << 32) | xor) & 0xFFFF_FFFFFFFF + def __generate_filename(self, prefix: str, extension: str) -> str: return f'{prefix}-{datetime.now().strftime("%y%m%d%H%M%S.%f")}.{extension}' @@ -835,11 +986,11 @@ class EnumAction(argparse.Action): if __name__ == '__main__': def cic_params_type(argument: str): params = argument.split(',') - disabled = bool(int(params[0])) - dd_mode = bool(int(params[1])) if len(params) >= 2 else None - seed = int(params[2], 0) if len(params) >= 3 else None - checksum = int(params[3], 0) if len(params) >= 4 else None - return (disabled, dd_mode, seed, checksum) + if (len(params) > 2): + raise argparse.ArgumentError() + seed = int(params[0], 0) if len(params) >= 1 else None + disabled = bool(int(params[1])) if len(params) >= 2 else None + return (seed, disabled) parser = argparse.ArgumentParser(description='SC64 control software') parser.add_argument('--backup-firmware', metavar='file', help='backup SC64 firmware and write it to specified file') @@ -851,7 +1002,7 @@ if __name__ == '__main__': 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('--cic-params', metavar='disabled,[dd_mode,[seed,[checksum]]]', type=cic_params_type, help='set CIC emulation parameters') + 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)') parser.add_argument('--rom', metavar='file', help='upload ROM from specified file') @@ -862,7 +1013,7 @@ if __name__ == '__main__': parser.add_argument('--disk', metavar='file', action='append', help='path to 64DD disk (.ndd format), can be specified multiple times') 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 (required for 64DD, IS-Viewer64 and GDB)') + 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') if (len(sys.argv) <= 1): @@ -913,10 +1064,6 @@ if __name__ == '__main__': sc64.set_led_enable(value) print(f'LED blinking set to [{"ENABLED" if value else "DISABLED"}]') - if (args.boot != None): - sc64.set_boot_mode(args.boot) - print(f'Boot mode set to [{args.boot.name}]') - if (args.tv != None): sc64.set_tv_type(args.tv) print(f'TV type set to [{args.tv.name}]') @@ -955,16 +1102,22 @@ if __name__ == '__main__': 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): - (disabled, dd_mode, seed, checksum) = args.cic_params - (seed, checksum) = sc64.set_cic_parameters(disabled, dd_mode, seed, checksum) + (seed, disabled) = args.cic_params + (seed, checksum, dd_mode) = sc64.set_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:12X}', end='') + print(f'seed: 0x{seed:02X}, checksum: 0x{checksum:012X}', end='') print(']') + else: + sc64.set_cic_parameters() - if (args.debug): + 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) if (args.backup_save):