An Example .EXE Program

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.