Skip to content
/ hello Public

A 23-byte “hello, world” program assembled with DEBUG.EXE in MS-DOS

License

Notifications You must be signed in to change notification settings

susam/hello

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Programming "Hello, World" in MS-DOS

The program HELLO.COM was developed on MS-DOS Version 6.22 using the DOS program named DEBUG.EXE. It is exactly 23 bytes in length. It can be used to print the string "hello, world" followed by newline to standard output.

Assemble

Here is the complete DEBUG.EXE session that creates a "hello, world" program:

C:\>debug
-A
1165:0100 MOV AH, 9
1165:0102 MOV DX, 108
1165:0105 INT 21
1165:0107 RET
1165:0108 DB 'hello, world', D, A, '$'
1165:0117
-G
hello, world

Program terminated normally
-N HELLO.COM
-R CX
CX 0000
:17
-W
Writing 00017 bytes
-Q

C:\>HELLO
hello, world

C:\>

Note that the N (name) command specifies the name of the file where we write the binary machine code to. Also, note that the W (write) command expects the registers BX and CX to contain the number of bytes to be written to the file. When DEBUG.EXE starts, BX is already initialized to 0, so we only set the register CX to 17 (decimal 23) with the R CX command above.

The debugger session inputs are archived in the file named HELLO.TXT, so the binary file named HELLO.COM can also be created by running the following DOS command:

DEBUG < HELLO.TXT

The binary executable file can be created on a Unix or Linux system using the printf command as follows:

echo B4 09 BA 08 01 CD 21 C3 68 65 6C 6C 6F 2C 20 77 6F 72 6C 64 0D 0A 24 | xxd -r -p > HELLO.COM

Unassemble

Here is a disassembly of HELLO.COM to confirm that it has been written correctly:

C:\>DEBUG
-N HELLO.COM
-L
-U 100 116
117C:0100 B409          MOV     AH,09
117C:0102 BA0801        MOV     DX,0108
117C:0105 CD21          INT     21
117C:0107 C3            RET
117C:0108 68            DB      68
117C:0109 65            DB      65
117C:010A 6C            DB      6C
117C:010B 6C            DB      6C
117C:010C 6F            DB      6F
117C:010D 2C20          SUB     AL,20
117C:010F 776F          JA      0180
117C:0111 726C          JB      017F
117C:0113 64            DB      64
117C:0114 0D0A24        OR      AX,240A
-D 100 116
117C:0100  B4 09 BA 08 01 CD 21 C3-68 65 6C 6C 6F 2C 20 77   ......!.hello, w
117C:0110  6F 72 6C 64 0D 0A 24                              orld..$

Run

To run this program on MS-DOS, simply enter the following command at the command prompt:

HELLO

INT 20 vs. RET

Another way to terminate a .COM program is to simply use the instruction INT 20. This consumes two bytes in the machine code: CD 20.

While producing the smallest possible executable is not the goal of this project, this project indulges in a little bit of size reduction by using the RET instruction to terminate the program. This consumes only one byte: C3. This works because when a .COM file starts, the register SP contains FFFE. The stack memory locations at offset FFFE and FFFF contain 00 and 00, respectively. Further, the memory address offset 0000 contains the instruction INT 20.

C:\>DEBUG HELLO.COM
-R SP
SP FFFE
:
-D FFFE
117C:FFF0                                            00 00
-U 0 1
117C:0000 CD20          INT     20

As a result, executing the RET instruction pops 0000 off the stack at FFFE and loads it into IP. This results in the intstruction INT 20 at offset 0000 getting executed which leads to program termination.

While both INT 20 and RET lead to successful program termination both in DOS as well as while debugging with DEBUG.EXE, there is some difference between them which affects the debugging experience. Terminating the program with INT 20 allows us to run the program repeatedly within the debugger by repeated applications of the G debugger command. But when we terminate the program with RET, we cannot run the program repeatedly in this manner. The program runs and terminates successfully the first time we run it in the debugger but the stack does not get reinitialized with zeros to prepare it for another execution of the program within the debugger. Therefore when we try to run the program the second time using the G command, the program does not terminate successfully. It hangs instead. It is possible to work around this by reinitializing the stack with the debugger command E FFFE 0 0 before running G again.

License

This is free and open source software. You can use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of it, under the terms of the MIT License. See LICENSE.md for details.

This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND, express or implied. See LICENSE.md for details.

More

The example presented in this document relies on INT 21 which is a DOS service. See the ALT subdirectory for example programs that do not rely on DOS services. These additional examples also show how to create boot sector programs that print "hello, world" on booting the computer.

There is also a 5-byte reboot program available at github.com/susam/reboot.