Text positioning and coloring using ANSI escape sequences on DOS.
ANSI.SYS is a device driver in the DOS family of operating systems that provides extra console functions through so-called ANSI escape sequences. It is partially based upon a subset of the text terminal control standard proposed by the ANSI X3L2 Technical Committee on Codes and Character Sets. As ANSI.SYS was not installed by default on MS-DOS (that used DISPLAY.SYS instead), and also because it was notoriously slow, little software took advantage of it and instead resorted to directly manipulating the IBM PC hardware. For further details, please have a look at the ANSI.SYS article in Wikipedia.
ANSI escape sequences are a standard for in-band signaling to control cursor location, color, font styling, and other options on video text terminals and terminal emulators. Certain sequences of bytes (normally starting with an ANSI escape character plus a left square bracket), if embedded into text, are interpreted by the terminal as commands, rather than text to display verbatim. With ANSI.SYS installed, these escape sequences can be used for text positioning and coloring on DOS. For further details, please have a look at the ANSI escape code article in Wikipedia.
FreeDOS is distributed with the driver NANSI.SYS installed by default. NANSI.SYS executes the same ANSI cursor control sequences as does the standard console driver ANSI.SYS, but significantly faster. It also offers several extra features, while still being simple, small, and cheap. Versions 3.4 and later are under the GPL, and are Free Software. You can find details about the driver in this article about NANSI.SYS and NNANSI.COM.
As ANSI escape sequences are pre-defined sequences of ASCII characters, they can be used in any programming language to do things like clearing the screen, moving the cursor to a given screen position, or display text with a given font or background color. This tutorial shows examples of the usage of ANSI escape sequences in C, FORTRAN, Pascal, and assembly (NASM) programs, as well as in DOS batch files, . All program samples have been developed and tested on FreeDOS 1.3 RC5, with the default NANSI.SYS driver resp. with NNANSI.COM.
Before viewing those examples, lets have a brief look at the ANSI escape sequences. Their general format is
ESC[<parameter-list><command-code>
where ESC means the ASCII code of the escape control character.
The parameter list is one or more (numeric) parameters, separated by a semicolon (;). The command code is one letter; important to note that this code is case-sensitive.
Here is a description of the sequences used in the tutorial samples (for a full description of the available commands and their usage, cf. the file NANSI.DOC, that you find in the DOC\NANSI subfolder of your FreeDOS installation directory.
Sequence | Command | Notes |
---|---|---|
ESC[2J | Clear screen | Clear the screen (using the actual background color) |
ESC[0m | Reset text attributes | Reset text color and text background to default colors and attributes (light gray on black; "normal" attributes) |
ESC[3Cm | Set text color | Set text color to 30 + C, where C is a 0 - 7 color code; cf. below |
ESC[4Cm | Set background color | Set text background to 40 + C, where C is a 0 - 7 color code; cf. below |
ESC[1m | Text attribute "bold" | Display the text with light colors (ex: red bold = light red; brown bold = yellow) |
ESC[5m | Text attribute "blink" | Display the text with blinking effect |
ESC[7m | Reverse video | Swap text color and text background color (black text on light gray background, for default colors) |
ESC[v;hH | Set cursor position | Position the cursor at position v,h; v being the vertical position (line): 1 - 25; h being the horizontal position (column): 1 - 80 |
Color codes are as follows:
Code | "Normal" color | Bold color |
---|---|---|
0 | black | dark gray |
1 | red | light red |
2 | green | light green |
3 | brown | yellow |
4 | blue | light blue |
5 | magenta | light magenta |
6 | cyan | light cyan |
7 | light gray | white |
Notes:
- The escape sequence ESC[4m should set the text attribute "underline". However, this does not work on my system.
- The escape sequence ESC[7m does notnot work correctly with NANSI.SYS. In fact, it always yields a black on light gray output, i.e. it always produces a reverse video with the default colors. To get reverse video for the actual text and background colors, use the NNANSI.COM driver instead.
- You can set text color, background color and text attribute in one command, separating the different parameters by a semicolon. Ex: the sequence for "yellow text on blue background" is ESC[33;44;1m
In the following paragraphs, you can find ANSI escape sequence program samples in various programming languages. Use the following link to download the source code.
ANSI escape sequences in C.
The escape control character has the hexadecimal ASCII code 1B (decimal 27). In a C program, hexadecimal values may be written using a hexadecimal escape sequence of the form \x<hexadecimal-digits>. The ASCII code of the escape control character can thus be written as the character constant '\x1b'. In C, ANSI escape sequences are strings starting with "\x1b[".
For example, to clear the screen from a C program, you can use the statement
printf("%s", "\x1b[2J");
In the sample program NANSI1.C, that writes some colored text to the screen, I have used the directive #define to define the clear screen and all text color, background and attributes related escape sequences as "manifest constants". Here is the code (I used Watcom C to create a 16-bit DOS executable; may work with other C compilers, too):
#include <stdio.h>
#define NANSI_CLS "\x1b[2J"
#define NANSI_NORMAL "\x1b[0m"
#define NANSI_BOLD "\x1b[1m"
#define NANSI_REVERSE "\x1b[7m"
#define NANSI_FG_BLACK "\x1b[30m"
#define NANSI_FG_RED "\x1b[31m"
#define NANSI_FG_GREEN "\x1b[32m"
#define NANSI_FG_BROWN "\x1b[33m"
#define NANSI_FG_BLUE "\x1b[34m"
#define NANSI_FG_MAGENTA "\x1b[35m"
#define NANSI_FG_CYAN "\x1b[36m"
#define NANSI_FG_LIGHTGRAY "\x1b[37m"
#define NANSI_FG_DARKGRAY "\x1b[30;1m"
#define NANSI_FG_LIGHTRED "\x1b[31;1m"
#define NANSI_FG_LIGHTGREEN "\x1b[32;1m"
#define NANSI_FG_YELLOW "\x1b[33;1m"
#define NANSI_FG_LIGHTBLUE "\x1b[34;1m"
#define NANSI_FG_LIGHTMAGENTA "\x1b[35;1m"
#define NANSI_FG_LIGHTCYAN "\x1b[36;1m"
#define NANSI_FG_WHITE "\x1b[37;1m"
#define NANSI_BG_BLACK "\x1b[40m"
#define NANSI_BG_RED "\x1b[41m"
#define NANSI_BG_GREEN "\x1b[42m"
#define NANSI_BG_BROWN "\x1b[43m"
#define NANSI_BG_BLUE "\x1b[44m"
#define NANSI_BG_MAGENTA "\x1b[45m"
#define NANSI_BG_CYAN "\x1b[46m"
#define NANSI_BG_LIGHTGRAY "\x1b[47m"
int main() {
printf("%s", NANSI_CLS);
printf("NANSI color test with C:\n\n");
printf("%sThis is red text\n", NANSI_FG_RED);
printf("%sThis is lightred text\n\n", NANSI_FG_LIGHTRED);
printf("%sThis is lightred text on blue background\n", NANSI_BG_BLUE);
printf("%sThis is yellow text on blue background\n\n", NANSI_FG_YELLOW);
printf("%s%sThis is white text on cyan background\n\n", NANSI_FG_WHITE, NANSI_BG_CYAN);
printf("%sThis is reversed colors text\n\n", NANSI_REVERSE);
printf("%s", NANSI_NORMAL);
printf("%sThis is magenta text\n", NANSI_FG_MAGENTA);
printf("%sThis is bold magenta text\n", NANSI_BOLD);
printf("%s\n", NANSI_NORMAL);
return(0);
}
The screenshots show the program output: on the left using NANSI.SYS, on the right using NNANSI.COM.
Notes:
- Unless you perform a ESC[0m (that resets all), the colors and attributes set stay until they are overwritten. So, with the text color set to light red, setting the background color to blue, will produce a light red on blue output. And with the blue background set, setting the text color to yellow will result in yellow on blue.
- On the right screenshot (usage of NNANSI.COM) you can see that reverse video of white on cyan is cyan on light gray (and not cyan on white). The reason for this is that the background colors are limited to the normal colors (no bold = lightening effect possible).
ANSI escape sequences in FORTRAN.
In FORTRAN, the character corresponding to a given ASCII code is obtained using the function CHAR(). Example: CHAR(27) returns the escape control character. Thus, in FORTRAN, ANSI escape sequences are concatenations of CHAR(27) and a string starting with '['.
For example, to clear the screen from a FORTRAN program, you can use the statement
WRITE (*,*) CHAR(27) // '[2J'
The sample program NANSI2.FOR displays a yellow on blue "Hello World", horizontally centered on the screen. To make the code more readable, I have declared the ASCII sequences using the parameter declarative. Here is the code (I used Watcom FORTRAN to create a 16-bit DOS executable; may work with other FORTRAN 77 compilers, too):
PROGRAM NANSI2
IMPLICIT NONE
CHARACTER*16 HELLO, SPACES
CHARACTER*3 CLS, RESET
CHARACTER*4 BG_BLUE
CHARACTER*6 FG_WHITE, LINE1, LINE2, LINE3
PARAMETER (HELLO = ' HELLO WORLD! ')
PARAMETER (SPACES = ' ')
PARAMETER (CLS = '[2J')
PARAMETER (RESET = '[0m')
PARAMETER (LINE1 = '[2;32H')
PARAMETER (LINE2 = '[3;32H')
PARAMETER (LINE3 = '[4;32H')
PARAMETER (FG_WHITE = '[37;1m')
PARAMETER (BG_BLUE = '[44m')
WRITE (*,*) CHAR(27) // CLS
WRITE (*,*) CHAR(27) // BG_BLUE
WRITE (*,*) CHAR(27) // FG_WHITE
WRITE (*,*) CHAR(27) // LINE1, SPACES
WRITE (*,*) CHAR(27) // LINE2, HELLO
WRITE (*,*) CHAR(27) // LINE3, SPACES
WRITE (*,*) CHAR(27) // RESET
END
And here is a screenshot of the program output.
ANSI escape sequences in Pascal.
Free Pascal (as Turbo Pascal) comes with the Crt unit, that contains all routines that you might need for text coloring and positioning. But, it is of course also possible to use the ANSI escape sequences (Crt unit not needed in this case). By the way, note that the ANSI color codes and the Pascal color constants of the Crt unit are not compatible (Examples: blue is 34/44 with ANSI, 1 with Pascal, red = 31/41 with ANSI, 4 with Pascal).
In Pascal, the character corresponding to a given ASCII code is obtained using the function Chr(). Example: Chr(27) returns the escape control character. Thus, in Pascal, ANSI escape sequences are concatenations of Chr(27) and a string starting with '['.
Calling the Write procedure with 2 arguments, you can clear the screen from a Pascal program using the statement
Write(Chr(27), '[2J');
The sample program NANSI.PAS displays, for each of the 8 background colors, two X with each of the 16 text colors. I have declared some constants for the escape sequences. The color related sequences are obtained by replacing the '0' of the 'black' sequence by the actual color code, adding a 'bold' sequence for the light colors. Color display and display of an ANSI escape sequence in general is done by calling functions. Here is the code (I built it with Free Pascal for go32v2; may work with other Pascal compilers, too):
program nansi;
uses
SysUtils;
const
NANSI_CLS = '2J';
NANSI_NORMAL = '0m';
NANSI_BOLD = '1m';
NANSI_FG = '30m';
NANSI_BG = '40m';
Colors: array[0..7] of string = (
'black', 'red', 'green', 'brown', 'blue', 'magenta', 'cyan', 'gray'
);
var
L, I, J: Integer;
S: string;
procedure NansiWrite(NANSISeq: string);
begin
Write(Chr(27), '[', NANSISeq);
end;
procedure NansiWriteColor(Ground: string; Colour: Integer);
var
NANSISeq: string;
Bold: Boolean;
begin
if Ground = 'fg' then
NANSISeq := NANSI_FG
else
NANSISeq := NANSI_BG;
Bold := False;
if Colour > 7 then begin
Colour := Colour - 7;
if Ground = 'fg' then
Bold := True;
end;
NANSISeq := StringReplace(NANSISeq, '0', IntToStr(Colour), []);
if Bold then begin
NANSISeq := StringReplace(NANSISeq, 'm', '', []);
NANSISeq := NANSISeq + ';' + NANSI_BOLD;
end;
NansiWrite(NANSISeq);
end;
begin
NansiWrite(NANSI_CLS);
Writeln('NANSI colors:'); Writeln;
for I := 0 to 7 do begin
S := Colors[I]; L := Length(S);
for J := 1 to 7 - L do
S := S + ' ';
NansiWrite(NANSI_NORMAL); Write(S, ': ');
NansiWriteColor('bg', I);
for J := 0 to 14 do begin
NansiWriteColor('fg', J); Write('XX');
if J < 14 then
Write(' ');
end;
Writeln;
end;
NansiWrite(NANSI_NORMAL);
Writeln; Writeln;
end.
And here is a screenshot of the program output.
ANSI escape sequences in assembly.
Using ANSI escape sequences in an assembly program is even easier than in other programming languages. As the character to displayed is the character coded by the ASCII code sent to the display, all we have to do is to place the value 1Bh (decimal 27) in the memory location from where display is done. In assembly, ANSI escape sequences are a sequence of bytes starting with 1Bh, 5Bh (where 5Bh = 91 decimal is the ASCII code of the left square bracket).
For example, to clear the screen from an assembly (NASM; 16-bit real mode) program, you can define the ANSI escape sequence as follows
cls db 1Bh, 5Bh, '2J', '$'
and sending it to the screen using the instructions
mov dx, cls
mov ah, 9
int 21h
The sample program HELLORED.ASM displays a "Hello World" in red. Here is the code (I built it with NASM creating a 16-bit real mode executable; similar code should work with other assemblers, too).
org 100h
section .text
start:
; Display message (calling the DOS write-string function)
mov dx, hello
mov ah, 9
int 0x21
; Terminate the program (calling the DOS exit function)
mov ax, 0x4c00
int 0x21
section .data
hello db 27, '[31m', 'Hello, World!', 13, 10, 27, '[0m', '$'
The screenshot shows the build (using my custom batch file) and the execution of the program.
ANSI escape sequences in DOS batch files.
For people, who work a lot with batch files, creating colored output or using other special console operations from within such files can be interesting. But, how do we tell the echo command to output an escape control character? Is there a way to code this or other control characters in the (Free)DOS editor? Yes, there is! The ASCII control characters (characters with ASCII codes from 0 to 31, plus 127) have a "character representation" made of CTRL+<letter-code>, and may this way be entered from the DOS command line (or as program input). Examples: CTRL+G corresponds to ASCII code 07h (BEL) and entering this key combination makes the computer beep; CTRL+H corresponds to ASCII code 07h (BS) and you can use this key combination instead of the backspace key. Another example is CTRL+M corresponding to ASCII code 0Dh (CR) and you can use this key combination instead of the ENTER key.
The escape control character (ASCII code 1Bh = decimal 27) can be entered in DOS using the key combination CTRL+[. The important thing to know is that you must use the "[" key as is on a US keyboard, this independently of your actual keyboard driver. At least it seems so, because on my laptop with German keyboard and using keyb with the German keyboard layout, I have to push CTRL+ΓΌ to enter the escape control character.
This is however only a part of the solution. If, in the FreeDOS editor, we enter CTRL+[, we don't enter a printable character, but execute a control character. So, what we need is a way to tell the editor that what we want actually is the printable character corresponding to ASCII code 1bH and not the control character. The FreeDOS editor allows to enter control characters as printable characters (instead of interpreting them as control codes) by prefixing them with CTRL+P. Thus, to enter the escape control character as character (not as ESC control code), push CTRL+P followed by CTRL+[. In the editor this will appear as a left arrow, what corresponds to the printable character assigned to ASCII code 1Bh on the original IBM PC, and also in the character set of code page 437.
The sample script NANSI.BAT writes some colored text to the screen. Here is the code (note that ESC represents the escape control character that you have to enter in the FreeDOS editor as the CTRL+P, CTRL+[ key sequence).
@echo off
echo ESC[2J
echo ESC[31mThis text is in red
echo ESC[1mThis text is in bold red = lightred
echo ESC[0m
echo ESC[32mThis text is in green
echo ESC[32;1mThis text is in bold green
echo ESC[5mThis text is in bold green and blinking
echo ESC[0m
echo ESC[44m This text is lightgray on blue background
echo ESC[1m This text is in bold lightgray = white on blue background
echo ESC[7mThis text is in reversed colors
echo ESC[0m
echo ESC[33;44;1m This text is in bold brown = yellow on blue background
echo ESC[4mThis text is NOT yellow underlined on blue backgraound
echo ESC[0m
The screenshot shows the screen output of the batch file.
Notes:
- Echoing the sequence ESC[0m (that resets all) also performs a (supplementary) newline.
- The correct reverse video display is due to the fact that NNANSI.COM was installed when taking the screenshot.
- You can also see on the screenshot that, just as NANSI.SYS, NNANSI.COM does not handle the "underline" text attribute (at least on my system).
Other ways to perform advanced console functions from within a batch file:
- FreeDOS induces the program VECHO.COM that is actually used to produce colored text in the FreeDOS startup messages. Cf. the included help document for details.
- Michael Mefford wrote the amazing program BATCHMAN.COM (use the following link to
download executable + documentation). It includes a lot of functions that may be
called from a batch file. Just two examples: renaming a directory; waiting for a given time before continuing the batch file processing. And, there are also functions
for clearing the screen, positioning the cursor and displaying colored text. Just copy the executable into your FreeDOS "bin" directory and in your batch file call a
given function using the command
batchman <function> <parameters>
Important to note that the batchman command line arguments are case-sensitive. Example:
batchman cls 1Fh
clears the screen with blue background color and sets the foreground color to white.
Enjoy a colorful DOS programming experience!
If you find this text helpful, please, support me and this website by signing my guestbook.