History · Control codes
ASCII control characters
Thirty-three codes built to drive teletypes, and the handful that still run your terminal.
Open an ASCII table and the first two columns hold abbreviations instead of characters: NUL, SOH, BEL, ESC. These are the control characters, the 32 codes from 0 to 31 plus DEL at 127. None of them prints anything. They were designed to make a machine do something: ring a bell, move the paper, start a tape reader, or mark where a message begins and ends.
Most were built for teleprinters and data links that are long gone. A few still do the job they were designed for, and several others have been given new ones.†
On a keyboard you reach them with Ctrl: Ctrl-G sends BEL, and Ctrl-M sends CR. Why Ctrl works that way is a story of its own.
| Hex | Code | Built to | Today |
|---|---|---|---|
| 00 | NUL | Fill time or blank tape | In use: ends C strings |
| 01 | SOH | Start a message heading | Rare |
| 02 | STX | Start the message text | Rare |
| 03 | ETX | End the text | Repurposed: Ctrl-C interrupts |
| 04 | EOT | End the transmission | Repurposed: Ctrl-D ends input |
| 05 | ENQ | Ask the far end to identify itself | Rare |
| 06 | ACK | Confirm receipt | Rare |
| 07 | BEL | Ring a bell | In use: terminal bell |
| 08 | BS | Move back one space | In use |
| 09 | HT | Jump to the next tab stop | In use |
| 0A | LF | Advance one line | In use |
| 0B | VT | Jump to a preset line | Rare |
| 0C | FF | Start a new page | Rare |
| 0D | CR | Return to the start of the line | In use |
| 0E | SO | Switch to another character set | Rare |
| 0F | SI | Switch back | Rare |
| 10 | DLE | Change the meaning of what follows, for link control | Rare |
| 11 | DC1 | Switch a device on | Repurposed: XON, Ctrl-Q |
| 12 | DC2 | Control a device | Rare |
| 13 | DC3 | Switch a device off | Repurposed: XOFF, Ctrl-S |
| 14 | DC4 | Stop a device | Rare |
| 15 | NAK | Reject what was received | Repurposed: Ctrl-U erases the line |
| 16 | SYN | Keep a synchronous link in step | Rare |
| 17 | ETB | End a block of data | Rare |
| 18 | CAN | Disregard the data sent with it | Rare |
| 19 | EM | Mark the end of the tape | Rare |
| 1A | SUB | Stand in for a garbled character | Repurposed: Ctrl-Z suspends |
| 1B | ESC | Change the meaning of what follows | In use: terminal sequences |
| 1C | FS | Separate files | Repurposed: Ctrl-\ quits |
| 1D | GS | Separate groups | Rare |
| 1E | RS | Separate records | Rare: JSON text sequences |
| 1F | US | Separate units | Rare |
| 7F | DEL | Erase a character on tape | In use |
Running the line
The largest group managed traffic between machines. A message could open with SOH (start of heading) and some routing information, move on to its body with STX (start of text), and close with ETX (end of text). EOT (end of transmission) said the sender was finished.
The receiving station replied with ACK (acknowledge) or NAK (negative acknowledge). On synchronous links, where both ends kept time with each other, SYN filled idle moments to hold them in step. DLE (data link escape) gave the next few characters a special meaning for controlling the link.
ENQ (enquiry) asked the far end to identify itself. Its 1963 name, WRU, put it more plainly: who are you? A Teletype Model 33 answered with a small drum of breakable tabs, coded with the station’s identity, which turned and sent that identity back down the line.
Few of these codes are seen today. One survivor: xterm still answers ENQ, though by default its answer is empty.
Moving the paper
The format effectors controlled layout on a printing terminal. BS (backspace) moved the print head back one position, so a typist could strike over a character, for example to underline it. HT (horizontal tab) jumped to the next tab stop, as on a typewriter. VT (vertical tab) moved down to a preset line, and FF (form feed) advanced to the top of the next page. CR and LF split a new line into two motions, returning the carriage and advancing the paper, a split that still causes trouble.
This group survived best. Tab, line feed and carriage return appear in nearly every text file, and XML 1.0 allows those three and bans every other code below 0x20.†
Vertical tab and form feed are mostly a legacy: xterm, for one, treats both as an ordinary line feed.
Bells and switches
BEL did what its name says. On a Teletype it rang a real bell, so a distant sender could call an operator over to the machine. DC1 to DC4 (device controls) were left general: the standard says only that they switch auxiliary devices on or off, with DC4 the preferred way to stop one. The Teletype Model 33 used DC1 and DC3 to switch an attached device, such as its paper-tape punch, on and off, and those two codes came to be known as XON and XOFF.
Both ideas lasted. Terminals still sound a bell when they receive BEL, and xterm also accepts it as the end of certain escape sequences. XON and XOFF became flow control: a device sends DC3 to say “stop sending” and DC1 to say “carry on.” That’s why Ctrl-S freezes the output in many Unix terminals, and Ctrl-Q brings it back.†
Files, groups, records, units
Four codes, FS, GS, RS and US (file, group, record and unit separator), were meant to divide data into nested levels, from the largest to the smallest. In the 1963 chart they were unnamed separators, S4 to S7.
They make ideal delimiters, because they never appear in ordinary text. In practice the comma and the tab won, and CSV has to quote any field that contains a comma. Unicode still gives them separator roles in bidirectional text, where FS, GS and RS end a paragraph and US ends a segment. RS has also found a modern job: RFC 7464 uses it to mark the start of each record in a stream of JSON texts. JSON doesn’t allow a raw control character, so an RS can only mean that a new record begins.
Shifting and escaping
SO (shift out) and SI (shift in) switched to another character set and back, much as telegraph codes had shifted between letters and figures. The pair was still being specified in the 1990s: ISO-2022-KR, a Korean email encoding defined in 1993, used SO to switch from ASCII into Korean characters and SI to switch back.
ESC did the same job more broadly. It changes the meaning of the characters that follow it, which is how terminals still receive colours and cursor movements.
Filler, mistakes and endings
NUL is the all-zero code. On paper tape, blank leader with no holes read as NUL, and the standard used it as filler to pass time or fill space. Today it marks the end of a string in C.
CAN (cancel) told the receiver to disregard data just sent. EM (end of medium) marked the physical end of a tape, or the end of its used part. SUB (substitute) stood in for a character that had arrived garbled.
SUB found a second career. CP/M recorded file sizes only in 128-byte blocks, so a text file could end partway through its last block, followed by leftover bytes. Programs marked the real end with Ctrl-Z, which sends SUB. DOS kept the habit, and the Windows copy command still stops at the first Ctrl-Z when it’s told a file is text.†
DEL, the code with every hole punched, has its own article.
The ones you still press
Several codes now do jobs ASCII never gave them. On Linux, the terminal driver watches for certain control characters as you type and acts on them itself:
- Ctrl-C sends ETX (end of text) and interrupts the running program.
- Ctrl-D sends EOT (end of transmission) and signals the end of input.
- Ctrl-Z sends SUB and suspends the program.
- Ctrl-U sends NAK and erases the line you’re typing.
- Ctrl-\ sends FS and quits the program.
Some of the new meanings echo the old names: end of text, end of transmission. Others have nothing to do with them. Either way, the meaning now comes from the terminal, not from ASCII.†
Read down the table’s last column and the pattern is clear. Most of the codes that laid out a page, such as tab, line feed and carriage return, are still everywhere. The ones that ran a link or a tape punch either retired or found work under a new name.
Continue reading
Sources
- American Standards Association, American Standard Code for Information Interchange, X3.4-1963, p. 5 (the 1963 code chart). — and Vint Cerf, “ASCII format for Network Interchange,” RFC 20, October 1969, which reproduces the control definitions of USAS X3.4-1968. https://www.rfc-editor.org/rfc/rfc20
- Tom Jennings, “An annotated history of some character codes,” including the sections ASCII-1963 and ASCII-1967. https://www.sr-ix.com/Archive/CharCodeHist/index.html
- Linux man-pages, “termios(3).” https://man7.org/linux/man-pages/man3/termios.3.html — The Open Group, POSIX.1-2024, Chapter 11 “General Terminal Interface.” https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap11.html — and Thomas E. Dickey, “XTerm Control Sequences.” https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
- The Unicode Consortium, The Unicode Standard, Version 18.0, §23.1 “Control Codes.” https://www.unicode.org/versions/Unicode18.0.0/core-spec/chapter-23/ — and W3C, Extensible Markup Language (XML) 1.0 (Fifth Edition), §2.2 “Characters” and §4.1 “Character and Entity References.” https://www.w3.org/TR/xml/
- N. Williams, “JavaScript Object Notation (JSON) Text Sequences,” RFC 7464, February 2015. https://www.rfc-editor.org/rfc/rfc7464 — and U. Choi, K. Chon and H. Park, “Korean Character Encoding for Internet Messages,” RFC 1557, December 1993. https://www.rfc-editor.org/rfc/rfc1557
- Michal Necasek, “Misconceptions on Top of Misconceptions,” OS/2 Museum. https://www.os2museum.com/wp/misconceptions-on-top-of-misconceptions/ — and Microsoft, “copy,” Windows Commands. https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/copy