300 Baud · engineering notebook

The engineering notebook

What is actually going on inside the machine, why it is built the way it is, and the measurements that settled the arguments. Everything here is checkable: the numbers came out of the test suite or out of experiments you can re-run.

The detector

What the detector actually computes

For each of the two tones, the incoming signal is multiplied by a sine and a cosine at that frequency, and both products are summed over exactly one bit period. That gives a complex number per tone. Its squared magnitude is how much energy the signal holds at that frequency over that window, and the difference between the two is the decision: positive means mark, negative means space.

Multiplying by both a sine and a cosine is what makes it work regardless of where in the tone's cycle the window happens to start. Use only one and the answer would swing with the phase, and the detector would read a perfectly good signal as garbage half the time. That pair is called quadrature, and it is the reason no phase recovery is needed anywhere in this code.

Mixing and then taking a moving sum is the same arithmetic as sliding a correlator along the signal, but it costs a fixed amount of work per sample instead of one multiply per sample per window position. At 48 kHz that is the difference between comfortable and unusable.

Why a flat window beats a tapered one, measured

A panel of four language models, asked what was wrong with this detector, said it suffered from spectral leakage and wanted a tapered window. That is a testable claim, so it was tested: character error rate against signal-to-noise ratio, rectangular window against Hann, the same seeded noise for both, 54 characters, five seeds each.

SNRRectangularHann
0 dB0 errors0 errors
-2 dB00
-3 dB03
-4 dB07
-6 dB233

The flat window is not merely adequate, it is better by a wide margin, and it should be. A flat sum over exactly one bit period is the matched filter for a constant-amplitude tone burst, and a matched filter maximises signal-to-noise ratio by construction. Tapering throws away signal energy at the edges of the window to buy rejection of distant frequencies, and the only competing tone here is 200 Hz away and already outside the main lobe. There is nothing to buy.

The general point is worth more than the specific result. An expert opinion about code is a hypothesis. This one took ten minutes to falsify.

Where the scope is tapped, and two versions that were wrong

The panel on the front page labelled “what is on the wire” took three attempts, and the two failures are both instructive.

The first sampled the detector's decision once per animation frame. Sixty points a second against a signal that changes three hundred times a second is not a scope, it is an aliaser, and for a while it looked fine because the carrier was not continuous. Once the carrier was held up for the whole call it collapsed into a flat line and the panel stopped showing anything at all.

The second drew the decision signal properly, from a ring buffer decimated to 3 kHz, which is ten points per bit. That is real and it is correct, and it was still the wrong thing to draw, because the decision signal is only defined for one band. It had nothing to say while the call was being placed, so the panel sat empty through the dial tone, the digits and the ringing, which is most of what there is to watch.

It now reads the line itself through an analyser on a tap that both directions feed. Both matters, because a scope across a two-wire pair sees the two directions superimposed, which is the whole reason they are put in different halves of the band. And the tap sits ahead of the monitor volume, which matters more: the speaker is muted for most of a call by ATM1, so a scope fed from behind the volume control would go blank exactly when there is most to look at.

Listening through the microphone, and what that checks

The original page said a real Bell 103 would decode its output. Internal audio tests did not establish that claim: the transmitter fed the detector inside the same tab. A physical modem introduces another implementation and an audio path that those tests do not cover.

The microphone switch gives the same detector an input stream from outside the tab. It can attempt to decode a recording of a Bell 103 call or another device running this page. It listens on the answer band, because that is the side a caller listens to.

Three constraints on the capture are switched off deliberately. Echo cancellation and noise suppression exist to protect speech, and both treat a continuous tone as precisely the sort of thing to remove. Automatic gain control hunts during the gaps between characters. All three are tuned for a human voice and all three would be competing with the detector for the same signal.

It is tested the way the rest of this is: a WAV is generated by the project's own transmitter, played into the browser as a microphone, and the assertion is that the text comes back out. This exercises the browser's capture path with generated audio. It does not establish interoperability with a physical Bell 103 modem or a telephone network.

How far down it still reads

Swept over forty seeds rather than five, the detector is clean at 9 dB and above, every seed, every character. From about 6 dB down it starts dropping the occasional character: at 0 dB, where the noise is exactly as loud as the signal, 32 seeds in 40 come back perfect and the error rate across the whole set stays under one percent. Below about minus one it comes apart properly rather than degrading.

This page previously said it read every character cleanly at 0 dB. That came from a five-seed sample that happened to be lucky, and the wider sweep contradicted it. The claim is now what the measurement says, and the sweep is a test rather than a thing somebody ran once.

The line quality control on the front page lets you hear this happen: it adds Gaussian noise to the audio before it is played and before it is demodulated, so what reaches your ears is what the detector is up against.

Finding a character without a clock

Why the half-window lag cancels

The detector's output at sample n describes the window ending at n, so it lags the signal by half a window. That looks like it should need correcting, and it does not, because the lag applies twice and cancels.

The start bit is found by watching for the decision to cross from positive to negative. That crossing happens when the window is half full of space, which is half a window after the transition really occurred. So the edge is late by half a window. The instants where data bits should be sampled are also read from the same lagged signal, so they need to be late by half a window too. Both offsets are the same size and the same sign, so reading the decision at the edge plus one and a half bit periods lands exactly on the centre of the first data bit. No correction term appears anywhere in the code, and that is why.

Two bugs about starting from cold

The first character of every transmission was being decoded out of a window that was only half full of signal, because the detector began clocking before it had a full bit period of audio behind it. The fix is to wait, which is what a real receiver does.

Carrier returning after a silence has exactly the same problem again: the window straddles the silence that came before. That is why there is a carrier detect gate that waits for a full window of continuous energy before the detector is believed. It measures total energy rather than the mark-minus-space difference, because that difference swings through zero every time the data changes, and a dropout detector built on it would fire on ordinary text.

The bug only an end-to-end test could find

Every unit test passed while the page was broken. Audio is modulated ten bytes at a time so a long screen starts arriving immediately rather than after the page has built two minutes of sound, and each chunk was being modulated from phase zero. That puts a step in the waveform at every buffer boundary: precisely the discontinuity the continuous-phase rule exists to prevent, moved from bit boundaries to buffer boundaries. The detector's window straddles the step and the characters around each join come out wrong.

No unit test could see it, because each of them modulated one buffer. It took a test that carries a whole session through real audio in deliberately awkward block sizes. The lesson generalises: a rule enforced inside a function is not enforced across calls to it.

Why there is no 1200 in the menu

The arithmetic that rules it out

Mark and space are 200 Hz apart. Telling two frequencies that close apart requires observing them for long enough to resolve 200 Hz, which means a window of very roughly one over 200 of a second, about five milliseconds. At 300 bits per second a bit lasts 3.3 milliseconds and the detector gets most of what it needs. At 1200 bits per second a bit lasts 0.83 milliseconds, nowhere near enough, and the two tones become indistinguishable.

This is not a shortcoming of this implementation. It is the reason the industry left frequency-shift keying behind for full-duplex speed. Wider tone spacing did carry FSK to 1200 bps in Bell 202 and V.23, at the cost of the simultaneous return channel; what could not be had at once was 1200 bps, frequency shift and full duplex on one voice circuit. A Bell 212A at 1200 bps does not shift frequency at all: it holds one carrier and shifts its phase, four phases carrying two bits at a time, so it moves 1200 bits per second while only changing symbol 600 times a second. The page offers 300 and 110, which are speeds a Bell 103 line actually ran, and a test asserts that 1200 stays broken so nobody restores it to the menu by mistake.

The handshake, in order

What happens between dialling and CONNECT

The answering modem raises its own mark tone, 2225 Hz, and sits on it. That tone does three jobs at once: it disables the echo suppressors the telephone network used on long-distance circuits, it tells the calling modem that something speaking Bell 103 picked up, and it is already the carrier that will hold for the rest of the call. It is not a separate signal, which is why this page generates it from the same transmitter that produces the carrier afterwards, with no seam where they meet.

The calling modem answers with its own mark tone at 1270 Hz, in the other half of the band, and from then on both tones are present continuously until somebody hangs up. Data is those tones moving. An idle line is them standing still.

The command set, all of it, and what each one actually does

The Hayes command set is finite and documented, so this modem answers the whole of it rather than a convenient subset. What varies is how much of a command can mean anything in a browser tab, and that is stated per command rather than hidden.

CommandWhat it does here
A/Repeats the last command. The only one with no AT in front.
ATAAnswer. Reports NO CARRIER, because nothing is calling you.
ATB0/B1CCITT V.21 or Bell 103 tones. Different frequencies entirely, which is why an American modem could not call a European one.
ATDDial. T and P choose tone or pulse, S dials the stored number, and W , @ ! are accepted as the pauses they were.
ATE0/E1Local echo off or on. Really off: type with E0 and nothing appears.
ATF0/F1Half or full duplex.
ATH0/H1Hang up, or lift the handset without dialling and listen to the line.
ATI0/I1Product code, or the ROM checksum. There is no ROM, and it says so rather than inventing a number.
ATL1-L3Monitor speaker volume. Three positions, and they change the gain.
ATM0-M3Speaker off, on until carrier, always on, or on except while dialling.
ATOBack online after an escape.
ATP/ATTWhether a bare ATD pulses or tones.
ATQ0/Q1Result codes on or off. With Q1 the modem answers nothing at all.
ATSn=v, ATSn?Read and write any S-register. Several of them genuinely drive this machine; see below.
ATV0/V1Numeric or worded result codes.
ATX0-X4How much the result codes are allowed to say. At X0 a connection is just CONNECT and a busy signal is indistinguishable from no carrier.
ATZReset, restoring a profile saved with AT&W if there is one.
AT&FBack to factory settings.
AT&P0/&P1Pulse make-break ratio, 39/61 or 33/67. It changes the audio.
AT&VPrint the whole active profile and every register.
AT&W, AT&ZStore the settings, store a dial string. Neither survives the page, which is the honest equivalent of a modem with no battery.
AT&C &D &G &J &L &M &S &T &X, ATN ATW ATYAccepted and remembered. These control RS-232 handshake lines, guard tones, jack wiring, leased lines, sync clocks and remote testing, none of which exists here. A script driving the modem should not get an ERROR for setting them, and pretending they did something would be worse.

The registers are not decoration either. S2 is the escape character and S12 the silence around it, so ATS2=126 genuinely makes three tildes the escape sequence and ATS2=255 genuinely turns escaping off. S3, S4 and S5 are the carriage return, line feed and backspace characters. S6 is the wait on dial tone, S7 sizes the ring-out, S9 decides when carrier is believed, S10 how long it may be gone, and S11 the touch-tone rate.

S9, and why the speaker cuts when it does

A modem does not trust a carrier the instant it appears. Register S9, Carrier Detect Response Time, is the number of tenths of a second the carrier must be present before the modem accepts it and raises carrier detect, and it left the factory set to 6. Six tenths of a second.

That single number is why a real modem's speaker cut out part-way through the answer tone rather than at the end of it. The default speaker setting, ATM1, means audible until carrier detect, so once the link was up the monitor went quiet and the rest of the tone played to nobody. This page reported CONNECT at the end of the whole three second tone at first, so you sat listening to a second of handshake after the call was already up.

Correcting that overshot, and the overshoot is the more interesting mistake. Cutting the speaker one S9 after the tone started left the entire handshake as a single short beep, because it collapsed the two halves of a connect into one. A link is not one modem shouting at a silent one. The answering end has to receive the originate carrier before there is a connection at all, so the order is: the tone arrives, this end waits until it has heard it for a full second and only then replies at 1270 Hz, and the far end needs the same second again before either can send data. Raising both carriers at CONNECT put the stretch where both tones are on the line together entirely after the monitor had already cut, which is why it sounded like nothing was being negotiated. It is audible now, and it is the only part of a 103 connect that sounds like two modems rather than one.

On the figure: a second is what V.25's answering procedure asks, which is roughly two seconds of silence for billing, then answer tone to a maximum of 3.3 seconds, with the calling end waiting in silence until it has detected that tone for a full second before it transmits anything. The first attempt at this used S9 instead, six tenths of a second, which is the right register for deciding when a carrier is believed but is not the figure that governs the handshake. That is why the connect still sounded clipped after the first correction. S9 still has the last word, though: it is a hard floor, so ATS9=25 drags the connect out to five seconds and the register stays causal.

Its mirror image is S10, Lost Carrier to Hang-up Delay: how long the tone may be gone before the modem gives up on the call. This one is set to 7, or seven tenths of a second, which is the Smartmodem 1200 factory value and the period-correct one for a board in 1987. Later modems shipped it at 14. What every manual insists on is not the number but the relationship: S10 must always exceed S9, because otherwise a momentary dropout satisfies the hang-up condition before it can satisfy the detect condition and the modem drops calls it should keep.

Both registers do something here rather than merely being echoed back. S9 decides when the speaker cuts, and S10 is the deadline the page holds the detector to: while a call is up it watches for the far end's tone, and if the tone is gone for longer than S10 the call ends with NO CARRIER. Try ATS10? to read it, or ATS9=25 to make the modem hang on for two and a half seconds before it believes a carrier at all.

The other phone in the house

Every extension in the building is wired across the same pair of copper. Your modem is not on a private connection to the board; it is one participant in a party that anybody can join by lifting a handset in the kitchen.

Doing so breaks the call in two ways at once. Lifting the receiver puts that telephone's circuitry across the pair, which changes the impedance the line presents and drops the level both carriers arrive at. It also opens a microphone into the room, so the far end's careful 2225 Hz mark now shares the wire with a refrigerator and a family conversation. The detector does not know any of this happened. It knows the tone it was tracking stopped making sense.

A brief interruption gives you a burst of gibberish where a menu was. Someone saying "hello?" is a human voice landing squarely in the 200 Hz gap between mark and space, and at that point S10 starts counting. Seven tenths of a second later there is no call, and the file you were three quarters of the way through is gone, because XMODEM has nowhere to keep a partial one. This is the origin of the household rule about the telephone, and of a particular volume of shouting up a staircase.

Call waiting was worse, because it needed nobody to do anything at all. A second caller put a tone on the line and the modem treated it exactly the way it treats an aunt saying hello. Which is why dial strings grew a prefix. ATDT*70,7025550143 switches call waiting off for the duration of the call, and the comma is a pause while the exchange takes that in. Both halves work here: the star is a real position on the touch-tone grid, and S8 decides how long the comma lasts, so ATS8=6 makes the modem wait six seconds in the middle of dialling and you can hear it do it.

Acoustic couplers, what they were, and why there is not one here

A coupler is a cradle with two rubber cups, and you push a telephone handset face down into it. One cup sits over the mouthpiece with a small loudspeaker pointing into it; the other sits over the earpiece with a microphone pointing into it. That is the entire device. To send a bit the modem plays a tone at its loudspeaker, the telephone's own mouthpiece hears it as though you had whistled down the line, and it goes out as an ordinary voice signal. To receive, the far end's tones come out of the telephone's earpiece as sound and the microphone in the other cup listens to them. The rubber is there to make a seal. Nothing is wired to anything: the two halves of the link are talking to each other out loud, through a telephone that has no idea it is carrying data.

Which explains its reputation. Room noise gets in, so you did not have a conversation next to a transfer, and a door slamming could cost you a character. It only worked at all because the Bell System had made every handset the same shape, so one pair of cups fitted the entire country's telephones. And the acoustic path is the weak link in the chain: a telephone mouthpiece is built to carry speech well enough to be understood, not to reproduce tones faithfully, and the practical ceiling for coupled modems was about 1200 bits per second. Wire the modem to the line and that ceiling is gone.

So it is worth saying plainly that 300 baud never required one. Bell 103 is a modulation scheme. It does not care whether its tones reach the line through a wire or through the air and a carbon microphone.

An acoustic coupler in sectionA telephone handset lies face down in a cradle with two rubber cups. The cup under the earpiece holds a microphone, which carries the far end's tones to the modem. The cup under the mouthpiece holds a loudspeaker, which the modem plays its own tones into. The handset's cord runs off to the telephone and the line, and nothing in the coupler touches the line electrically.earpiecemouthpiecemicrophoneloudspeakerthe couplermodemwhat the far end sentwhat this end sendsto the telephoneNothing in the coupler touches the line. The two ends are talking out loud.

What decided it was regulation. Bell 103 arrived in 1962 as AT&T's own dataset, wired straight into a network AT&T also owned, so the original article was direct connect. Everyone else's equipment was the problem: until the FCC's Part 68 registration scheme took effect in the second half of the 1970s, connecting third party gear electrically to the network was not generally permitted, and a coupler was the way around it. Sound is not an electrical connection. So couplers were what independent manufacturers shipped, and they stayed common into the early 1980s while Part 68 registration was still an expense worth avoiding.

Which is why this machine cannot have one, and the reason is sitting in plain sight in the command set. A coupler has no electrical contact with the line, so it cannot seize it, cannot dial it, and cannot tell whether the far end ever answered. You dialled by hand on the telephone, listened for the tone yourself, and pushed the handset into the cups. A modem that answers ATDT is by definition wired to the line. The Hayes Smartmodem of 1981 is the machine this one is modelled on, and it plugged into an RJ-11.

Dialling, and why zero takes longest

Touch tones are two frequencies at once, one from a row and one from a column of a four by four grid. Two simultaneous tones is what makes them impossible to trigger by accident: no single voice or whistle lands on two grid frequencies at the same moment, which was the whole design problem.

Pulse dialling contains no tone whatsoever. The line is interrupted, once per unit, ten times a second, and the exchange counts the interruptions. A rotary dial is a clockwork timer for doing exactly that, and zero is encoded as ten pulses rather than none, which is why it takes a full second to dial and why phone numbers full of zeros were unpopular. Both are generated here from that description rather than from recordings, and the tests measure the audio to confirm the frequencies are where they should be.

The board, and getting files off it

Colour, and the bit that made it bright

A PC text screen gave every cell an attribute byte: three bits of foreground colour, one bit of intensity, three bits of background, and one bit that made it blink. So the famous sixteen colours are not sixteen choices. They are eight colours and a brightness switch, and that switch is the reason two separate bugs on this site were about colour rather than about drawing.

ANSI has no intensity bit. It has bold. So bright cyan is not a colour in ANSI, it is cyan with the bold attribute set, written ESC[1;36m. Everything follows from that, including both traps.

The first is that ANSI's colour numbering is not the PC's. ANSI counts black, red, green, yellow, blue, magenta, cyan, white. The PC attribute byte counts black, blue, green, cyan, red, magenta, brown, light grey. Red and blue trade places, and so do yellow and cyan. This project had its palette written in ANSI order and a converter mapping ANSI to DOS on top of it, so four of the eight colours were converted twice and came out as each other. Red drew blue for a while. The test that should have caught it asserted palette slot numbers, which is the arithmetic agreeing with itself; it asserts hex now.

The second is that bold is an attribute and selecting a colour does not clear it. A plain 36 after a 1;36 sets the colour and leaves the intensity exactly where it was, so everything after the first bright thing on a screen keeps drawing bright. The frame around the welcome banner changed shade half way down for precisely this reason: the title in the second row switched the intensity on and nothing switched it off. Both shades pass a contrast check, which is why the contrast suite had nothing to say about it and only asserting the intended colour caught it. Real BBS ANSI is littered with 0;3Xm because every author eventually met this.

The face the screen had to have

A board draws its frames out of CP437 box-drawing characters, and those only join into continuous rules under two conditions. The line box has to be the character cell, which means leading of exactly one. And the glyph has to be as tall as that line box, which is not up to the page at all.

The second condition is the one that is easy to miss, and missing it is why setting the leading correctly did not fix anything on its own. Several common fallback monospace faces draw a vertical bar noticeably shorter than a full line, so every rule arrives with a nick in it at each row boundary and the box looks dashed. Which face a visitor gets is a property of their device, so on a page whose entire visual vocabulary is box drawing, it cannot be left to chance.

The terminal therefore carries its own: DejaVu Sans Mono, subset to exactly the 222 glyphs this terminal is capable of drawing and nothing else, which is why it is under nine kilobytes. Its vertical bar measures 1.188 em against a 1 em row, so it laps into the row below rather than falling short, and the rules join.

Carrying a known face has a consequence worth recording. The screen is sized by measuring rendered text, so the measurement has to be repeated once the real face arrives, and it must not measure the element it is resizing. An earlier version did exactly that inside a ResizeObserver, which converged only for as long as the terminal never quite reached the edge of its container. A face with a slightly different advance width tipped it into a loop: grow until a scrollbar appears, shrink because the scrollbar narrowed the container, grow again when it vanishes. It derives the size from the container alone now, which has no path back to its own input.

Why the screen is code page 437

Everything above character 127 in a BBS screen is box drawing, shading and block characters from the IBM PC's character set. That set is the entire visual vocabulary of the form. A terminal that treats those bytes as Latin-1 renders every screen as accented gibberish, so the terminal here implements the real mapping. The corner that starts most BBS frames is byte 201, which is a double-line top-left corner in code page 437 and a capital E with an acute accent in Latin-1.

Why CRC replaced the checksum in XMODEM

XMODEM shipped with a one-byte checksum: add up the 128 data bytes and keep the low byte. It catches a great deal, and it misses anything that leaves the sum unchanged. The clearest case is two bytes swapping places, which alters the data and not the total at all.

The test suite demonstrates exactly that: two byte arrays that differ only in order produce an identical checksum and different CRC-16 values. Both schemes are implemented here, because a receiver that only spoke checksum was still a thing you met in 1987, and the receiver chooses which by sending either NAK or the letter C to open the transfer.

What was on the other end

The longer version, with FidoNet, the door games and the art scene, is on the bulletin board world.

The boards, and why they were local

The Oasis on this page has one node, and that was the ordinary case. A board was usually one computer, one modem and one telephone line, in somebody's spare room, answering one caller at a time. If it was busy you hung up and tried again in ten minutes. Sysops set a time limit per caller per day for the same reason a restaurant has tables: the line was the whole capacity of the place.

Distance decided everything else. The call was a telephone call, priced like one, so a board twenty miles away could cost more per evening than the computer had. Boards were therefore local institutions, the list of numbers you had was the list of numbers inside your own area code, and a board with callers from three states was either famous or expensive.

FidoNet got around it by not being live. Boards called each other in the small hours on a schedule and handed over mail and message bases in batches, so a reply to somebody in another city left tonight and got there over the next day or two. Store and forward, running on the fact that long distance was cheap at four in the morning.

The services you paid for by the hour

Alongside the hobbyist boards were the commercial ones: CompuServe, The Source, GEnie, Delphi and Quantum Link, which became America Online, with Prodigy alongside them on a flat monthly fee rather than by the hour. These were not a PC in a spare room. They were mainframes with tens of thousands of subscribers, and they billed by connect time, which made the clock in the corner of your terminal program a spending meter.

Some tariffs charged by speed as well, so a faster modem cost more per minute. That sounds backwards until you notice it is the same trade the board made with its single line: what was scarce was not the data, it was the port you were sitting in.

There is a longer account of those services, what the hourly meter did to how people behaved inside them, and the three months in 1996 that ended it, on the walled gardens.

How a service in Ohio was a local call

A national service that everyone had to phone long distance would have been unaffordable, and this was solved before the internet was a consumer thing. Telenet and Tymnet ran packet-switched networks with dial-in nodes in most cities of any size. You called a local number, reached a device called a PAD, which assembles and disassembles packets, told it which service you wanted, and it carried your session across the country as packets while you paid for a local call.

It is worth sitting with that for a second, because it is the shape of the thing that replaced it. The last mile was analogue and dialled, the middle was packet switched and shared, and the user typed a name rather than a telephone number. Everything except the last mile survived.

Why the status line says 8-N-1

Eight data bits, no parity, one stop bit. This machine offers nothing else, and it was not always the obvious answer.

The older convention was seven bits with even parity, and it came from the hardware: a teletype generated the parity bit in the keyboard itself, so seven data bits and a parity bit was simply what the equipment did. Plenty of the multi-user systems of the mid 1980s inherited that and expected callers to match it, which is why terminal programs had the setting on the front panel rather than buried.

Seven bits carry ASCII and nothing else. Eight carry the CP437 box drawing that every screen on this site is made of, and they carry a binary file without an encoding step in the middle. Once a board was somewhere you went to draw screens and move programs around, the parity bit was worth more as a data bit, and error checking moved up a layer to where XMODEM could do it properly on a whole block.

The machine, drawn

A character on the wire

Ten bits, and only eight of them are yours. The line rests high, at mark. A character starts by pulling it low for exactly one bit period, which is the only timing reference the receiver gets, and ends by putting it back so the next start bit is unambiguous.

The ten bits of one character at 8-N-1A square wave. The line sits high at mark, drops low for one start bit, carries eight data bits least significant first, then returns high for the stop bit.10markmarkstartb0b1b2b3b4b5b6b7stop3.3 ms per bit at 300 baud, so 33 ms for the character

The handshake, end to end

Nine and a bit seconds from typing the command to the first character of the board, and every stretch of it is a register or a fact about the telephone network rather than a number somebody liked.

The phases of placing a callA timeline: two seconds of dial tone set by register S6, 1.4 seconds of touch tones set by S11, 1.8 seconds while the exchange routes the call, one two-second ring, a two-second billing delay, then the answer tone. Carrier detect asserts 0.6 seconds into the answer tone, set by register S9, and the monitor speaker cuts at that instant.dial toneS6, 2.0sdiallingS11, 1.4srouting1.8sone ring2.0sbilling delay2.0sanswer tone2225 Hzcarrier detect: S9, 0.6 s inCONNECT 300, and the speaker cutsthe tone keeps going; you just stop hearing it

Where the tones sit

A telephone channel passes roughly 300 to 3000 Hz because that is enough for speech and no more. Everything a modem ever did on a dial-up line had to happen inside that slot. It is worth seeing how little room that is, and worth knowing that the thing which eventually broke the limit did not get cleverer inside the slot: it stopped using it.

The voice band, and the four Bell 103 tones inside itA frequency scale from 0 to 4000 Hz. The usable voice channel runs from 300 to 3000 Hz. The originate pair sits at 1070 and 1270 Hz and the answer pair at 2025 and 2225 Hz, all four well inside the channel.03001000200030004000 Hz1070127020252225the whole voice channel, 300 to 3000 Hzoriginate 1070 / 1270 · answer 2025 / 2225ADSL later used this same pair of wires up to about 1.1 MHz, above the voice channel entirely

An XMODEM block, and the conversation around it

A hundred and thirty-three bytes carry a hundred and twenty-eight. The block number travels twice, the second time inverted, so a single bad bit in it cannot be read as a different block.

The layout of one XMODEM frameFive fields: a start-of-header byte, the block number, the block number inverted, 128 data bytes, and a two-byte CRC or a one-byte checksum.SOHblk255-blk128 data bytesCRC-16133 bytes on the wire, of which 128 are the file. At 300 baud one block takes 4.4 seconds.
The XMODEM conversationThe receiver opens by sending C to ask for CRC mode. The sender sends a block, the receiver acknowledges each one. A corrupted block is negatively acknowledged and resent unchanged. At the end the sender sends EOT, the receiver negatively acknowledges the first one deliberately, and only acknowledges the second.SENDERRECEIVERC (give me CRC)block 1ACKblock 2 (corrupted)NAKblock 2 again, unchangedACKEOT

What the picture leaves out is the ending, and the ending is where this implementation had a bug. The receiver does not acknowledge the first EOT. It answers it with NAK, on purpose, because a single byte of line noise that happens to decode as 0x04 would otherwise finish the transfer and report success, handing back a file that is simply missing its tail. This implementation repeats the EOT; noise does not. Christensen's original specification has the sender send one EOT and the receiver ACK it, so the second round here is local hardening rather than XMODEM as specified. Fuzzing this at one corrupted byte in eighty produced a wrong file in twenty of a hundred and eighty-seven completed transfers before that guard went in, and none after it.

What came after

Every rung of it, from 110 baud to fibre, is on every speed.

Why this connect is so short, and where the screech went

Almost everyone who used a modem remembers a long negotiating howl: rising and falling tones, a burst that sounds like static, then silence. It lasted twenty to sixty seconds depending on the modems and the line. That sound is real, and it is not this machine.

It belongs to V.32bis, V.34 and V.90. Those modems have something to argue about. They must agree which standard both ends support, then measure the line they have actually been given, then train their equalisers and echo cancellers against its particular distortions and reflections, then agree a speed, and fall back and try again if the line will not carry it. The tones you hear are that conversation: the probing, the training sequences, the retries.

A Bell 103 has none of that to do. There is one speed. There is one modulation. There is no fallback ladder, no line probe, no equaliser to train, and no capability to exchange, because there is only one capability. The answering modem raises its tone, the calling modem waits for it, and the call is up. That wait is the roughly one second of continuous tone V.25 asks for, not the six tenths of S9 — the correction is made earlier on this page and this paragraph used to contradict it. Either way the point stands: it is the whole of what a 300 baud connect had to say.

This is the clearest thing the machine can teach about its own era. The famous sound is not the sound of a modem. It is the sound of a modem having to be clever, and in 1962 there was nothing to be clever about.

There is still something to hear after carrier detect, and by default you do not: the calling modem answers with its own 1270 Hz tone, so for the rest of the call both tones sit on the line together. ATM1, the factory speaker setting, cuts the monitor at carrier detect precisely so you are not listening to that for the length of the call. ATM2 keeps it on if you want it.

The file transfer protocols: XMODEM, YMODEM, ZMODEM

Ward Christensen’s XMODEM specification (1977) is what this board runs. 128-byte blocks, one round trip each, an 8-bit checksum that CRC-16 later replaced. It knows nothing about the file: not its name, not its length, which is why the padding at the end cannot be distinguished from real data and why a transferred file often gained a few trailing Ctrl-Z bytes.

XMODEM-1K kept the protocol and grew the block to 1024 bytes. Fewer round trips is a straight win once the line is fast enough that waiting costs more than resending.

YMODEM added a block zero carrying the filename and size, which finally let a transfer know what it was moving and let one session carry several files. It made CRC the default rather than an option. YMODEM-g dropped the per-block acknowledgement entirely and streamed, on the assumption that the modem underneath was already correcting errors; any error at all aborts the whole transfer.

ZMODEM streams properly. Variable-length frames, a 32-bit CRC, and the feature everybody actually remembers: crash recovery, so a transfer that died at 90 percent resumed from 90 percent instead of starting again. On a metered long-distance call that was the difference between a protocol you tolerated and one you chose.

The through-line is the round trip. XMODEM waits after every block; at 300 baud the waiting is nothing next to the sending. As lines got faster the waiting became the whole cost, and each protocol in turn found a way to stop doing it.

The speeds, on the same pair of copper wires

Everything below ran down the same two wires into the same house. What changed was how much cleverness was applied to the same slice of spectrum.

StandardRateHow
Bell 103 (1962)300 bpsShifts a tone between two frequencies. This machine.
Bell 212A (1976)1200 bpsShifts the phase of one carrier, two bits per symbol.
V.22bis2400 bpsPhase and amplitude together, four bits per symbol.
V.329600 bpsEcho cancellation, so both directions use the whole band.
V.32bis14.4 kbpsA denser constellation of the same idea.
V.3428.8, later 33.6 kbpsMeasures the line and shapes itself to fit it.
V.9056 kbps down, 33.6 upAsymmetric, because it only works if one end is already digital.
ADSLMegabitsStops using the voice channel at all.

That last row is the punchline of the whole page. Every modem before it was trapped inside 300 to 3000 Hz, because the telephone network treated the signal as speech and threw away everything outside the range speech needs. ADSL does not send speech. It puts a splitter on the line and uses the copper up to about 1.1 MHz, hundreds of times the bandwidth, on the wire that was already in the wall. The limit was never the wire. It was the channel the wire had been assigned.

Why the modem eventually did the error checking itself

XMODEM checks its own blocks because in 1977 nothing else would. That changed: MNP and then V.42 put error correction inside the modem, so the data crossing the line was already being retried and verified beneath the file transfer. V.42bis added compression on top, which is why a 14.4k modem could move plain text faster than 14.4 kbps and why compressed files went no faster at all.

Once the modem was reliable, a protocol that stopped after every block to ask “did you get that” was paying for a guarantee it already had. That is the reason YMODEM-g and ZMODEM streaming made sense when they did and would have been reckless a decade earlier.

T1 and T3, which are a different story

A modem exists because the telephone network was analogue and would only carry sound. The other answer to the same problem was to make the network itself digital, and that started at the same time: the T1 carrier went into Bell System trunks in 1962, the same year as the Bell 103. It carries 1.544 Mbps, arranged as 24 digitised voice channels. T3 is 28 T1s bonded, about 44.7 Mbps.

Those were never in your house. They ran between exchanges and, later, into businesses that could afford a leased line. The interesting part is that the digital network arrived first and worked inwards: by the time V.90 reached 56k, most of the path was already digital, and the only analogue stretch left was the last mile to the handset. That is exactly why V.90 is asymmetric. Downstream has one analogue-to-digital conversion to survive and upstream has two, so the two directions get different speeds on the same call.

How the other connects on this site were made

The speed ladder on the front page will play you a connect for each rung up to V.90, and it is worth being exact about what that is, because the rest of this site earns its keep by being real and this part cannot be.

The Bell 103 rung is the working modem. Everything above it is a reconstruction. V.32bis, V.34 and V.90 are enormous pieces of signal processing, with echo cancellation, trellis coding, adaptive equalisers and, for V.90, a model of the telephone company's own converter. Implementing one in a browser tab is a project, not a section of a page.

So each connect is assembled from the tone frequencies, the sequence and the approximate durations that the relevant standard publishes. ANSam is 2100 Hz with a phase reversal every 450 ms and 15 Hz amplitude modulation at an index of 0.2, because that is what V.8 specifies, and the tests measure the synthesised audio against all three of those figures. The V.34 line probe is a comb of tones every 150 Hz from 150 to 3750, and a test checks both that the tones are there and that the gaps between them are empty. The phases carrying scrambled data are the one frankly impressionistic part: band-limited noise across the voice band, which is what trained data sounds like without being what it is.

One phase is not a reconstruction at all. Every standard from V.32 onward opens by negotiating in V.21 at 300 bits per second, because that is the one modulation every modem can be assumed to speak, and this project has a real V.21. Those phases are genuinely modulated, and the proof is a test that demodulates them with the same detector the front page uses and gets the bytes back. The fast part of a fast modem starts by talking at this speed.

The 300 baud still inside every fax

Dial-up died and this speed did not, because it went somewhere most people stopped looking. A fax machine is a modem with a scanner and a printer bolted to it. Same voice band, same telephone call, same problem of getting data across a channel built for a human voice.

The T.30 procedure that governs a fax call opens with tones. The calling machine sends CNG, 1100 Hz for half a second every three seconds, which is how a fax announces that it is not a person. The answering machine replies with CED at 2100 Hz, the same answer tone that opens a data call and disables echo cancellers for the same reason.

Then comes the part worth the whole section. Both machines switch to V.21 channel 2 at 300 bits per second and hold their entire negotiation there. The answering machine sends a Digital Identification Signal listing what it can do, page size and resolution and error correction and speed. The caller replies with a Digital Command Signal choosing from that list, then sends a training check at whatever speed they settled on.

So the image might travel at 14,400 under V.17, or at 33,600 on a Super G3 machine using V.34, but the conversation that decides it happens at exactly the rate this page runs at, in the same modulation family, with a mark and a space and ten bits to a character. And because fax machines stayed backward compatible, one bought today can still hold that conversation with one from the 1980s. The speed at the top of this page is not a museum piece. It is the layer everything faster still has to agree on.

What else still dials, and why

Fax is the one with real numbers behind it. Reporting in 2024 put roughly a sixth of businesses still using it for something they consider critical, and United States healthcare alone at billions of pages a year. The reasons given are rarely technical. A fax produces a delivery record that regulators and courts already accept, it does not pass through shared internet infrastructure, and the compliance question of whether it is allowed has been settled for decades, which is a real advantage over anything that has not.

Beyond that, honesty is better than a long list. Dial-up hung on longest wherever a device had to work without a network to depend on, on a line that already existed and was paid for: payment terminals, alarm panels, utility telemetry, and console access to equipment whose network is the thing that has failed. Most of that has moved to IP, and the remainder is shrinking rather than holding steady. It would be pleasant to claim the modem is quietly everywhere. It is not. It is in the fax, and in a thinning set of places where a telephone line is the last thing still working.

Sources

Back to the modem