The HELLO.EXE program in Figure 3-7 demonstrates the fundamental structure of an assembly-language program that is destined to become a .EXE file. At minimum, it should have a module name, a code segment, a stack segment, and a primary procedure that receives control of the computer from MS-DOS after the program is loaded. The HELLO.EXE program also contains a data segment to provide a more complete example.
The NAME, TITLE, and PAGE directives were covered in the HELLO.COM example program and are used in the same manner here, so we'll move to the first new item of interest. After a few comments and EQU statements, we come to a declaration of a code segment that begins on line 21 with a SEGMENT command and ends on line 41 with an ENDS command. As in the HELLO.COM example program, the label in the leftmost field of the line gives the code segment the name _TEXT. The operand fields at the right end of the line give the attributes WORD, PUBLIC, and `CODE'.
Following the code-segment instruction, we find an ASSUME statement on line 23. Notice that, unlike the equivalent statement in the HELLO.COM program, the ASSUME statement in this program specifies several different segment names. Again, remember that this statement has no direct effect on the contents of the segment registers but affects only the operation of the assembler itself.
1: name hello
2: page 55,132
3: title HELLO.EXE--print Hello on terminal
4: ;
5: ; HELLO.EXE: demonstrates various components
6: ; of a functional .EXE-type assembly-
7: ; language program, use of segments,
8: ; and an MS-DOS function call.
9: ;
10: ; Ray Duncan, May 1988
11: ;
12:
13: stdin equ 0 ; standard input handle
14: stdout equ 1 ; standard output handle
15: stderr equ 2 ; standard error handle
16:
17: cr equ 0dh ; ASCII carriage return
18: lf equ 0ah ; ASCII linefeed
19:
20:
21: _TEXT segment word public 'CODE'
22:
23: assume cs:_TEXT,ds:_DATA,ss:STACK
24:
25: print proc far ; entry point from MS-DOS
26:
27: mov ax,_DATA ; make our data segment
28: mov ds,ax ; addressable...
29:
30: mov ah,40h ; function 40h = write
31: mov bx,stdout ; standard output handle
32: mov cx,msg_len ; length of message
33: mov dx,offset msg ; address of message
34: int 21h ; transfer to MS-DOS
35:
36: mov ax,4c00h ; exit, return code = 0
37: int 21h ; transfer to MS-DOS
38:
39: print endp
40:
41: _TEXT ends
42:
43:
44: _DATA segment word public 'DATA'
45:
46: msg db cr,lf ; message to display
47: db 'Hello World!',cr,lf
48:
49: msg_len equ $-msg ; length of message
50:
51 _DATA ends
52:
53:
54: STACK segment para stack `STACK'
55:
56: db 128 dup (?)
57:
58: STACK ends
59:
60: end print ; defines entry point
Figure 3-7. The HELLO.EXE program listing.
Within the code segment, the main print procedure is declared by the PROC command on line 25 and closed with ENDP on line 39. Because the procedure resides in a .EXE file, we have given it the FAR attribute as an example, but the attribute is really irrelevant because the program is so small and the procedure is not called by anything else in the same program.
The print procedure first initializes the DS register, as indicated in the earlier ASSUME statement, loading it with a value that causes it to point to the base of the data area. (MS-DOS automatically sets up the CS and SS registers.) Next, the procedure uses MS-DOS Int 21H Function 40H to display the message Hello World! on the screen, just as in the HELLO.COM program. Finally, the procedure exits back to MS-DOS with an Int 21H Function 4CH on lines 36 and 37, passing a return code of zero (which by convention means a success).
Lines 44 through 51 declare a data segment named _DATA, which contains the variables and constants the program will use. If the various modules of a program contain multiple data segments with the same name, the linker will collect them and place them in the same physical memory segment.
Lines 54 through 58 establish a stack segment; PUSH and POP instructions will access this area of scratch memory. Before MS-DOS transfers control to a .EXE program, it sets up the SS and SP registers according to the declared size and location of the stack segment. Be sure to allow enough room for the maximum stack depth that can occur at runtime, plus a safe number of extra words for registers pushed onto the stack during an MS-DOS service call. If the stack overflows, it may damage your other code and data segments and cause your program to behave strangely or even to crash altogether!
The END statement on line 60 winds up our brief HELLO.EXE program, telling the assembler that it has reached the end of the source file and providing the label of the program's point of entry from MS-DOS.
The differences between .COM and .EXE programs are summarized in Figure 3-8.
.COM program .EXE program
Maximum size 65,536 bytes minus 256 No limit
bytes for PSP and 2 bytes
for stack
Entry point PSP:0100H Defined by END statement
AL at entry 00H if default FCB #1 has Same
valid drive, 0FFH if
invalid drive
AH at entry 00H if default FCB #2 has Same
valid drive, 0FFH if
invalid drive
CS at entry PSP Segment containing module
with entry point
IP at entry 0100H Offset of entry point within
its segment
DS at entry PSP PSP
ES at entry PSP PSP
SS at entry PSP Segment with STACK attribute
SP at entry 0FFFEH or top word in Size of segment defined with
available memory, STACK attribute
whichever is lower
Stack at entry Zero word Initialized or uninitialized
Stack size 65,536 bytes minus 256 Defined in segment with
bytes for PSP and size of STACK attribute
executable code and data
Subroutine calls Usually NEAR NEAR or FAR
Exit method Int 21H Function 4CH Int 21H Function 4CH
preferred, NEAR RET if preferred
MS-DOS version 1
Size of file Exact size of program Size of program plus header
(multiple of 512 bytes)
Figure 3-8. Summary of the differences between .COM and .EXE programs, including their entry conditions.