home *** CD-ROM | disk | FTP | other *** search
/ For Beginners & Professional Hackers / cd.iso / docum / advdos.doc / s1c16 < prev    next >
Encoding:
Text File  |  1992-04-21  |  43.3 KB  |  844 lines

  1. ────────────────────────────────────────────────────────────────────────────
  2. Chapter 16  Compatibility and Portability
  3.  
  4.   At the beginning of this book, we surveyed the history of MS-DOS and saw
  5.   that new versions come along nearly every year, loosely coupled to the
  6.   introduction of new models of personal computers. We then focused on each
  7.   of the mainstream issues of MS-DOS applications programming: the user
  8.   interface; mass storage; memory management; control of "child" processes;
  9.   and special classes of programs, such as filters, interrupt handlers, and
  10.   device drivers.
  11.  
  12.   It's now time to close the circle and consider two global concerns of
  13.   MS-DOS programming: compatibility and portability. For your programs to
  14.   remain useful in a constantly evolving software and hardware environment,
  15.   you must design them so that they perform reliably on any reasonable
  16.   machine configuration and exploit available system resources; in addition,
  17.   you should be able to upgrade them easily for new versions of MS-DOS, for
  18.   new machines, and, for that matter, for completely new environments such
  19.   as MS OS/2.
  20.  
  21.  
  22. Degrees of Compatibility
  23.  
  24.   If we look at how existing MS-DOS applications use the operating system
  25.   and hardware, we find that we can assign them to one of four categories:
  26.  
  27.   ■  MS-DOS─compatible applications
  28.  
  29.   ■  ROM BIOS─compatible applications
  30.  
  31.   ■  Hardware-compatible applications
  32.  
  33.   ■  "Ill-behaved" applications
  34.  
  35.   MS-DOS─compatible applications use only the documented MS-DOS function
  36.   calls and do not call the ROM BIOS or access the hardware directly. They
  37.   use ANSI escape sequences for screen control, and their input and output
  38.   is redirectable. An MS-DOS─compatible application will run on any machine
  39.   that supports MS-DOS, regardless of the machine configuration. Because of
  40.   the relatively poor performance of MS-DOS's built-in display and serial
  41.   port drivers, few popular programs other than compilers, assemblers, and
  42.   linkers fall into this category.
  43.  
  44.   ROM BIOS─compatible applications use the documented MS-DOS and ROM BIOS
  45.   function calls but do not access the hardware directly. As recently as
  46.   three years ago, this strategy might have significantly limited a
  47.   program's potential market. Today, the availability of high-quality
  48.   IBM-compatible ROM BIOSes from companies such as Phoenix has ensured the
  49.   dominance of the IBM ROM BIOS standard; virtually no machines are being
  50.   sold in which a program cannot rely as much on the ROM BIOS interface as
  51.   it might on the MS-DOS interface. However, as we noted in Chapters 6 and
  52.   7, the ROM BIOS display and serial drivers are still not adequate to the
  53.   needs of high-performance interactive applications, so the popular
  54.   programs that fall into this category are few.
  55.  
  56.   Hardware-compatible applications generally use MS-DOS functions for mass
  57.   storage, memory management, and the like, and use a mix of MS-DOS and ROM
  58.   BIOS function calls and direct hardware access for their user interfaces.
  59.   The amount of hardware dependence in such programs varies widely. For
  60.   example, some programs only write characters and attributes into the video
  61.   controller's regen buffer and use the ROM BIOS to switch modes and
  62.   position the cursor; others bypass the ROM BIOS video driver altogether
  63.   and take complete control of the video adapter. As this book is written,
  64.   the vast majority of the popular MS-DOS "productivity" applications (word
  65.   processors, databases, telecommunications programs, and so on) can be
  66.   placed somewhere in this category.
  67.  
  68.   "Ill-behaved" applications are those that rely on undocumented MS-DOS
  69.   function calls or data structures, interception of MS-DOS or ROM BIOS
  70.   interrupts, or direct access to mass storage devices (bypassing the MS-DOS
  71.   file system). These programs tend to be extremely sensitive to their
  72.   environment and typically must be "adjusted" in order to work with each
  73.   new MS-DOS version or PC model. Virtually all popular terminate-
  74.   and-stay-resident (TSR) utilities, network programs, and disk
  75.   repair/optimization packages are in this category.
  76.  
  77. Writing Well-Behaved MS-DOS Applications
  78.  
  79.   Your choice of MS-DOS functions, ROM BIOS functions, or direct hardware
  80.   access to solve a particular problem must always be balanced against
  81.   performance needs; and, of course, the user is the final judge of a
  82.   program's usefulness and reliability. Nevertheless, you can follow some
  83.   basic guidelines, outlined below, to create well-behaved applications that
  84.   are likely to run properly under future versions of MS-DOS and under
  85.   multitasking program managers that run on top of MS-DOS, such as Microsoft
  86.   Windows.
  87.  
  88.   Program structure
  89.  
  90.   Design your programs as .EXE files with separate code, data, and stack
  91.   segments; shun the use of .COM files. Use the Microsoft conventions for
  92.   segment names and attributes discussed in Chapter 3. Inspect the
  93.   environment block at runtime to locate your program's overlays or data
  94.   files; don't "hard-wire" a directory location into the program.
  95.  
  96.   Check host capabilities
  97.  
  98.   Obtain the MS-DOS version number with Int 21H Function 30H during your
  99.   program's initialization and be sure that all of the functions your
  100.   program requires are actually available. If you find that the host MS-DOS
  101.   version is inadequate, be careful about which functions you call to
  102.   display an error message and to terminate.
  103.  
  104.   Use the enhanced capabilities of MS-DOS versions 3 and 4 when your program
  105.   is running under those versions. For example, you can specify a sharing
  106.   mode when opening a file with Int 21H Function 3DH, you can create
  107.   temporary or unique files with Int 21H Functions 5AH and 5BH, and you
  108.   can obtain extended error information (including a recommended recovery
  109.   strategy) with Int 21H Function 59H. Section 2 of this book contains
  110.   version-dependency information for each MS-DOS function.
  111.  
  112.   Input and output
  113.  
  114.   Use the handle file functions exclusively and extend full path support
  115.   throughout your application (being sure to allow for the maximum possible
  116.   path length during user input of filenames). Use buffered I/O whenever
  117.   possible. The device drivers in MS-DOS versions 2.0 and later can handle
  118.   strings as long as 64 KB, and performance will be improved if you write
  119.   fewer, larger records as opposed to many short ones.
  120.  
  121.   Avoid the use of FCBs, the Int 25H or Int 26H functions, or the ROM BIOS
  122.   disk driver. If you must use FCBs, close them when you are done with them
  123.   and don't move them around while they are open. Avoid reopening FCBs that
  124.   are already open or reclosing FCBs that have already been closed──these
  125.   seemingly harmless practices can cause problems when network software is
  126.   running.
  127.  
  128.   Memory management
  129.  
  130.   During your program's initialization, release any memory that is not
  131.   needed by the program. (This is especially important for .COM programs.)
  132.   If your program requires extra memory for buffers or tables, allocate that
  133.   memory dynamically when it is needed and release it as soon as it is no
  134.   longer required. Use expanded memory, when it is available, to minimize
  135.   your program's demands on conventional memory.
  136.  
  137.   As a general rule, don't touch any memory that is not owned by your
  138.   program. To set or inspect interrupt vectors, use Int 21H Functions 25H
  139.   and 35H rather than editing the interrupt vector table directly. If you
  140.   alter the contents of interrupt vectors, save their original values and
  141.   restore them before the program exits.
  142.  
  143.   Process management
  144.  
  145.   To isolate your program from dependencies on PSP structure and relocation
  146.   information, use the EXEC function (Int 21H Function 4BH) when loading
  147.   overlays or other programs. Terminate your program with Int 21H Function
  148.   4CH, passing a zero return code if the program executes successfully and
  149.   a nonzero code if an error is encountered. Your program's parent can then
  150.   test this return code with Int 21H Function 4DH or, in a batch file, with
  151.   the IF ERRORLEVEL statement.
  152.  
  153.   Exception handling
  154.  
  155.   Install Ctrl-C (Int 23H) and critical-error (Int 24H) handlers so that
  156.   your program cannot be terminated unexpectedly by the user's entry of
  157.   Ctrl-C or Ctrl-Break or by a hardware I/O failure. This is particularly
  158.   important if your program uses expanded memory or installs its own
  159.   interrupt handlers.
  160.  
  161. ROM BIOS and Hardware-Compatible Applications
  162.  
  163.   When you feel the need to introduce ROM BIOS or hardware dependence for
  164.   performance reasons, keep it isolated to small, well-documented procedures
  165.   that can be easily modified when the hardware changes. Use macros and
  166.   equates to hide hardware characteristics and to avoid spreading "magic
  167.   numbers" throughout your program.
  168.  
  169.   Check host capabilities
  170.  
  171.   If you use ROM BIOS functions in your program, you must check the machine
  172.   model at runtime to be sure that the functions your program needs are
  173.   actually available. There is a machine ID byte at F000:FFFEH whose value
  174.   is interpreted as follows:
  175.  
  176.   ──────────────────────────────────────────────────────────────────────────
  177.   F8H                      PS/2 Models 70 and 80
  178.  
  179.   F9H                      PC Convertible
  180.  
  181.   FAH                      PS/2 Model 30
  182.  
  183.   FBH                      PC/XT (later models)
  184.  
  185.   FCH                      PC/AT, PC/XT-286, PS/2 Models 50 and 60
  186.  
  187.   FDH                      PCjr
  188.  
  189.   FEH                      PC/XT (early models)
  190.  
  191.   FFH                      PC "Classic"
  192.   ──────────────────────────────────────────────────────────────────────────
  193.  
  194.   In some cases, submodels can be identified; see Int 15H Function C0H on
  195.   page 573. Section 3 of this book contains version-dependency information
  196.   for each ROM BIOS function.
  197.  
  198.   When writing your own direct video drivers, you must determine the type
  199.   and capabilities of the video adapter by a combination of Int 10H calls,
  200.   reading ports, and inspection of the ROM BIOS data area at 0040:0000H and
  201.   the memory reserved for the EGA or VGA ROM BIOS, among other things. The
  202.   techniques required are beyond the scope of this book but are well
  203.   explained in Programmer's Guide to PC and PS/2 Video Systems (Microsoft
  204.   Press, 1987).
  205.  
  206.   Avoid unstable hardware
  207.  
  208.   Some areas of IBM personal computer architecture have remained remarkably
  209.   stable from the original IBM PC, based on a 4.77 MHz 8088, to today's PS/2
  210.   Model 80, based on a 20 MHz 80386. IBM's track record for upward
  211.   compatibility in its video and serial communications controllers has been
  212.   excellent; in many cases, the same hardware-dependent code that was
  213.   written for the original IBM PC runs perfectly well on an IBM PS/2 Model
  214.   80. Other areas of relative hardware stability are:
  215.  
  216.   ■  Sound control via port 61H
  217.  
  218.   ■  The 8253 timer chip's channels 0 and 2 (ports 40H, 42H, and 43H)
  219.  
  220.   ■  The game adapter at port 201H
  221.  
  222.   ■  Control of the interrupt system via the 8259 PIC's mask register at
  223.      port 21H
  224.  
  225.   However, direct sound generation and manipulation of the 8253 timer or
  226.   8259 PIC are quite likely to cause problems if your program is run under a
  227.   multitasking program manager such as Microsoft Windows or DesqView.
  228.  
  229.   Keyboard mapping, the keyboard controller, and the floppy and fixed disk
  230.   controllers are areas of relative hardware instability. Programs that
  231.   bypass MS-DOS for keyboard or disk access are much less likely to function
  232.   properly across the different PC models and are also prone to interfere
  233.   with each other and with well-behaved applications.
  234.  
  235.  
  236. OS/2 Compatibility
  237.  
  238.   MS-DOS is upwardly compatible in several respects with OS/2, Microsoft's
  239.   multitasking protected-mode virtual memory operating system for 80286 and
  240.   80386 computers. The OS/2 graphical user interface (the Presentation
  241.   Manager) is nearly identical to Microsoft Windows 2.0. OS/2 versions 1.0
  242.   and 1.1 use exactly the same disk formats as MS-DOS so that files may
  243.   easily be moved between MS-DOS and OS/2 systems. Most important, OS/2
  244.   includes a module called the "DOS Compatibility Environment" or "3.x Box,"
  245.   which can run one MS-DOS application at a time alongside protected-mode
  246.   OS/2 applications.
  247.  
  248.   The 3.x Box traps Int 21H function calls and remaps them into OS/2
  249.   function calls, emulating an MS-DOS 3.3 environment with the file-sharing
  250.   module (SHARE.EXE) loaded but returning a major version number of 10
  251.   instead of 3 for Int 21H Function 30H. The 3.x Box also supports most ROM
  252.   BIOS calls, either by emulating their function or by interlocking the
  253.   device and then calling the original ROM BIOS routine. In addition, the
  254.   3.x Box maintains the ROM BIOS data area, provides timer ticks to
  255.   applications via Int 1CH, and supports certain undocumented MS-DOS
  256.   services and data structures so that most TSR utilities can function
  257.   properly. Nevertheless, the 3.x Box's emulation of MS-DOS is not perfect,
  258.   and you must be aware of certain constraints on MS-DOS applications
  259.   running under OS/2.
  260.  
  261.   The most significant restriction on an MS-DOS application is that it does
  262.   not receive any CPU cycles when it is in the background. That is, when a
  263.   protected-mode application has been "selected," so that the user can
  264.   interact with it, the MS-DOS application is frozen. If the MS-DOS
  265.   application has captured any interrupt vectors (such as the serial port or
  266.   timer tick), these interrupts will not be serviced until the application
  267.   is again selected and in the foreground. OS/2 must freeze MS-DOS
  268.   applications when they are in the background because they execute in real
  269.   mode and are thus not subject to hardware memory protection; nothing else
  270.   ensures that they will not interfere with a protected-mode process that
  271.   has control of the screen and keyboard.
  272.  
  273.   Use of FCBs is restricted in the 3.x Box, as it is under MS-DOS 3 or 4
  274.   with SHARE.EXE loaded. A file cannot be opened with an FCB if any other
  275.   process is using it. The number of FCBs that can be simultaneously opened
  276.   is limited to 16 or to the number specified in a CONFIG.SYS FCBS=
  277.   directive. Even when the handle file functions are used, these functions
  278.   may fail unexpectedly due to the activity of other processes (for example,
  279.   if a protected-mode process has already opened the file with "deny all"
  280.   sharing mode); most MS-DOS applications are not written with file sharing
  281.   in mind, and they do not handle such errors gracefully.
  282.  
  283.   Direct writes to a fixed disk using Int 26H or Int 13H are not allowed.
  284.   This prevents the file system from being corrupted, because protected-mode
  285.   applications running concurrently with the MS-DOS application may also be
  286.   writing to the same disk. Imagine the mess if a typical MS-DOS unerase
  287.   utility were to alter the root directory and FAT at the same time that a
  288.   protected-mode database program was updating its file and indexes!
  289.  
  290.   MS-DOS applications that attempt to reprogram the 8259 to move the
  291.   interrupt vector table or that modify interrupt vectors already belonging
  292.   to an OS/2 device driver are terminated by the operating system. MS-DOS
  293.   applications can change the 8259's interrupt-mask register, disable and
  294.   reenable interrupts at their discretion, and read or write any I/O port.
  295.   The obvious corollary is that an MS-DOS program running in the 3.x Box can
  296.   crash the entire OS/2 system at any time; this is the price for allowing
  297.   real-mode applications to run at all.
  298.  
  299. Porting MS-DOS Applications to OS/2
  300.  
  301.   The application program interface (API) provided by OS/2 to protected-mode
  302.   programs is quite different from the familiar Int 21H interface of MS-DOS
  303.   and the OS/2 3.x Box. However, the OS/2 API is functionally a proper
  304.   superset of MS-DOS. This makes it easy to convert well-behaved MS-DOS
  305.   applications to run in OS/2 protected mode, whence they can be enhanced to
  306.   take advantage of OS/2's virtual memory, multitasking, and interprocess
  307.   communication capabilities.
  308.  
  309.   To give you a feeling for both the nature of the OS/2 API and the
  310.   practices that should be avoided in MS-DOS programming if portability to
  311.   OS/2 is desired, I will outline my own strategy for converting existing
  312.   MS-DOS assembly-language programs to OS/2. For the purposes of discussion,
  313.   I have divided the conversion process into five steps and have assigned
  314.   each an easily remembered buzzword:
  315.  
  316.   1.  Segmentation
  317.  
  318.   2.  Rationalization
  319.  
  320.   3.  Encapsulation
  321.  
  322.   4.  Conversion
  323.  
  324.   5.  Optimization
  325.  
  326.   The first three stages can (and should) be performed and tested in the
  327.   MS-DOS environment; only the last two require OS/2 and the protected-mode
  328.   programming tools. As you read on, you may notice that an MS-DOS program
  329.   that follows the compatibility guidelines presented earlier in this
  330.   chapter requires relatively little work to make it run in protected mode.
  331.   This is the natural benefit of working with the operating system instead
  332.   of against it.
  333.  
  334.   Segmentation
  335.  
  336.   Most of the 80286's protected-mode capabilities revolve around a change in
  337.   the way memory is addressed. In real mode, the 80286 essentially emulates
  338.   an 8088/86 processor, and the value in a segment register corresponds
  339.   directly to a physical memory address. MS-DOS runs on the 80286 in real
  340.   mode.
  341.  
  342.   When an 80286 is running in protected mode, as it does under OS/2, an
  343.   additional level of indirection is added to memory addressing.
  344. Although the 80386 has additional modes and addressing capabilities,
  345. current versions of OS/2 use the 80386 as though it were an 80286.
  346.  A segment
  347.   register holds a selector, which is an index to a table of descriptors. A
  348.   descriptor defines the physical address and length of a memory segment,
  349.   its characteristics (executable, read-only data, or read/write data) and
  350.   access rights, and whether the segment is currently resident in RAM or has
  351.   been swapped out to disk. Each time a program loads a segment register or
  352.   accesses memory, the 80286 hardware checks the associated descriptor and
  353.   the program's privilege level, generating a fault if the selector or
  354.   memory operation is not valid. The fault acts like a hardware interrupt,
  355.   allowing the operating system to regain control and take the appropriate
  356.   action.
  357.  
  358.   This scheme of memory addressing in protected mode has two immediate
  359.   consequences for application programs. The first is that application
  360.   programs can no longer perform arithmetic on the contents of segment
  361.   registers (because selectors are magic numbers and have no direct
  362.   relationship to physical memory addresses) or use segment registers for
  363.   storage of temporary values. A program must not load a segment register
  364.   with anything but a legitimate selector provided by the OS/2 loader or
  365.   resulting from an OS/2 memory allocation function call. The second
  366.   consequence is that a program must strictly segregate machine code
  367.   ("text") from data, placing them in separate segments with distinct
  368.   selectors (because a selector that is executable is not writable, and vice
  369.   versa).
  370.  
  371.   Accordingly, the first step in converting a program for OS/2 is to turn it
  372.   into a .EXE-type program that uses the Microsoft segment, class, and group
  373.   conventions described in Chapter 3. At minimum, the program must have one
  374.   code segment and one data segment, and should declare a group──with the
  375.   special name DGROUP──that contains the "near" data segment, stack, and
  376.   local heap (if any). At the same time, you should remove or rewrite any
  377.   code that performs direct manipulation of segment values.
  378.  
  379.   After restructuring and segmentation, reassemble and link your program and
  380.   check to be sure it still works as expected under MS-DOS. Changing or
  381.   adding segmentation often uncovers hidden addressing assumptions in the
  382.   code, so it is best to track these problems down before making other
  383.   substantive changes to the program.
  384.  
  385.   Rationalization
  386.  
  387.   Once you've successfully segmented your program so that it can be linked
  388.   and executed as a .EXE file under MS-DOS, the next step is to rationalize
  389.   your code. By rationalization I mean converting your program into a
  390.   completely well-behaved MS-DOS application.
  391.  
  392.   First, you must ruthlessly eliminate any elements that manipulate the
  393.   peripheral device adapters directly, alter interrupt priorities, edit the
  394.   system interrupt-vector table, or depend on CPU speed or characteristics
  395.   (such as timing loops). In protected mode, control of the interrupt system
  396.   is completely reserved to the operating system and its device drivers, I/O
  397.   ports may be read or written by an application only under very specific
  398.   conditions, and timing loops burn up CPU cycles that can be used by other
  399.   processes.
  400.  
  401.   As I mentioned earlier in this chapter, display routines constitute the
  402.   most common area of hardware dependence in an MS-DOS application. Direct
  403.   manipulation of the video adapter and its regen buffer poses obvious
  404.   difficulties in a multitasking, protected-memory environment such as OS/2.
  405.   For porting purposes, you must convert all routines that write text to the
  406.   display, modify character attributes, or affect cursor shape or position
  407.   into Int 21H Function 40H calls using ANSI escape sequences or into ROM
  408.   BIOS Int 10H calls. Similarly, you must convert all hardware-dependent
  409.   keyboard operations to Int 21H Function 3FH or ROM BIOS Int 16H calls.
  410.  
  411.   Once all hardware dependence has been expunged from your program, your
  412.   next priority is to make it well-behaved in its use of system memory.
  413.   Under MS-DOS an application is typically handed all remaining memory in
  414.   the system to do with as it will; under OS/2 the converse is true: A
  415.   process is initially allocated only enough memory to hold its code,
  416.   declared data storage, and stack. You can make the MS-DOS loader behave
  417.   like the OS/2 loader by linking your application with the /CPARMAXALLOC
  418.   switch. Alternatively, your program can give up all extra memory during
  419.   its initialization with Int 21H Function 4AH, as recommended earlier in
  420.   this chapter.
  421.  
  422.   After your program completes its initialization sequence, it should
  423.   dynamically obtain and release any additional memory it may require for
  424.   buffers and tables with MS-DOS Int 21H Functions 48H and 49H. To ensure
  425.   compatibility with protected mode, limit the size of any single allocated
  426.   block to 65,536 bytes or less, even though MS-DOS allows larger blocks to
  427.   be allocated.
  428.  
  429.   Finally, you must turn your attention to file and device handling. Replace
  430.   any calls to FCB file functions with their handle-based equivalents,
  431.   because OS/2 does not support FCBs in protected mode at all. Check
  432.   pathnames for validity within the application; although MS-DOS and the 3.x
  433.   Box silently truncate a name or extension, OS/2 refuses to open or create
  434.   a file in protected mode if the name or extension is too long and returns
  435.   an error instead. Replace any use of the predefined handles for the
  436.   standard auxiliary and standard list devices with explicit opens of COM1,
  437.   PRN, LPT1, and so on, using the resulting handle for read and write
  438.   operations. OS/2 does not supply processes with standard handles for the
  439.   serial communications port or printer.
  440.  
  441.   Encapsulation
  442.  
  443.   When you reach this point, with a well-behaved, segmented MS-DOS
  444.   application in hand, the worst of a port to OS/2 is behind you. You are
  445.   now ready to prepare your program for true conversion to protected-mode
  446.   operation by encapsulating, in individual subroutines, every part of the
  447.   program that is specific to the host operating system. The objective here
  448.   is to localize the program's "knowledge" of the environment into small
  449.   procedures that can be subsequently modified without affecting the
  450.   remainder of the program.
  451.  
  452.   As an example of encapsulation, consider a typical call by an MS-DOS
  453.   application to write a string to the standard output device (Figure
  454.   16-1). In order to facilitate conversion to OS/2, you would replace every
  455.   instance of such a write to a file or device with a call to a small
  456.   subroutine that "hides" the mechanics of the actual operating-system
  457.   function call, as illustrated in Figure 16-2.
  458.  
  459.   Another candidate for encapsulation, which does not necessarily involve an
  460.   operating-system function call, is the application's code to gain access
  461.   to command-line parameters, environment-block variables, and the name of
  462.   the file it was loaded from. Under MS-DOS, this information is divided
  463.   between the program segment prefix (PSP) and the environment block, as we
  464.   saw in Chapters 3 and 12; under OS/2, there is no such thing as a PSP,
  465.   and the program filename and command-line information are appended to the
  466.   environment block.
  467.  
  468.   ──────────────────────────────────────────────────────────────────────────
  469.   stdin   equ     0               ; standard input handle
  470.   stdout  equ     1               ; standard output handle
  471.   stderr  equ     2               ; standard error handle
  472.  
  473.   msg     db      'This is a sample message'
  474.   msg_len equ     $-msg
  475.  
  476.           .
  477.           .
  478.           .
  479.           mov     dx,seg msg      ; DS:DX = message address
  480.           mov     ds,dx
  481.           mov     dx,offset DGROUP:msg
  482.           mov     cx,msg_len      ; CX = message length
  483.           mov     bx,stdout       ; BX = handle
  484.           mov     ah,40h          ; AH = function 40h write
  485.           int     21h             ; transfer to MS-DOS
  486.           jc      error           ; jump if error
  487.           cmp     ax,msg_len      ; all characters written?
  488.           jne     diskfull        ; no, device is full
  489.           .
  490.           .
  491.           .
  492.   ──────────────────────────────────────────────────────────────────────────
  493.  
  494.   Figure 16-1.  Typical in-line code for an MS-DOS function call. This
  495.   particular sequence writes a string to the standard output device. Since
  496.   the standard output might be redirected to a file without the program's
  497.   knowledge, it must also check that all of the requested characters were
  498.   actually written; if the returned length is less than the requested
  499.   length, this usually indicates that the standard output has been
  500.   redirected to a disk file and that the disk is full.
  501.  
  502.   ──────────────────────────────────────────────────────────────────────────
  503.   stdin   equ     0               ; standard input handle
  504.   stdout  equ     1               ; standard output handle
  505.   stderr  equ     2               ; standard error handle
  506.  
  507.   msg     db      'This is a sample message'
  508.   msg_len equ     $-msg
  509.  
  510.           .
  511.           .
  512.           .
  513.           mov     dx,seg msg      ; DS:DX = message address
  514.           mov     ds,dx
  515.           mov     dx,offset DGROUP:msg
  516.           mov     cx,msg_len      ; CX = message length
  517.           mov     bx,stdout       ; BX = handle
  518.           call    write           ; perform the write
  519.           jc      error           ; jump if error
  520.           cmp     ax,msg_len      ; all characters written?
  521.           jne     diskfull        ; no, device is full
  522.           .
  523.           .
  524.           .
  525.  
  526.   write   proc    near            ; write to file or device
  527.                                   ; Call with:
  528.                                   ; BX = handle
  529.                                   ; CX = length of data
  530.                                   ; DS:DX = address of data
  531.                                   ; returns:
  532.                                   ; if successful, carry clear
  533.                                   ; and AX = bytes written
  534.                                   ; if error, carry set
  535.                                   ; and AX = error code
  536.  
  537.           mov     ah,40h          ; function 40h = write
  538.           int     21h             ; transfer to MS-DOS
  539.           ret                     ; return status in CY and AX
  540.  
  541.   write   endp
  542.  
  543.           .
  544.           .
  545.           .
  546.   ──────────────────────────────────────────────────────────────────────────
  547.  
  548.   Figure 16-2.  Code from Figure 16-1 after "encapsulation." The portion of
  549.   the code that is operating-system dependent has been isolated inside a
  550.   subroutine that is called from other points within the application.
  551.  
  552.   When you have completed the encapsulation of system services and access to
  553.   the PSP and environment, subject your program once more to thorough
  554.   testing under MS-DOS. This is your last chance, while you are still
  555.   working in a familiar milieu and have access to your favorite debugging
  556.   tools, to detect any subtle errors you may have introduced during the
  557.   three conversion steps discussed thus far.
  558.  
  559.   Conversion
  560.  
  561.   Next, you must rewrite each system-dependent procedure you created during
  562.   the encapsulation stage to conform to the OS/2 protected-mode API. In
  563.   contrast to MS-DOS functions, which are actuated through software
  564.   interrupts and pass parameters in registers, OS/2 API functions are
  565.   requested through a far call to a named entry point. Parameters are passed
  566.   on the stack, along with the addresses of variables within the calling
  567.   program's data segment that will receive any results returned by the
  568.   function. The status of an operation is returned in register AX──zero if
  569.   the function succeeded, an error code otherwise. All other registers are
  570.   preserved.
  571.  
  572.   Although it is not my intention here to provide a detailed introduction to
  573.   OS/2 programming, Figure 16-3 illustrates the final form of our previous
  574.   example, after conversion for OS/2. Note especially the addition of the
  575.   extrn statement, the wlen variable, and the simulation of the MS-DOS
  576.   function status. This code may not be elegant, but it serves the purpose
  577.   of limiting the necessary changes to a very small portion of the source
  578.   file. Some OS/2 functions (such as DosOpen) require parameters that have
  579.   no counterpart under MS-DOS; you can usually select reasonable values for
  580.   these extra parameters that will make their existence temporarily
  581.   invisible to the remainder of the application.
  582.  
  583.   ──────────────────────────────────────────────────────────────────────────
  584.   stdin   equ     0               ; standard input handle
  585.   stdout  equ     1               ; standard output handle
  586.   stderr  equ     2               ; standard error handle
  587.  
  588.           extrn   DosWrite:far
  589.  
  590.   msg     db      'This is a sample message'
  591.   msg_len equ     $-msg
  592.  
  593.   wlen    dw      ?               ; receives actual number
  594.                                   ; of bytes written
  595.  
  596.           .
  597.           .
  598.           .
  599.           mov     dx,seg msg      ; DS:DX = message address
  600.           mov     ds,dx
  601.           mov     dx,offset DGROUP:msg
  602.           mov     cx,msg_len      ; CX = message length
  603.           mov     bx,stdout       ; BX = handle
  604.           call    write           ; perform the write
  605.           jc      error           ; jump if error
  606.           cmp     ax,msg_len      ; all characters written?
  607.           jne     diskfull        ; no, device is full
  608.           .
  609.           .
  610.           .
  611.  
  612.   write   proc    near            ; write to file or device
  613.                                   ; call with:
  614.                                   ; BX = handle
  615.                                   ; CX = length of data
  616.                                   ; DS:DX = address of data
  617.                                   ; returns:
  618.                                   ; if successful, carry clear
  619.                                   ; and AX = bytes written
  620.                                   ; if error, carry set
  621.                                   ; and AX = error code
  622.  
  623.           push    bx              ; handle
  624.           push    ds              ; address of data
  625.           push    dx
  626.           push    cx              ; length of data
  627.           push    ds              ; receives length written
  628.           mov     ax,offset DGROUP:wlen
  629.           push    ax
  630.           call    DosWrite        ; transfer to OS/2
  631.           or      ax,ax           ; did write succeed?
  632.           jnz     write1          ; jump, write failed
  633.           mov     ax,wlen         ; no error, OR cleared CY
  634.           ret                     ; and AX := bytes written
  635.  
  636.   write1: stc                     ; write error, return CY set
  637.           ret                     ; and AX = error number
  638.  
  639.   write   endp
  640.  
  641.           .
  642.           .
  643.           .
  644.   ──────────────────────────────────────────────────────────────────────────
  645.  
  646.   Figure 16-3.  Code from Figure 16-2 after "conversion." The MS-DOS
  647.   function call has been replaced with the equivalent OS/2 function call.
  648.   Since the knowledge of the operating system has been hidden inside the
  649.   subroutine by the previous encapsulation step, the surrounding program's
  650.   requests for write operations should run unchanged. Note that the OS/2
  651.   function had to be declared as an external name with the "far" attribute,
  652.   and that a variable named wlen was added to the data segment of the
  653.   application to receive the actual number of bytes written.
  654.  
  655.   Figures 16-4, 16-5, and 16-6 list the OS/2 services that are equivalent
  656.   to selected MS-DOS and ROM BIOS Int 21H, Int 10H, and Int 16H calls.
  657.   MS-DOS functions related to FCBs and PSPs are not included in these tables
  658.   because OS/2 does not support either of these structures. The MS-DOS
  659.   terminate-and-stay-resident functions are also omitted. Because OS/2 is a
  660.   true multitasking system, a process doesn't need to terminate in order to
  661.   stay resident while another process is running.
  662.  
  663.  
  664.   MS-DOS               Description                     OS/2 function
  665.   ──────────────────────────────────────────────────────────────────────────
  666.   Int 21H Function
  667.   0                   Terminate process               DosExit
  668.   1                   Character input with echo       KbdCharIn
  669.   2                   Character output                VioWrtTTY
  670.   3                   Auxiliary input                 DosRead
  671.   4                   Auxiliary output                DosWrite
  672.   5                   Printer output                  DosWrite
  673.   6                   Direct console I/O              KbdCharIn,
  674.                                                        VioWrtTTY
  675.   7                   Unfiltered input without echo   KbdCharIn
  676.   8                   Character input without echo    KbdCharIn
  677.   9                   Display string                  VioWrtTTY
  678.   0AH (10)            Buffered keyboard input         KbdStringIn
  679.   0BH (11)            Check input status              KbdPeek
  680.   0CH (12)            Reset buffer and input          KbdFlushBuffer,
  681.                                                        KbdCharIn
  682.   0DH (13)            Disk reset                      DosBufReset
  683.   0EH (14)            Select disk                     DosSelectDisk
  684.   19H (25)            Get current disk                DosQCurDisk
  685.   1BH (27)            Get default drive data          DosQFSInfo
  686.   1CH (28)            Get drive data                  DosQFSInfo
  687.   2AH (42)            Get date                        DosGetDateTime
  688.   2BH (43)            Set date                        DosSetDateTime
  689.   2CH (44)            Get time                        DosGetDateTime
  690.   2DH (45)            Set time                        DosSetDateTime
  691.   2EH (46)            Set verify flag                 DosSetVerify
  692.   30H (48)            Get MS-DOS version              DosGetVersion
  693.   36H (54)            Get drive allocation            DosQFSInfo
  694.                        information
  695.   38H (56)            Get or set country              DosGetCtryInfo
  696.                        information
  697.   39H (57)            Create directory                DosMkdir
  698.   3AH (58)            Delete directory                DosRmdir
  699.   3BH (59)            Set current directory           DosChdir
  700.   3CH (60)            Create file                     DosOpen
  701.   3DH (61)            Open file                       DosOpen
  702.   3EH (62)            Close file                      DosClose
  703.   3FH (63)            Read file or device             DosRead
  704.   40H (64)            Write file or device            DosWrite
  705.   41H (65)            Delete file                     DosDelete
  706.   42H (66)            Set file pointer                DosChgFilePtr
  707.   43H (67)            Get or set file attributes      DosQFileMode,
  708.                                                        DosSetFileMode
  709.   44H (68)            I/O control (IOCTL)             DosDevIOCtl
  710.   45H (69)            Duplicate handle                DosDupHandle
  711.   46H (70)            Redirect handle                 DosDupHandle
  712.   47H (71)            Get current directory           DosQCurDir
  713.   48H (72)            Allocate memory block           DosAllocSeg
  714.   49H (73)            Release memory block            DosFreeSeg
  715.   4AH (74)            Resize memory block             DosReAllocSeg
  716.   4BH (75)            Execute program                 DosExecPgm
  717.   4CH (76)            Terminate process with          DosExit
  718.                        return code
  719.   4DH (77)            Get return code                 DosCWait
  720.   4EH (78)            Find first file                 DosFindFirst
  721.   4FH (79)            Find next file                  DosFindNext
  722.   54H (84)            Get verify flag                 DosQVerify
  723.   56H (86)            Rename file                     DosMove
  724.   57H (87)            Get or set file date and time   DosQFileInfo,
  725.                                                        DosSetFileInfo
  726.   59H (89)            Get extended error              DosErrClass
  727.                        information
  728.   5BH (91)            Create new file                 DosOpen
  729.   5CH (92)            Lock or unlock file region      DosFileLocks
  730.   65H (101)           Get extended country            DosGetCtryInfo
  731.                        information
  732.   66H (102)           Get or set code page            DosGetCp,
  733.                                                        DosSetCp
  734.   67H (103)           Set handle count                DosSetMaxFH
  735.   68H (104)           Commit file                     DosBufReset
  736.   6CH (108)           Extended open file              DosOpen
  737.   ──────────────────────────────────────────────────────────────────────────
  738.  
  739.  
  740.   Figure 16-4.  Table of selected MS-DOS function calls and their OS/2
  741.   counterparts. Note that OS/2 functions are typically more powerful and
  742.   flexible than the corresponding MS-DOS functions, and that this is not a
  743.   complete list of OS/2 services.
  744.  
  745.   ROM BIOS           Description                         OS/2 function
  746.   ──────────────────────────────────────────────────────────────────────────
  747.   Int 10H Function
  748.   0                 Select display mode                 VioSetMode
  749.   1                 Set cursor type                     VioSetCurType
  750.   2                 Set cursor position                 VioSetCurPos
  751.   3                 Get cursor position                 VioGetCurPos
  752.   6                 Initialize or scroll window up      VioScrollUp
  753.   7                 Initialize or scroll window down    VioScrollDn
  754.   8                 Read character and attribute        VioReadCellStr
  755.   9                 Write character and attribute       VioWrtNCell
  756.   0AH (10)          Write character                     VioWrtNChar
  757.   0EH (14)          Write character in teletype mode    VioWrtTTY
  758.   0FH (15)          Get display mode                    VioGetMode
  759.   10H (16)          Set palette, border color, etc.     VioSetState
  760.   13H (19)          Write string in teletype mode       VioWrtTTY
  761.   ──────────────────────────────────────────────────────────────────────────
  762.  
  763.   Figure 16-5.  Table of ROM BIOS Int 10H video-display driver functions
  764.   used by MS-DOS applications and their OS/2 equivalents. This is not a
  765.   complete list of OS/2 video services.
  766.  
  767.   ROM BIOS           Description                         OS/2 function
  768.   ──────────────────────────────────────────────────────────────────────────
  769.   Int 16H Function
  770.   0                 Read keyboard character             KbdCharIn
  771.   1                 Get keyboard status                 KbdPeek
  772.   2                 Get keyboard flags                  KbdGetStatus
  773.   ──────────────────────────────────────────────────────────────────────────
  774.  
  775.   Figure 16-6.  Table of ROM BIOS Int 16H keyboard driver functions used by
  776.   MS-DOS applications and their OS/2 equivalents. This is not a complete
  777.   list of OS/2 keyboard services.
  778.  
  779.   Optimization
  780.  
  781.   Once your program is running in protected mode, it is time to unravel some
  782.   of the changes made for purposes of conversion and to introduce various
  783.   optimizations. Three obvious categories should be considered:
  784.  
  785.   1.  Modifying the program's user-interface code for the more powerful OS/2
  786.       keyboard and display API functions.
  787.  
  788.   2.  Incorporating 80286-specific machine instructions where appropriate.
  789.  
  790.   3.  Revamping the application to exploit the OS/2 facilities that are
  791.       unique to protected mode. (Of course, the application benefits from
  792.       OS/2's virtual memory capabilities automatically; it can allocate
  793.       memory until physical memory and disk swapping space are exhausted.)
  794.  
  795.   Modifying subroutines that encapsulate user input and output to take
  796.   advantage of the additional functionality available under OS/2 is
  797.   straight-forward, and the resulting performance improvements can be quite
  798.   dramatic. For example, the OS/2 video driver offers a variety of services
  799.   that are far superior to the screen support in MS-DOS and the ROM BIOS,
  800.   including high-speed display of strings and attributes at any screen
  801.   position, "reading back" selected areas of the display into a buffer, and
  802.   scrolling in all four directions.
  803.  
  804.   The 80286-specific machine instructions can be very helpful in reducing
  805.   code size and increasing execution speed. The most useful instructions are
  806.   the shifts and rotates by an immediate count other than one, the
  807.   three-operand multiply where one of the operands is an immediate (literal)
  808.   value, and the push immediate value instruction (particularly handy for
  809.   setting up OS/2 function calls). For example, in Figure 16-3, the
  810.   sequence
  811.  
  812.   mov     ax,offset DGROUP:wlen
  813.   push    ax
  814.  
  815.   could be replaced by the single instruction
  816.  
  817.   push    offset DGROUP:wlen
  818.  
  819.   Restructuring an application to take full advantage of OS/2's
  820.   protected-mode capabilities requires close study of both the application
  821.   and the OS/2 API, but such study can pay off with sizable benefits in
  822.   performance, ease of maintenance, and code sharing. Often, for instance,
  823.   different parts of an application are concerned with I/O devices of vastly
  824.   different speeds, such as the keyboard, disk, and video display. It both
  825.   simplifies and enhances the application to separate these elements into
  826.   subprocesses (called threads in OS/2) that execute asynchronously,
  827.   communicate through shared data structures, and synchronize with each
  828.   other, when necessary, using semaphores.
  829.  
  830.   As another example, when several applications are closely related and
  831.   contain many identical or highly similar procedures, OS/2 allows you to
  832.   centralize those procedures in a dynamic link library. Routines in a
  833.   dynamic link library are bound to a program at its load time (rather than
  834.   by LINK, as in the case of traditional runtime libraries) and are shared
  835.   by all the processes that need them. This reduces the size of each
  836.   application .EXE file and allows more efficient use of memory. Best of
  837.   all, dynamic link libraries drastically simplify code maintenance; the
  838.   routines in the libraries can be debugged or improved at any time, and the
  839.   applications that use them will automatically benefit the next time they
  840.   are executed.
  841.  
  842.  
  843.  
  844.