15

I'm currently trying to experiment with ANSI escape sequences on various terminals/terminal emulators, so I'd like to know if there's an expedient way of typing these sequences on a DOS command prompt, a DOS equivalent of echo -e "\x1B" on modern Linux distributions.

I'm trying to do this in DOSBox, to be more specific.

3
  • There is some information and examples here that may help, but I think they are intended for use in batch files. The escape character for DOS is ASCII 27 and the only way I could send it to DOSBox was by copying-and-pasting the character from one of the demo batch files, although I was not able to get any ANSI sequences working typed directly at the console because I think the shell "swallows" that character in some way. Mar 2 at 0:55
  • 3
    The important bit is having characters sent to the ANSI driver, i.e. written to the console. The Esc key is normally interpreted and not echoed back to the console, so it is not easy to type from the command line. I agree with the answers that say write a small script. Mar 2 at 8:38
  • Another approach is to use a DOS based ANSI art editor like The Draw and save your ANSI to a file. Then you can simply use the DOS 'type' command to display the file.
    – Geo...
    Mar 2 at 21:17

5 Answers 5

18

[All of the examples have been tried using a real PC and DOS 3.20]

TL;DR: Use a Batch File For Experiments

The most simple way to start testing wold be a batch file providing the needed ESC character. The most simple way to create it is using COPY:

COPY CON: ESC.BAT CR

It's important to type the following exactly as shown

ECHOSPACEESC%1CTRL-Z

This creates a batch file outputting ESC and a single parameter. For screen clear now one can just type:

ESC [2JCR

Note, this will of course still intermingle with DOS command line after each use. Still, it should give an easy start for experiments :))


Details

I'd like to know if there's an expedient way of typing these sequences on a DOS command prompt, a DOS equivalent of echo "\x1B" on modern Linux distributions.

Not as easy, as all versions of COMMAND.COM's command line editor will interpret pressing the ESC key as command to cancel the input line, thus the ESC code will not be outputted to ANSI.SYS. Any use of ESC will need to bypass interpretation by the line editor.

Method 1: Numpad-Codes

With a real PC an alternative is using the numeric keypad code input. For ESC that would work out as:

  • Press and hold ALT
  • Press and release 2 on the numeric keypad
  • Press and release 7 on the numeric keypad
  • Release ALT

The screen should now show ^[. Note that the square bracket is not used for the sequence, but represents ESC itself. Thus an echo for clear screen could be entered as:

echo ^[[2J

Of course that only works with a real PC - and a fitting keyboard - or a full emulation. Many (including DOSbox *1) will not work as expected.

Method 2: Use of COPY

Using COPY CON: CON: will copy all console input, until CTRL-Z is pressed, without any further interpretation back to console output. Thus we no longer need the numpad function but can use the ESC key straight away:

^[[2J^Z

This will nicely clear the screen, but then of course output the usual 1 file(s) copied summary :(

Again, this will work on a real PC, may or may not work on emulated PC.

Method 3: Use a Data File

Any text file TYPEd will be outputted without interpretation. Thus storing escape sequences in short text files and outputting those using TYPE can execute them. The file can either be created by again using COPY, or an editor

Creating Files Using COPY

The quickest way may be again COPY:

COPY CON: X.X
^[[2J^Z
        1 file(s) copied

TYPE X.X will now clear the screen.

Creating Files Using EDIT

DOS 5.0 added a full screen editor (*2) here ESC can be entered as character if preceded by CTRL-P. On screen those will be shown as a left pointing arrow.

Method 4: Use a Batch File

Batch file output is as well not influenced by parsing. Thus any ESC combination can be put into a batch file using COPY or EDIT as schown.

Most important here, when started with @ECHO OFF, the commands itself will not be outputted.Thus any combination of ESC sequences and regular Text can be produced.


DOSboxing

I'm trying to do this in DOSBox, to be more specific.

Tough luck. DOSbox does not only emulate the PC, but as well DOS itself. The later rather limited. It(*1) does not support above copy trick and doesn't come with edit.

Then again, as Stephen Kitt mentions, COMMAND.COM replacement like 4DOS (*3) might help. It should be possible to simply execute either in DOSbox, although I haven't tested this so far.


*1 - At least not Version 0.74-3 as I have installed.

*2 - Before 5.0 there was only EDLIN ... I'd rather spare us this exercise :))

*3 - Also packaged as NDOS with Norton Utilities for DOS starting with version 4.

11
  • 3
    prompt $e lets you put an Escape character in your prompt so you can add color or other escape codes to the prompt.
    – Bavi_H
    Mar 2 at 9:04
  • 1
    You could create a short text file with DEBUG -- certainly far less user-friendly than a proper text editor, but still preferable to EDLIN.
    – john_e
    Mar 2 at 11:40
  • 1
    You can boot a real DOS in DOSBox.
    – Joshua
    Mar 2 at 16:41
  • I remembered how to mount directories in DOSBox, so I don't necessarily have to produce the file from within DOS. What is the byte sequence that the COPY CON: trick generates? Mar 2 at 23:17
  • @EnronEvolved Exactly as shown,so just ECHO , followed by ESC (1Bh) followed by` %1`, so COMMAND will insert the first parameter typed. It's all about inserting the 1Bh as single character.
    – Raffzahn
    Mar 3 at 0:36
7

If you have a copy of DEBUG in your emulated DOS environment, you could write a small program to output an ESC followed by whatever you want. (Anything after ; is a comment and shouldn't be typed in).

A>debug
-A100                          ; Start assembling at 0100h
0905:0100 MOV DL,1B            ; Output the ESC character
0905:0102 MOV AH,02            ; C_WRITE, output character
0905:0104 INT 21               ; Call DOS
0905:0106 MOV SI,80            ; Point SI at command tail
0905:0109 LODSB                ; Get first byte (command length)
0905:010A INC SI               ; Skip over next byte (space)
0905:010B MOV CL,AL            ; CL = number of bytes to output
0905:010D MOV CH,0             ; CX = number of bytes to output
0905:010F AND AL,AL            ; Are there 0 bytes to output?
0905:0111 JZ 011C              ; If so skip the loop
0905:0113 LODSB                ; Get a byte from the command tail
0905:0114 MOV DL,AL            ; And output it
0905:0116 MOV AH,02            ; C_WRITE again
0905:0118 INT 21               ; Call DOS
0905:011A LOOP 0113            ; Continue for the specified number of bytes
0905:011C INT 20               ; Terminate the program
0905:011E                      ; Press RETURN by itself to end assembly
-RCX                           ; Set CX to the number of bytes to save
CX 0000
:1E                            ; which is 1Eh
-NESC.COM                      ; Set filename to ESC.COM
-W                             ; Write it out
Writing 001E bytes
-Q                             ; And now quit DEBUG.

(Note the above program actually outputs one byte more than it should -- fixing it is left as an exercise for the reader)

Once you have ESC.COM, you can type commands like ESC [2J and see their effect.

6
  • 1
    Nice, this sort of solution is time-appropriate too ;-). You could fix the off-by-one and avoid writing escape with nothing else by replacing the space at 0x81 with escape and outputting it in the main loop. Mar 2 at 13:00
  • 1
    Lovely. Yes. that's as well a solution strictly with DOS onboard tools - full early DOS at least. And in addition to Stephen's improvement, using JCXZ instead of AND AL,AL/JZ saves a line to be typed :)) Not to mention that his remark can be fulfilled by simply loading AL with ESC afterwards and skipping the LODSB for the first cycle. Damn, that stuff always gets me carried away. Did I already say I like your answer?
    – Raffzahn
    Mar 2 at 15:01
  • 2
    As long as the user doesn't include $ in the text they want printed, of course.
    – john_e
    Mar 2 at 17:30
  • 2
    This may be not very important for this purpose, but the byte at PSP offset 0x81 is not guaranteed to be a space. It may equally well be =, /, ; or , (or maybe others). Also, it would be better to strip DEBUG output from this script, to make it possible to copy-paste it into a terminal or write it directly into a file and pipe it in. Mar 2 at 17:59
  • 2
    The $ caveat can be avoided by using service 0x40 instead. Mar 2 at 18:41
6

Here are a couple of methods in addition to Raffzahn’s selection.

4DOS’s escape handling

4DOS supports its own escapes on the command-line and in batch files.

Using a keyboard shortcut, the escape character can be produced with the 4DOS escape (Ctrlx by default) followed by e; for example, echoSpaceCtrlx[2JEnter will clear the screen.

Another approach is to use the %= sequence:

echo %=e[2J

Both forms of escape (Ctrlx and %=) can be followed by one of these characters:

  • b to produce a backspace
  • c to produce a comma
  • e to produce an escape character
  • f to produce a form feed
  • k to produce a back quote
  • n to produce a line feed
  • q to produce a double quote
  • r to produce a carriage return
  • s to produce a space
  • t to produce a tab character

DOSBox-X and TYPE CON

DOSBox doesn’t offer any great features with respect to ANSI.SYS escapes, but DOSBox-X does support the TYPE CON trick: running

TYPE CON

will allow you to enter any character sequence you like, including the escape character, and see its effect immediately. There is one caveat though: you’ll have to type ANSI sequences blind, they are interpreted as you type them.

4
  • I have to admit, I never tried 4DOS with DOSbox. Seems like a great idea to try. Also, didn't some DR-DOS version add something similar?
    – Raffzahn
    Mar 2 at 9:10
  • I don’t remember anything similar in DR DOS off-hand, I’d have to check! I wonder if some of the command-line editing TSRs might have similar support (but they might not work well in DOSBox). Mar 2 at 11:48
  • 1
    That nested <kbd> tag looks really wrong. Mar 2 at 17:21
  • 3
    @user3840170 I use that to indicate keys that should be pressed with Ctrl held down. Mar 2 at 17:56
2

Inspired by the other programs written using Debug, I went and wrote a slightly more complicated example. This one allows to use a number of escape codes using the backslash. It also scans for leading blanks and skips those, except if a leading blank is escaped with a backslash. Backslashes can be included by escaping them as doubled backslashes. The \e escape will write an escape byte.

The program is presented as a Script for lDebug, targetting my DOS debugger. This allows us to automatically fix up some branch and variable addresses without needing to examine the output of a debugger session. The SLD is to be run using a command like y esc.sld to lDebug.

Here's esc.sld:

a 100
 mov si, 80
 mov di, si
 mov dx, si
 lodsb
 mov ah, 0
 xchg cx, ax
 inc cx
 .
r v12 = aao ; loop for blanks
a
 dec cx
 .
r v03 = aao ; adjust to jump to end
a
 jz 100
 lodsb
 cmp al, (#' ')
 je (v12)   ; jump to loop for blanks
 cmp al, 9
 je (v12)   ; jump to loop for blanks
 dec si     ; reload non-blank byte
 .
r v10 = aao ; loop for text
a
 lodsb
 cmp al, (#'\')
 .
r v00 = aao
a
 je 100     ; adjust to jump to escaped
 .
r v13 = aao ; store
a
 stosb
 loop (v10) ; jump to loop for text
 .
r v11 = aao ; end
a
 mov cx, di ; -> behind stored
 sub cx, dx ; = length
 mov ah, 40
 mov bx, 1  ; stdout
 int 21     ; write
 mov ax, 4C00
 int 21
 .
r v12 = aao ; escaped
a
 dec cx
 jz (v11)   ; jump to end
 lodsb
 cmp al, (#'a')
 jb (aao + 8)
 cmp al, (#'z')
 ja (aao + 4)
 xor al, (#'A' ^ #'a')
 .
r v01 = aao ; adjust table address
a
 mov bx, 100
 .
r v14 = aao ; table loop
a
 inc bx
 inc bx
 cmp byte [bx], 0
 je (v13)
 cmp al, byte [bx]
 jne (v14)
 mov al, byte [bx + 1]
 jmp (v13)
 .
r v15 = aao ; table
e aao "E", #27, "N", #10, "R", #13, "T", #9, "B", #7, "S", #32, "F", #12, 0
r bxcx aeo - 100
a v03
 jz (v11)
 .
a v00
 je (v12)
 .
a v01
 mov bx, (v15 - 2)
 .
n esc.com
w 100

And here's a listing created using the following command: ldebug /c=u,100,151;d,152,l,F;q esc.com > esc.lst

&u,100,151
2ADB:0100 BE8000            mov     si, 0080
2ADB:0103 89F7              mov     di, si
2ADB:0105 89F2              mov     dx, si
2ADB:0107 AC                lodsb
2ADB:0108 B400              mov     ah, 00
2ADB:010A 91                xchg    ax, cx
2ADB:010B 41                inc     cx
2ADB:010C 49                dec     cx
2ADB:010D 7412              jz      0121
2ADB:010F AC                lodsb
2ADB:0110 3C20              cmp     al, 20
2ADB:0112 74F8              jz      010C
2ADB:0114 3C09              cmp     al, 09
2ADB:0116 74F4              jz      010C
2ADB:0118 4E                dec     si
2ADB:0119 AC                lodsb
2ADB:011A 3C5C              cmp     al, 5C
2ADB:011C 7413              jz      0131
2ADB:011E AA                stosb
2ADB:011F E2F8              loop    0119
2ADB:0121 89F9              mov     cx, di
2ADB:0123 29D1              sub     cx, dx
2ADB:0125 B440              mov     ah, 40
2ADB:0127 BB0100            mov     bx, 0001
2ADB:012A CD21              int     21
2ADB:012C B8004C            mov     ax, 4C00
2ADB:012F CD21              int     21
2ADB:0131 49                dec     cx
2ADB:0132 74ED              jz      0121
2ADB:0134 AC                lodsb
2ADB:0135 3C61              cmp     al, 61
2ADB:0137 7206              jb      013F
2ADB:0139 3C7A              cmp     al, 7A
2ADB:013B 7702              ja      013F
2ADB:013D 3420              xor     al, 20
2ADB:013F BB5001            mov     bx, 0150
2ADB:0142 43                inc     bx
2ADB:0143 43                inc     bx
2ADB:0144 803F00            cmp     byte [bx], 00
2ADB:0147 74D5              jz      011E
2ADB:0149 3A07              cmp     al, [bx]
2ADB:014B 75F5              jnz     0142
2ADB:014D 8A4701            mov     al, [bx+01]
2ADB:0150 EBCC              jmp     011E
&d,152,l,F
2ADB:0150        45 1B 4E 0A 52 0D-54 09 42 07 53 20 46 0C   E.N.R.T.B.S F.
2ADB:0160  00                     -                        .
&q
1

Here are a couple of variants of john_e’s answer.

The first avoids writing one byte extra by counting the Escape character as one of the characters in the loop:

18F1:0100 XOR AX, AX    ; Clear AX
18F1:0102 MOV SI, 80    ; Point to command-line length in PSP
18F1:0105 LODSB         ; Load command-line length
18F1:0106 MOV CX, AX    ; Store it in CX (loop counter)
18F1:0108 JCXZ 118      ; If zero, jump to exit
18F1:010A INC SI        ; Skip past the separator
18F1:010B MOV DL, 1B    ; Prepare to write the Escape character
18F1:010D MOV AH, 02    ; DOS write single character function
18F1:010F JMP 114       ; Jump to the write
18F1:0111 LODSB         ; Load a single byte (from the command-line)
18F1:0112 MOV DL, AL    ; Move it to DL (character to be written)
18F1:0114 INT 21        ; Do it
18F1:0116 LOOP 111      ; Loop over the command-line arguments
18F1:0118 INT 20        ; Exit
18F1:011A

(Write 0x1A bytes, this is slightly shorter.)

The second adds the escape character to the command-line arguments and hands them off to the DOS write to file or device function:

192E:0100 XOR CX, CX                  ; Clear CX
192E:0102 MOV CL, [80]                ; Load command-line length
192E:0106 MOV BYTE PTR [81], 1B       ; Store Escape at the beginning of the arguments
192E:010B MOV BX, 1                   ; Standard output
192E:010E MOV DX, 81                  ; Point to command-line arguments
192E:0111 MOV AH, 40                  ; DOS write to file or device function
192E:0113 INT 21                      ; Do it
192E:0115 INT 20                      ; Exit
192E:0117

(Write 0x17 bytes.)

The second variant requires DOS 2.0 or later, but that isn’t an issue because ANSI.SYS itself can only be used with DOS 2.0 or later (that’s when loadable device driver support was added).

0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .