Overview

A protocol-level walkthrough of how the gateway negotiates and moves data with the XMODEM family — the same code path serves XMODEM, XMODEM-1K, and YMODEM.

XMODEM (Ward Christensen, 1977) is the simplest of the file-transfer protocols the gateway speaks: a stop-and-wait, block-at-a-time scheme where the sender transmits one fixed-size block, waits for the receiver to acknowledge it, and only then sends the next. Every retro terminal program ever written understands it, which is why it is the lowest common denominator for moving files to and from a Commodore 64, a CP/M box, or an AltairDuino.

The gateway implements three members of the family on a single shared send/receive engine (src/xmodem.rs):

VariantBlockHeader byteAdds
XMODEM128 bytesSOH (0x01)The 1977 original.
XMODEM-1K1024 bytesSTX (0x02)8× bigger blocks → less per-block overhead.
YMODEM1024 bytesSTX + a block 0A SOH “block 0” carrying filename, size, and mtime.

Two things are negotiated at the start of every transfer and are worth keeping straight: the error-check method (CRC-16 or the older 8-bit checksum) and, on receive, the block size (decided per block from the header byte). Both are covered in detail below.

Who drives what: in XMODEM the receiver always speaks first — it pokes the sender with a request byte to kick the transfer off and to choose the error-check method. That makes the role of “gateway as receiver” (upload) and “gateway as sender” (download) genuinely different, so they are documented separately.

Control Bytes

The handful of bytes that drive the handshake.

ByteHexNameMeaning
SOH0x01Start of HeaderA 128-byte block follows.
STX0x02Start of TextA 1024-byte block follows.
EOT0x04End of TransmissionNo more data — the file is complete.
ACK0x06AcknowledgeBlock received intact; send the next one.
NAK0x15Negative AcknowledgeBad block (or “start in checksum mode”); resend.
C0x43(ASCII ‘C’)Receiver request: “start, and use CRC-16.”
CAN0x18CancelTwo in a row aborts the transfer.
SUB0x1ASubstitutePadding for the final short block.

The Three Variants
XMODEM (128-byte blocks)

The original. Each block is announced with SOH and carries exactly 128 bytes of payload. Small blocks mean a corrupted block is cheap to resend, which is why it survives on noisy links, but the per-block ACK round-trip caps throughput.

XMODEM-1K (1024-byte blocks)

Identical framing, but each block is announced with STX and carries 1024 bytes — eight times the payload for the same header and one ACK round-trip, so it is markedly faster on a clean link. The two sizes can be mixed within one transfer: the receiver branches on the header byte (SOH vs STX) for every block, so a sender may stream 1K blocks and drop to a 128-byte block for a short final chunk.

YMODEM (block 0 + 1K data)

XMODEM-1K plus metadata. Before the data, the sender transmits a block 0 — an SOH block whose block number is 0x00 (complement 0xFF) — whose 128-byte payload holds the filename, the exact file size, and optionally the modification time, all NUL-separated. Because the receiver learns the real size up front, it can truncate the file to the byte rather than guessing where the padding starts — which is the only way to receive a file that legitimately ends in 0x1A bytes (many binaries do) without corruption.


CRC-16 vs. Checksum

Two ways to detect a corrupted block — negotiated by the receiver's very first byte.

Checksum (1977)CRC-16 (later)
Trailer size1 byte2 bytes
ComputationSum of the data bytes, mod 256CRC-16/CCITT, polynomial 0x1021
Requested byReceiver sends NAK (0x15)Receiver sends C (0x43)
CatchesMost single-byte errorsFar more multi-byte / burst errors

The choice is made entirely by which byte the receiver sends to start the transfer. A C says “I speak CRC-16, please use it”; a NAK says “fall back to the plain checksum.” CRC is strongly preferred — it catches error patterns a simple sum misses — so the gateway always asks for CRC first and only drops to checksum for genuinely ancient senders that never implemented it.

Auto-detect safety net: a few vintage senders ignore the C request and just start sending checksum blocks anyway. On the first block the gateway validates the trailer in the mode it asked for and, if that fails, re-checks it under the other method. If the alternate matches, it silently locks the whole session to that method (pushing back the one stray trailer byte). So a CRC request that meets a checksum sender still completes cleanly.

Upload — Gateway as Receiver

You send a file from your terminal to the gateway. The gateway is the receiver, so it drives the handshake.

Step 1 — Pick the file and protocol

From the File Transfer menu press U, type a filename, and choose X on the upload-protocol screen. The single X entry accepts plain XMODEM, XMODEM-1K, and YMODEM — the gateway auto-detects which one your terminal actually uses.

Step 2 — Negotiate the error-check method

The gateway pokes the sender

  1. The gateway immediately sends C (0x43) to request CRC-16, and repeats it on an interval (xmodem_negotiation_retry_interval, default 7 s) so a sender that starts late still hears it.
  2. If the sender begins transmitting before roughly two-thirds of the negotiation window (xmodem_negotiation_timeout, default 45 s) elapses, the transfer proceeds in CRC mode.
  3. If that window passes with no block, the gateway switches to sending NAK (0x15) for the remainder — the checksum request — rescuing pre-CRC clients.
  4. If the whole window expires with no response, the gateway gives up with a negotiation-timeout error.
Step 3 — Read the first block (and discover the variant)

The first byte decides everything

  1. STX (0x02) → this is a 1024-byte block (XMODEM-1K).
  2. SOH (0x01) with block number 0x00 and complement 0xFF → this is a YMODEM block 0. The gateway parses the filename + size, validates its CRC, sends ACK, then sends a second C to start the data phase.
  3. SOH (0x01) with any other block number → an ordinary 128-byte data block.
  4. On this first data block the auto-detect check (above) runs: if the trailer doesn't validate in the requested method, the other method is tried and the session locks to whichever matches.
Step 4 — Acknowledge each block, in order

The stop-and-wait loop

  1. For every block the gateway reads: header byte, block number, its one's-complement, the data (128 or 1024 bytes per the header), then the trailer (2 CRC bytes, or 1 checksum byte).
  2. It validates three things: the complement matches the block number, the CRC/checksum is correct, and the block number is the one expected next.
  3. All good → the gateway sends ACK (0x06) and the sender advances to the next block.
  4. Bad CRC/checksum or a bad complement → the gateway sends NAK (0x15) and the sender resends the same block, up to xmodem_max_retries (default 10) consecutive errors before it cancels.
  5. A re-sent copy of an already-accepted block (its number matches one of the last two seen) is simply ACK'd again without being stored — this recovers from a lost ACK without duplicating data.
  6. A valid block carrying a different, non-duplicate block number is an unrecoverable loss of sync (XMODEM has no way to request a specific block), so the gateway cancels with three CAN (0x18) bytes — the spec-mandated response.
  7. If no block arrives within the block timeout (xmodem_block_timeout, default 20 s), the gateway NAKs to re-prompt the sender — which retransmits the block it's still awaiting an ACK for — bounded by the same retry count rather than abandoning the transfer on the first quiet moment.
Step 5 — End of transfer

EOT and saving

  1. When the file is done the sender transmits EOT (0x04); the gateway answers with a final ACK.
  2. YMODEM only: the gateway then sends one more C and consumes the end-of-batch “null block 0” (an all-zero filename) that signals “no more files,” and ACKs it.
  3. The file is written to the transfer directory. YMODEM truncates to the exact size from block 0; plain XMODEM / XMODEM-1K have no size field, so trailing SUB (0x1A) padding is stripped.

Download — Gateway as Sender

You pull a file from the gateway to your terminal. Here the gateway is the sender, so it waits for your terminal to drive the handshake.

Step 1 — Pick the file and exact protocol

Press D, pick the file from the numbered list, then choose the protocol. On download you select the variant explicitly, because the gateway must commit to a block size before it starts sending.

Step 2 — Wait for the receiver's mode request

The receiver chooses CRC or checksum

  1. The gateway waits (up to xmodem_negotiation_timeout, default 45 s) for your terminal to send its opening byte.
  2. C (0x43) → the gateway sends every trailer as a 2-byte CRC-16.
  3. NAK (0x15) → the gateway sends every trailer as a 1-byte checksum.
  4. If neither arrives in time, the gateway aborts with a negotiation-timeout error.
Step 3 — (YMODEM only) Send block 0

Announce the filename and size

  1. For YMODEM the gateway first sends block 0: an SOH block, number 0x00, complement 0xFF, carrying the filename and exact byte size.
  2. It waits for the receiver's ACK, then a C, which together start the data phase. Plain XMODEM and XMODEM-1K skip this step entirely.
Step 4 — Stream the data blocks

One block, one ACK, repeat

  1. Blocks are numbered from 1, wrapping 255 → 0 → 1… on long files. Each is: header, block number, its complement, the data, then the trailer in the negotiated method.
  2. XMODEM (X): every block is a 128-byte SOH block.
  3. XMODEM-1K (1): blocks are 1024-byte STX blocks; the gateway may drop to a 128-byte SOH block for a short final chunk rather than padding a whole 1K block.
  4. The last block is padded to its full size with SUB (0x1A) when the file doesn't end on a block boundary.
  5. After sending a block the gateway waits for ACK. A NAK (or a read timeout, xmodem_block_timeout, default 20 s) makes it resend the same block, up to xmodem_max_retries (default 10) times.
Step 5 — End of transfer

EOT handshake

  1. After the last data block the gateway sends EOT (0x04).
  2. Per the classic spec a receiver may NAK the first EOT; the gateway resends it and completes once it sees the ACK.
  3. The transfer summary (bytes / blocks / elapsed time) is then shown.

Block Anatomy

Exactly what goes on the wire for one block.

FieldBytesNotes
Header1SOH (128-byte block) or STX (1024-byte block)
Block number11, 2, 3 … wrapping at 256. (Block 0 = YMODEM header.)
Complement1One's-complement of the block number (255 - n) — a cheap header sanity check.
Data128 or 1024The payload, padded with SUB (0x1A) on the final short block.
Trailer2 (CRC) or 1 (checksum)CRC-16/CCITT (poly 0x1021), or the 8-bit sum, per negotiation.

So a single CRC-mode block looks like this on the wire:

128-byte CRC block (133 bytes total):
  01  3C  C3  [ ...128 data bytes... ]  9A  7F
  ^   ^   ^                             ^^^^^^
  SOH #60 ~#60                          CRC-16

128-byte checksum block (132 bytes total):
  01  3C  C3  [ ...128 data bytes... ]  8E
  ^   ^   ^                             ^^
  SOH #60 ~#60                          sum

1024-byte CRC block (1029 bytes total):
  02  05  FA  [ ...1024 data bytes... ]  CRC CRC
  ^   ^   ^
  STX #5  ~#5

Error Handling & Limits
SituationWhat happens
Bad CRC / checksum or bad complementReceiver sends NAK; sender resends the same block (bounded by xmodem_max_retries consecutive errors).
Lost ACK (duplicate block arrives)The duplicate is ACK'd again but not stored, keeping the streams in sync.
Valid block, wrong (non-duplicate) numberAn unrecoverable loss of sync — the receiver cancels with three CAN bytes.
No block within the block timeoutThe receiver NAKs to re-prompt the sender (which retransmits), bounded by xmodem_max_retries; the sender likewise resends on its own timeout.
Too many retriesThe transfer is cancelled with three CAN (0x18) bytes.
Deliberate abortTwo consecutive CAN bytes from either side abort the session (a lone CAN is treated as line noise).
File exceeds 8 MBThe receiver aborts with three CAN bytes.
Telnet binary dataOver telnet, 0xFF (IAC) and bare CR are escaped per RFC 854 when IAC mode is on — toggle it with I on the File Transfer menu. SSH and serial links skip this.

Tunables

One shared set of keys covers XMODEM, XMODEM-1K, and YMODEM (they share the code path). Editable from the telnet Configuration → File Transfer menu, the GUI / web File Transfer → More… popup, or egateway.conf — changes apply on the next transfer, no restart.

KeyDefaultMeaning
xmodem_negotiation_timeout45 sHow long to wait for the far end to start.
xmodem_negotiation_retry_interval7 sGap between C / NAK pokes during the handshake.
xmodem_block_timeout20 sPer-block read deadline once the transfer is live.
xmodem_max_retries10Resends of one block before giving up.
See also: the File Transfer section of the User Manual for the menu walkthrough, and the Kermit Reference for the 7-bit-clean alternative. ZMODEM is the faster choice on clean modern links; XMODEM is the universal fallback.

References

Deep-dive reference pages for every protocol and interface, plus the character-set tables and ANSI escape-sequence reference.

XMODEM (this page)

128-byte blocks; CRC-16 / checksum negotiation.

YMODEM

Block-0 metadata, batch, exact size truncation.

ZMODEM

Streaming; ZDLE, CRC-32, autostart, resume.

Kermit

Send-Init negotiation; F / A / D / Z / B transfer.

Punter

C1 dual checksum, two-phase, GOO/BAD/ACK.

AT Commands

Hayes command set, S-registers, +++ escape.

Telnet

IAC negotiation, every option, the NVT data phase.

SSH

Server, gateway, host keys, TOFU, auth modes.

Character Code Tables

Hex tables for every encoding: ASCII, ANSI, PETSCII, ATASCII, Baudot/ITA2, ZX Spectrum, TRS-80.

ANSI Escape Sequences

Cursor, colour/SGR, erase, and screen-mode escape codes, with the raw hex bytes.