Assembly language is assembly language. Most machine instructions you use in a DOS program are the same instructions you use in an OS/2 program. When you start making calls to the operating system, however, things change.
You should understand the following differences between the two operating systems before attempting to write an OS/2 program.
Summary: System calls control I/O and screen access.
OS/2 is similar to DOS in that it offers a series of system calls that perform tasks such as opening or closing a disk file. The OS/2 system calls that handle keyboard input (KbdCharIn, for example) correspond to the interrupt 16h instructions in DOS. The OS/2 system calls for screen output (VioScrollDn, for example) correspond to DOS interrupt 10h calls. And the OS/2 disk and operating-system calls (DosGetDateTime, for example) correspond to DOS interrupt 21h calls.
The effect is similar, but the way you actually make the calls is different. In DOS, you issue an interrupt. In OS/2, you make the system call with the INVOKE directive or the CALL instruction.
OS/2 is designed for advanced processors, and you may want to write programs that take advantage of the new instructions available on the 80286–80486. To use the new instructions and still target OS/2 1.x, place a .286 directive at the beginning of your source code.
In general, you should avoid the directives that enable privileged instructions (.286P, .386P, and .486P), unless you are writing system-level code.
Many OS/2 programs can be converted to run under DOS as well. To write programs to run on all DOS and OS/2 systems, use the default processor setting (.8086).
Summary: MASM 6.0 provides OS2.INC and OS2.LIB.
OS/2 programs must be linked to the system-call import library, OS2.LIB. The best way to perform this task is to use the INCLUDELIB directive, as shown in the example in the next section. In addition, you can include the OS2.INC file as an alternative to adding the prototypes for the OS/2 functions to your file.
The OS2.LIB file makes system calls possible; it contains import definitions for all system calls. An import definition specifies the name of a procedure and the dynamic-link library (DLL) where the procedure resides. You can learn more about DLLs in Chapter 18, “Creating Dynamic-Link Libraries.” To create an OS/2 application, however, you need to know only that OS2.LIB is required.
Unlike DOS, OS/2 automatically initializes all segment registers as required by the standard segment model. No special start-up sequence is required, although OS/2 places useful information in AX, BX, and CX (see Section 17.6, “Register and Memory Initialization”) that you may want to save.
Summary: OS/2 1.x uses the Pascal calling convention.
OS/2 system calls follow the Pascal calling and naming conventions. One way to enforce these conventions is to specify PASCAL in the .MODEL directive, then use the INVOKE directive to generate the correct code. Another is to include the OS2.INC file, which uses the PROTO directive to prototype the functions to follow the Pascal conventions. The prototypes specify Pascal as the calling convention. OS/2 functions return a value in AX. A nonzero value indicates an error. All registers except AX are preserved.
The OS/2 2.x operating system uses different calling conventions. See the documentation provided with that product.
To exit an OS/2 program, call the OS/2 system function DosExit. If you use the .EXIT directive and the OS_OS2 attribute of the .MODEL statement, the assembler automatically generates the proper system call if you have a prototype for DosExit.
Although OS/2 makes some operations easier, it does impose restrictions on the programmer. You cannot do segment arithmetic. That is, you cannot attempt to measure the distance between segments by subtracting one segment from another. In general, you also cannot add values to segment registers. Either operation may cause a protection violation, which would immediately terminate the program.
Under OS/2, segment registers do not hold physical addresses; they hold “segment selectors.” A segment selector is an index into the system's descriptor tables that hold the actual addresses. You can copy the segment selector or use it to access data, but you should not try to modify it.
Huge pointer arithmetic is therefore different under OS/2. Under DOS, you can handle huge pointers easily by checking the OVERFLOW? flag after you increment or add to an offset address. If the result overflows (exceeds 64K), then you increment the segment address. Under OS/2, manipulation of huge pointers requires special techniques. See your OS/2 documentation for more information.