aboutsummaryrefslogtreecommitdiffstats
path: root/tools/gateway.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--tools/gateway.py66
1 files changed, 45 insertions, 21 deletions
diff --git a/tools/gateway.py b/tools/gateway.py
index 81a70af..3129773 100644
--- a/tools/gateway.py
+++ b/tools/gateway.py
@@ -1,18 +1,22 @@
#!/usr/bin/env python3
-"""gateway.py - a host-side "link cable adapter" for gbos networking.
+"""gateway.py - 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.
+Wraps the emulator, owns its link-port serial (stdio), speaks SLIP, and does
+the real network work the Game Boy can't (DNS/TCP/HTTP). Modes:
-usage: tools/gateway.py [rom]
+ echo - bounce every frame back (loopback test)
+ http - treat each frame as a URL, HTTP GET it, stream the body back
+
+Reply frames are typed: b'D'+chunk for data, b'E' to end the stream.
+
+usage: tools/gateway.py [--mode echo|http] [--cmd "wget example.com"] [--rom PATH]
"""
-import os, sys, subprocess, threading, time
+import os, sys, subprocess, threading, time, argparse, urllib.request
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
+CHUNK = 200
+LIMIT = 800 # cap the fetched body (link is ~1-32 KB/s)
def slip_encode(payload):
out = bytearray([END])
@@ -23,13 +27,28 @@ def slip_encode(payload):
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 http_fetch(payload):
+ url = bytes(payload).decode("latin1", "replace").strip()
+ if not url.startswith("http"):
+ url = "http://" + url
+ sys.stderr.write("\n[gw] GET %s\n" % url)
+ try:
+ req = urllib.request.Request(url, headers={"User-Agent": "gbos/0.1"})
+ with urllib.request.urlopen(req, timeout=8) as r:
+ body = r.read(LIMIT)
+ except Exception as e:
+ body = ("ERR: " + str(e)).encode()
+ sys.stderr.write("[gw] %d bytes\n" % len(body))
+ frames = [b"D" + body[i:i+CHUNK] for i in range(0, len(body), CHUNK)]
+ frames.append(b"E")
+ return frames
+
+def handle(payload, mode):
+ if mode == "http":
+ return http_fetch(payload)
+ return [bytes(payload)] # echo
-def reader(out, inp):
+def reader(out, inp, mode):
buf, in_frame, esc = bytearray(), False, False
while True:
b = out.read(1)
@@ -38,8 +57,8 @@ def reader(out, inp):
c = b[0]
if c == END:
if in_frame and buf:
- reply = handle_frame(buf)
- inp.write(slip_encode(reply)); inp.flush()
+ for f in handle(buf, mode):
+ inp.write(slip_encode(f)); inp.flush()
buf, in_frame, esc = bytearray(), False, False
else:
buf, in_frame, esc = bytearray(), True, False
@@ -55,12 +74,17 @@ def reader(out, inp):
sys.stderr.flush()
def main():
- p = subprocess.Popen([EMU, "--headless", "--uncapped", ROM],
+ ap = argparse.ArgumentParser()
+ ap.add_argument("--mode", default="echo", choices=["echo", "http"])
+ ap.add_argument("--cmd", default="necho")
+ ap.add_argument("--rom", default=os.path.join(os.path.dirname(__file__), "..", "gbos.gb"))
+ a = ap.parse_args()
+ p = subprocess.Popen([EMU, "--headless", "--uncapped", a.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
+ threading.Thread(target=reader, args=(p.stdout, p.stdin, a.mode), daemon=True).start()
+ time.sleep(1.0)
+ p.stdin.write((a.cmd + "\n").encode()); p.stdin.flush()
+ time.sleep(4.0)
p.stdin.write(b"exit\n"); p.stdin.flush()
try: p.wait(timeout=3)
except Exception: p.kill()