Text positioning and coloring using ANSI escape sequences on Windows.
Some time ago, I wrote an article about Text positioning and coloring using ANSI escape sequences on DOS, where I showed how, thanks to the (N)ANSI.SYS display driver, we can use the so-called ANSI escape sequences to clear the screen, to position the cursor at a given screen position, to display with a given text and background color. And, I said that we can use these escape sequences from programs written in whatever programming language, including assembly and DOS batch files.
Is this possible on Windows, too? How could it be possible, as on modern windows systems there is no CONFIG.SYS file that we use on DOS to load the (N)ANSI.SYS driver?
The short answer to this question is: Yes, it is possible. The longer one is: It is possible, but only if the shell used supports the ANSI escape sequences. Microsoft Windows evolved a lot during the last years, and common standards not included in the old releases become more and more available. Thus, up from Windows 10, Command Prompt supports the ANSI escape sequences. However, on Windows 10, this support is very limited: It works correctly with batch files, but trying to write colored text from a C or FORTRAN program just displays the sequences as text. On the other hand, Windows 11 Command Prompt has a very good support of the ANSI escape sequences.
To use the sequences on Windows 10, you'll need a third party shell. If you have MYSYS2 installed (cf. my article Using Sublime Text as IDE for GCC/GFortran development), you can use the MSYS2 UCRT64 (or one of the other included shells). Support for the ANSI sequences is all right, except that there seems to be a problem with clearing the screen (a similar problem exists in Command Prompt on Windows 11). I would recommend to install the open source terminal ConEmu. It has plenty of nice features, and fully supports the ANSI escape sequences.
Installing ConEmu on Windows 10.
"ConEmu-Maximus5 aims to be a handy, comprehensive, fast and reliable terminal window where you may host any console application developed either for WinAPI (cmd, powershell, far) or Unix PTY (cygwin, msys, wsl bash). As Windows console window enhancement (local terminal emulator), it presents multiple consoles and simple GUI applications as one customizable tabbed GUI window with various features", the software is described on the Sourceforge website. Here is the link to the ConEmu documentation.
You can download ConEmu from the Github website. The installer includes both the 32-bit and 64-bit version. It's in the first window of the installation process that you can choose which version to install. During setup, you can also choose the components to be installed. I just pushed the Next button, accepting the default installation options.
Note: The installation and the startup of ConEmu didn't cause any problem. However, when I run my ANSI related batch file, the application was blocked by Avast Antivirus Free, that said that the file CmdInit.cmd would be infected with IDP.Generic.
Considering that it doesn't make any sense that a rather well known open source program, hosted on Github, should include some malware, and letting confirm me that the file is not infected by scanning the installation folder with Malwarebytes Free, I advised the antivirus to create an exception for this file (what means that it has not to be moved to quarantine, and that it has not to be scanned in future). That this was nothing but a false positive was further confirmed by uploading and checking the file at VirusTotal.
Using ANSI escape sequences in batch files.
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. If the terminal emulator supports the ANSI escape sequences, they can be used for clearing the screen, text positioning, and text coloring. You can find some further explanations concerning the sequences in my Text positioning and coloring using ANSI escape sequences on DOS tutorial. For a detailed description, please have a look at the ANSI escape code article in Wikipedia.
To send the command corresponding to a given ANSI escape sequence from within a batch file to the screen, we have to include the sequence into the string that will be displayed using the echo command. Example: echo <ESC>[91mThis text is in red. In this pseudo-command, <ESC> means the ASCII code of the ESC control character (character code 1Bh = 27 decimal, and corresponding to the ESC key on the keyboard). The problem is now, how can we insert this hexadecimal code into a batch file? There are editors that allow the input of the DOS code page control characters using some specific key combinations; on DOS, with the standard editor, for example, the combination for the ESC control character is CTRL+P, followed by CTRL+[. In VSCode these characters may be entered thanks to the Insert Unicode (by brunnerh) extension. There is another way, however, too.
The Windows command line command for /f may be used for iterating and file parsing, where "file" may also
be a string such an echo command in the batch file, where the for command is used. This way, we can replace an environment variable like
%ESC% by the ESC control character code, and this when iterating all lines in the batch file. For details concerning the
for command, have a look at the description at learn.microsoft.com. Don't worry if you don't understand how this works. Just add the following as first line in your batch file:
for /f %%a in ('echo prompt $E^| cmd') do set "ESC=%%a"
and then, in your echo commands, simple use %ESC% to denote the ESC control character.
The batch file ansi.bat displays some colored text to the screen.
@echo off
for /f %%a in ('echo prompt $E^| cmd') do set "ESC=%%a"
echo.
echo This text is default light gray on black
echo %ESC%[7mThis text is in reversed default colors%ESC%[0m
echo.
echo %ESC%[97mThis text is in white
echo %ESC%[44mThis text is in white on blue background
echo %ESC%[4mThis text is white and underlined on blue background
echo.
echo %ESC%[91m%ESC%[24mThis text is red and not underlined on blue background
echo %ESC%[7mThis text is in reversed previous colors
echo %ESC%[0m
Color codes are 30-37 for normal color foreground, 40-47 for normal color background, 90-97 for bright color foreground, 100-107 for bright color background (not sure if these work in all terminals). What the color actually displayed looks like (which RGB value is used for a given color code) depends on the terminal.
The batch file works correctly in Command Prompt of both Windows 10 and Windows 11 (and, of course, in MSYS2 UCRT64 and ConEmu). The screenshot below has been taken on Windows 10.
Line 1 is Command Prompt default colors (light gray on black). Line 2 reverses these colors, getting black on light gray. Line 3 sets the foreground color to white (keeping black as background color). Line 4 sets the background to blue (keeping white as foreground color). Line 5 sets the "underline" attribute (without changing the colors). Line 6 sets the foreground color to light red and the style attribute to "normal" (with the background color not being changed, this gives light red text with the "underline" attribute removed). Line 7 reverses red on blue (giving blue text on light red background). Note, that <ESC>[0m resets all values to their default.
Using 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 ansi.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 color and other text attributes related escape sequences as "manifest constants". I built it in MYSYS2 UCRT64 using
the command gcc ansi.c -o ansi.exe). Here is the code:
#include <stdio.h>
#define ANSI_CLS "\x1b[2J"
#define ANSI_NORMAL "\x1b[0m"
#define ANSI_BOLD "\x1b[1m"
#define ANSI_REVERSE "\x1b[7m"
#define ANSI_FG_BLACK "\x1b[30m"
#define ANSI_FG_RED "\x1b[31m"
#define ANSI_FG_GREEN "\x1b[32m"
#define ANSI_FG_BROWN "\x1b[33m"
#define ANSI_FG_BLUE "\x1b[34m"
#define ANSI_FG_MAGENTA "\x1b[35m"
#define ANSI_FG_CYAN "\x1b[36m"
#define ANSI_FG_LIGHTGRAY "\x1b[37m"
#define ANSI_FG_DARKGRAY "\x1b[30;1m"
#define ANSI_FG_LIGHTRED "\x1b[31;1m"
#define ANSI_FG_LIGHTGREEN "\x1b[32;1m"
#define ANSI_FG_YELLOW "\x1b[33;1m"
#define ANSI_FG_LIGHTBLUE "\x1b[34;1m"
#define ANSI_FG_LIGHTMAGENTA "\x1b[35;1m"
#define ANSI_FG_LIGHTCYAN "\x1b[36;1m"
#define ANSI_FG_WHITE "\x1b[37;1m"
#define ANSI_BG_BLACK "\x1b[40m"
#define ANSI_BG_RED "\x1b[41m"
#define ANSI_BG_GREEN "\x1b[42m"
#define ANSI_BG_BROWN "\x1b[43m"
#define ANSI_BG_BLUE "\x1b[44m"
#define ANSI_BG_MAGENTA "\x1b[45m"
#define ANSI_BG_CYAN "\x1b[46m"
#define ANSI_BG_LIGHTGRAY "\x1b[47m"
int main() {
printf("%s", ANSI_CLS);
printf("ANSI color test with C:\n\n");
printf("%sThis is red text\n", ANSI_FG_RED);
printf("%sThis is lightred text\n\n", ANSI_FG_LIGHTRED);
printf("%sThis is lightred text on blue background\n", ANSI_BG_BLUE);
printf("%sThis is yellow text on blue background\n\n", ANSI_FG_YELLOW);
printf("%s", ANSI_NORMAL);
printf("%s%sThis is white text on cyan background\n\n", ANSI_FG_WHITE, ANSI_BG_CYAN);
printf("%s", ANSI_NORMAL);
printf("%sThis is reversed colors text\n\n", ANSI_REVERSE);
printf("%s", ANSI_NORMAL);
printf("%sThis is magenta text\n", ANSI_FG_MAGENTA);
printf("%sThis is bold magenta text\n", ANSI_BOLD);
printf("%s\n", ANSI_NORMAL);
return(0);
}
The screenshot on the left shows how I built and ran the program in MYSYS2 UCRT64, the "clear screen" command not clearing the screen. The screenshot on the right shows the execution of the program in Windows 10 Command Prompt, the ANSI escape sequences not being interpreted as commands but displayed verbatim.
The screenshots below show the execution of the program in Windows 11 Command Prompt, where, as on MYSYS2 UCRT64, there are some issues with clearing the screen. In fact, the screen is cleared, but the first output line of the program will not be the first on the screen, but will be further downwards, depending on the vertical position from where the program actually was started. On the screenshot on the left, there were several lines on the screen, when I launched ansi.exe, thus the program output is shifted some lines downwards. There is, however, a simple work-around: Just execute the command cls before running the program. As you can see on the screenshot on the right, this results in a proper display. In fact, the line with the command prompt text and the program launching command is still there, but Windows scrolls the screen - look at the vertical scrollbar - in order to get the first output line of the program displayed at the first (visible) line of the console.
And here is the output of the program in ConEmu: Full support of the ANSI escape sequences, including the "clear screen" command. This is exactly how we expect it to be. Except, perhaps, for the normal (dark) colors, that are not well visible; also, the colors differ from those in Command Prompt and UCRT64. No problem: ConEmu includes a whole bunch of configuration settings, and you can set the colors to any RGB value that you want!
Note: If you look at the C source code above, you may perhaps say that there are several printf("%s", ANSI_NORMAL) instructions that are useless. You might be right: It's possible that the program works correctly without them. If I added them, it's because some times (?) the empty lines didn't display in black but in the background color actually set (blue resp. cyan). I don't know under what conditions this happened. Anyway, adding the commands to reset all (what in particular resets the background color to black) solved the problem...
Using 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 ansi2.for writes a white text onto a blue background, the whole at a given screen position. I built it in MYSYS2 UCRT64 using
the command gfortran ansi2.for -o ansi2.exe. Here is the code:
PROGRAM ANSI2
IMPLICIT NONE
CHARACTER*16 HELLO, SPACES
CHARACTER*4 CLS, RESET
CHARACTER*5 BG_BLUE
CHARACTER*7 FG_WHITE, LINE1, LINE2, LINE3
PARAMETER (HELLO = ' HELLO WORLD! ')
PARAMETER (SPACES = ' ')
PARAMETER (CLS = CHAR(27) // '[2J')
PARAMETER (RESET = CHAR(27) // '[0m')
PARAMETER (LINE1 = CHAR(27) // '[2;32H')
PARAMETER (LINE2 = CHAR(27) // '[3;32H')
PARAMETER (LINE3 = CHAR(27) // '[4;32H')
PARAMETER (FG_WHITE = CHAR(27) // '[37;1m')
PARAMETER (BG_BLUE = CHAR(27) // '[44m')
WRITE (*, *) CLS
WRITE (*, *) BG_BLUE, FG_WHITE, LINE1, SPACES, RESET
WRITE (*, *) BG_BLUE, FG_WHITE, LINE2, HELLO, RESET
WRITE (*, *) BG_BLUE, FG_WHITE, LINE3, SPACES, RESET
WRITE (*,*)
END
The program does not work in Windows 10 Command Prompt, where the ANSI escape sequences are displayed verbatim. It works all correctly in Windows 11 Command Prompt and, of course, in ConEmu. In MYSYS2 UCRT64, problems with the "clear screen" command are possible (cf. further down in the text). The screenshot shows the program output in ConEmu.
As a difference with the C program, the FORTRAN program performs a "clear screen" in UCRT64. However, the screen may not be completely cleared. This may happen if the length of the command line string exceeds 80 characters, the characters after position 80 remaining on the screen, as shown on the screenshot below. If I say "may happen", it's because sometimes it happens, other times all characters are cleared. No idea, when the issue occurs and when not...
Note: If you look at the FORTRAN source code above, you may perhaps wonder why I reset the color attributes after each ANSI escape sequence output. Why not writing the commands for the foreground and background color, then writing the commands for the text positioning and the text, and finally writing the reset command (as I did in the DOS version of the program). The reason is the display in Windows 11 Command Prompt. Doing as I did on DOS will result in the display of a blue rectangle (which is actually a non-visible character, a space, I suppose, displayed with the background color "blue", used in the display operations before) at the beginning of the last output line (as shown on the screenshot below).
No idea why this happens. Is there a problem with my FORTRAN code? Why would the display then be correct in ConEmu? I spent lots of time to solve the problem. Doing as shown in the FORTRAN source above works fine and the display in Windows 11 Command Prompt is all correct...
Using ANSI escape sequences in assembly.
Using ANSI escape sequences in an assembly program is even easier than in other programming languages. As the character, that will be 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 program (using the C function printf for print-out), we can define the ANSI escape sequence
as follows
cls db 1Bh, '[2J', 00h
and send it to the screen using the instructions
mov rcx, cls
call printf
The sample program ansi.asm displays a white on blue text at a given screen position. I built the program in the
SASM IDE using the FASM 64-bit assembler. Here is the code:
format ELF64
section '.data' writeable
cls db 1Bh, '[2J', 00h
reset db 1Bh, '[0m', 00h
fgwhite db 1Bh, '[37;1m', 00h
bgblue db 1Bh, '[44m', 00h
line1 db 1Bh, '[2;32H', ' ', 00h
line2 db 1Bh, '[3;32H', ' Assembly is fun! ', 00h
line3 db 1Bh, '[4;32H', ' ', 00h
eol db 0Dh, 0Ah, 00h
section '.text' executable
public main
extrn printf
main:
mov rbp, rsp
sub rsp, 32
and rsp, -16
mov rcx, cls
call printf
mov rcx, bgblue
call printf
mov rcx, fgwhite
call printf
mov rcx, line1
call printf
mov rcx, line2
call printf
mov rcx, line3
call printf
mov rcx, reset
call printf
mov rcx, eol
call printf
mov rsp, rbp
xor rax, rax
ret
This program displays correctly in Windows 11 Command Prompt. No need to reset the text attributes after each ANSI escape sequence print-out, as we had to do with FORTRAN.
Use the following link to download the source code of the tutorial program samples.
If you find this text helpful, please, support me and this website by signing my guestbook.