mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-11-25 07:06:52 +01:00
[SC64][SW] Improved direct boot process
This commit is contained in:
parent
8a8bf665f9
commit
b30315e14e
@ -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`
|
|
||||||
|
@ -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 |
|
||||||
| --- | -------------- | ------ | --------------------------------------------- |
|
| --- | -------------- | ------ | --------------------------------------------- |
|
||||||
|
@ -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 (
|
||||||
|
@ -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 {
|
||||||
|
@ -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]));
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
214
sw/pc/sc64.py
214
sw/pc/sc64.py
@ -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):
|
||||||
|
Loading…
Reference in New Issue
Block a user