mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-11-25 15:16:53 +01:00
pc software done
This commit is contained in:
parent
0e79411740
commit
9373fcfe44
@ -846,7 +846,7 @@
|
|||||||
(effects (font (size 1 1) (thickness 0.15)))
|
(effects (font (size 1 1) (thickness 0.15)))
|
||||||
(tstamp 14a13283-4e54-4226-a07c-af565889337c)
|
(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)))
|
(effects (font (size 1 1) (thickness 0.15)))
|
||||||
(tstamp 818ee031-0985-4f05-ac35-c39f534af651)
|
(tstamp 818ee031-0985-4f05-ac35-c39f534af651)
|
||||||
)
|
)
|
||||||
@ -1303,7 +1303,7 @@
|
|||||||
(tags "TQFP QFP")
|
(tags "TQFP QFP")
|
||||||
(property "Sheetfile" "sc64v2.kicad_sch")
|
(property "Sheetfile" "sc64v2.kicad_sch")
|
||||||
(property "Sheetname" "")
|
(property "Sheetname" "")
|
||||||
(path "/f5284160-b8bd-4591-b634-bcdefecda6c2")
|
(path "/7adce0b2-9540-4d77-8cf1-4df964c29563")
|
||||||
(attr smd)
|
(attr smd)
|
||||||
(fp_text reference "U8" (at 0 -12.35) (layer "F.SilkS")
|
(fp_text reference "U8" (at 0 -12.35) (layer "F.SilkS")
|
||||||
(effects (font (size 1 1) (thickness 0.15)))
|
(effects (font (size 1 1) (thickness 0.15)))
|
||||||
|
@ -10854,7 +10854,7 @@
|
|||||||
(property "Reference" "R8" (id 0) (at 196.85 383.5399 0)
|
(property "Reference" "R8" (id 0) (at 196.85 383.5399 0)
|
||||||
(effects (font (size 1.27 1.27)) (justify right))
|
(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))
|
(effects (font (size 1.27 1.27)) (justify right))
|
||||||
)
|
)
|
||||||
(property "Footprint" "Resistor_SMD:R_0603_1608Metric" (id 2) (at 196.088 384.81 90)
|
(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")
|
(reference "R7") (unit 1) (value "12k") (footprint "Resistor_SMD:R_0603_1608Metric")
|
||||||
)
|
)
|
||||||
(path "/d1984805-4314-4f1c-9ab1-479802945b94"
|
(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"
|
(path "/38f041d3-336d-4c45-9e25-efeece1e2340"
|
||||||
(reference "R9") (unit 1) (value "5.1k") (footprint "Resistor_SMD:R_0603_1608Metric")
|
(reference "R9") (unit 1) (value "5.1k") (footprint "Resistor_SMD:R_0603_1608Metric")
|
||||||
|
274
sw/pc/sc64.py
274
sw/pc/sc64.py
@ -1,8 +1,12 @@
|
|||||||
|
import argparse
|
||||||
|
import os
|
||||||
import queue
|
import queue
|
||||||
import serial
|
import serial
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
|
from datetime import datetime
|
||||||
from dd64 import BadBlockError, DD64Image
|
from dd64 import BadBlockError, DD64Image
|
||||||
from enum import IntEnum
|
from enum import Enum, IntEnum
|
||||||
from serial.tools import list_ports
|
from serial.tools import list_ports
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
@ -15,13 +19,16 @@ class ConnectionException(Exception):
|
|||||||
|
|
||||||
class SC64Serial:
|
class SC64Serial:
|
||||||
__disconnect = False
|
__disconnect = False
|
||||||
__serial = None
|
__serial: Optional[serial.Serial] = None
|
||||||
__thread_read = None
|
__thread_read = None
|
||||||
__thread_write = None
|
__thread_write = None
|
||||||
__queue_output = queue.Queue()
|
__queue_output = queue.Queue()
|
||||||
__queue_input = queue.Queue()
|
__queue_input = queue.Queue()
|
||||||
__queue_packet = queue.Queue()
|
__queue_packet = queue.Queue()
|
||||||
|
|
||||||
|
__VID = 0x0403
|
||||||
|
__PID = 0x6014
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
ports = list_ports.comports()
|
ports = list_ports.comports()
|
||||||
device_found = False
|
device_found = False
|
||||||
@ -30,7 +37,7 @@ class SC64Serial:
|
|||||||
raise ConnectionException('Serial port is already open')
|
raise ConnectionException('Serial port is already open')
|
||||||
|
|
||||||
for p in ports:
|
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:
|
try:
|
||||||
self.__serial = serial.Serial(p.device, timeout=0.1, write_timeout=5.0)
|
self.__serial = serial.Serial(p.device, timeout=0.1, write_timeout=5.0)
|
||||||
self.__reset_link()
|
self.__reset_link()
|
||||||
@ -180,22 +187,23 @@ class SC64Serial:
|
|||||||
|
|
||||||
class SC64:
|
class SC64:
|
||||||
class __Address(IntEnum):
|
class __Address(IntEnum):
|
||||||
ROM = 0x0000_0000
|
SDRAM = 0x0000_0000
|
||||||
|
FLASH = 0x0400_0000
|
||||||
|
BUFFER = 0x0500_0000
|
||||||
|
EEPROM = 0x0500_2000
|
||||||
FIRMWARE = 0x0200_0000
|
FIRMWARE = 0x0200_0000
|
||||||
DDIPL = 0x03BC_0000
|
DDIPL = 0x03BC_0000
|
||||||
SAVE = 0x03FE_0000
|
SAVE = 0x03FE_0000
|
||||||
SHADOW = 0x04FE_0000
|
SHADOW = 0x04FE_0000
|
||||||
BUFFER = 0x0500_0000
|
|
||||||
EEPROM = 0x0500_2000
|
|
||||||
|
|
||||||
class __Length(IntEnum):
|
class __Length(IntEnum):
|
||||||
ROM = (64 * 1024 * 1024)
|
SDRAM = (64 * 1024 * 1024)
|
||||||
FIRMWARE = (2 * 1024 * 1024)
|
FLASH = (16 * 1024 * 1024)
|
||||||
|
BUFFER = (8 * 1024)
|
||||||
|
EEPROM = (2 * 1024)
|
||||||
DDIPL = (4 * 1024 * 1024)
|
DDIPL = (4 * 1024 * 1024)
|
||||||
SAVE = (128 * 1024)
|
SAVE = (128 * 1024)
|
||||||
SHADOW = (128 * 1024)
|
SHADOW = (128 * 1024)
|
||||||
BUFFER = (8 * 1024)
|
|
||||||
EEPROM = (2 * 1024)
|
|
||||||
|
|
||||||
class __SaveLength(IntEnum):
|
class __SaveLength(IntEnum):
|
||||||
NONE = 0
|
NONE = 0
|
||||||
@ -203,7 +211,7 @@ class SC64:
|
|||||||
EEPROM_16K = (2 * 1024)
|
EEPROM_16K = (2 * 1024)
|
||||||
SRAM = (32 * 1024)
|
SRAM = (32 * 1024)
|
||||||
FLASHRAM = (128 * 1024)
|
FLASHRAM = (128 * 1024)
|
||||||
SRAM_X3 = (3 * 32 * 1024)
|
SRAM_3X = (3 * 32 * 1024)
|
||||||
|
|
||||||
class __CfgId(IntEnum):
|
class __CfgId(IntEnum):
|
||||||
BOOTLOADER_SWITCH = 0
|
BOOTLOADER_SWITCH = 0
|
||||||
@ -306,13 +314,26 @@ class SC64:
|
|||||||
return self.__get_int(data)
|
return self.__get_int(data)
|
||||||
|
|
||||||
def __write_memory(self, address: int, data: bytes) -> None:
|
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:
|
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:
|
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_WRITE_ENABLE, False)
|
||||||
self.__set_config(self.__CfgId.ROM_SHADOW_ENABLE, False)
|
self.__set_config(self.__CfgId.ROM_SHADOW_ENABLE, False)
|
||||||
self.__set_config(self.__CfgId.DD_MODE, self.__DDMode.NONE)
|
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_config(self.__CfgId.BUTTON_MODE, self.__ButtonMode.NONE)
|
||||||
self.set_cic_parameters()
|
self.set_cic_parameters()
|
||||||
|
|
||||||
def upload_rom(self, data: bytes):
|
def upload_rom(self, data: bytes, use_shadow: bool=True):
|
||||||
if (len(data) > self.__Length.ROM):
|
rom_length = len(data)
|
||||||
|
if (rom_length > self.__Length.SDRAM):
|
||||||
raise ValueError('ROM size too big')
|
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:
|
def upload_ddipl(self, data: bytes) -> None:
|
||||||
if (len(data) > self.__Length.DDIPL):
|
if (len(data) > self.__Length.DDIPL):
|
||||||
@ -340,6 +373,8 @@ class SC64:
|
|||||||
save_type = self.SaveType(self.__get_config(self.__CfgId.SAVE_TYPE))
|
save_type = self.SaveType(self.__get_config(self.__CfgId.SAVE_TYPE))
|
||||||
if (save_type not in self.SaveType):
|
if (save_type not in self.SaveType):
|
||||||
raise ConnectionError('Unknown save type fetched from SC64 device')
|
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]):
|
if (len(data) != self.__SaveLength[save_type.name]):
|
||||||
raise ValueError('Wrong save data length')
|
raise ValueError('Wrong save data length')
|
||||||
address = self.__Address.SAVE
|
address = self.__Address.SAVE
|
||||||
@ -347,6 +382,32 @@ class SC64:
|
|||||||
address = self.__Address.EEPROM
|
address = self.__Address.EEPROM
|
||||||
self.__write_memory(address, data)
|
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:
|
def set_boot_mode(self, mode: BootMode) -> None:
|
||||||
if (mode not in self.BootMode):
|
if (mode not in self.BootMode):
|
||||||
raise ValueError('Boot mode outside of allowed values')
|
raise ValueError('Boot mode outside of allowed values')
|
||||||
@ -368,23 +429,14 @@ class SC64:
|
|||||||
raise ValueError('Save type outside of allowed values')
|
raise ValueError('Save type outside of allowed values')
|
||||||
self.__set_config(self.__CfgId.SAVE_TYPE, type)
|
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):
|
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):
|
if (len(checksum) != 6):
|
||||||
raise ValueError('CIC checksum length outside of allowed values')
|
raise ValueError('CIC checksum length outside of allowed values')
|
||||||
args = [0, 0]
|
data = bytes([1 if dd_mode else 0, seed])
|
||||||
if (dd_mode):
|
data = [*data, *checksum]
|
||||||
args[0] |= (1 << 24)
|
self.__link.execute_cmd(cmd=b'B', args=[self.__get_int(data[0:4]), self.__get_int(data[4:8])])
|
||||||
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)
|
|
||||||
|
|
||||||
def update_firmware(self, data: bytes) -> None:
|
def update_firmware(self, data: bytes) -> None:
|
||||||
address = self.__Address.FIRMWARE
|
address = self.__Address.FIRMWARE
|
||||||
@ -399,9 +451,9 @@ class SC64:
|
|||||||
if (packet == None):
|
if (packet == None):
|
||||||
raise ConnectionException('Update timeout')
|
raise ConnectionException('Update timeout')
|
||||||
(cmd, data) = packet
|
(cmd, data) = packet
|
||||||
status = self.__UpdateStatus(self.__get_int(data))
|
|
||||||
if (cmd != b'F'):
|
if (cmd != b'F'):
|
||||||
raise ConnectionException('Wrong update status packet')
|
raise ConnectionException('Wrong update status packet')
|
||||||
|
status = self.__UpdateStatus(self.__get_int(data))
|
||||||
print(f'Update status [{status.name}]')
|
print(f'Update status [{status.name}]')
|
||||||
if (status == self.__UpdateStatus.ERROR):
|
if (status == self.__UpdateStatus.ERROR):
|
||||||
raise ConnectionException('Update error, device is most likely bricked')
|
raise ConnectionException('Update error, device is most likely bricked')
|
||||||
@ -417,7 +469,7 @@ 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 __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_READ_BLOCK = 1
|
||||||
CMD_WRITE_BLOCK = 2
|
CMD_WRITE_BLOCK = 2
|
||||||
cmd = self.__get_int(data[0:])
|
cmd = self.__get_int(data[0:])
|
||||||
@ -427,7 +479,7 @@ class SC64:
|
|||||||
head = (track_head_block >> 1) & 0x1
|
head = (track_head_block >> 1) & 0x1
|
||||||
block = track_head_block & 0x1
|
block = track_head_block & 0x1
|
||||||
try:
|
try:
|
||||||
if (not dd.loaded):
|
if (not dd or not dd.loaded):
|
||||||
raise BadBlockError
|
raise BadBlockError
|
||||||
if (cmd == CMD_READ_BLOCK):
|
if (cmd == CMD_READ_BLOCK):
|
||||||
block_data = dd.read_block(track, head, 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:
|
def debug_loop(self, isv: bool=False, disks: Optional[list[str]]=None) -> None:
|
||||||
dd = None
|
dd = None
|
||||||
drive_type = None
|
|
||||||
current_image = 0
|
current_image = 0
|
||||||
next_image = 0
|
next_image = 0
|
||||||
|
|
||||||
self.__set_config(self.__CfgId.ROM_WRITE_ENABLE, isv)
|
self.__set_config(self.__CfgId.ROM_WRITE_ENABLE, isv)
|
||||||
self.__set_config(self.__CfgId.ISV_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):
|
if (disks):
|
||||||
dd = DD64Image()
|
dd = DD64Image()
|
||||||
|
drive_type = None
|
||||||
for path in disks:
|
for path in disks:
|
||||||
try:
|
try:
|
||||||
dd.load(path)
|
dd.load(path)
|
||||||
@ -469,15 +525,22 @@ class SC64:
|
|||||||
dd = None
|
dd = None
|
||||||
print(f'64DD disabled, incorrect disk images provided: {e}')
|
print(f'64DD disabled, incorrect disk images provided: {e}')
|
||||||
break
|
break
|
||||||
|
|
||||||
drive_type = self.__DDDriveType.RETAIL if drive_type == 'retail' else self.__DDDriveType.DEVELOPMENT
|
|
||||||
|
|
||||||
if (dd):
|
if (dd):
|
||||||
self.__set_config(self.__CfgId.DD_MODE, self.__DDMode.FULL)
|
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.DD_DISK_STATE, self.__DDDiskState.EJECTED)
|
||||||
self.__set_config(self.__CfgId.BUTTON_MODE, self.__ButtonMode.USB_PACKET)
|
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):
|
while (True):
|
||||||
packet = self.__link.get_packet()
|
packet = self.__link.get_packet()
|
||||||
if (packet != None):
|
if (packet != None):
|
||||||
@ -489,44 +552,131 @@ class SC64:
|
|||||||
if (cmd == b'U'):
|
if (cmd == b'U'):
|
||||||
self.__handle_usb_packet(data)
|
self.__handle_usb_packet(data)
|
||||||
if (cmd == b'B'):
|
if (cmd == b'B'):
|
||||||
if (dd.loaded):
|
if (not dd.loaded):
|
||||||
self.__set_config(self.__CfgId.DD_DISK_STATE, self.__DDDiskState.EJECTED)
|
|
||||||
dd.unload()
|
|
||||||
print(f'64DD disk ejected [{disks[current_image]}]')
|
|
||||||
else:
|
|
||||||
dd.load(disks[next_image])
|
dd.load(disks[next_image])
|
||||||
self.__set_config(self.__CfgId.DD_DISK_STATE, self.__DDDiskState.INSERTED)
|
self.__set_config(self.__CfgId.DD_DISK_STATE, self.__DDDiskState.INSERTED)
|
||||||
current_image = next_image
|
current_image = next_image
|
||||||
next_image += 1
|
next_image += 1
|
||||||
if (next_image >= len(disks)):
|
if (next_image >= len(disks)):
|
||||||
next_image = 0
|
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__':
|
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:
|
try:
|
||||||
sc64 = SC64()
|
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.reset_state()
|
||||||
|
|
||||||
# sc64.set_save_type(SC64.SaveType.EEPROM_4K)
|
if (args.boot != None):
|
||||||
# with open('S:/n64/saves/SM64.eep', 'rb+') as f:
|
sc64.set_boot_mode(args.boot)
|
||||||
# sc64.upload_save(f.read())
|
print(f'Boot mode set to [{args.boot.name}]')
|
||||||
# with open('S:/n64/roms/baserom.us.z64', 'rb+') as f:
|
|
||||||
# sc64.upload_rom(f.read())
|
|
||||||
# sc64.set_boot_mode(SC64.BootMode.ROM)
|
|
||||||
|
|
||||||
# with open('S:/n64/64dd/ipl/NDDJ2.n64', 'rb+') as f:
|
if (args.tv != None):
|
||||||
# sc64.upload_ddipl(f.read())
|
sc64.set_tv_type(args.tv)
|
||||||
# sc64.set_boot_mode(SC64.BootMode.DDIPL)
|
print(f'TV type set to [{args.tv.name}]')
|
||||||
|
|
||||||
# try:
|
if (args.cic != None):
|
||||||
# disks = [
|
sc64.set_cic_seed(args.cic)
|
||||||
# 'S:/n64/64dd/rtl/NUD-DMPJ-JPN (sealed).ndd',
|
print(f'CIC seed set to [0x{args.cic:X}]')
|
||||||
# 'S:/n64/64dd/rtl/NUD-DMBJ-JPN.ndd'
|
|
||||||
# ]
|
if (args.rtc):
|
||||||
# sc64.debug_loop(disks=disks)
|
sc64.set_rtc(datetime.now())
|
||||||
# except KeyboardInterrupt:
|
|
||||||
# pass
|
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:
|
except ConnectionException as e:
|
||||||
print(f'SC64 error: {e}')
|
print(f'\nSC64 error: {e}')
|
||||||
|
Loading…
Reference in New Issue
Block a user