1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
#!/usr/bin/env python3
"""gateway.py - a host-side "link cable adapter" for gbos networking.
Wraps the emulator, owns its link-port serial (stdio), and speaks SLIP. This
milestone just ECHOES every frame back (loopback), so `necho` on gbos can prove
a framed round-trip. Console (ASCII) bytes on the same channel are printed.
usage: tools/gateway.py [rom]
"""
import os, sys, subprocess, threading, time
EMU = os.path.expanduser("~/dev/gbc/build/sl0pboy")
ROM = sys.argv[1] if len(sys.argv) > 1 else os.path.join(os.path.dirname(__file__), "..", "gbos.gb")
END, ESC, ESC_END, ESC_ESC = 0xC0, 0xDB, 0xDC, 0xDD
def slip_encode(payload):
out = bytearray([END])
for c in payload:
if c == END: out += bytes([ESC, ESC_END])
elif c == ESC: out += bytes([ESC, ESC_ESC])
else: out.append(c)
out.append(END)
return bytes(out)
def handle_frame(payload):
"""The 'network'. For now: echo. (Later: DNS/TCP/HTTP/IRC.)"""
sys.stderr.write("\n[gw] frame in : %r\n" % bytes(payload))
sys.stderr.write("[gw] frame out: %r (echo)\n" % bytes(payload))
return bytes(payload)
def reader(out, inp):
buf, in_frame, esc = bytearray(), False, False
while True:
b = out.read(1)
if not b:
return
c = b[0]
if c == END:
if in_frame and buf:
reply = handle_frame(buf)
inp.write(slip_encode(reply)); inp.flush()
buf, in_frame, esc = bytearray(), False, False
else:
buf, in_frame, esc = bytearray(), True, False
elif in_frame:
if esc:
c = {ESC_END: END, ESC_ESC: ESC}.get(c, c); esc = False; buf.append(c)
elif c == ESC:
esc = True
else:
buf.append(c)
else:
sys.stderr.write(chr(c) if 32 <= c < 127 else ("\n" if c == 10 else "."))
sys.stderr.flush()
def main():
p = subprocess.Popen([EMU, "--headless", "--uncapped", ROM],
stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=0)
threading.Thread(target=reader, args=(p.stdout, p.stdin), daemon=True).start()
time.sleep(1.0) # boot to the shell
p.stdin.write(b"necho\n"); p.stdin.flush()
time.sleep(2.0) # let the framed round-trip happen
p.stdin.write(b"exit\n"); p.stdin.flush()
try: p.wait(timeout=3)
except Exception: p.kill()
if __name__ == "__main__":
main()
|