Acorn Econet Bridge — architecture overview

← Acorn Econet Bridge

Acorn Econet Bridge — architecture overview

This is a top-down description of how the Acorn Econet Bridge works, assembled from the annotated disassembly of its 8 KiB firmware. It's intended as the entry point for the other analysis documents in this directory: each idiom or subsystem described here is linked to a more detailed piece that goes into the implementation specifics.

The Bridge is a small, self-contained, piece of network infrastructure — it does one job, does it quietly, and was designed to be invisible to the users whose traffic passes through it. The firmware is correspondingly compact: 8 KiB of ROM runs the whole thing, on an independent 6502 CPU with 8 KiB of RAM for buffering. No operating system. No bootloader. No configuration interface. Just two Econet ports, two jumper-set network numbers, and a protocol that discovers the rest at runtime.

What the Bridge does

At its simplest: an Econet network segment can span about 500 metres. Larger installations use multiple segments connected by bridges. Each bridge sits between two segments, listening promiscuously on both, and relaying traffic from one to the other when the destination network is on the far side.

Stations address their frames using a two-part identifier: (network, station). The network number identifies which Econet segment; the station number identifies a specific host on that segment. A station sending to (net=6, stn=22) doesn't need to know which bridge(s) lie between it and network 6 — it just transmits the frame, and if it's not for a station on the local segment, any bridge that has a route will pick it up and forward.

The bridging is transparent. Senders and receivers never see the bridge as an entity; it's effectively part of the wiring.

Hardware

The bridge box contains:

Notably absent:

Ian Stocks's reverse-engineered schematic shows the board layout.

Boot

The reset handler at &E000 runs the following sequence end-to-end:

  1. Initialise the routing tables. init_reachable_nets clears two 256-byte tables at &025A (reachable_via_b) and &035A (reachable_via_a), then marks the Bridge's own two network numbers and the broadcast slot (255) as known. See below for what these tables mean.

  2. Initialise both ADLCs. adlc_a_full_reset and adlc_b_full_reset each send the standard sequence CR1=&C1, CR4=&1E, CR3=&00, CR1=&82, CR2=&67 to their ADLC — reset both sections, configure 8-bit NRZ, then enter idle listen mode.

  3. Size the RAM. ram_test scans pages from &1800 upward, writing &AA and &55 patterns to each and reading them back; the last page that verifies is recorded at &82 as top_ram_page. The test uses an INC $00 between each write and read as a defence against data-bus residue and address-line aliasing — see Anti-aliasing in the Econet Bridge's RAM test.

  4. Announce presence on both sides. build_announce_b constructs a bridge-announcement frame addressed as a full broadcast (dst_stn = dst_net = &FF) with control byte &80, port &9C, and a single payload byte giving the Bridge's other-side network number. The same frame is then transmitted first on side A (payload = net_num_b) and then on side B (after patching the payload to net_num_a). See One frame, two broadcasts.

  5. Fall through to the main loop. No "boot complete" flag, no observable boundary between reset and steady state; execution just continues into the main loop.

If any of the final wait_adlc_*_idle or transmit_frame_* calls takes its timeout path, the reset handler is bypassed entirely and execution goes straight into the main loop anyway — see Escape-to-main control flow for the mechanism.

Steady state: the main loop

The main loop at &E051 is small. Its header re-arms both ADLCs (clearing any lingering status from a previous frame), then enters a tight polling loop at main_loop_poll (&E079) that tests SR1 bit 7 — the IRQ summary — on each chip in turn:

The idle path decrements a 16-bit timer (announce_tmr_lo/hi). When the timer reaches zero, it invokes the re-announcement code, which rebuilds and retransmits the bridge-announcement frame — on side A if announce_flag's bit 7 is clear, on side B otherwise — and decrements a counter. Once the counter runs out, announce_flag is cleared and the idle path goes quiet until something else re-enables it.

The pattern is deliberately simple. There are no buffers queued between decisions, no stateful transactions in progress between polling iterations, no threads. Every iteration starts from a clean ADLC state and either processes one inbound frame, or processes one re-announce tick, or does nothing. Failures don't need per-iteration recovery logic because every iteration is functionally idempotent.

Inbound frame processing

When an ADLC IRQ fires, one of the two rx_frame_? handlers runs. Each has the same three-stage structure:

  1. Addressing filter. The first two bytes of the incoming frame (rx_dst_stn, rx_dst_net) are drained from the RX FIFO. If the destination network is zero (meaning "my local network" from the sender's perspective) or not in the reachable_via_? table, the frame is dropped and the Bridge returns to listening.

  2. Drain. The rest of the frame is read into a 20-byte buffer at &023C onward. After the drain, an end-of-frame check verifies the ADLC's Frame Valid bit; if the frame is corrupt or truncated, the Bridge aborts.

  3. Dispatch. If the destination is (&FF, &FF) — a full broadcast — and the port is &9C (the bridge-protocol port), the control byte is used to dispatch to a bridge-protocol handler (&80, &81, &82, &83). Anything else falls to the forwarding path.

The forwarding path — rx_a_forward / rx_b_forward — is where the real work happens, and where the architecture gets genuinely clever: see Bridging the four-way handshake for how the Bridge implements Econet's scout/ACK/data/ACK transaction across two segments.

The bridge protocol

Four control-byte values on port &9C define the bridge protocol:

The query responses cleverly reuse the control-byte and port-number fields of the response data frame to carry the two bytes of information they need to send back: the Bridge's two network numbers. No data payload is needed — the answer fits in the frame header.

Failure and self-test

The firmware doesn't have anything that looks like an error-handling framework. There are no exception vectors, no panic routines, no "log and continue" helpers. Every routine that might fail takes the PLA / PLA / JMP main_loop exit (see Escape-to-main control flow). This is the only recovery mechanism, and it works by jumping back to a place where the invariants are known to hold.

The self-test is entered via the IRQ/BRK vector when the push-button is pressed. It runs an indefinite loop of eight sub-tests:

  1. Zero-page integrity check
  2. ROM checksum (expected sum mod 256 = &55)
  3. &55/&AA pattern test across 8 KiB of RAM
  4. Incrementing-byte pattern test (catches address-line faults)
  5. ADLC register-state checks on both chips
  6. Loopback test A → B (requires a cable between the two ports)
  7. Loopback test B → A
  8. Network-number jumper check (expects net_num_a=1, net_num_b=2)

The LED serves four distinct functional states — lit solid during a healthy self-test, dark in normal operation, and two different blink patterns for the two classes of failure (countable for specific failures, uncountable for RAM failures). See The self-test LED for the full failure-mode analysis.

Reading list

The writeups in docs/analysis/ cover specific aspects in depth:

Cross-references back to the source

The annotated assembly and structured JSON for the disassembled ROM:

External references: