mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2024-11-25 23:24:15 +01:00
escaping
This commit is contained in:
parent
bad51d71c6
commit
9bca165b4c
@ -20,6 +20,10 @@ module cpu_usb (
|
|||||||
logic tx_write;
|
logic tx_write;
|
||||||
logic [7:0] tx_wdata;
|
logic [7:0] tx_wdata;
|
||||||
|
|
||||||
|
logic rx_escape_valid;
|
||||||
|
logic rx_escape_ack;
|
||||||
|
logic [7:0] rx_escape;
|
||||||
|
|
||||||
logic cpu_rx_read;
|
logic cpu_rx_read;
|
||||||
logic cpu_tx_write;
|
logic cpu_tx_write;
|
||||||
|
|
||||||
@ -45,9 +49,10 @@ module cpu_usb (
|
|||||||
always_comb begin
|
always_comb begin
|
||||||
bus.rdata = 32'd0;
|
bus.rdata = 32'd0;
|
||||||
if (bus.ack) begin
|
if (bus.ack) begin
|
||||||
case (bus.address[2:2])
|
case (bus.address[3:2])
|
||||||
0: bus.rdata = {26'd0, usb_pwren, usb_enabled, 2'b00, ~tx_full, ~rx_empty};
|
0: bus.rdata = {25'd0, rx_escape_valid, usb_pwren, usb_enabled, 2'b00, ~tx_full, ~rx_empty};
|
||||||
1: bus.rdata = {24'd0, rx_rdata};
|
1: bus.rdata = {24'd0, rx_rdata};
|
||||||
|
2: bus.rdata = {24'd0, rx_escape};
|
||||||
default: bus.rdata = 32'd0;
|
default: bus.rdata = 32'd0;
|
||||||
endcase
|
endcase
|
||||||
end
|
end
|
||||||
@ -60,6 +65,8 @@ module cpu_usb (
|
|||||||
tx_flush <= 1'b0;
|
tx_flush <= 1'b0;
|
||||||
cpu_tx_write <= 1'b0;
|
cpu_tx_write <= 1'b0;
|
||||||
|
|
||||||
|
rx_escape_ack <= 1'b0;
|
||||||
|
|
||||||
if (sys.reset) begin
|
if (sys.reset) begin
|
||||||
usb_enabled <= 1'b0;
|
usb_enabled <= 1'b0;
|
||||||
end else begin
|
end else begin
|
||||||
@ -67,6 +74,7 @@ module cpu_usb (
|
|||||||
case (bus.address[2:2])
|
case (bus.address[2:2])
|
||||||
2'd0: begin
|
2'd0: begin
|
||||||
if (bus.wmask[0]) begin
|
if (bus.wmask[0]) begin
|
||||||
|
rx_escape_ack <= bus.wdata[7];
|
||||||
{usb_enabled, tx_flush, rx_flush} <= bus.wdata[4:2];
|
{usb_enabled, tx_flush, rx_flush} <= bus.wdata[4:2];
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -101,7 +109,11 @@ module cpu_usb (
|
|||||||
.tx_flush(tx_flush),
|
.tx_flush(tx_flush),
|
||||||
.tx_full(tx_full),
|
.tx_full(tx_full),
|
||||||
.tx_write(tx_write),
|
.tx_write(tx_write),
|
||||||
.tx_wdata(tx_wdata)
|
.tx_wdata(tx_wdata),
|
||||||
|
|
||||||
|
.rx_escape_valid(rx_escape_valid),
|
||||||
|
.rx_escape_ack(rx_escape_ack),
|
||||||
|
.rx_escape(rx_escape)
|
||||||
);
|
);
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
@ -16,9 +16,15 @@ module usb_ft1248 (
|
|||||||
input tx_flush,
|
input tx_flush,
|
||||||
output tx_full,
|
output tx_full,
|
||||||
input tx_write,
|
input tx_write,
|
||||||
input [7:0] tx_wdata
|
input [7:0] tx_wdata,
|
||||||
|
|
||||||
|
output rx_escape_valid,
|
||||||
|
input rx_escape_ack,
|
||||||
|
output [7:0] rx_escape
|
||||||
);
|
);
|
||||||
|
|
||||||
|
parameter bit [7:0] ESCAPE_CHARACTER = 8'h1B;
|
||||||
|
|
||||||
// FIFOs
|
// FIFOs
|
||||||
|
|
||||||
logic rx_full;
|
logic rx_full;
|
||||||
@ -29,6 +35,9 @@ module usb_ft1248 (
|
|||||||
logic tx_read;
|
logic tx_read;
|
||||||
logic [7:0] tx_rdata;
|
logic [7:0] tx_rdata;
|
||||||
|
|
||||||
|
logic rx_wdata_valid;
|
||||||
|
logic rx_escape_active;
|
||||||
|
|
||||||
intel_fifo_8 fifo_8_rx_inst (
|
intel_fifo_8 fifo_8_rx_inst (
|
||||||
.clock(sys.clk),
|
.clock(sys.clk),
|
||||||
.sclr(rx_flush || !usb_enabled),
|
.sclr(rx_flush || !usb_enabled),
|
||||||
@ -56,6 +65,41 @@ module usb_ft1248 (
|
|||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// Escape character detection
|
||||||
|
|
||||||
|
always_comb begin
|
||||||
|
rx_write = 1'b0;
|
||||||
|
if (rx_wdata_valid) begin
|
||||||
|
rx_write = rx_escape_active ? rx_wdata == ESCAPE_CHARACTER : rx_wdata != ESCAPE_CHARACTER;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
always_ff @(posedge sys.clk) begin
|
||||||
|
if (sys.reset || !usb_enabled) begin
|
||||||
|
rx_escape_valid <= 1'b0;
|
||||||
|
rx_escape_active <= 1'b0;
|
||||||
|
end else begin
|
||||||
|
if (rx_escape_ack) begin
|
||||||
|
rx_escape_valid <= 1'b0;
|
||||||
|
end
|
||||||
|
|
||||||
|
if (rx_wdata_valid) begin
|
||||||
|
if (!rx_escape_active) begin
|
||||||
|
if (rx_wdata == ESCAPE_CHARACTER) begin
|
||||||
|
rx_escape_active <= 1'b1;
|
||||||
|
end
|
||||||
|
end else begin
|
||||||
|
rx_escape_active <= 1'b0;
|
||||||
|
rx_escape <= rx_wdata;
|
||||||
|
if (rx_wdata != ESCAPE_CHARACTER) begin
|
||||||
|
rx_escape_valid <= 1'b1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
// FT1248 interface controller
|
// FT1248 interface controller
|
||||||
|
|
||||||
typedef enum bit [1:0] {
|
typedef enum bit [1:0] {
|
||||||
@ -149,7 +193,7 @@ module usb_ft1248 (
|
|||||||
end
|
end
|
||||||
|
|
||||||
always_ff @(posedge sys.clk) begin
|
always_ff @(posedge sys.clk) begin
|
||||||
rx_write <= 1'b0;
|
rx_wdata_valid <= 1'b0;
|
||||||
tx_read <= 1'b0;
|
tx_read <= 1'b0;
|
||||||
|
|
||||||
if (clock_phase[P_RISING]) begin
|
if (clock_phase[P_RISING]) begin
|
||||||
@ -161,7 +205,7 @@ module usb_ft1248 (
|
|||||||
end else begin
|
end else begin
|
||||||
case (state)
|
case (state)
|
||||||
S_TRY_RX: begin
|
S_TRY_RX: begin
|
||||||
if (!rx_full) begin
|
if (!rx_full && !rx_escape_valid) begin
|
||||||
state <= S_COMMAND;
|
state <= S_COMMAND;
|
||||||
is_cmd_write <= 1'b0;
|
is_cmd_write <= 1'b0;
|
||||||
nibble_counter <= 2'b11;
|
nibble_counter <= 2'b11;
|
||||||
@ -202,9 +246,9 @@ module usb_ft1248 (
|
|||||||
if (clock_phase[P_RISING]) begin
|
if (clock_phase[P_RISING]) begin
|
||||||
rx_wdata <= {usb_miosi_input, rx_wdata[7:4]};
|
rx_wdata <= {usb_miosi_input, rx_wdata[7:4]};
|
||||||
if (nibble_counter[0]) begin
|
if (nibble_counter[0]) begin
|
||||||
rx_write <= !is_cmd_write;
|
rx_wdata_valid <= !is_cmd_write;
|
||||||
end
|
end
|
||||||
if (usb_miso_input || (!is_cmd_write && rx_full) || (is_cmd_write && tx_empty)) begin
|
if (usb_miso_input || (!is_cmd_write && (rx_full || rx_escape_valid)) || (is_cmd_write && tx_empty)) begin
|
||||||
state <= is_cmd_write ? S_TRY_RX : S_TRY_TX;
|
state <= is_cmd_write ? S_TRY_RX : S_TRY_TX;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
256
sw/pc/dum.py
256
sw/pc/dum.py
@ -1,256 +0,0 @@
|
|||||||
from serial import Serial, SerialException
|
|
||||||
from serial.tools import list_ports
|
|
||||||
import argparse
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SC64Exception(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SC64:
|
|
||||||
__CFG_ID_SCR = 0
|
|
||||||
__CFG_ID_SDRAM_SWITCH = 1
|
|
||||||
__CFG_ID_SDRAM_WRITABLE = 2
|
|
||||||
__CFG_ID_DD_ENABLE = 3
|
|
||||||
__CFG_ID_SAVE_TYPE = 4
|
|
||||||
__CFG_ID_CIC_SEED = 5
|
|
||||||
__CFG_ID_TV_TYPE = 6
|
|
||||||
__CFG_ID_SAVE_OFFEST = 7
|
|
||||||
__CFG_ID_DD_OFFEST = 8
|
|
||||||
__CFG_ID_SKIP_BOOTLOADER = 9
|
|
||||||
__CFG_ID_FLASH_OPERATION = 10
|
|
||||||
__CFG_ID_RECONFIGURE = 11
|
|
||||||
|
|
||||||
__SC64_VERSION_V2 = 0x53437632
|
|
||||||
|
|
||||||
__UPDATE_OFFSET = 0x03B60000
|
|
||||||
|
|
||||||
__CHUNK_SIZE = 1024 * 1024
|
|
||||||
|
|
||||||
__MIN_ROM_LENGTH = 0x101000
|
|
||||||
__DDIPL_ROM_LENGTH = 0x400000
|
|
||||||
|
|
||||||
__serial = None
|
|
||||||
|
|
||||||
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.__find_sc64()
|
|
||||||
|
|
||||||
|
|
||||||
def __del__(self) -> None:
|
|
||||||
if (self.__serial):
|
|
||||||
self.__serial.close()
|
|
||||||
|
|
||||||
|
|
||||||
def __align(self, value: int, align: int) -> int:
|
|
||||||
return (value + (align - 1)) & ~(align - 1)
|
|
||||||
|
|
||||||
|
|
||||||
def __read(self, bytes: int) -> bytes:
|
|
||||||
return self.__serial.read(bytes)
|
|
||||||
|
|
||||||
|
|
||||||
def __write(self, data: bytes) -> None:
|
|
||||||
self.__serial.write(data)
|
|
||||||
|
|
||||||
|
|
||||||
def __write_dummy(self, length: int) -> None:
|
|
||||||
self.__write(bytes(length))
|
|
||||||
|
|
||||||
|
|
||||||
def __read_int(self) -> int:
|
|
||||||
return int.from_bytes(self.__read(4), byteorder="big")
|
|
||||||
|
|
||||||
|
|
||||||
def __write_int(self, value: int) -> None:
|
|
||||||
self.__write(value.to_bytes(4, byteorder="big"))
|
|
||||||
|
|
||||||
|
|
||||||
def __read_cmd_status(self, cmd: str) -> None:
|
|
||||||
if (self.__read(4).decode() != f"CMP{cmd[0]}"):
|
|
||||||
raise SC64Exception("Wrong command response")
|
|
||||||
|
|
||||||
|
|
||||||
def __write_cmd(self, cmd: str, arg1: int, arg2: int) -> None:
|
|
||||||
self.__write(f"CMD{cmd[0]}".encode())
|
|
||||||
self.__write_int(arg1)
|
|
||||||
self.__write_int(arg2)
|
|
||||||
|
|
||||||
|
|
||||||
def __find_sc64(self) -> None:
|
|
||||||
ports = list_ports.comports()
|
|
||||||
device_found = False
|
|
||||||
|
|
||||||
for p in ports:
|
|
||||||
if (p.vid == 0x0403 and p.pid == 0x6014 and p.serial_number.startswith("SC64")):
|
|
||||||
try:
|
|
||||||
self.__serial = Serial(p.device, timeout=5.0, write_timeout=5.0)
|
|
||||||
self.__probe_device()
|
|
||||||
except SerialException or SC64Exception:
|
|
||||||
if (self.__serial):
|
|
||||||
self.__serial.close()
|
|
||||||
continue
|
|
||||||
device_found = True
|
|
||||||
break
|
|
||||||
|
|
||||||
if (not device_found):
|
|
||||||
raise SC64Exception("No SummerCart64 device was found")
|
|
||||||
|
|
||||||
|
|
||||||
def __probe_device(self) -> None:
|
|
||||||
self.__write_cmd("V", 0, 0)
|
|
||||||
version = self.__read_int()
|
|
||||||
self.__read_cmd_status("V")
|
|
||||||
if (version != self.__SC64_VERSION_V2):
|
|
||||||
raise SC64Exception(f"Unknown hardware version: {hex(version)}")
|
|
||||||
|
|
||||||
|
|
||||||
def __query_config(self, id: int, arg: int = 0) -> int:
|
|
||||||
self.__write_cmd("Q", id, arg)
|
|
||||||
value = self.__read_int()
|
|
||||||
self.__read_cmd_status("Q")
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
def __change_config(self, id: int, arg: int = 0, ignore_response: bool = False) -> None:
|
|
||||||
self.__write_cmd("C", id, arg)
|
|
||||||
if (not ignore_response):
|
|
||||||
self.__read_cmd_status("C")
|
|
||||||
|
|
||||||
|
|
||||||
def __read_file_from_sdram(self, file: str, offset: int, length: int, align: int = 2) -> None:
|
|
||||||
with open(file, "wb") as f:
|
|
||||||
transfer_size = self.__align(length, align)
|
|
||||||
self.__write_cmd("R", offset, transfer_size)
|
|
||||||
current_position = 0
|
|
||||||
while (current_position < length):
|
|
||||||
chunk_size = min(self.__CHUNK_SIZE, length - current_position)
|
|
||||||
f.write(self.__read(chunk_size))
|
|
||||||
current_position += chunk_size
|
|
||||||
if (transfer_size != length):
|
|
||||||
self.__read(transfer_size - length)
|
|
||||||
self.__read_cmd_status("R")
|
|
||||||
|
|
||||||
|
|
||||||
def __write_file_to_sdram(self, file: str, offset: int, align: int = 2, min_length: int = 0) -> None:
|
|
||||||
with open(file, "rb") as f:
|
|
||||||
length = os.fstat(f.fileno()).st_size
|
|
||||||
transfer_size = self.__align(max(length, min_length), align)
|
|
||||||
self.__write_cmd("W", offset, transfer_size)
|
|
||||||
while (f.tell() < length):
|
|
||||||
self.__write(f.read(min(self.__CHUNK_SIZE, length - f.tell())))
|
|
||||||
if (transfer_size != length):
|
|
||||||
self.__write_dummy(transfer_size - length)
|
|
||||||
self.__read_cmd_status("W")
|
|
||||||
|
|
||||||
|
|
||||||
def __get_save_length(self) -> None:
|
|
||||||
save_type = self.__query_config(self.__CFG_ID_SAVE_TYPE)
|
|
||||||
return {
|
|
||||||
0: 0,
|
|
||||||
1: 512,
|
|
||||||
2: 2048,
|
|
||||||
3: (32 * 1024),
|
|
||||||
4: (128 * 1024),
|
|
||||||
5: (3 * 32 * 1024),
|
|
||||||
6: (128 * 1024),
|
|
||||||
7: 0
|
|
||||||
}[save_type]
|
|
||||||
|
|
||||||
|
|
||||||
def __reconfigure(self) -> None:
|
|
||||||
magic = self.__query_config(self.__CFG_ID_RECONFIGURE)
|
|
||||||
self.__change_config(self.__CFG_ID_RECONFIGURE, magic, ignore_response=True)
|
|
||||||
time.sleep(0.2)
|
|
||||||
|
|
||||||
|
|
||||||
def update_firmware(self, file: str, backup_file: str = None) -> None:
|
|
||||||
if (backup_file != None):
|
|
||||||
backup_length = self.__query_config(self.__CFG_ID_FLASH_OPERATION, self.__UPDATE_OFFSET)
|
|
||||||
self.__read_file_from_sdram(backup_file, self.__UPDATE_OFFSET, backup_length)
|
|
||||||
self.__write_file_to_sdram(file, self.__UPDATE_OFFSET)
|
|
||||||
saved_timeout = self.__serial.timeout
|
|
||||||
self.__serial.timeout = 20.0
|
|
||||||
self.__change_config(self.__CFG_ID_FLASH_OPERATION, self.__UPDATE_OFFSET)
|
|
||||||
self.__serial.timeout = saved_timeout
|
|
||||||
self.__reconfigure()
|
|
||||||
self.__probe_device()
|
|
||||||
|
|
||||||
|
|
||||||
def set_skip_bootloader(self, enable: bool) -> None:
|
|
||||||
self.__change_config(self.__CFG_ID_SKIP_BOOTLOADER, 1 if enable else 0)
|
|
||||||
|
|
||||||
|
|
||||||
def download_rom(self, file: str, length: int, offset: int = 0) -> None:
|
|
||||||
self.__read_file_from_sdram(file, offset, length)
|
|
||||||
|
|
||||||
|
|
||||||
def upload_rom(self, file: str, offset: int = 0) -> None:
|
|
||||||
self.__write_file_to_sdram(file, offset, min_length=self.__MIN_ROM_LENGTH)
|
|
||||||
|
|
||||||
|
|
||||||
def set_save_type(self, type: int) -> None:
|
|
||||||
if (type >= 0 and type <= 6):
|
|
||||||
self.__change_config(self.__CFG_ID_SAVE_TYPE, type)
|
|
||||||
else:
|
|
||||||
raise SC64Exception("Save type outside of supported values")
|
|
||||||
|
|
||||||
|
|
||||||
def download_save(self, file: str) -> None:
|
|
||||||
length = self.__get_save_length()
|
|
||||||
if (length > 0):
|
|
||||||
offset = self.__query_config(self.__CFG_ID_SAVE_OFFEST)
|
|
||||||
self.__read_file_from_sdram(file, offset, length)
|
|
||||||
else:
|
|
||||||
raise SC64Exception("Can't read save data - no save type is set")
|
|
||||||
|
|
||||||
|
|
||||||
def upload_save(self, file: str) -> None:
|
|
||||||
length = self.__get_save_length()
|
|
||||||
save_length = os.path.getsize(file)
|
|
||||||
if (length <= 0):
|
|
||||||
raise SC64Exception("Can't write save data - no save type is set")
|
|
||||||
elif (length != save_length):
|
|
||||||
raise SC64Exception("Can't write save data - save file size is different than expected")
|
|
||||||
else:
|
|
||||||
offset = self.__query_config(self.__CFG_ID_SAVE_OFFEST)
|
|
||||||
self.__write_file_to_sdram(file, offset)
|
|
||||||
|
|
||||||
|
|
||||||
def set_dd_enable(self, enable: bool) -> None:
|
|
||||||
self.__change_config(self.__CFG_ID_DD_ENABLE, 1 if enable else 0)
|
|
||||||
|
|
||||||
|
|
||||||
def download_dd_ipl(self, file: str) -> None:
|
|
||||||
dd_ipl_offset = self.__query_config(self.__CFG_ID_DD_OFFEST)
|
|
||||||
self.__read_file_from_sdram(file, dd_ipl_offset, length=self.__DDIPL_ROM_LENGTH)
|
|
||||||
|
|
||||||
|
|
||||||
def upload_dd_ipl(self, file: str, offset: int = None) -> None:
|
|
||||||
if (offset != None):
|
|
||||||
self.__change_config(self.__CFG_ID_DD_OFFEST, offset)
|
|
||||||
dd_ipl_offset = self.__query_config(self.__CFG_ID_DD_OFFEST)
|
|
||||||
self.__write_file_to_sdram(file, dd_ipl_offset, min_length=self.__DDIPL_ROM_LENGTH)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
try:
|
|
||||||
sc64 = SC64()
|
|
||||||
sc64.update_firmware("../../fw/output_files/SC64_update.bin", "SC64_backup.bin")
|
|
||||||
sc64.set_skip_bootloader(False)
|
|
||||||
sc64.set_save_type(1)
|
|
||||||
sc64.set_dd_enable(True)
|
|
||||||
sc64.upload_rom("rom.z64")
|
|
||||||
sc64.upload_dd_ipl("ddipl.z64")
|
|
||||||
sc64.upload_save("saves/save.eep")
|
|
||||||
sc64.download_rom("rom.bin", 0x1000)
|
|
||||||
sc64.download_save("save.bin")
|
|
||||||
sc64.download_dd_ipl("dd_ipl.bin")
|
|
||||||
except SC64Exception as e:
|
|
||||||
print(e)
|
|
2
sw/pc/requirements.txt
Normal file
2
sw/pc/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
progressbar2==3.55.0
|
||||||
|
pyserial==3.5
|
460
sw/pc/sc64.py
Normal file
460
sw/pc/sc64.py
Normal file
@ -0,0 +1,460 @@
|
|||||||
|
from serial import Serial, SerialException
|
||||||
|
from serial.tools import list_ports
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import progressbar
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SC64Exception(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SC64:
|
||||||
|
__CFG_ID_SCR = 0
|
||||||
|
__CFG_ID_SDRAM_SWITCH = 1
|
||||||
|
__CFG_ID_SDRAM_WRITABLE = 2
|
||||||
|
__CFG_ID_DD_ENABLE = 3
|
||||||
|
__CFG_ID_SAVE_TYPE = 4
|
||||||
|
__CFG_ID_CIC_SEED = 5
|
||||||
|
__CFG_ID_TV_TYPE = 6
|
||||||
|
__CFG_ID_SAVE_OFFEST = 7
|
||||||
|
__CFG_ID_DD_OFFEST = 8
|
||||||
|
__CFG_ID_SKIP_BOOTLOADER = 9
|
||||||
|
__CFG_ID_FLASH_OPERATION = 10
|
||||||
|
__CFG_ID_RECONFIGURE = 11
|
||||||
|
|
||||||
|
__SC64_VERSION_V2 = 0x53437632
|
||||||
|
|
||||||
|
__UPDATE_OFFSET = 0x03B60000
|
||||||
|
|
||||||
|
__CHUNK_SIZE = 256 * 1024
|
||||||
|
|
||||||
|
__MIN_ROM_LENGTH = 0x101000
|
||||||
|
__DDIPL_ROM_LENGTH = 0x400000
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.__serial = None
|
||||||
|
self.__progress_init = None
|
||||||
|
self.__progress_value = None
|
||||||
|
self.__progress_finish = None
|
||||||
|
self.__find_sc64()
|
||||||
|
|
||||||
|
|
||||||
|
def __del__(self) -> None:
|
||||||
|
if (self.__serial):
|
||||||
|
self.__serial.close()
|
||||||
|
|
||||||
|
|
||||||
|
def __set_progress_init(self, value: int, label: str) -> None:
|
||||||
|
if (self.__progress_init != None):
|
||||||
|
self.__progress_init(value, label)
|
||||||
|
|
||||||
|
|
||||||
|
def __set_progress_value(self, value: int) -> None:
|
||||||
|
if (self.__progress_value != None):
|
||||||
|
self.__progress_value(value)
|
||||||
|
|
||||||
|
|
||||||
|
def __set_progress_finish(self) -> None:
|
||||||
|
if (self.__progress_finish != None):
|
||||||
|
self.__progress_finish()
|
||||||
|
|
||||||
|
|
||||||
|
def __align(self, value: int, align: int) -> int:
|
||||||
|
return (value + (align - 1)) & ~(align - 1)
|
||||||
|
|
||||||
|
|
||||||
|
def __escape(self, data: bytes) -> bytes:
|
||||||
|
return re.sub(b'\x1B', b'\x1B\x1B', data)
|
||||||
|
|
||||||
|
|
||||||
|
def __reset_link(self) -> None:
|
||||||
|
self.__serial.write(b'\x1BR')
|
||||||
|
|
||||||
|
|
||||||
|
def __read(self, bytes: int) -> bytes:
|
||||||
|
return self.__serial.read(bytes)
|
||||||
|
|
||||||
|
|
||||||
|
def __write(self, data: bytes) -> None:
|
||||||
|
self.__serial.write(self.__escape(data))
|
||||||
|
|
||||||
|
|
||||||
|
def __write_dummy(self, length: int) -> None:
|
||||||
|
self.__write(bytes(length))
|
||||||
|
|
||||||
|
|
||||||
|
def __read_int(self) -> int:
|
||||||
|
return int.from_bytes(self.__read(4), byteorder="big")
|
||||||
|
|
||||||
|
|
||||||
|
def __write_int(self, value: int) -> None:
|
||||||
|
self.__write(value.to_bytes(4, byteorder="big"))
|
||||||
|
|
||||||
|
|
||||||
|
def __read_cmd_status(self, cmd: str) -> None:
|
||||||
|
if (self.__read(4) != f"CMP{cmd[0]}".encode(encoding="ascii")):
|
||||||
|
raise SC64Exception("Wrong command response")
|
||||||
|
|
||||||
|
|
||||||
|
def __write_cmd(self, cmd: str, arg1: int, arg2: int) -> None:
|
||||||
|
self.__write(f"CMD{cmd[0]}".encode())
|
||||||
|
self.__write_int(arg1)
|
||||||
|
self.__write_int(arg2)
|
||||||
|
|
||||||
|
|
||||||
|
def __find_sc64(self) -> None:
|
||||||
|
ports = list_ports.comports()
|
||||||
|
device_found = False
|
||||||
|
|
||||||
|
if (self.__serial != None and self.__serial.isOpen()):
|
||||||
|
self.__serial.close()
|
||||||
|
|
||||||
|
for p in ports:
|
||||||
|
if (p.vid == 0x0403 and p.pid == 0x6014 and p.serial_number.startswith("SC64")):
|
||||||
|
try:
|
||||||
|
self.__serial = Serial(p.device, timeout=1.0, write_timeout=1.0)
|
||||||
|
self.__serial.flushOutput()
|
||||||
|
self.__reset_link()
|
||||||
|
while (self.__serial.in_waiting):
|
||||||
|
self.__serial.read_all()
|
||||||
|
time.sleep(0.1)
|
||||||
|
self.__probe_device()
|
||||||
|
except (SerialException, SC64Exception):
|
||||||
|
if (self.__serial):
|
||||||
|
self.__serial.close()
|
||||||
|
continue
|
||||||
|
device_found = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if (not device_found):
|
||||||
|
raise SC64Exception("No SummerCart64 device was found")
|
||||||
|
|
||||||
|
|
||||||
|
def __probe_device(self) -> None:
|
||||||
|
self.__write_cmd("V", 0, 0)
|
||||||
|
version = self.__read_int()
|
||||||
|
self.__read_cmd_status("V")
|
||||||
|
if (version != self.__SC64_VERSION_V2):
|
||||||
|
raise SC64Exception(f"Unknown hardware version: {hex(version)}")
|
||||||
|
|
||||||
|
|
||||||
|
def __query_config(self, id: int, arg: int = 0) -> int:
|
||||||
|
self.__write_cmd("Q", id, arg)
|
||||||
|
value = self.__read_int()
|
||||||
|
self.__read_cmd_status("Q")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def __change_config(self, id: int, arg: int = 0, ignore_response: bool = False) -> None:
|
||||||
|
self.__write_cmd("C", id, arg)
|
||||||
|
if (not ignore_response):
|
||||||
|
self.__read_cmd_status("C")
|
||||||
|
|
||||||
|
|
||||||
|
def __read_file_from_sdram(self, file: str, offset: int, length: int, align: int = 2) -> None:
|
||||||
|
with open(file, "wb") as f:
|
||||||
|
transfer_size = self.__align(length, align)
|
||||||
|
self.__set_progress_init(transfer_size, os.path.basename(f.name))
|
||||||
|
self.__write_cmd("R", offset, transfer_size)
|
||||||
|
while (f.tell() < length):
|
||||||
|
chunk_size = min(self.__CHUNK_SIZE, length - f.tell())
|
||||||
|
f.write(self.__read(chunk_size))
|
||||||
|
self.__set_progress_value(f.tell())
|
||||||
|
if (transfer_size != length):
|
||||||
|
self.__read(transfer_size - length)
|
||||||
|
self.__read_cmd_status("R")
|
||||||
|
self.__set_progress_finish()
|
||||||
|
|
||||||
|
|
||||||
|
def __write_file_to_sdram(self, file: str, offset: int, align: int = 2, min_length: int = 0) -> None:
|
||||||
|
with open(file, "rb") as f:
|
||||||
|
length = os.fstat(f.fileno()).st_size
|
||||||
|
transfer_size = self.__align(max(length, min_length), align)
|
||||||
|
self.__set_progress_init(transfer_size, os.path.basename(f.name))
|
||||||
|
self.__write_cmd("W", offset, transfer_size)
|
||||||
|
while (f.tell() < length):
|
||||||
|
self.__write(f.read(min(self.__CHUNK_SIZE, length - f.tell())))
|
||||||
|
self.__set_progress_value(f.tell())
|
||||||
|
if (transfer_size != length):
|
||||||
|
self.__write_dummy(transfer_size - length)
|
||||||
|
self.__read_cmd_status("W")
|
||||||
|
self.__set_progress_finish()
|
||||||
|
|
||||||
|
|
||||||
|
def __get_save_length(self) -> None:
|
||||||
|
save_type = self.__query_config(self.__CFG_ID_SAVE_TYPE)
|
||||||
|
return {
|
||||||
|
0: 0,
|
||||||
|
1: 512,
|
||||||
|
2: 2048,
|
||||||
|
3: (32 * 1024),
|
||||||
|
4: (128 * 1024),
|
||||||
|
5: (3 * 32 * 1024),
|
||||||
|
6: (128 * 1024)
|
||||||
|
}[save_type]
|
||||||
|
|
||||||
|
|
||||||
|
def __reconfigure(self) -> None:
|
||||||
|
magic = self.__query_config(self.__CFG_ID_RECONFIGURE)
|
||||||
|
self.__change_config(self.__CFG_ID_RECONFIGURE, magic, ignore_response=True)
|
||||||
|
time.sleep(0.2)
|
||||||
|
|
||||||
|
|
||||||
|
def backup_firmware(self, file: str) -> None:
|
||||||
|
length = self.__query_config(self.__CFG_ID_FLASH_OPERATION, self.__UPDATE_OFFSET)
|
||||||
|
self.__read_file_from_sdram(file, self.__UPDATE_OFFSET, length)
|
||||||
|
|
||||||
|
|
||||||
|
def update_firmware(self, file: str) -> None:
|
||||||
|
self.__write_file_to_sdram(file, self.__UPDATE_OFFSET)
|
||||||
|
saved_timeout = self.__serial.timeout
|
||||||
|
self.__serial.timeout = 20.0
|
||||||
|
self.__change_config(self.__CFG_ID_FLASH_OPERATION, self.__UPDATE_OFFSET)
|
||||||
|
self.__serial.timeout = saved_timeout
|
||||||
|
self.__reconfigure()
|
||||||
|
self.__find_sc64()
|
||||||
|
|
||||||
|
|
||||||
|
def set_skip_bootloader(self, enable: bool) -> None:
|
||||||
|
self.__change_config(self.__CFG_ID_SKIP_BOOTLOADER, 1 if enable else 0)
|
||||||
|
|
||||||
|
|
||||||
|
def set_tv_type(self, type: int = None) -> None:
|
||||||
|
if (type == None or type == 3):
|
||||||
|
self.__change_config(self.__CFG_ID_TV_TYPE, 3)
|
||||||
|
elif (type >= 0 and type <= 2):
|
||||||
|
self.__change_config(self.__CFG_ID_TV_TYPE, type)
|
||||||
|
else:
|
||||||
|
raise SC64Exception("TV type outside of supported values")
|
||||||
|
|
||||||
|
|
||||||
|
def get_tv_type_label(self, type: int) -> None:
|
||||||
|
if (type < 0 or type > 2):
|
||||||
|
return "Unknown"
|
||||||
|
return {
|
||||||
|
0: "PAL",
|
||||||
|
1: "NTSC",
|
||||||
|
2: "MPAL"
|
||||||
|
}[type]
|
||||||
|
|
||||||
|
|
||||||
|
def set_cic_seed(self, type: int = None) -> None:
|
||||||
|
if (type == None):
|
||||||
|
self.__change_config(self.__CFG_ID_CIC_SEED, 0xFFFF)
|
||||||
|
else:
|
||||||
|
self.__change_config(self.__CFG_ID_CIC_SEED, type)
|
||||||
|
|
||||||
|
|
||||||
|
def download_rom(self, file: str, length: int, offset: int = 0) -> None:
|
||||||
|
self.__read_file_from_sdram(file, offset, length)
|
||||||
|
|
||||||
|
|
||||||
|
def upload_rom(self, file: str, offset: int = 0) -> None:
|
||||||
|
self.__write_file_to_sdram(file, offset, min_length=self.__MIN_ROM_LENGTH)
|
||||||
|
|
||||||
|
|
||||||
|
def set_save_type(self, type: int) -> None:
|
||||||
|
if (type >= 0 and type <= 6):
|
||||||
|
self.__change_config(self.__CFG_ID_SAVE_TYPE, type)
|
||||||
|
else:
|
||||||
|
raise SC64Exception("Save type outside of supported values")
|
||||||
|
|
||||||
|
|
||||||
|
def get_save_type_label(self, type: int) -> None:
|
||||||
|
if (type < 0 or type > 6):
|
||||||
|
return "Unknown"
|
||||||
|
return {
|
||||||
|
0: "No save",
|
||||||
|
1: "EEPROM 4Kb",
|
||||||
|
2: "EEPROM 16Kb",
|
||||||
|
3: "SRAM 256Kb",
|
||||||
|
4: "FlashRAM 1Mb",
|
||||||
|
5: "SRAM 768Kb",
|
||||||
|
6: "FlashRAM 1Mb (Pokemon Stadium 2 special case)"
|
||||||
|
}[type]
|
||||||
|
|
||||||
|
|
||||||
|
def download_save(self, file: str) -> None:
|
||||||
|
length = self.__get_save_length()
|
||||||
|
if (length > 0):
|
||||||
|
offset = self.__query_config(self.__CFG_ID_SAVE_OFFEST)
|
||||||
|
self.__read_file_from_sdram(file, offset, length)
|
||||||
|
else:
|
||||||
|
raise SC64Exception("Can't read save data - no save type is set")
|
||||||
|
|
||||||
|
|
||||||
|
def upload_save(self, file: str) -> None:
|
||||||
|
length = self.__get_save_length()
|
||||||
|
save_length = os.path.getsize(file)
|
||||||
|
if (length <= 0):
|
||||||
|
raise SC64Exception("Can't write save data - no save type is set")
|
||||||
|
elif (length != save_length):
|
||||||
|
raise SC64Exception("Can't write save data - save file size is different than expected")
|
||||||
|
else:
|
||||||
|
offset = self.__query_config(self.__CFG_ID_SAVE_OFFEST)
|
||||||
|
self.__write_file_to_sdram(file, offset)
|
||||||
|
|
||||||
|
|
||||||
|
def set_dd_enable(self, enable: bool) -> None:
|
||||||
|
self.__change_config(self.__CFG_ID_DD_ENABLE, 1 if enable else 0)
|
||||||
|
|
||||||
|
|
||||||
|
def download_dd_ipl(self, file: str) -> None:
|
||||||
|
dd_ipl_offset = self.__query_config(self.__CFG_ID_DD_OFFEST)
|
||||||
|
self.__read_file_from_sdram(file, dd_ipl_offset, length=self.__DDIPL_ROM_LENGTH)
|
||||||
|
|
||||||
|
|
||||||
|
def upload_dd_ipl(self, file: str, offset: int = None) -> None:
|
||||||
|
if (offset != None):
|
||||||
|
self.__change_config(self.__CFG_ID_DD_OFFEST, offset)
|
||||||
|
dd_ipl_offset = self.__query_config(self.__CFG_ID_DD_OFFEST)
|
||||||
|
self.__write_file_to_sdram(file, dd_ipl_offset, min_length=self.__DDIPL_ROM_LENGTH)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SC64ProgressBar:
|
||||||
|
__LABEL_LENGTH = 30
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, sc64: SC64) -> None:
|
||||||
|
self.__sc64 = sc64
|
||||||
|
self.__widgets = [
|
||||||
|
"[ ",
|
||||||
|
progressbar.FormatLabel("{variables.label}", new_style=True),
|
||||||
|
" | ",
|
||||||
|
progressbar.Bar(left="", right=""),
|
||||||
|
" ",
|
||||||
|
progressbar.Percentage(),
|
||||||
|
" | ",
|
||||||
|
progressbar.DataSize(prefixes=(' ', 'Ki', 'Mi')),
|
||||||
|
" | ",
|
||||||
|
progressbar.AdaptiveTransferSpeed(),
|
||||||
|
" ]"
|
||||||
|
]
|
||||||
|
self.__variables = {"label": ""}
|
||||||
|
self.__bar = None
|
||||||
|
|
||||||
|
|
||||||
|
def __enter__(self) -> None:
|
||||||
|
if (self.__sc64):
|
||||||
|
self.__sc64._SC64__progress_init = self.__progress_init
|
||||||
|
self.__sc64._SC64__progress_value = self.__progress_value
|
||||||
|
self.__sc64._SC64__progress_finish = self.__progress_finish
|
||||||
|
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
||||||
|
if (self.__sc64):
|
||||||
|
self.__sc64._SC64__progress_init = None
|
||||||
|
self.__sc64._SC64__progress_value = None
|
||||||
|
self.__sc64._SC64__progress_finish = None
|
||||||
|
|
||||||
|
|
||||||
|
def __progress_init(self, value: int, label: str) -> None:
|
||||||
|
if (self.__bar == None):
|
||||||
|
self.__bar = progressbar.ProgressBar(widgets=self.__widgets, variables=self.__variables)
|
||||||
|
justified_label = label.ljust(self.__LABEL_LENGTH)
|
||||||
|
if (len(justified_label) > self.__LABEL_LENGTH):
|
||||||
|
justified_label = f"{justified_label[:self.__LABEL_LENGTH - 3]}..."
|
||||||
|
self.__bar.variables.label = justified_label
|
||||||
|
self.__bar.start(max_value=value)
|
||||||
|
|
||||||
|
|
||||||
|
def __progress_value(self, value: int) -> None:
|
||||||
|
self.__bar.update(value)
|
||||||
|
|
||||||
|
|
||||||
|
def __progress_finish(self) -> None:
|
||||||
|
self.__bar.finish(end="")
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description='SummerCart64 one stop control center')
|
||||||
|
parser.add_argument('-b', default=False, action='store_true', required=False, help="skip internal bootloader")
|
||||||
|
parser.add_argument('-t', default="3", required=False, help='set TV type (0 - 2)')
|
||||||
|
parser.add_argument('-c', default="0xFFFF", required=False, help='set CIC seed')
|
||||||
|
parser.add_argument('-s', default="0", required=False, help='set save type (0 - 6)')
|
||||||
|
parser.add_argument('-d', default=False, action='store_true', required=False, help='enable 64DD emulation')
|
||||||
|
parser.add_argument('-r', default=False, action='store_true', required=False, help='perform reading operation instead of writing')
|
||||||
|
parser.add_argument('-l', default="0x101000", required=False, help='specify ROM length to read')
|
||||||
|
parser.add_argument('-u', default=None, required=False, help='path to update file')
|
||||||
|
parser.add_argument('-e', default=None, required=False, help='path to save file')
|
||||||
|
parser.add_argument('-i', default=None, required=False, help='path to DDIPL file')
|
||||||
|
parser.add_argument('rom', default=None, help='path to ROM file', nargs='?')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if (len(sys.argv) <= 1):
|
||||||
|
parser.print_help()
|
||||||
|
parser.exit()
|
||||||
|
|
||||||
|
try:
|
||||||
|
sc64 = SC64()
|
||||||
|
|
||||||
|
skip_bootloader = args.b
|
||||||
|
save_type = int(args.s)
|
||||||
|
tv_type = int(args.t)
|
||||||
|
cic_seed = int(args.c, 0)
|
||||||
|
dd_enable = args.d
|
||||||
|
is_read = args.r
|
||||||
|
rom_length = int(args.l, 0)
|
||||||
|
update_file = args.u
|
||||||
|
save_file = args.e
|
||||||
|
dd_ipl_file = args.i
|
||||||
|
rom_file = args.rom
|
||||||
|
|
||||||
|
with SC64ProgressBar(sc64):
|
||||||
|
if (update_file):
|
||||||
|
if (is_read):
|
||||||
|
sc64.backup_firmware(update_file)
|
||||||
|
else:
|
||||||
|
sc64.update_firmware(update_file)
|
||||||
|
|
||||||
|
print(f"Setting save type to [{sc64.get_save_type_label(save_type)}]")
|
||||||
|
sc64.set_save_type(save_type)
|
||||||
|
|
||||||
|
if (not is_read):
|
||||||
|
print(f"Setting skip internal bootloader to [{'True' if skip_bootloader else 'False'}]")
|
||||||
|
sc64.set_skip_bootloader(skip_bootloader)
|
||||||
|
|
||||||
|
print(f"Setting TV type to [{sc64.get_tv_type_label(tv_type)}]")
|
||||||
|
sc64.set_tv_type(tv_type)
|
||||||
|
|
||||||
|
print(f"Setting CIC seed to [0x{cic_seed:02X}]")
|
||||||
|
sc64.set_cic_seed(cic_seed)
|
||||||
|
|
||||||
|
print(f"Setting 64DD emulation to [{'Enabled' if skip_bootloader else 'Disabled'}]")
|
||||||
|
sc64.set_dd_enable(dd_enable)
|
||||||
|
|
||||||
|
if (rom_file):
|
||||||
|
if (is_read):
|
||||||
|
if (rom_length > 0):
|
||||||
|
sc64.download_rom(rom_file, rom_length)
|
||||||
|
else:
|
||||||
|
raise SC64Exception("ROM length should be larger than 0")
|
||||||
|
else:
|
||||||
|
sc64.upload_rom(rom_file)
|
||||||
|
|
||||||
|
if (dd_ipl_file):
|
||||||
|
if (is_read):
|
||||||
|
sc64.download_dd_ipl(dd_ipl_file)
|
||||||
|
else:
|
||||||
|
sc64.upload_dd_ipl(dd_ipl_file)
|
||||||
|
|
||||||
|
if (save_file):
|
||||||
|
if (is_read):
|
||||||
|
sc64.download_save(save_file)
|
||||||
|
else:
|
||||||
|
sc64.upload_save(save_file)
|
||||||
|
|
||||||
|
except SC64Exception as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
parser.exit(1)
|
@ -76,7 +76,9 @@ typedef volatile struct i2c_regs {
|
|||||||
typedef volatile struct usb_regs {
|
typedef volatile struct usb_regs {
|
||||||
io32_t SCR;
|
io32_t SCR;
|
||||||
io8_t DR;
|
io8_t DR;
|
||||||
io8_t __padding[3];
|
io8_t __padding_1[3];
|
||||||
|
io8_t ESCAPE;
|
||||||
|
io8_t __padding_2[3];
|
||||||
} usb_regs_t;
|
} usb_regs_t;
|
||||||
|
|
||||||
#define USB_BASE (0x40000000UL)
|
#define USB_BASE (0x40000000UL)
|
||||||
@ -88,6 +90,8 @@ typedef volatile struct usb_regs {
|
|||||||
#define USB_SCR_FLUSH_TX (1 << 3)
|
#define USB_SCR_FLUSH_TX (1 << 3)
|
||||||
#define USB_SCR_ENABLED (1 << 4)
|
#define USB_SCR_ENABLED (1 << 4)
|
||||||
#define USB_SCR_PWREN (1 << 5)
|
#define USB_SCR_PWREN (1 << 5)
|
||||||
|
#define USB_SCR_ESCAPE_PENDING (1 << 6)
|
||||||
|
#define USB_SCR_ESCAPE_ACK (1 << 7)
|
||||||
|
|
||||||
|
|
||||||
typedef volatile struct uart_regs {
|
typedef volatile struct uart_regs {
|
||||||
|
@ -13,18 +13,19 @@ static bool rx_byte (uint8_t *data) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t rx_word_current_byte = 0;
|
||||||
|
static uint32_t rx_word_buffer = 0;
|
||||||
|
|
||||||
static bool rx_word (uint32_t *data) {
|
static bool rx_word (uint32_t *data) {
|
||||||
static uint8_t current_byte = 0;
|
|
||||||
static uint32_t buffer = 0;
|
|
||||||
uint8_t tmp;
|
uint8_t tmp;
|
||||||
|
|
||||||
while (rx_byte(&tmp)) {
|
while (rx_byte(&tmp)) {
|
||||||
buffer = (buffer << 8) | tmp;
|
rx_word_buffer = (rx_word_buffer << 8) | tmp;
|
||||||
current_byte += 1;
|
rx_word_current_byte += 1;
|
||||||
if (current_byte == 4) {
|
if (rx_word_current_byte == 4) {
|
||||||
current_byte = 0;
|
rx_word_current_byte = 0;
|
||||||
*data = buffer;
|
*data = rx_word_buffer;
|
||||||
buffer = 0;
|
rx_word_buffer = 0;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -43,12 +44,13 @@ static bool tx_byte (uint8_t data) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t tx_word_current_byte = 0;
|
||||||
|
|
||||||
static bool tx_word (uint32_t data) {
|
static bool tx_word (uint32_t data) {
|
||||||
static uint8_t current_byte = 0;
|
while (tx_byte(data >> ((3 - tx_word_current_byte) * 8))) {
|
||||||
while (tx_byte(data >> ((3 - current_byte) * 8))) {
|
tx_word_current_byte += 1;
|
||||||
current_byte += 1;
|
if (tx_word_current_byte == 4) {
|
||||||
if (current_byte == 4) {
|
tx_word_current_byte = 0;
|
||||||
current_byte = 0;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -152,25 +154,25 @@ void usb_debug_reset (void) {
|
|||||||
USB->SCR = USB_SCR_FLUSH_TX | USB_SCR_FLUSH_RX;
|
USB->SCR = USB_SCR_FLUSH_TX | USB_SCR_FLUSH_RX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t rx_cmd_current_byte = 0;
|
||||||
|
static uint32_t rx_cmd_buffer = 0;
|
||||||
|
|
||||||
static bool rx_cmd (uint32_t *data) {
|
static bool rx_cmd (uint32_t *data) {
|
||||||
static uint8_t current_byte = 0;
|
|
||||||
static uint32_t buffer = 0;
|
|
||||||
uint8_t tmp;
|
uint8_t tmp;
|
||||||
|
|
||||||
while (rx_byte(&tmp)) {
|
while (rx_byte(&tmp)) {
|
||||||
current_byte += 1;
|
rx_cmd_current_byte += 1;
|
||||||
if ((current_byte != 4) && (tmp != (USB_CMD_TOKEN >> (8 * (4 - current_byte)) & 0xFF))) {
|
if ((rx_cmd_current_byte != 4) && (tmp != (USB_CMD_TOKEN >> (8 * (4 - rx_cmd_current_byte)) & 0xFF))) {
|
||||||
current_byte = 0;
|
rx_cmd_current_byte = 0;
|
||||||
buffer = 0;
|
rx_cmd_buffer = 0;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
buffer = (buffer << 8) | tmp;
|
rx_cmd_buffer = (rx_cmd_buffer << 8) | tmp;
|
||||||
if (current_byte == 4) {
|
if (rx_cmd_current_byte == 4) {
|
||||||
current_byte = 0;
|
rx_cmd_current_byte = 0;
|
||||||
*data = buffer;
|
*data = rx_cmd_buffer;
|
||||||
buffer = 0;
|
rx_cmd_buffer = 0;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -179,6 +181,19 @@ static bool rx_cmd (uint32_t *data) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void handle_escape (void) {
|
||||||
|
if (USB->SCR & USB_SCR_ESCAPE_PENDING) {
|
||||||
|
if (USB->ESCAPE == 'R') {
|
||||||
|
if (p.dma_in_progress) {
|
||||||
|
dma_stop();
|
||||||
|
while (dma_busy());
|
||||||
|
}
|
||||||
|
usb_init();
|
||||||
|
}
|
||||||
|
USB->SCR |= USB_SCR_ESCAPE_ACK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void usb_init (void) {
|
void usb_init (void) {
|
||||||
USB->SCR = USB_SCR_ENABLED | USB_SCR_FLUSH_TX | USB_SCR_FLUSH_RX;
|
USB->SCR = USB_SCR_ENABLED | USB_SCR_FLUSH_TX | USB_SCR_FLUSH_RX;
|
||||||
@ -186,10 +201,18 @@ void usb_init (void) {
|
|||||||
p.state = STATE_IDLE;
|
p.state = STATE_IDLE;
|
||||||
p.debug_rx_busy = false;
|
p.debug_rx_busy = false;
|
||||||
p.debug_tx_busy = false;
|
p.debug_tx_busy = false;
|
||||||
|
|
||||||
|
rx_word_current_byte = 0;
|
||||||
|
rx_word_buffer = 0;
|
||||||
|
tx_word_current_byte = 0;
|
||||||
|
rx_cmd_current_byte = 0;
|
||||||
|
rx_cmd_buffer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void process_usb (void) {
|
void process_usb (void) {
|
||||||
|
handle_escape();
|
||||||
|
|
||||||
switch (p.state) {
|
switch (p.state) {
|
||||||
case STATE_IDLE:
|
case STATE_IDLE:
|
||||||
if (p.debug_tx_busy) {
|
if (p.debug_tx_busy) {
|
||||||
|
Loading…
Reference in New Issue
Block a user