module BBS::Xmodem
XMODEM downloads: the sending side of Ward Christensen’s protocol and its CRC and 1K variants, the transfer the terminals of a Commodore 64 (CCGMS, Novaterm, UltimateTerm and the like) know besides SyncTERM, Qodem and lrzsz’s rx.
BBS::Xmodem.send_file(session, bytes) BBS::Xmodem.send_file(session, bytes, one_k: true) # => :ok, :cancelled, :timeout or :hangup
XMODEM carries no file name: the caller names the file in the terminal before the transfer starts. The last block is padded with SUB (0x1a) up to its size, so the received file may be longer than the original.
The bytes go out as they are, past the PETSCII encoder, with 0xFF as a doubled telnet IAC (telnet: false for a plain byte pipe), and the line is asked into telnet BINARY mode for the transfer.
Constants
- ABORT
-
What the sender writes to cancel: eight
CAN, then eight backspaces to erase their echo. - ACK
-
The receiver took the block.
- BINARY_OFF
-
Leaves telnet BINARY mode both ways.
- BINARY_ON
-
Asks the terminal for telnet BINARY mode both ways.
- BLOCK
-
The data bytes of a classic block.
- BLOCK_1K
-
The data bytes of an XMODEM-1K block.
- BLOCK_NUMBERS
-
The block numbers count modulo this.
- BLOCK_WAIT
-
Seconds to wait for the answer to one block.
- BYTE_MASK
-
The largest byte, for the ones’ complement of a block number and the checksum.
- CAN
-
Cancel: two in a row end the transfer.
- CANCEL_COUNT
-
The
CANbytes in a row from the receiver that cancel the transfer. - CANCEL_KEYS
-
The keys the caller cancels the start of a transfer with: Esc and Ctrl-C (RUN/STOP on a Commodore 64).
- CRC_START
-
The receiver’s first byte when it wants a CRC-16 instead of a checksum.
- EOT
-
End of transmission: the sender has no more blocks.
- NAK
-
The receiver wants the block again; as the first byte, it asks for a transfer with an arithmetic checksum.
- QUIET
-
Seconds of silence that end the transfer: what the terminal still sends before it (a late
C,NAKorCAN) is dropped, so the board does not read it as keys. - QUIET_MAX
-
The most seconds spent dropping the terminal’s late bytes.
- SOH
-
Start of a 128-byte block.
- STX
-
Start of a 1024-byte block (XMODEM-1K).
- SUB
-
The padding byte of the last block (Ctrl-Z).
- TELNET_IAC
-
The telnet IAC byte, doubled in the data on a telnet line.
- TRIES
-
Times a block (or the
EOT) is sent before the transfer is given up. - WAIT
-
Seconds to wait for the receiver to start, by default.
Public Instance Methods
Source
# File lib/bbs/xmodem.rb, line 118 def blocks(data, size) (0...data.bytesize).step(size).map do |pos| data.byteslice(pos, size).ljust(size, SUB.chr) end end
Cuts data into the blocks of a transfer: Strings of size bytes, the last one padded with SUB. An empty data gives no blocks.
Source
# File lib/bbs/xmodem.rb, line 114 def checksum(bytes) = bytes.sum & BYTE_MASK
Returns the arithmetic checksum of the String bytes: the sum of the bytes modulo 256.
Source
# File lib/bbs/xmodem.rb, line 128 def frame(number, chunk, crc:) number &= BYTE_MASK head = [chunk.bytesize == BLOCK_1K ? STX : SOH, number, BYTE_MASK - number].pack('C*') tail = crc ? [Zmodem.crc16(chunk)].pack('n') : [checksum(chunk)].pack('C') head + chunk + tail end
# File lib/bbs/xmodem.rb, line 108 def send_file(session, data, one_k: false, wait: WAIT, telnet: true) Sender.new(session, telnet: telnet).run(data.to_s.b, one_k: one_k, wait: wait) end
Sends data (a String of bytes) to the caller’s terminal over session.
- session
-
the
BBS::Sessionto send on - data
-
the file’s bytes
- one_k
-
1024-byte blocks (XMODEM-1K) when the receiver asked for a CRC;
falsesends 128-byte blocks - wait
-
seconds to wait for the receiver to start
- telnet
-
double 0xFF bytes as telnet IAC and ask for BINARY mode;
falsefor a plain byte pipe
The receiver picks the check: a C gets CRC-16, a NAK the arithmetic checksum (and 128-byte blocks, even with one_k). Esc or Ctrl-C from the caller gives up while the board waits for the start.
Returns :ok when the terminal acknowledged the end of the file, :cancelled when the caller or the terminal gave up, :timeout when the receiver did not start within wait seconds or stopped answering, :hangup when the line dropped.