[SC64][SW] Improved direct boot process

This commit is contained in:
Mateusz Faderewski 2023-02-19 15:50:57 +01:00
parent 8a8bf665f9
commit b30315e14e
9 changed files with 259 additions and 73 deletions

View File

@ -1,11 +1,11 @@
- [First time setup](#first-time-setup) - [First time setup](#first-time-setup)
- [Firmware backup/update](#firmware-backupupdate)
- [Internal flashcart state](#internal-flashcart-state) - [Internal flashcart state](#internal-flashcart-state)
- [Uploading game/save](#uploading-gamesave) - [Uploading game/save](#uploading-gamesave)
- [Downloading save](#downloading-save) - [Downloading save](#downloading-save)
- [Running 64DD games](#running-64dd-games) - [Running 64DD games](#running-64dd-games)
- [Direct boot option](#direct-boot-option) - [Direct boot option](#direct-boot-option)
- [Debug terminal](#debug-terminal) - [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 ## 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` 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` `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. 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 ## 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. 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 ## 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. 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 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). 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 ## Debug terminal
`sc64.py` supports UNFLoader protocol and has same functionality implemented as desktop program. Use argument `--debug` to activate it. `sc64.py` supports UNFLoader protocol and has same functionality implemented as aforementioned 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_backup_package.bin`.
To update SC64 firmware run `python3 sc64.py --update-firmware sc64_update_package.bin`

View File

@ -113,10 +113,12 @@ type: *enum* | default: `0`
- `0` - Load menu from SD card - `0` - Load menu from SD card
- `1` - Attempt to boot application loaded in ROM section - `1` - Attempt to boot application loaded in ROM section
- `2` - Attempt to boot 64DD IPL - `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. 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. Use this setting to force specific CIC seed.
By setting value `0xFFFF` bootloader will try to guess needed values from loaded ROM IPL3. 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. Use this setting to force specific TV type.
By setting value `3` bootloader will try to guess TV type from loaded ROM header. 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 ## 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 | | id | name | type | description |
| --- | -------------- | ------ | --------------------------------------------- | | --- | -------------- | ------ | --------------------------------------------- |

View File

@ -1,6 +1,7 @@
#include "boot.h" #include "boot.h"
#include "crc32.h" #include "crc32.h"
#include "io.h" #include "io.h"
#include "vr4300.h"
extern uint32_t ipl2 __attribute__((section(".data"))); extern uint32_t ipl2 __attribute__((section(".data")));
@ -15,7 +16,7 @@ static const ipl3_crc32_t ipl3_crc32[] = {
{ .crc32 = 0x587BD543, .seed = 0xAC }, // 5101 { .crc32 = 0x587BD543, .seed = 0xAC }, // 5101
{ .crc32 = 0x6170A4A1, .seed = 0x3F }, // 6101 { .crc32 = 0x6170A4A1, .seed = 0x3F }, // 6101
{ .crc32 = 0x009E9EA3, .seed = 0x3F }, // 7102 { .crc32 = 0x009E9EA3, .seed = 0x3F }, // 7102
{ .crc32 = 0x90BB6CB5, .seed = 0x3F }, // x102 { .crc32 = 0x90BB6CB5, .seed = 0x3F }, // 6102/7101
{ .crc32 = 0x0B050EE0, .seed = 0x78 }, // x103 { .crc32 = 0x0B050EE0, .seed = 0x78 }, // x103
{ .crc32 = 0x98BC2C86, .seed = 0x91 }, // x105 { .crc32 = 0x98BC2C86, .seed = 0x91 }, // x105
{ .crc32 = 0xACC8580A, .seed = 0x85 }, // x106 { .crc32 = 0xACC8580A, .seed = 0x85 }, // x106
@ -83,13 +84,23 @@ static bool boot_get_cic_seed (boot_info_t *info) {
} }
void boot (boot_info_t *info, bool detect_tv_type, bool detect_cic_seed) { void boot (boot_info_t *info, bool detect_tv_type, bool detect_cic_seed) {
if (detect_tv_type && !boot_get_tv_type(info)) { if (detect_tv_type) {
if (!boot_get_tv_type(info)) {
info->tv_type = OS_INFO->tv_type; info->tv_type = OS_INFO->tv_type;
} }
}
if (detect_cic_seed && !boot_get_cic_seed(info)) { if (detect_cic_seed) {
if (!boot_get_cic_seed(info)) {
info->cic_seed = 0x3F; 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; OS_INFO->mem_size_6105 = OS_INFO->mem_size;
@ -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->MADDR, 0);
cpu_io_write(&AI->LEN, 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); uint32_t pi_config = pi_io_read(base);
cpu_io_write(&PI->DOM[0].LAT, pi_config & 0xFF); 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); 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_src = base;
io32_t *ipl3_dst = SP_MEM->DMEM; 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); tv_type = (info->tv_type & 0x03);
reset_type = (info->reset_type & 0x01); reset_type = (info->reset_type & 0x01);
cic_seed = (info->cic_seed & 0xFF); 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]); stack_pointer = (void *) UNCACHED(&SP_MEM->IMEM[1020]);
asm volatile ( asm volatile (

View File

@ -49,7 +49,8 @@ typedef enum {
BOOT_MODE_MENU = 0, BOOT_MODE_MENU = 0,
BOOT_MODE_ROM = 1, BOOT_MODE_ROM = 1,
BOOT_MODE_DDIPL = 2, BOOT_MODE_DDIPL = 2,
BOOT_MODE_DIRECT = 3 BOOT_MODE_DIRECT_ROM = 3,
BOOT_MODE_DIRECT_DDIPL = 4,
} sc64_boot_mode_t; } sc64_boot_mode_t;
typedef enum { typedef enum {

View File

@ -1,5 +1,6 @@
#include "button.h" #include "button.h"
#include "cfg.h" #include "cfg.h"
#include "cic.h"
#include "dd.h" #include "dd.h"
#include "flash.h" #include "flash.h"
#include "fpga.h" #include "fpga.h"
@ -47,8 +48,9 @@ typedef enum {
typedef enum { typedef enum {
BOOT_MODE_MENU = 0, BOOT_MODE_MENU = 0,
BOOT_MODE_ROM = 1, BOOT_MODE_ROM = 1,
BOOT_MODE_DD = 2, BOOT_MODE_DDIPL = 2,
BOOT_MODE_DIRECT = 3 BOOT_MODE_DIRECT_ROM = 3,
BOOT_MODE_DIRECT_DDIPL = 4,
} boot_mode_t; } boot_mode_t;
typedef enum { typedef enum {
@ -315,11 +317,15 @@ bool cfg_update (uint32_t *args) {
return isv_set_address(args[1]); return isv_set_address(args[1]);
break; break;
case CFG_ID_BOOT_MODE: case CFG_ID_BOOT_MODE:
if (args[1] > BOOT_MODE_DIRECT) { if (args[1] > BOOT_MODE_DIRECT_DDIPL) {
return true; return true;
} }
p.boot_mode = args[1]; 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; break;
case CFG_ID_SAVE_TYPE: case CFG_ID_SAVE_TYPE:
return cfg_set_save_type((save_type_t) (args[1])); return cfg_set_save_type((save_type_t) (args[1]));

View File

@ -24,7 +24,6 @@
// SOFTWARE. // SOFTWARE.
#include <stdbool.h>
#include "cic.h" #include "cic.h"
#include "hw.h" #include "hw.h"
#include "led.h" #include "led.h"
@ -336,8 +335,7 @@ void cic_reset_parameters (void) {
} }
void cic_set_parameters (uint32_t *args) { void cic_set_parameters (uint32_t *args) {
cic_disabled = (args[0] >> 24) & (1 << 1); cic_disabled = (args[0] >> 24) & (1 << 0);
cic_dd_mode = (args[0] >> 24) & (1 << 0);
cic_seed = (args[0] >> 16) & 0xFF; cic_seed = (args[0] >> 16) & 0xFF;
cic_checksum[0] = (args[0] >> 8) & 0xFF; cic_checksum[0] = (args[0] >> 8) & 0xFF;
cic_checksum[1] = args[0] & 0xFF; cic_checksum[1] = args[0] & 0xFF;
@ -347,6 +345,10 @@ void cic_set_parameters (uint32_t *args) {
cic_checksum[5] = args[1] & 0xFF; cic_checksum[5] = args[1] & 0xFF;
} }
void cic_set_dd_mode (bool enabled) {
cic_dd_mode = enabled;
}
void cic_hw_init (void) { 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_FALLING, cic_irq_reset_falling);
hw_gpio_irq_setup(GPIO_ID_N64_RESET, GPIO_IRQ_RISING, cic_irq_reset_rising); hw_gpio_irq_setup(GPIO_ID_N64_RESET, GPIO_IRQ_RISING, cic_irq_reset_rising);

View File

@ -2,11 +2,13 @@
#define CIC_H__ #define CIC_H__
#include <stdbool.h>
#include <stdint.h> #include <stdint.h>
void cic_reset_parameters (void); void cic_reset_parameters (void);
void cic_set_parameters (uint32_t *args); void cic_set_parameters (uint32_t *args);
void cic_set_dd_mode (bool enabled);
void cic_hw_init (void); void cic_hw_init (void);
void cic_task (void); void cic_task (void);

View File

@ -1,8 +1,8 @@
#include "version.h" #include "version.h"
#define VERSION_API_USB (1) #define VERSION_API_USB (2)
#define VERSION_API_N64 (1) #define VERSION_API_N64 (2)
uint32_t version_api (version_api_type_t type) { uint32_t version_api (version_api_type_t type) {

View File

@ -7,6 +7,7 @@ import serial
import socket import socket
import sys import sys
import time import time
from binascii import crc32
from datetime import datetime from datetime import datetime
from dd64 import BadBlockError, DD64Image from dd64 import BadBlockError, DD64Image
from enum import Enum, IntEnum from enum import Enum, IntEnum
@ -286,7 +287,8 @@ class SC64:
MENU = 0 MENU = 0
ROM = 1 ROM = 1
DDIPL = 2 DDIPL = 2
DIRECT = 3 DIRECT_ROM = 3
DIRECT_DDIPL = 4
class SaveType(IntEnum): class SaveType(IntEnum):
NONE = 0 NONE = 0
@ -319,7 +321,7 @@ class SC64:
SCREENSHOT = 4 SCREENSHOT = 4
GDB = 0xDB GDB = 0xDB
__MIN_SUPPORTED_API_VERSION = 1 __MIN_SUPPORTED_API_VERSION = 2
__isv_line_buffer: bytes = b'' __isv_line_buffer: bytes = b''
__debug_header: Optional[bytes] = None __debug_header: Optional[bytes] = None
@ -546,19 +548,6 @@ class SC64:
def set_save_type(self, type: SaveType) -> None: def set_save_type(self, type: SaveType) -> None:
self.__set_config(self.__CfgId.SAVE_TYPE, type) 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: def set_led_enable(self, enabled: bool) -> None:
self.__set_setting(self.__SettingId.LED_ENABLE, enabled) self.__set_setting(self.__SettingId.LED_ENABLE, enabled)
@ -593,6 +582,169 @@ class SC64:
raise ConnectionException('Error while getting firmware backup') raise ConnectionException('Error while getting firmware backup')
return self.__read_memory(address, length) 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')]
print(data)
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: def __generate_filename(self, prefix: str, extension: str) -> str:
return f'{prefix}-{datetime.now().strftime("%y%m%d%H%M%S.%f")}.{extension}' return f'{prefix}-{datetime.now().strftime("%y%m%d%H%M%S.%f")}.{extension}'
@ -835,11 +987,11 @@ class EnumAction(argparse.Action):
if __name__ == '__main__': if __name__ == '__main__':
def cic_params_type(argument: str): def cic_params_type(argument: str):
params = argument.split(',') params = argument.split(',')
disabled = bool(int(params[0])) if (len(params) > 2):
dd_mode = bool(int(params[1])) if len(params) >= 2 else None raise argparse.ArgumentError()
seed = int(params[2], 0) if len(params) >= 3 else None seed = int(params[0], 0) if len(params) >= 1 else None
checksum = int(params[3], 0) if len(params) >= 4 else None disabled = bool(int(params[1])) if len(params) >= 2 else None
return (disabled, dd_mode, seed, checksum) return (seed, disabled)
parser = argparse.ArgumentParser(description='SC64 control software') 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('--backup-firmware', metavar='file', help='backup SC64 firmware and write it to specified file')
@ -851,7 +1003,7 @@ if __name__ == '__main__':
parser.add_argument('--boot', type=SC64.BootMode, action=EnumAction, help='set boot mode') 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('--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', 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('--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('--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') parser.add_argument('--rom', metavar='file', help='upload ROM from specified file')
@ -862,7 +1014,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('--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('--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('--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') parser.add_argument('--download-memory', metavar='file', help='download whole memory space and write it to specified file')
if (len(sys.argv) <= 1): if (len(sys.argv) <= 1):
@ -913,10 +1065,6 @@ if __name__ == '__main__':
sc64.set_led_enable(value) sc64.set_led_enable(value)
print(f'LED blinking set to [{"ENABLED" if value else "DISABLED"}]') 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): if (args.tv != None):
sc64.set_tv_type(args.tv) sc64.set_tv_type(args.tv)
print(f'TV type set to [{args.tv.name}]') print(f'TV type set to [{args.tv.name}]')
@ -955,16 +1103,22 @@ if __name__ == '__main__':
sc64.upload_ddipl(f.read()) sc64.upload_ddipl(f.read())
print('done') 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): if (args.cic_params != None):
(disabled, dd_mode, seed, checksum) = args.cic_params (seed, disabled) = args.cic_params
(seed, checksum) = sc64.set_cic_parameters(disabled, dd_mode, seed, checksum) (seed, checksum, dd_mode) = sc64.set_cic_parameters(seed, disabled)
print('CIC parameters set to [', end='') print('CIC parameters set to [', end='')
print(f'{"DISABLED" if disabled else "ENABLED"}, ', end='') print(f'{"DISABLED" if disabled else "ENABLED"}, ', end='')
print(f'{"DDIPL" if dd_mode else "ROM"}, ', 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(']') 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) sc64.debug_loop(isv=args.isv, disks=args.disk, gdb_port=args.gdb)
if (args.backup_save): if (args.backup_save):