Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 15 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ publish = false
default-run = "copperline"

[features]
default = ["midi", "frontend", "wasm-boards", "control", "ctl-bin"]
default = ["midi", "frontend", "wasm-boards", "control", "ctl-bin", "net-nat"]
display-plan-trace = []
# Local investigation switches that deliberately alter timing or rendering.
# Normal builds keep these environment-variable overrides inert so release
Expand Down Expand Up @@ -54,6 +54,10 @@ bench-bin = []
# (`--control` headless, `--control-gui` windowed). Uses std::net + threads,
# so browser builds (--no-default-features) compile it out.
control = ["dep:serde_json"]
# User-mode NAT backend for emulated NICs (`[a2065] net = "nat"`, src/net/nat/):
# a slirp-style virtual gateway giving the guest outbound IPv4 with no host
# privileges. Uses std::net + threads, so browser builds compile it out.
net-nat = ["dep:smoltcp"]
# The `copperline-ctl` command-line client for the control protocol.
ctl-bin = ["dep:serde_json"]

Expand Down Expand Up @@ -92,6 +96,16 @@ arboard = { version = "3", default-features = false, features = ["wayland-data-c
# Config in wasmboard.rs). Pin the version: save-state replay of a plugin's
# linear-memory snapshot is only guaranteed within one wasmtime build.
wasmtime = { version = "=27.0.0", default-features = false, features = ["cranelift", "runtime"], optional = true }
# Userspace TCP/IP stack for the NAT backend (src/net/nat/): terminates the
# guest's ARP and TCP on the virtual gateway so flows can be spliced onto host
# sockets. Pure Rust, 0BSD, lean feature set: Ethernet + IPv4 + TCP sockets
# only (DHCP/DNS/UDP/ICMP are handled at frame level with the wire parsers).
smoltcp = { version = "0.12", default-features = false, features = [
"alloc",
"medium-ethernet",
"proto-ipv4",
"socket-tcp",
], optional = true }
# Already pulled in transitively; used directly only for localtime_r (local
# time-zone filename stamps) and, on macOS, the pthread QoS class used for the
# optional realtime-priority feature (see src/priority.rs).
Expand Down
13 changes: 8 additions & 5 deletions copperline.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -199,12 +199,15 @@ slow = "512K"


# A2065 Ethernet board (Am7990 LANCE, driven by the SANA-II a2065.device).
# net picks the host network backend: "loopback" echoes transmitted frames
# back (self-contained), "none" leaves the NIC isolated. Omit the section
# for no board. Host networking is non-deterministic, so a fitted NIC
# breaks byte-identical replay/save-state determinism while traffic flows.
# net picks the host network backend: "nat" is a slirp-style userspace NAT
# giving the guest outbound IPv4 with no host privileges (guest 10.0.2.15/24,
# gateway 10.0.2.2, DNS 10.0.2.3, DHCP built in); "loopback" echoes
# transmitted frames back (self-contained); "none" leaves the NIC isolated.
# Omit the section for no board. Host networking is non-deterministic, so a
# fitted NIC breaks byte-identical replay/save-state determinism while
# traffic flows.
# [a2065]
# net = "loopback"
# net = "nat"


# SCSI bus with up to seven drives, on any machine model. Preferred over [ide]
Expand Down
36 changes: 23 additions & 13 deletions docs/guide/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ machine; the other flags then override individual values on top of it, just as
explicit `[cpu]`/`[chipset]`/`[memory]` sections override a `[machine]`
profile in a config file.

The audio, serial, and parallel surface has matching per-run flags too --
`--audio-device`, `--audio-channel-mode`, `--audio-stereo-separation`,
The audio, serial, parallel, and network surface has matching per-run flags
too -- `--audio-device`, `--audio-channel-mode`, `--audio-stereo-separation`,
`--serial`, `--midi-in`, `--midi-out`, `--parallel`, `--sampler-audio-input`,
`--sampler-input-gain` -- described with their `[audio]`, `[serial]`, and
`[parallel]` keys below.
`--sampler-input-gain`, `--a2065-net` -- described with their `[audio]`,
`[serial]`, `[parallel]`, and `[a2065]` keys below.

## Top level

Expand Down Expand Up @@ -855,17 +855,27 @@ assigns addresses.

```toml
[a2065]
net = "loopback" # or "none" for an isolated NIC
net = "nat" # or "loopback"; "none" for an isolated NIC
```

Fits a Commodore A2065 Ethernet board (Am7990 LANCE) on the Zorro chain.
`net` selects the host network backend: `"loopback"` echoes transmitted
frames back (self-contained, useful for driver bring-up), `"none"` leaves
the NIC isolated. Omit the section entirely for no board. Note that host
networking is inherently non-deterministic: inbound frames arrive on the
host's schedule, not the emulated clock, so a NIC board breaks
byte-identical replay and save-state determinism while traffic flows. See
[](../zorro) for the board details.
Fits a Commodore A2065 Ethernet board (Am7990 LANCE) on the Zorro chain;
`--a2065-net BACKEND` is the matching per-run flag. `net` selects the host
network backend:

- `"nat"` -- userspace NAT: the guest gets outbound IPv4 internet through a
virtual gateway with no host privileges or setup, identically on Linux,
macOS, and Windows. Configure the guest's TCP/IP stack with IP
`10.0.2.15`, netmask `255.255.255.0`, gateway `10.0.2.2`, DNS `10.0.2.3`
(or let it BOOTP/DHCP). Outbound only, IPv4 only.
- `"loopback"` -- echoes transmitted frames back (self-contained, useful
for driver bring-up).
- `"none"` -- the NIC is fitted but isolated.

Omit the section entirely for no board. Note that host networking is
inherently non-deterministic: inbound frames arrive on the host's
schedule, not the emulated clock, so a NIC board breaks byte-identical
replay and save-state determinism while traffic flows. See [](../zorro)
for the board details and the NAT's limitations.

## `[debug]` -- diagnostics

Expand Down
25 changes: 20 additions & 5 deletions docs/internals/peripherals.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,12 +164,27 @@ Am7990 LANCE and 32 KiB of on-board RAM, driven by the AmigaOS SANA-II
`a2065.device`. Unlike the DMAC boards the LANCE never masters the Amiga
bus: its init block, descriptor rings, and packet buffers all live in the
board's own RAM, which the CPU reaches through the board window, so the
board is self-contained and owns a host `NetBackend` (`net.rs`) for real
frames. Networking is inherently non-deterministic, so a fitted NIC
board is self-contained and owns a host `NetBackend` (`net/`) for real
frames. The LANCE engine models the Am7990 programming surface a real
driver exercises: TX and RX buffer chaining (STP..ENP spans across
descriptors), the stored FCS trailer (MCNT counts it; drivers read the
payload as `MCNT - 4`), the init-block MODE gates (DTX/DRX and the LOOP
internal-loopback self-test SANA-II drivers run at power-up), and MISS on
an RX ring overrun.

The `nat` backend (`net/nat/`, `net-nat` build feature) is a slirp-style
userspace NAT: a dedicated `a2065-nat` thread owns a smoltcp interface
that terminates ARP and the guest's TCP on the virtual gateway
(10.0.2.2, DNS forwarder 10.0.2.3, guest 10.0.2.15/24), splices each TCP
flow onto a non-blocking host socket, NATs UDP per flow, resolves DNS
through the host's own resolver, and answers BOOTP/DHCP and ICMP echo at
frame level. Frames cross to the emulated NIC over bounded channels that
drop on overflow, so the emulator thread never blocks on the host
network. Networking is inherently non-deterministic, so a fitted NIC
breaks byte-identical replay while traffic flows; save states record only
the chosen backend and bring up a fresh one on load. The board and
backend story, including the WASM plugin `net` capability, is covered in
[](../zorro).
the chosen backend and bring up a fresh one on load (flows die; the
guest's TCP retransmits). The board and backend story, including the WASM
plugin `net` capability, is covered in [](../zorro).

## CDTV (`cdtv.rs`, `cdrom.rs`)

Expand Down
37 changes: 31 additions & 6 deletions docs/zorro.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,19 +183,44 @@ config:

```toml
[a2065]
net = "loopback" # host network backend; "none" for an isolated NIC
net = "nat" # host network backend; "loopback", or "none" for isolation
```

(`--a2065-net BACKEND` is the matching per-run flag.)

Unlike the DMAC boards, the LANCE does not master the Amiga bus: its init
block, descriptor rings, and packet buffers live in the board's own 32 KiB RAM
(which the CPU reaches through the board window), so the board is self-contained
and owns its host network backend directly.

Host network backends live in `src/net.rs` behind the `NetBackend` trait. Built
in today is **loopback** (transmitted frames are queued straight back -- useful
for a self-contained demo and for tests); a userspace NAT (libslirp/smoltcp) and
a host TAP bridge are planned and will slot in behind `make_backend` under build
features. TAP will require host privileges and interface setup; NAT will not.
Host network backends live in `src/net/` behind the `NetBackend` trait. Two are
built in:

- **`nat`** -- userspace NAT (`src/net/nat/`, behind the default-on `net-nat`
build feature): a slirp-style virtual gateway that NATs the guest's outbound
IPv4 onto ordinary host sockets. No host privileges, drivers, or setup, and
identical behavior on Linux, macOS, and Windows. The guest sees the QEMU/slirp
segment -- configure its TCP/IP stack with:

| Setting | Value |
|---|---|
| IP address | `10.0.2.15` (or use the built-in BOOTP/DHCP server) |
| Netmask | `255.255.255.0` |
| Gateway | `10.0.2.2` |
| DNS server | `10.0.2.3` |

DNS is answered through the host's own resolver; TCP and UDP to the gateway
address reach the host's `127.0.0.1`, so guest software can talk to services
on the host. Limitations: outbound only (no inbound connections or port
forwards yet), IPv4 only, no IP fragmentation, and ICMP echo is answered
locally by the gateway for any destination -- a ping "succeeding" proves the
NAT is up, not that the target is reachable.
- **`loopback`** -- transmitted frames are queued straight back; useful for a
self-contained demo, driver bring-up, and tests.

A host TAP bridge would slot in behind `make_backend` the same way but is not
implemented; TAP would require host privileges and per-OS interface setup,
which is exactly what `nat` avoids.

**Networking is non-deterministic.** Inbound frames arrive on the host's
schedule, not the emulated clock, so a fitted A2065 (or any `net`-capable WASM
Expand Down
Loading
Loading