Skip to content

Implement virtual serial channels and a TCP listen mode for emulator guests#6

Open
peacedudes wants to merge 10 commits into
DrPitre:mainfrom
peacedudes:virtual-serial
Open

Implement virtual serial channels and a TCP listen mode for emulator guests#6
peacedudes wants to merge 10 commits into
DrPitre:mainfrom
peacedudes:virtual-serial

Conversation

@peacedudes

Copy link
Copy Markdown

This implements the virtual serial channel side of the DriveWire protocol
in the Swift host, and adds a TCP listening driver so emulators like
XRoar and MAME (whose "becker port" connects out to a DriveWire server)
can use it. Everything follows DriveWire Specification.md in this repo.

What's included

  • VirtualChannel + all nine serial opcodes in DriveWireHost
    (OP_SERINIT/SERTERM/SERSETSTAT/SERGETSTAT/SERREAD/SERREADM/SERWRITE/ SERWRITEM/FASTWRITE), replacing the previous stubs. Each processor
    consumes the exact operand length from the spec so the byte stream can't
    desync (the stubs returned 1 unconditionally, which desynced on any real
    guest traffic). The SERREAD poll response implements the full response
    table: single interactive byte, bulk count for SERREADM, channel-closed
    status, with round-robin fairness between channels.
  • A new delegate callback channelDataAvailable(host:channel:data:)
    with a protocol-extension default, so existing conformers compile
    unchanged.
  • DriveWireTCPServerDriver (Network.framework): listens for the
    emulator on a becker port (default 65504) and bridges each virtual
    channel N to its own TCP port (channelPortBase + N) — connect with
    nc/telnet and you're talking to /N1 on the guest. Output while no
    client is attached is buffered (bounded) and replayed on connect.
  • drivewire-cli --becker-port listen mode alongside the existing
    serial mode (--channel-port-base, --channels; validate() requires
    exactly one of --port/--becker-port).
  • 17 unit tests covering channel lifecycle, writes (including opcodes
    split across reads), poll/read cycles, over-request handling, and a
    real-socket loopback test of the TCP driver.

Scope note

The per-channel TCP bridges are part of the new TCP server driver, so the
full experience (connect to a port, get a /N1 session) is available in
--becker-port mode today. In serial mode (--port, real hardware) the
host now parses and tracks all virtual-channel traffic correctly, but no
consumer is wired to the channels yet — DriveWireDelegate. channelDataAvailable and DriveWireHost.writeToChannel(_:channel:) are
the seam; attaching the same TCP bridges to the serial driver would be a
natural follow-up.

Two guest-compatibility fixes found by live NitrOS-9 testing

Tested end-to-end against NitrOS-9 EOU (dw_becker) under XRoar:

  1. OP_DWINIT must answer non-zero. The NitrOS-9 driver interprets a
    zero response as a DW3 server and disables its DW4 extensions —
    including the virtual channel poller, so SERREAD polling never starts.
    The host now answers 0xFF.
  2. The EOU boot emits bare $64 $00 pairs (OP_SERWRITEM naming a
    never-opened channel, with no count byte). Parsing a count there
    swallows the following ~100 bytes (including the guest's SERINIT).
    The host now consumes exactly two bytes for a SERWRITEM addressed to an
    unopened channel, matching how existing servers behave.

Also fixed en route

  • DriveWireTests target deployment 13.0 → 14.1 (@Observable in
    DriveWireHost requires 14+; the test target didn't build).
  • testInsert referenced a nonexistent DriveWireError enum
    (→ DriveWireHostError).

Verification

  • xcodebuild -scheme DriveWire test — all new tests pass (two
    pre-existing tests reference a local disk image path and are untouched).
  • Live: NitrOS-9 EOU boots under XRoar with -cart-becker; a shell on
    /N1 is fully usable over nc localhost 6811mdir, dir, date
    all round-trip.

🤖 Generated with Claude Code

@peacedudes

Copy link
Copy Markdown
Author

To try it

xcodebuild -project swift/DriveWire.xcodeproj -scheme drivewire-cli -configuration Debug build
drivewire-cli --becker-port 65504 --drive0 <your disk> --verbose

Then point an emulator's becker port at it — XRoar's default target is already
localhost:65504:

xroar -m coco3 -cart becker ...

Boot a DW4-capable NitrOS-9 (the EOU dw_becker variant has dwio_becker,
scdwv, and the N* descriptors baked into OS9Boot). In the guest:

shell /n1&

and on the host, channel 1 is TCP port 6811:

nc localhost 6811

One gotcha before you judge it broken: raw nc line endings. The guest
wants bare CR as line terminator (your terminal's Enter sends LF), and its
output comes back CR-only, so it overprints on one row. Fix is stty -icrnl -icanon -echo on the way in and an unbuffered CR→NL conversion on the way
out (tr line-buffers and holds back the prompt, since prompts have no
trailing newline).

The harness we drive it with — tmux session wrapping drivewire-cli + XRoar +
the tty plumbing above, plus scripted send/expect for unattended use — is
here if useful as a reference:
https://github.com/peacedudes/os9exec/blob/arm64-uae-integration/tools/nitros9repl.sh

@peacedudes

Copy link
Copy Markdown
Author

Updated: the branch now merges your current main (the new host with the dw command API is taken as-is) and re-attaches just the TCP transport on top: DriveWireTCPServerDriver, the --becker-port CLI mode, and a small bridgeChannels(_:) seam so bridged channels get raw terminal sessions while unbridged channels still reach your dw command processor. Your main had already picked up both NitrOS-9 protocol fixes, so the remaining diff is only the transport. All tests pass and the merged server was re-verified live against a NitrOS-9 EOU boot under XRoar (mdir round-trip on /N1).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant