pc software done

This commit is contained in:
Polprzewodnikowy 2022-08-24 20:42:30 +02:00
parent 0e79411740
commit 9373fcfe44
3 changed files with 236 additions and 86 deletions

View File

@ -846,7 +846,7 @@
(effects (font (size 1 1) (thickness 0.15)))
(tstamp 14a13283-4e54-4226-a07c-af565889337c)
)
(fp_text value "12k" (at 0 1.43) (layer "F.Fab")
(fp_text value "10k" (at 0 1.43) (layer "F.Fab")
(effects (font (size 1 1) (thickness 0.15)))
(tstamp 818ee031-0985-4f05-ac35-c39f534af651)
)
@ -1303,7 +1303,7 @@
(tags "TQFP QFP")
(property "Sheetfile" "sc64v2.kicad_sch")
(property "Sheetname" "")
(path "/f5284160-b8bd-4591-b634-bcdefecda6c2")
(path "/7adce0b2-9540-4d77-8cf1-4df964c29563")
(attr smd)
(fp_text reference "U8" (at 0 -12.35) (layer "F.SilkS")
(effects (font (size 1 1) (thickness 0.15)))

View File

@ -10854,7 +10854,7 @@
(property "Reference" "R8" (id 0) (at 196.85 383.5399 0)
(effects (font (size 1.27 1.27)) (justify right))
)
(property "Value" "12k" (id 1) (at 196.85 386.0799 0)
(property "Value" "10k" (id 1) (at 196.85 386.0799 0)
(effects (font (size 1.27 1.27)) (justify right))
)
(property "Footprint" "Resistor_SMD:R_0603_1608Metric" (id 2) (at 196.088 384.81 90)
@ -11649,7 +11649,7 @@
(reference "R7") (unit 1) (value "12k") (footprint "Resistor_SMD:R_0603_1608Metric")
)
(path "/d1984805-4314-4f1c-9ab1-479802945b94"
(reference "R8") (unit 1) (value "12k") (footprint "Resistor_SMD:R_0603_1608Metric")
(reference "R8") (unit 1) (value "10k") (footprint "Resistor_SMD:R_0603_1608Metric")
)
(path "/38f041d3-336d-4c45-9e25-efeece1e2340"
(reference "R9") (unit 1) (value "5.1k") (footprint "Resistor_SMD:R_0603_1608Metric")

View File

@ -1,8 +1,12 @@
import argparse
import os
import queue
import serial
import sys
import time
from datetime import datetime
from dd64 import BadBlockError, DD64Image
from enum import IntEnum
from enum import Enum, IntEnum
from serial.tools import list_ports
from threading import Thread
from typing import Optional
@ -15,13 +19,16 @@ class ConnectionException(Exception):
class SC64Serial:
__disconnect = False
__serial = None
__serial: Optional[serial.Serial] = None
__thread_read = None
__thread_write = None
__queue_output = queue.Queue()
__queue_input = queue.Queue()
__queue_packet = queue.Queue()
__VID = 0x0403
__PID = 0x6014
def __init__(self) -> None:
ports = list_ports.comports()
device_found = False
@ -30,7 +37,7 @@ class SC64Serial:
raise ConnectionException('Serial port is already open')
for p in ports:
if (p.vid == 0x0403 and p.pid == 0x6014 and p.serial_number.startswith('SC64')):
if (p.vid == self.__VID and p.pid == self.__PID and p.serial_number.startswith('SC64')):
try:
self.__serial = serial.Serial(p.device, timeout=0.1, write_timeout=5.0)
self.__reset_link()
@ -180,22 +187,23 @@ class SC64Serial:
class SC64:
class __Address(IntEnum):
ROM = 0x0000_0000
SDRAM = 0x0000_0000
FLASH = 0x0400_0000
BUFFER = 0x0500_0000
EEPROM = 0x0500_2000
FIRMWARE = 0x0200_0000
DDIPL = 0x03BC_0000
SAVE = 0x03FE_0000
SHADOW = 0x04FE_0000
BUFFER = 0x0500_0000
EEPROM = 0x0500_2000
class __Length(IntEnum):
ROM = (64 * 1024 * 1024)
FIRMWARE = (2 * 1024 * 1024)
SDRAM = (64 * 1024 * 1024)
FLASH = (16 * 1024 * 1024)
BUFFER = (8 * 1024)
EEPROM = (2 * 1024)
DDIPL = (4 * 1024 * 1024)
SAVE = (128 * 1024)
SHADOW = (128 * 1024)
BUFFER = (8 * 1024)
EEPROM = (2 * 1024)
class __SaveLength(IntEnum):
NONE = 0
@ -203,7 +211,7 @@ class SC64:
EEPROM_16K = (2 * 1024)
SRAM = (32 * 1024)
FLASHRAM = (128 * 1024)
SRAM_X3 = (3 * 32 * 1024)
SRAM_3X = (3 * 32 * 1024)
class __CfgId(IntEnum):
BOOTLOADER_SWITCH = 0
@ -306,13 +314,26 @@ class SC64:
return self.__get_int(data)
def __write_memory(self, address: int, data: bytes) -> None:
self.__link.execute_cmd(cmd=b'M', args=[address, len(data)], data=data)
if (len(data) > 0):
self.__link.execute_cmd(cmd=b'M', args=[address, len(data)], data=data, timeout=10.0)
def __read_memory(self, address: int, length: int) -> bytes:
return self.__link.execute_cmd(cmd=b'm', args=[address, length])
if (length > 0):
return self.__link.execute_cmd(cmd=b'm', args=[address, length], timeout=10.0)
return bytes([])
def __erase_flash_region(self, address: int, length: int) -> None:
if (address < self.__Address.FLASH):
raise ValueError('Flash erase address or length outside of possible range')
if ((address + length) > (self.__Address.FLASH + self.__Length.FLASH)):
raise ValueError('Flash erase address or length outside of possible range')
erase_block_size = self.__get_config(self.__CfgId.FLASH_ERASE_BLOCK)
if ((address % erase_block_size != 0) or (length % erase_block_size != 0)):
raise ValueError('Flash erase address or length not aligned to block size')
for offset in range(address, address + length, erase_block_size):
self.__set_config(self.__CfgId.FLASH_ERASE_BLOCK, offset)
def reset_state(self) -> None:
self.__set_config(self.__CfgId.BOOTLOADER_SWITCH, False)
self.__set_config(self.__CfgId.ROM_WRITE_ENABLE, False)
self.__set_config(self.__CfgId.ROM_SHADOW_ENABLE, False)
self.__set_config(self.__CfgId.DD_MODE, self.__DDMode.NONE)
@ -326,10 +347,22 @@ class SC64:
self.__set_config(self.__CfgId.BUTTON_MODE, self.__ButtonMode.NONE)
self.set_cic_parameters()
def upload_rom(self, data: bytes):
if (len(data) > self.__Length.ROM):
def upload_rom(self, data: bytes, use_shadow: bool=True):
rom_length = len(data)
if (rom_length > self.__Length.SDRAM):
raise ValueError('ROM size too big')
self.__write_memory(self.__Address.ROM, data)
sdram_length = rom_length
shadow_enabled = use_shadow and rom_length > (self.__Length.SDRAM - self.__Length.SHADOW)
if (shadow_enabled):
sdram_length = (self.__Length.SDRAM - self.__Length.SHADOW)
shadow_length = rom_length - sdram_length
if (self.__read_memory(self.__Address.SHADOW, shadow_length) != data[sdram_length:]):
self.__erase_flash_region(self.__Address.SHADOW, self.__Length.SHADOW)
self.__write_memory(self.__Address.SHADOW, data[sdram_length:])
if (self.__read_memory(self.__Address.SHADOW, shadow_length) != data[sdram_length:]):
raise ConnectionException('Shadow ROM program failure')
self.__write_memory(self.__Address.SDRAM, data[:sdram_length])
self.__set_config(self.__CfgId.ROM_SHADOW_ENABLE, shadow_enabled)
def upload_ddipl(self, data: bytes) -> None:
if (len(data) > self.__Length.DDIPL):
@ -340,6 +373,8 @@ class SC64:
save_type = self.SaveType(self.__get_config(self.__CfgId.SAVE_TYPE))
if (save_type not in self.SaveType):
raise ConnectionError('Unknown save type fetched from SC64 device')
if (save_type == self.SaveType.NONE):
raise ValueError('No save type set inside SC64 device')
if (len(data) != self.__SaveLength[save_type.name]):
raise ValueError('Wrong save data length')
address = self.__Address.SAVE
@ -347,6 +382,32 @@ class SC64:
address = self.__Address.EEPROM
self.__write_memory(address, data)
def download_save(self) -> bytes:
save_type = self.SaveType(self.__get_config(self.__CfgId.SAVE_TYPE))
if (save_type not in self.SaveType):
raise ConnectionError('Unknown save type fetched from SC64 device')
if (save_type == self.SaveType.NONE):
raise ValueError('No save type set inside SC64 device')
address = self.__Address.SAVE
length = self.__SaveLength[save_type.name]
if (save_type == self.SaveType.EEPROM_4K or save_type == self.SaveType.EEPROM_16K):
address = self.__Address.EEPROM
return self.__read_memory(address, length)
def set_rtc(self, t: datetime) -> None:
to_bcd = lambda v: ((int((v / 10) % 10) << 4) | int(int(v) % 10))
data = bytes([
to_bcd(t.weekday() + 1),
to_bcd(t.hour),
to_bcd(t.minute),
to_bcd(t.second),
0,
to_bcd(t.year),
to_bcd(t.month),
to_bcd(t.day),
])
self.__link.execute_cmd(cmd=b'T', args=[self.__get_int(data[0:4]), self.__get_int(data[4:8])])
def set_boot_mode(self, mode: BootMode) -> None:
if (mode not in self.BootMode):
raise ValueError('Boot mode outside of allowed values')
@ -368,23 +429,14 @@ class SC64:
raise ValueError('Save type outside of allowed values')
self.__set_config(self.__CfgId.SAVE_TYPE, type)
def set_cic_parameters(self, dd_mode: bool=False, seed: int=0x3F, checksum: bytes=[0xA5, 0x36, 0xC0, 0xF1, 0xD8, 0x59]) -> None:
def set_cic_parameters(self, dd_mode: bool=False, seed: int=0x3F, checksum: bytes=bytes([0xA5, 0x36, 0xC0, 0xF1, 0xD8, 0x59])) -> None:
if (seed < 0 or seed > 0xFF):
raise ValueError('CIC seed outside of allowed values')
if (len(checksum) != 6):
raise ValueError('CIC checksum length outside of allowed values')
args = [0, 0]
if (dd_mode):
args[0] |= (1 << 24)
args[0] |= (seed << 16)
args[0] |= (checksum[0] << 8) | checksum[1]
args[1] |= (
(checksum[2] << 24) |
(checksum[3] << 16) |
(checksum[4] << 8) |
(checksum[5])
)
self.__link.execute_cmd(cmd=b'B', args=args)
data = bytes([1 if dd_mode else 0, seed])
data = [*data, *checksum]
self.__link.execute_cmd(cmd=b'B', args=[self.__get_int(data[0:4]), self.__get_int(data[4:8])])
def update_firmware(self, data: bytes) -> None:
address = self.__Address.FIRMWARE
@ -399,9 +451,9 @@ class SC64:
if (packet == None):
raise ConnectionException('Update timeout')
(cmd, data) = packet
status = self.__UpdateStatus(self.__get_int(data))
if (cmd != b'F'):
raise ConnectionException('Wrong update status packet')
status = self.__UpdateStatus(self.__get_int(data))
print(f'Update status [{status.name}]')
if (status == self.__UpdateStatus.ERROR):
raise ConnectionException('Update error, device is most likely bricked')
@ -417,7 +469,7 @@ class SC64:
raise ConnectionException('Error while getting firmware backup')
return self.__read_memory(address, length)
def __handle_dd_packet(self, dd: DD64Image, data: bytes) -> None:
def __handle_dd_packet(self, dd: Optional[DD64Image], data: bytes) -> None:
CMD_READ_BLOCK = 1
CMD_WRITE_BLOCK = 2
cmd = self.__get_int(data[0:])
@ -427,7 +479,7 @@ class SC64:
head = (track_head_block >> 1) & 0x1
block = track_head_block & 0x1
try:
if (not dd.loaded):
if (not dd or not dd.loaded):
raise BadBlockError
if (cmd == CMD_READ_BLOCK):
block_data = dd.read_block(track, head, block)
@ -448,15 +500,19 @@ class SC64:
def debug_loop(self, isv: bool=False, disks: Optional[list[str]]=None) -> None:
dd = None
drive_type = None
current_image = 0
next_image = 0
self.__set_config(self.__CfgId.ROM_WRITE_ENABLE, isv)
self.__set_config(self.__CfgId.ISV_ENABLE, isv)
if (isv):
print('IS-Viewer64 support set to [ENABLED]')
if (self.__get_config(self.__CfgId.ROM_SHADOW_ENABLE)):
print('ROM shadow enabled - ISV support will NOT work (use --no-shadow option to disable it)')
if (disks):
dd = DD64Image()
drive_type = None
for path in disks:
try:
dd.load(path)
@ -469,15 +525,22 @@ class SC64:
dd = None
print(f'64DD disabled, incorrect disk images provided: {e}')
break
drive_type = self.__DDDriveType.RETAIL if drive_type == 'retail' else self.__DDDriveType.DEVELOPMENT
if (dd):
self.__set_config(self.__CfgId.DD_MODE, self.__DDMode.FULL)
self.__set_config(self.__CfgId.DD_DRIVE_TYPE, drive_type)
self.__set_config(self.__CfgId.DD_DRIVE_TYPE, {
'retail': self.__DDDriveType.RETAIL,
'development': self.__DDDriveType.DEVELOPMENT
}[drive_type])
self.__set_config(self.__CfgId.DD_DISK_STATE, self.__DDDiskState.EJECTED)
self.__set_config(self.__CfgId.BUTTON_MODE, self.__ButtonMode.USB_PACKET)
print('64DD enabled, loaded disks:')
for disk in disks:
print(f' - {os.path.basename(disk)}')
print('Press button on SC64 device to cycle through provided disks')
print('Debug loop started, use Ctrl-C to exit')
try:
while (True):
packet = self.__link.get_packet()
if (packet != None):
@ -489,44 +552,131 @@ class SC64:
if (cmd == b'U'):
self.__handle_usb_packet(data)
if (cmd == b'B'):
if (dd.loaded):
self.__set_config(self.__CfgId.DD_DISK_STATE, self.__DDDiskState.EJECTED)
dd.unload()
print(f'64DD disk ejected [{disks[current_image]}]')
else:
if (not dd.loaded):
dd.load(disks[next_image])
self.__set_config(self.__CfgId.DD_DISK_STATE, self.__DDDiskState.INSERTED)
current_image = next_image
next_image += 1
if (next_image >= len(disks)):
next_image = 0
print(f'64DD disk inserted [{disks[current_image]}]')
print(f'64DD disk inserted - {os.path.basename(disks[current_image])}')
else:
self.__set_config(self.__CfgId.DD_DISK_STATE, self.__DDDiskState.EJECTED)
dd.unload()
print(f'64DD disk ejected - {os.path.basename(disks[current_image])}')
except KeyboardInterrupt:
if (dd and dd.loaded):
self.__set_config(self.__CfgId.DD_DISK_STATE, self.__DDDiskState.EJECTED)
class EnumAction(argparse.Action):
def __init__(self, **kwargs):
type = kwargs.pop('type', None)
if type is None:
raise ValueError('No type was provided')
if not issubclass(type, Enum):
raise TypeError('Provided type is not an Enum subclass')
items = (choice.lower().replace('_', '-') for (choice, _) in type.__members__.items())
kwargs.setdefault('choices', tuple(items))
super(EnumAction, self).__init__(**kwargs)
self.__enum = type
def __call__(self, parser, namespace, values, option_string):
key = str(values).upper().replace('-', '_')
value = self.__enum[key]
setattr(namespace, self.dest, value)
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='SC64 control software')
parser.add_argument('--backup', help='backup SC64 firmware and write it to specified file')
parser.add_argument('--update', help='update SC64 firmware from specified file')
parser.add_argument('--reset-state', action='store_true', help='reset SC64 internal state')
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('--rtc', action='store_true', help='update clock in SC64 to system time')
parser.add_argument('--rom', help='upload ROM from specified file')
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('--save-type', type=SC64.SaveType, action=EnumAction, help='set save type')
parser.add_argument('--save', help='upload save from specified file')
parser.add_argument('--backup-save', help='download save and write it to specified file')
parser.add_argument('--ddipl', help='upload 64DD IPL from specified file')
parser.add_argument('--disk', action='append', help='path to 64DD disk (.ndd format), can be specified multiple times')
parser.add_argument('--isv', action='store_true', help='enable IS-Viewer64 support')
parser.add_argument('--debug', action='store_true', help='run debug loop (required for IS-Viewer64 and 64DD)')
if (len(sys.argv) <= 1):
parser.print_help()
parser.exit()
args = parser.parse_args()
try:
sc64 = SC64()
if (args.backup):
with open(args.backup, 'wb+') as f:
print('Generating backup, this might take a while... ', end='', flush=True)
f.write(sc64.backup_firmware())
print('done')
if (args.update):
with open(args.update, 'rb+') as f:
print('Updating firmware, this might take a while... ', end='', flush=True)
sc64.update_firmware(f.read())
print('done')
if (args.reset_state):
sc64.reset_state()
# sc64.set_save_type(SC64.SaveType.EEPROM_4K)
# with open('S:/n64/saves/SM64.eep', 'rb+') as f:
# sc64.upload_save(f.read())
# with open('S:/n64/roms/baserom.us.z64', 'rb+') as f:
# sc64.upload_rom(f.read())
# sc64.set_boot_mode(SC64.BootMode.ROM)
if (args.boot != None):
sc64.set_boot_mode(args.boot)
print(f'Boot mode set to [{args.boot.name}]')
# with open('S:/n64/64dd/ipl/NDDJ2.n64', 'rb+') as f:
# sc64.upload_ddipl(f.read())
# sc64.set_boot_mode(SC64.BootMode.DDIPL)
if (args.tv != None):
sc64.set_tv_type(args.tv)
print(f'TV type set to [{args.tv.name}]')
# try:
# disks = [
# 'S:/n64/64dd/rtl/NUD-DMPJ-JPN (sealed).ndd',
# 'S:/n64/64dd/rtl/NUD-DMBJ-JPN.ndd'
# ]
# sc64.debug_loop(disks=disks)
# except KeyboardInterrupt:
# pass
if (args.cic != None):
sc64.set_cic_seed(args.cic)
print(f'CIC seed set to [0x{args.cic:X}]')
if (args.rtc):
sc64.set_rtc(datetime.now())
if (args.rom):
with open(args.rom, 'rb+') as f:
print('Uploading ROM... ', end='', flush=True)
sc64.upload_rom(f.read(), use_shadow=args.no_shadow)
print('done')
if (args.save_type != None):
sc64.set_save_type(args.save_type)
print(f'Save type set to [{args.save_type.name}]')
if (args.save):
with open(args.save, 'rb+') as f:
print('Uploading save... ', end='', flush=True)
sc64.upload_save(f.read())
print('done')
if (args.ddipl):
with open(args.ddipl, 'rb+') as f:
print('Uploading 64DD IPL... ', end='', flush=True)
sc64.upload_ddipl(f.read())
print('done')
if (args.debug):
sc64.debug_loop(isv=args.isv, disks=args.disk)
if (args.backup_save):
with open(args.backup_save, 'wb+') as f:
print('Downloading save... ', end='', flush=True)
f.write(sc64.download_save())
print('done')
except ValueError as e:
print(f'\nValue error: {e}')
except ConnectionException as e:
print(f'SC64 error: {e}')
print(f'\nSC64 error: {e}')