#!/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()