[SC64][SW] Fixed setting CIC parameters, added GDB socket support in sc64.py

This commit is contained in:
Mateusz Faderewski 2023-02-08 16:34:38 +01:00
parent 982db95fa2
commit 39dccd5e00

View File

@ -4,6 +4,7 @@ import argparse
import os import os
import queue import queue
import serial import serial
import socket
import sys import sys
import time import time
from datetime import datetime from datetime import datetime
@ -313,9 +314,11 @@ class SC64:
RAWBINARY = 2 RAWBINARY = 2
HEADER = 3 HEADER = 3
SCREENSHOT = 4 SCREENSHOT = 4
GDB = 0xDB
__isv_line_buffer: bytes = b'' __isv_line_buffer: bytes = b''
__debug_header: Optional[bytes] = None __debug_header: Optional[bytes] = None
__gdb_client: Optional[socket.socket] = None
def __init__(self) -> None: def __init__(self) -> None:
self.__link = SC64Serial() self.__link = SC64Serial()
@ -496,16 +499,18 @@ 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: int=0x3F, checksum: bytes=bytes([0xA5, 0x36, 0xC0, 0xF1, 0xD8, 0x59])) -> None: 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): if (seed < 0 or seed > 0xFF):
raise ValueError('CIC seed outside of allowed values') raise ValueError('CIC seed outside of allowed values')
if (len(checksum) != 6):
raise ValueError('CIC checksum length outside of allowed values')
mode = (1 << 1) if disabled else 0 mode = (1 << 1) if disabled else 0
mode |= (1 << 0) if dd_mode else 0 mode |= (1 << 0) if dd_mode else 0
data = bytes([mode, seed]) data = bytes([mode, seed])
data = [*data, *checksum] 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])]) self.__link.execute_cmd(cmd=b'B', args=[self.__get_int(data[0:4]), self.__get_int(data[4:8])])
return (seed, checksum)
def update_firmware(self, data: bytes, status_callback: Optional[Callable[[str], None]]=None) -> None: def update_firmware(self, data: bytes, status_callback: Optional[Callable[[str], None]]=None) -> None:
address = self.__Address.FIRMWARE address = self.__Address.FIRMWARE
@ -607,6 +612,35 @@ class SC64:
print('Screenshot header data is invalid') print('Screenshot header data is invalid')
else: else:
print('Got screenshot packet without header data') print('Got screenshot packet without header data')
elif (datatype == self.__DebugDatatype.GDB):
self.__handle_gdb_datatype(packet)
def __handle_gdb_socket(self, gdb_port: int) -> None:
MAX_PACKET_SIZE = 65536
gdb_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
gdb_socket.bind(('localhost', gdb_port))
gdb_socket.listen()
while (True):
(self.__gdb_client, address) = gdb_socket.accept()
client_address = f'{address[0]}:{address[1]}'
print(f'[GDB]: New connection ({client_address})')
while (True):
try:
data = self.__gdb_client.recv(MAX_PACKET_SIZE)
if (len(data) == 0):
break
self.debug_send(self.__DebugDatatype.GDB, data)
except:
self.__gdb_client.close()
break
print(f'[GDB]: Connection closed ({client_address})')
def __handle_gdb_datatype(self, data: bytes) -> None:
if (self.__gdb_client):
try:
self.__gdb_client.sendall(data)
except:
pass
def __handle_debug_input(self) -> None: def __handle_debug_input(self) -> None:
running = True running = True
@ -641,7 +675,7 @@ class SC64:
except EOFError: except EOFError:
running = False running = False
def debug_loop(self, isv: int=0, disks: Optional[list[str]]=None) -> None: def debug_loop(self, isv: int=0, disks: Optional[list[str]]=None, gdb_port: Optional[int]=None) -> None:
dd = None dd = None
current_image = 0 current_image = 0
next_image = 0 next_image = 0
@ -651,7 +685,7 @@ class SC64:
if (isv != 0): if (isv != 0):
print(f'IS-Viewer64 support set to [ENABLED] at ROM offset [0x{(isv):08X}]') print(f'IS-Viewer64 support set to [ENABLED] at ROM offset [0x{(isv):08X}]')
if (self.__get_config(self.__CfgId.ROM_SHADOW_ENABLE)): if (self.__get_config(self.__CfgId.ROM_SHADOW_ENABLE)):
print('ROM shadow enabled - IS-Viewer64 will NOT work (use --no-shadow option to disable it)') print('ROM shadow enabled - IS-Viewer64 will NOT work (use --no-shadow option while uploading ROM to disable it)')
if (disks): if (disks):
dd = DD64Image() dd = DD64Image()
@ -682,6 +716,11 @@ class SC64:
print(f' - {os.path.basename(disk)}') print(f' - {os.path.basename(disk)}')
print('Press button on SC64 device to cycle through provided disks') print('Press button on SC64 device to cycle through provided disks')
if (gdb_port):
gdb_thread = Thread(target=lambda: self.__handle_gdb_socket(gdb_port), daemon=True)
gdb_thread.start()
print(f'GDB server started and listening on port [{gdb_port}]')
print('\nDebug loop started, press Ctrl-C to exit\n') print('\nDebug loop started, press Ctrl-C to exit\n')
try: try:
@ -741,6 +780,14 @@ class EnumAction(argparse.Action):
if __name__ == '__main__': 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)
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')
parser.add_argument('--update-firmware', metavar='file', help='update SC64 firmware from specified file') parser.add_argument('--update-firmware', metavar='file', help='update SC64 firmware from specified file')
@ -750,7 +797,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', help='set CIC emulation parameters') parser.add_argument('--cic-params', metavar='disabled,[dd_mode,[seed,[checksum]]]', 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')
@ -760,7 +807,8 @@ if __name__ == '__main__':
parser.add_argument('--ddipl', metavar='file', help='upload 64DD IPL from specified file') parser.add_argument('--ddipl', metavar='file', help='upload 64DD IPL from specified file')
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('--debug', action='store_true', help='run debug loop (required for 64DD and IS-Viewer64)') 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('--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):
@ -843,19 +891,16 @@ if __name__ == '__main__':
print('done') print('done')
if (args.cic_params != None): if (args.cic_params != None):
# TODO: calculate checksum in script (disabled, dd_mode, seed, checksum) = args.cic_params
params = args.cic_params.split(',') (seed, checksum) = sc64.set_cic_parameters(disabled, dd_mode, seed, checksum)
if (len(params) != 4): print('CIC parameters set to [', end='')
raise ValueError('Incorrect number of CIC parameters') print(f'{"DISABLED" if disabled else "ENABLED"}, ', end='')
disabled = (params[0] != '0') print(f'{"DDIPL" if dd_mode else "ROM"}, ', end='')
dd_mode = (params[1] != '0') print(f'seed: 0x{seed:02X}, checksum: 0x{checksum:12X}', end='')
seed = int(params[2]) print(']')
checksum = int(params[3]).to_bytes(6, byteorder='big', signed=False)
sc64.set_cic_parameters(disabled, dd_mode, seed, checksum)
print(f'CIC parameters set to [{"DISABLED, " if disabled else ""}{"DDIPL" if dd_mode else "ROM"}, seed: 0x{seed:02X}, checksum: 0x{int(params[3]):12X}]')
if (args.debug): if (args.debug):
sc64.debug_loop(isv=args.isv, disks=args.disk) sc64.debug_loop(isv=args.isv, disks=args.disk, gdb_port=args.gdb)
if (args.backup_save): if (args.backup_save):
with open(args.backup_save, 'wb') as f: with open(args.backup_save, 'wb') as f: