Introduction: How to Write the World's Smallest "Hello World!" Executable for PC

Ok, it may sound useless for modern PCs... but is amazing to watch how a 21 bytes .COM file can display text on screen. even on a 286 if you still have them :)
This guide will use "debug.exe", a nice app that comes after DOS 2.0 and you possibly have it on Windows folder if your running Windows OS. So, no need to download fancy assemblers or compilers.
I'll try to explain each step... but dont expect more that a text on screen...

A nice description about DEBUG.EXE can be found in...
http://mirror.href.com/thestarman/asm/debug/debug.htm
and http://en.wikipedia.org/wiki/DEBUG

Step 1: Undestanding DEBUG.EXE

Our task should be creating a very small executable (in this case is a .COM file) in x86 assembly that should print "Hello World!" on the screen.

We will be using DEBUG.EXE that was released after DOS 2.0, it's mainly used to debug 16-bits applications.

You possibly have it on Windows System folder if your running Windows OS.
In Windows XP, it can be found in C:\WINDOWS\SYSTEM32\DEBUG.EXE

Enter in command-lines (I hope you know how to do this... if not, maybe this tutorial isn't good for you), first step should be calling DEBUG.EXE, since C:\WINDOWS\SYTEM32 is included in "PATH" enviroment, you can call it any path or drive:

C:\> debug.exe
-

You can type "?" then press ENTER to see the help.

(Again) A nice description about DEBUG.EXE can be found in...
http://mirror.href.com/thestarman/asm/debug/debug.htm
and http://en.wikipedia.org/wiki/DEBUG

Step 2: Start Coding the Real Thing!!

Now is time to code the Hello World executable! :)
I hope you know a little about CPUs, if not... then maybe you will have a little dificulty to undestand this!
We will be using function 09h (WRITE STRING TO STANDARD OUTPUT) of DOS Interrupt (INT 21h).

Some description about this function:

AH = 09h - WRITE STRING TO STANDARD OUTPUT
Entry: DS:DX -> '$'-terminated string
Return: AL = 24h
Notes: C/Break are checked

In assembly, all text after ';' are comments... but note that DEBUG.EXE doesn't support that, so, don't type them

The DS (Data Segment) register always point the correct segmentation before the .COM is executed.
what we need is (in x86 assembly language):

MOV AH, 09h ; AH register is used on DOS Interrupt to define what function should call
MOV DX, OFFSET helloworldstring ; DX register will hold the offset of the string
INT 21h ; Call the DOS Interrupt
RET ; Return from call, in this case it will terminate our Hello World app
helloworldstring DB "Hello World!$" ; the string himself...

the point is that we will not be using a assembler, but DEBUG.EXE, so we need to type this in a different way (note that numbering values are in hexadecimal):

- a 0100
????:0100 MOV AH, 09
????:0102 MOV DX, 0000
????:0105 INT 21
????:0107 RET
????:0108 DB "Hello World!$"
????:0115 (Press ENTER)

Notice i've pointed DX to :0000 because i didn't knew where the string should be stored. Now that i know is at :0108, i can easilly fix that opcode:

- a 0102
????:0102 MOV DX, 0108
????:0105 (Press ENTER)

Optionally, you can replace "RET" with "INT 20" (INT 20h = Terminate Application) or use function 0x4C of DOS Interrupt:
MOV AH, 4C ; Function 0x4C
MOV AL, 00 ; Return ERRORCODE 0 (OK!)
INT 21 ; Call DOS Interrupt

But if your following this tutorial for the first time, is better to use "RET" for now...

We are done coding!! Pretty easy, hum? :)
No? Don't undestand what it do? Well..

MOV AH, 09 ; Make Register AH (8-Bits Register) be 0x09
MOV DX, 0000 ; Make Register DX (16-Bits Register) be 0x0000
INT 21 ; Call Interrupt 0x21 (0x21 = DOS Interrupt)
RET ; Return from a call (No, it has nothing to do with "INT 0x21", but will terminate the app)

Now don't play around if you don't know what your doing or DEBUG.EXE will possibly crash (if your in DOS, that means you have to reboot your PC).
Lastlly if you don't undestand what is AH / DX / DS Registers i've talked about? See a register as a tiny 8/16 bits memory cell where the CPU use to process the data (because using memory in RAM is actually slower that using registers).

Some general use registers:
AL,BL,CL,DL = 8-Bits Registers
AH,BH,CH,DH = 8-Bits Registers
AX,BX,CX,DX = 16-Bits Registers, this is actually *L (Low Byte) and *H (High Byte) together

Some segment registers:
CS = 16-Bits Register (Code Segment)
DS = 16-Bits Register (Data Segment)

Some ASM example, note that RIGHT copy to LEFT:

MOV AL, 0x05 ; AL = 0x05, BL = ???
MOV BL, AL ; AL = 0x05, BL = 0x05
MOV AL, 0x02 ; AL = 0x02, BL = 0x05

Next we will debug and check the code...

Step 3: Debugging and Checking the Code

Should be wise to check your code before you execute it because if something incorrect there's a big chance of DEBUG.EXE will pobably crash and all your work is lost :(

First we will dissassemble back the app in memory:

- u 0100
????:0100 B409 MOV AH, 09
????:0102 BA0801 MOV DX, 0108
????:0105 CD21 INT 21
????:0107 C3 RET

The rest is the string data that is mistakelly taken as code, the app ends at ????:0115

Why not check now the data? to do so:

- d 0100
????:0100 B4 09 BA 08 01 CD 21 C3-48 65 6C 6F 20 57 6F ......!.Hello Wo
????:0110 72 6C 64 21 24 ?? ?? ??-?? ?? ?? ?? ?? ?? ?? rld!$

Data after ????:0115 is trash that will never be executed or saved to the .COM, so, is ok if they are different from the snapshot :)

Our next step should be executing the Hello World APP!! Yay.

Step 4: Executing the Code (...inside DEBUG.EXE)!!

Now is the time you were waiting for... well, almoust. :)

To execute, do this:

-g
Hello World!
Program terminated normally

Well, it seems that went well! Not bad, we are almost done!

It should report that it was terminated normally after you execute it, if not, there a big chance for the screen to be covered with trash characters or maybe DEBUG.EXE crash, this shouldn't happen if you took the prev. step carefully ;)

If you're curious: Code & Data in .COM files are stored and executed in ????:0100 memory, DS and CS point to the current Segment where the code is...

Our next step should be storing the data into a .COM file so we can execute it without needing DEBUG.EXE (also it will save our entire work for far!) ;)

Step 5: Storing the Hello World Executable to "hellow.com"

Finally is the moment of trust! We will finally create our beloved smallest Hello World executable for PC!

To finish our app, type:

-n C:\hellow.com
-r BX
BX 0000
:0
-r CX
CX 0000
:15
-w
Writing 00015 bytes

Note that i've used 15 due for numeric values in DEBUG.EXE are threated as hexadecimals: 0x15 = 21 decimal
After writing, "hellow.com" file is created at C:\ !!!
You can change the filename and path of command "n" at your choice.
Before we terminate, let me explain what each command do:

"n" = Name (filename that will be used to load/save files into DEBUG's Memory)
"r" = Register (used to change register value, but while writing to file BX:CX it's used to specify the length to write)
"w" = Write to a file or to a sector on a disk if you specify the drive and the sector.

But let's not worry about "w" command being able to write directlly to a disk sector, if you just write "w (address)", nothing bad happens.
I wont teach you how to read/write to a sector but in case you want to read/write to a sector of a disk, use the Drive A (0) for experiments.
Don't try to modify the floopy disk boot image with this Hello World code because it won't work, INTR 21h is only installed when COMMAND.COM is executed.

To finish the tutorial, type:

-q

Then execute the "hellow.com" file!

C:\WINDOWS> cd C:\
C:\> hellow.com
Hello World!
C:\>

Congratulations! We have our Hello World executable created and running! :D

Step 6: Still Not Happy With the Result?

Still want more that just a Hello World?

Well, ok, i'll show you some usefull stuff, but only recommended for experienced PC users with a little knoledge of CPUs :)
From now on, try to SAVE BEFORE EXECUTE, because if the code go bad for some unknown reason, you will loose everything in memory if you didnt saved first.

to load a file, use "debug.exe (filename)", also you wont need the "n" command.

This extra code will make the app to wait for the user to press a key:
MOV AH, 00 ; "Function 00h - get character from keyboard" of Interrupt 16h
INT 16 ; Interrupt 16h

The "Hello World!" text with new-line at the end (0x24 is "$" if you forgot):
DB "Hello World!",0D,0A,24

Multi-line text:
DB "First Line!!",0D,0A
DB "Second Line!!",0D,0A,24

Correct way to terminate current application in DOS:
MOV AH, 4C ; Function 4Ch - "EXIT" - TERMINATE WITH RETURN CODE
MOV AL, 00 ; Errorlevel 0
INT 21 ; Interrupt 21h - DOS Interrupt

You can jump to other locations in the code with JMP opcode:
JMP 0180 ; Jump to ????:0180

You can read a key press and jump to another location of the program if the key match (DON'T WORK VERY WELL WHEN EXECUTED INSIDE DEBUG.EXE, IT MAY CRASH).
MOV AH, 08 ; function 08h - CHARACTER INPUT WITHOUT ECHO
INT 21 ; Call DOS Intrerrupt, AL register will have the key that the user pressed.
CMP AL, 31 ; Compare AL with 0x31 (0x31 = Character '1')
JZ 0180 ; Jump to 0180 if AL equal to 31
JNZ 0200 ; Jump to 0200 if AL not equal to 31

If you want to go serious with x86 assembly, I'll recommend:
NASM, best for 16-Bits DOS: http://www.nasm.us/
MASM32, best for 32-Bits WIN: http://www.masm32.com/
The best is, they are both free and much nicer than DEBUG.EXE for managing asm code!

Thanks for reading my instructable and have fun! :)

Comments

author
spaceman1 (author)2015-03-08

Thank you - I miss those days :)

author
Metalbrain (author)2006-08-19

You can cut one more byte replacing MOV AH, 09h with XCHG AX,BP

author
JustBurn (author)Metalbrain2007-01-03

There's guarantee that before the COM file is executed the register BP holds value 0x0009 ? If not, some unexpected things may happens (or a crash)... A lot of different versions of DOS OS exist (The most common ones now are MS-DOS), so probably shouldn't trust that general registers are initialized all the same between different versions of DOS. Also if I'm not wrong, only SP, DS, ES, SS, CS and IP are initialized, the rest is expected to be random (Remember I'm talking about 286 registers).

author
mh76dk (author)JustBurn2012-07-19

You can make it faster (cpu-cycle wise) using XOR AX,AX instead of MOV AX,0

author
mh76dk (author)mh76dk2012-07-19

Sorry, that should be XOR DX,DX but im sure you get my point :)

author
mh76dk (author)mh76dk2012-07-19

two more points: MOV AH, 0x09 leaves you with undefined AL (or maybe im not seeing where you zero out AL. secondly - holy lateness batman, didnt notice how old this instructable was untill just now :)

author
dahdash (author)2008-12-22

Thanks!

author
N3kk3tsu (author)2008-10-21

I liked it very much! Where can i find more tutorials and examples about Debug.exe? Where can i find more of your tutorials? Thank you!

author
JustBurn (author)N3kk3tsu2008-10-22

Thanks! You can google or check the wikipedia: http://en.wikipedia.org/wiki/DEBUG
If you want to play around with x86 assembly, I'll recommend:
NASM, best for 16-Bits DOS: http://www.nasm.us/
MASM32, best for 32-Bits WIN: http://www.masm32.com/
They are both free.

author
N3kk3tsu (author)JustBurn2008-10-23

Thank you so much ;D

author
dx340 (author)2008-05-10

try this:

@Echo Off
msg * Hello World
pause

=)

author
JustBurn (author)dx3402008-10-22

nice! although message service only work on Win2K based OS.. but still fun to do some interesting pop-up messages with batching :)

author
duct tape (author)2007-06-28

most assemblers have some quality of code checking to make sure that the code doesn't do much other than intended. Debug however, does not. This makes it dangerous to use to much, as I learned first hand a while ago. I was doing something like this, and I typed in the wrong register. When I ran it, garbage characters spewed across the screen and made a very annoying sound. It was even more annoying that you couldn't close out the program without shutting off the computer. Of course, the next day I loaded it into my bro's computer and changed the registry to start it on boot. ;)

author
Bolicao (author)2007-05-18

Hello World! I liked it! :) Could ne1 explain me how to ask for two numbers and sum them using windows xp debuger? Thank you

author
aceman 569 (author)2007-05-05

I didn't understand a thing you said in this instructable.... :'(

author
mikesty (author)2006-07-17

echo Hello World! ;) Pretty neat stuff though. You know quite a bit about this :)

author
JustBurn (author)mikesty2006-07-17

Thank's :D I just love this kind of hardcore stuff, it remind me when had my 286 PC and lost hours messing with the system, playing EGA/VGA games and getting my nerves to make some programs to work because didn't had enough free convensional memory (640K!)... oh, "hellow.bat": @echo Hello World! 18 Bytes! But .BAT is basically like scripting, not real machine-code :P The "@" character avoid the command to be echoed, btw.

author
mikesty (author)JustBurn2006-07-18

Bahaha, did gates not once say, "No one will ever need more than 640K!" or something like that?

author
Mz3FRS (author)mikesty2006-09-24

HAHA-back in the day, trying to configure your system to free up more conventional memory was half the fun of playing games! nearly makes me miss DOS.

author
mark101 (author)Mz3FRS2007-01-11

when did you pick that picture of "Cosmo Kramer" as an avatar?
for those who haven't seen the N-bomb http://www.friedbrains.com/video35.htm

author
JustBurn (author)mikesty2006-07-19

"640K ought to be enough for anybody.", but Bill Gates deny that actually said this. :)

http://en.wikiquote.org/wiki/Bill_Gates

author
Mr.Devious (author)2007-01-03

I made a dos executable a couple years back. It was a nasty hd bomber. Wiped a few people's drives with it, they pissed me off.

author
JustBurn (author)Mr.Devious2007-01-03

Now that's a guy that no one should piss him off. =P

Releasing virus is a serious crime now, i remember the case of the "ILoveYou" virus, the programmer were a woman, she got caught and sent to jail.

For my point of view, the HDD Bombers (Destroy file-system) and/or some BIOS ones(Make the PC un-bootable anymore without a BIOS reflash) were always the most scary type of virus to me.

author
Mr.Devious (author)JustBurn2007-01-06

Yea that would scare me. And it wasn't exactly a hd bomber, but it did just that. I wanted to test it to see if it worked and all it was somewhat simple format command and it wrecked the guys hd because it was too late when he canceled it. I'm not out to wreck people's computers nor am I a hacker. I was just a nasty little shit back then :-P

author
Delta629 (author)2006-10-03

Cool, I haven't seen debug used since the late '80s.