dreamcast/client.py

221 lines
5.8 KiB
Python

import serial
import struct
import sys
import time
import fcntl
import os
#dest = 0xac21_0000
#dest = 0xac02_0000
dest = 0xac01_0000
ret = []
def sync(ser, b, wait=0.1):
l = []
for i, c in enumerate(b):
if i % 32 == 0 and i != 0:
print(i, end=' ')
sys.stdout.flush()
ser.write(bytes([c]))
time.sleep(1000 / 1000000)
if ser.in_waiting == 0:
time.sleep(0.001)
while ser.in_waiting > 0:
res = ser.read(ser.in_waiting)
l.extend(res)
time.sleep(0.001)
time.sleep(wait)
res = ser.read(ser.in_waiting)
l.extend(res)
return bytes(l)
def symmetric(ser, b):
l = []
mem = memoryview(b)
i = 0
while i < len(b):
chunk_size = 128
if i % 1024 == 0:
print(i, end=' ')
sys.stdout.flush()
while True:
if ser.in_waiting:
res = ser.read(ser.in_waiting)
l.extend(res)
if len(l) + chunk_size >= i:
break
time.sleep(1000 / 1000000)
chunk_size = min(chunk_size, len(b) - i)
assert chunk_size > 0, chunk_size
ser.write(mem[i:i+chunk_size])
i += chunk_size
orig_count = 1000
count = orig_count
while count > 0:
if len(l) >= len(b):
break
time.sleep(1 / orig_count)
if ser.in_waiting:
res = ser.read(ser.in_waiting)
l.extend(res)
count -= 1
return bytes(l)
def start_data(ser, b):
_ = ser.read(ser.in_waiting)
size = len(b)
args = struct.pack("<II", size, dest)
ret = sync(ser, b'DATA' + args)
if ret != b'data' and ret != b'data\n' and ret != b'\ndata\n' and ret != b'\x00data\n':
print(".", end=' ')
print(ret)
time.sleep(10)
sys.stdout.flush()
sync(ser, b'prime')
start_data(ser, b)
return
print("\nDATA")
def do(ser, b):
start_data(ser, b)
start = time.monotonic()
ret = symmetric(ser, b)
#print("\nHERE", len(ret), len(b))
end = time.monotonic()
duration = end - start
print("\n\nduration:", duration, "\n\n")
print(ret[-5:])
print(len(b), len(ret))
if ret[:-5] != b and False:
print("ret != b; dumped to asdf.bin")
with open('asdf.bin', 'wb') as f:
f.write(ret[:-5])
print("did not jump")
return
args = struct.pack("<I", dest)
ret = sync(ser, b'JUMP' + args, wait=0)
print("JUMP", ret)
console(ser)
seen_length = 16
def compare_str(s, seen, seen_ix):
assert len(s) <= seen_length, s
start = seen_length + (seen_ix - len(s))
for i, c in enumerate(s):
if c != seen[(start + i) % seen_length]:
return False
return True
framebuffer_mode = False
framebuffer = []
def console(ser):
print("console:")
global framebuffer_mode
global framebuffer
fd = fcntl.fcntl(sys.stdin, fcntl.F_GETFL)
fcntl.fcntl(sys.stdin, fcntl.F_SETFL, fd | os.O_NONBLOCK)
seen = [0] * seen_length
seen_ix = 0
while True:
while True:
c = sys.stdin.read(1)
if not c:
break
print("write", repr(c))
ser.write(c.encode('utf-8'))
b = ser.read(ser.in_waiting)
if b == b'':
continue
for c in b:
seen[seen_ix % seen_length] = c
seen_ix += 1
if framebuffer_mode:
framebuffer.append(c)
if len(framebuffer) % 1000 == 0:
print(len(framebuffer), (640 * 480 * 2), file=sys.stderr)
sys.stderr.flush()
else:
sys.stderr.buffer.write(bytes([c]))
sys.stderr.flush()
if compare_str(b"<<FRAMEBUFFER>>", seen, seen_ix):
sys.stderr.write("\nMATCH\n")
sys.stderr.flush()
framebuffer_mode = True
framebuffer = []
if compare_str(b"<</FRAMEBUFFER>>", seen, seen_ix):
framebuffer_mode = False
sys.stderr.write("<</FRAMEBUFFER>>\n")
sys.stderr.flush()
with open('framebuffer.bin', 'wb') as f:
f.write(bytes(framebuffer[:-16]))
with open(sys.argv[1], 'rb') as f:
b = f.read()
def baudrate_from_scbrr2(n):
return 1562500 / (n+1)
def change_rate(old_rate, new_rate):
with serial.Serial(port='/dev/ttyUSB0',
baudrate=baudrate_from_scbrr2(old_rate),
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=1,
xonxoff=False,
#rtscts=False,
rtscts=True,
) as ser:
buf = b'\x00\x00\x00\x00'
start_data(ser, buf)
ret = symmetric(ser, buf)
print('ret', ret)
print("change rate", int(baudrate_from_scbrr2(new_rate)))
args = struct.pack("<I", new_rate & 0xff)
ret = sync(ser, b'RATE' + args, wait=1)
print(ret)
old_rate = 4
new_rate = 4
if old_rate != new_rate:
change_rate(old_rate, new_rate)
with serial.Serial(port='/dev/ttyUSB0',
baudrate=baudrate_from_scbrr2(new_rate),
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=1,
xonxoff=False,
rtscts=False,
#rtscts=True,
) as ser:
ser.read(ser.in_waiting)
ser.read(ser.in_waiting)
ser.reset_input_buffer()
ser.reset_output_buffer()
ser.read(ser.in_waiting)
#console(ser)
print("waiting: ", end=' ')
sys.stdout.flush()
do(ser, b)