The following program prints Hello, world. It runs under OS/2 protected mode.
; HELLO.ASM
;
.MODEL small, pascal, OS_OS2
.286
INCLUDELIB os2.lib
INCLUDE os2.inc
.STACK
.DATA
message BYTE "Hello, world.", 13, 10 ; Message to print
bytecount DWORD ? ; Holds number of
; bytes written
.CODE
.STARTUP
push 1 ; Select standard output
push ds ; Pass address of message
push OFFSET message
push LENGTHOF message ; Pass length of message
push ds ; Pass address of count
push OFFSET bytecount ; returned by function
call DosWrite ; Call system write
; function
.EXIT 0 ; Exit with 0 return code
END
Summary: .STARTUP and .EXIT automatically generate code.
The .STARTUP and .EXIT directives are very useful because they automatically produce correct code for the operating-system type specified with the .MODEL directive (see Section 2.2, “Using Simplified Segment Directives”). As described in Section 17.6, OS/2 initializes all segment registers; therefore, .STARTUP does nothing but indicate the starting point. To correctly exit an OS/2 program, you must call the DosExit function. The DosExit prototype is always available to MASM programs.
In the example above, .EXIT automatically generates the following code under OS/2:
.EXIT 0
0011 6A 01 * push +000000001h ; Action 1 ends all threads
0013 6A 00 * push +000000000h ; Pass 0 return code
0015 9A ---- 0000 E * call DosExit ; Call system function
END
Between .STARTUP and .EXIT, the entire program consists of a single call to the DosWrite function. The program pushes the parameters on the stack and then makes the call. No POP or ADD instructions are needed to restore the stack after DosWrite returns; DosWrite observes the Pascal calling convention and restores the stack itself before returning.
The .MODEL statement helps ensure that the assembler produces correct code for calling DosWrite:
.MODEL small, pascal, OS_OS2
When you run HELLO.EXE, OS/2 looks at the import definitions in the executable-file header and makes sure that all needed DLLs are in memory. It then loads any needed DLLs not already in memory.
The assembler must be informed that DosWrite and DosExit are far and observe the Pascal calling convention. This information is in the prototype.
In the call to DosWrite, note that although OFFSET message is an immediate operand, the program pushes it directly onto the stack. This operation is legal on 80186–80486 processors but not on the 8086 or 8088:
push OFFSET message
Summary: The processors you want to target determine the instructions you should use.
Since OS/2 programs can execute only on the 80286 or later processors, it is reasonable to use extended operations not supported by the 8086. However, if you want to write a program that can be converted to run under both OS/2 and DOS (as shown in Section 17.5), then you should write code that can run on the 8086. For example,
mov ax, OFFSET msg
push ax
The following revision of the sample program illustrates the usefulness of the INVOKE directive. This version does everything the previous example did with far fewer statements:
; HELLO.ASM
.MODEL small, pascal, OS_OS2
INCLUDE os2.inc
INCLUDELIB os2.lib
.STACK
.DATA
message BYTE "Hello, world.", 13, 10 ; Message to print
bytecount DWORD ? ; Holds number of
; bytes written
.CODE
.STARTUP
INVOKE DosWrite,
1,
ADDR message,
LENGTHOF message,
ADDR bytecount
.EXIT 0 ; Exit with return code 0
END
The INVOKE directive generates a call to the given procedure after first pushing all other arguments on the stack. Like a call statement in a high-level language, the INVOKE directive handles types in a sophisticated way.