This page is about the very essentials of how to implement video terminal functionality. Terminals are connected to the OS over serial port, but could use network too with the telnet and ssh protocols.
Originally terminals were line printers. The user typed in a command, pressed enter, and the computer executed that command and printed the results line by line. This is where command line interface originates. It understood the basic ASCII control characters, like ASCII 8 (backspace), ASCII 10 (line feed), ASCII 13 (carriage return) etc. but nothing else.
Although this input and output method works for simple programs, it is very complicated to implement a text editor for example. Many editors kept the line terminal heritage, like Vi, but to provide a more comfortable interface, the printer was replaced with a screen. With it come the need to position the cursor on the screen, hence video terminal and video terminal sequences was born.
There were many more or less successful terminals (most notably the VT52), but finally Digital Equipment Corporation's VT100 got widespread and its codes got standardized. The VT100 codes (also called ANSI escape codes) are the most widely emulated sequences, ever.
When your OS is communicating over the serial like, it is very likely that on the other end there's a terminal. Either a real one, or a terminal emulator, like PuTTY or minicom. It is the terminal's duty to handle the keyboard, the key modifier keys (like Shift, Control, Alt), and send terminal key sequences. It is also the terminal's duty to interpret the received terminal codes (sometimes also called escape sequences) and visualize them properly. These sequences always start with the ASCII 27 (escape) character, "\033" in C literals, and terminated by a latin uppercase or lowercase letter or tilde ("~"). In between there might be numeric parameters (0-9) separated by comma ("," and ";") but such arguments are always prefixed by "[". This two bytes start sequence "\033[" also called Control Sequence Introducer, or CSI.
This section is about the bytes your OS might receive from a terminal. It is important to know that terminals are different, some are configurable, so they might send different codes. For a comprehensive list of all possible key codes, consult the termcap database.
|ASCII 3||Break (^C)||the Break signal|
|ASCII 7||Bell (^G)||makes a beep|
|ASCII 8||BS (^H)||Backspace, moves the cursor left and delete, some terminals send ASCII 127 instead|
|ASCII 9||Tab (^I)||Tabulator, moves to cursor to the next multiple of 8 coloumn on the right|
|ASCII 10||Line Feed (^J)||move the cursor down (on modern machines line feed and carriage return is combined and called newline)|
|ASCII 13||Carriage Return (^M)||Return or Enter key, move the cursor to the left most coloumn|
|ASCII 27||Escape (^[)||start the command sequences|
|ASCII 32 - 126||keys||"normal" keys|
|ASCII 127 or \033[3~||Delete||delete without moving the cursor, some terminals send ASCII 127 instead of ASCII 8, but \033[3~ is standard|
|\033[A||Up||up arrow key|
|\033[B||Down||down arrow key|
|\033[C||Right||right arrow key|
|\033[D||Left||left arrow key|
|\033[5~ or \033[S||PgUp||the Page Up key|
|\033[6~ or \033[T||PgDn||the Page Down key|
|\033[7~ or \033[H||Home||the Home key|
|\033[8~||End||the End key|
|\033[11~ to \033[21~||F1 - F10||function keys|
|\033[row;colR||Status report||answer to the \033[6n code|
Note that cursor keys are standard, but there might be differences with the Home, End, PgUp, PgDn and Fn keys depending on the terminal. Key modifiers (like Shift, Caps Lock, Control, Alt, Compose etc.) are always interpreted by the terminal, and have no codes.
These are the bytes that your OS might send to a terminal. Terminals and terminal emulators are different, they usually understand different codes. However there's a subset of codes that all guaranteed to understand, and those are the VT100 codes. Modern terminal emulators usually support more, the VT220 subset is the smallest common denominator. Everything above that requires exact terminal type detection and differentiated codes.
|\033[nA||CUU||move the cursor up n rows|
|\033[nB||CUD||move the cursor down n rows|
|\033[nC||CUF||move the cursor right n coloumns|
|\033[nD||CUB||move the cursor left n coloumns|
|\033[nE||CNL||move the cursor to the beginning of line n down|
|\033[nF||CPL||move the cursor to the beginning of line n up|
|\033[nG||CHA||move the cursor to coloumn n|
|\033[H||CUP||move the cursor to the top left corner of screen|
|\033[row;colH||CUP||move the cursor|
|\033[row;colf||HVP||move the cursor|
|\033[J or \033[0J||ED||clear the screen from cursor to the end|
|\033[1J||ED||clear the screen from beginning to the cursor|
|\033[2J||ED||clear the entire screen|
|\033[K or \033[0K||EL||clear the line from cursor to the end|
|\033[1K||EL||clear the line from beginning to the cursor|
|\033[2K||EL||clear the entire line|
|\033[S||SU||scroll up one page|
|\033[T||SD||scroll down one page|
|\033[m||SGR||reset color attributes|
|\033[1m||SGR||select bold or intensive color|
|\033[30-37m||SGR||select foreground color|
|\033[40-47m||SGR||select background color|
The Select Graphic Rendition (or SGR) codes are often concatenated into one command, for example to select bright white on blue background \033[1;37;0;44m. Some terminals understands color codes 90 - 97 (same as 1;30 - 1;37) and 100 - 107 (same as 1;40 - 1;47).
For the foreground and background color codes, these are as follows:
|FG||BG||Name||RGB (VGA colors)|
|1;30||1;40||Bright Black (Gray)||85,85,85|
First and most, you should detect the size of the screen. This is done by moving the cursor over the screen, and then query the position.
\033[999;999H\033[6n\033[H = your OS to the terminal
\033[row;colR = the terminal's answer, screen size
To detect the exact terminal type to determine if you can use advanced codes (like \033[38;2;r;g;bm RGB foreground color), well, not possible, as there's no commonly supported identify command. Each terminal has a special command with a specific answer that you can use. Or simply leave it to the user, many OS use the TERM environment variable to select a code set from the termcap database.
Implementing A Terminal
Once your OS is sufficiently advanced, you'll probably want to implement a terminal emulator on your OS too so that you can receive connections. For that, you must interpret these sequences, display them on screen accordingly, and convert the keyboard scancodes into ASCII sequences. Under DOS, this exactly what ANSI.SYS does. For Linux, take a look at minicom's source code.