home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft_Programmers_Library.7z / MPL / msj / msj2.txt < prev    next >
Encoding:
Text File  |  2013-11-08  |  1.4 MB  |  30,322 lines

Text Truncated. Only the first 1MB is shown below. Download the file for the complete contents.
  1.  Microsoft Systems Journal Volume 2
  2.  
  3.  ────────────────────────────────────────────────────────────────────────────
  4.  
  5.  Vol. 2 No. 1 Table of Contents
  6.  
  7.  ────────────────────────────────────────────────────────────────────────────
  8.  
  9.  IRMA: A 3278 Terminal Emulator for Micro-to-Mainframe Communication
  10.  
  11.  The development team at Digital Communications Associates (DCA) took the
  12.  IBM(R) PC and grafted an EBCDIC-speaking synchronous coaxial cable
  13.  system into it. The result, IRMA(R), is a precedent-setting
  14.  PC-to-mainframe link reflecting innovative systems design and
  15.  hardware/software engineering.
  16.  
  17.  
  18.  Upgrading Applications for Multi-user Environments
  19.  
  20.  Software developers who hesitated to write multi-user versions of their
  21.  products have a clearer upgrade path to follow with the appearance of IBM's
  22.  PC Network and software modules such as MS-DOS(R) 3.x, MS-Net, and the
  23.  NetBIOS. We look at rewriting applications for multi-user environments.
  24.  
  25.  
  26.  Expanded Memory: Writing Programs That Break the 640K Barrier
  27.  
  28.  Once, 640K of memory seemed like more than enough, but as programs written
  29.  for DOS grew larger, the 640K limit was just too small. The LIM Expaneded
  30.  Memory Specifications (EMS) defines a standard that allows developers
  31.  to access expanded memory beyond 640K.
  32.  
  33.  
  34.  Keep Track of Your Windows Memory with FREEMEM
  35.  
  36.  Not every Microsoft(R) Windows program is long and complex. This short
  37.  (fewer than 100 lines of code), simple program not only displays the amount
  38.  of available memory in an icon at the bottom of the screen and updates it
  39.  every second, it also reveals a good deal about programming in Windows.
  40.  
  41.  
  42.  A Guide to Debugging with CodeView
  43.  
  44.  The CodeView(TM) debugger included with the Microsoft(R) C Compiler,
  45.  Version 4.00, provides flexible commands and a higher degree of integration
  46.  than previous debuggers. This behind-the-scenes view of CodeView's
  47.  development focuses on the design of the debugger and its use in a
  48.  typical situation.
  49.  
  50.  
  51.  Page Description Languages: High-Level Languages for Printer Independence
  52.  
  53.  Page description languages (PDLs) are becoming popular as a precise
  54.  and formalized means of controlling printer output. We take a look at
  55.  the three major languages that appear to be serious market
  56.  contenders──Postscript(TM), Interpress(TM) and Document Description
  57.  Language (DDL).
  58.  
  59.  
  60.  DIAL 2.0 Provides Software Developers With Integrated Support System
  61.  
  62.  As part of its commitment to upgrade technical support to developers and
  63.  programmers, Microsoft offers a new, improved version of Direct Information
  64.  Access Line (DIAL) service, which includes access to bulletin boards,
  65.  direct technical assistance, and interactive develper forums.
  66.  
  67.  
  68.  Rich Text Format Standard Makes Transferring Text Easier
  69.  
  70.  Microsoft is proposing the Rich Text Format (RTF) as a standard for moving
  71.  formatted text between applications. RTF allows the transfer of text from
  72.  one application to another without losing the formatting and without
  73.  worrying about translating text to each application's unique file format.
  74.  
  75.  
  76.  Ask Dr. Bob
  77.  
  78.  
  79.  Carl's Toolbox
  80.  
  81.  
  82.  EDITOR'S NOTE
  83.  
  84.  We are pleased to present what might reasonably be called the first "real"
  85.  issue of Microsoft Systems Journal. This issue is twice as large as previous
  86.  issues and contains a good deal more of the technical support material that
  87.  we had planned from the beginning. We believe that MSJ is already on its way
  88.  to becoming an important source of information and support for the
  89.  development community, but in order to achieve this goal we need your
  90.  feedback. We hope that you will write us at the address below, or via MCI
  91.  Mail (MSJ), with your comments, questions, criticisms, or complaints.
  92.  
  93.  One of MSJ's prime objectives is to promote excellence and innovation in
  94.  software design and development. Therefore, we are especially proud to
  95.  present a programmer-oriented article on IRMA, the revolutionary board from
  96.  Digital Communications Associates (DCA) that enables an IBM PC to emulate
  97.  IBM 3278 or 3279 terminals, allowing communication between mainframes and
  98.  PCs. IRMA, first released in 1983, took the industry by storm: DCA has sold
  99.  over 350,000 boards. Though considered a hardware product, IRMA's success is
  100.  really due to its supporting software and firmware, representing a triumph
  101.  of engineering and design.
  102.  
  103.  Standardization and communication between different systems is a hot topic
  104.  these days, and many developers are looking for data exchange standards to
  105.  help ease the disparity among different systems. This issue of MSJ has an
  106.  article on the Rich Text Format, a standard proposed by Microsoft to encode
  107.  formatted text. To fully illustrate RTF's utility, we include details of the
  108.  specs used by three page description language (PDL) vendors to create a
  109.  universally formatted text design.
  110.  
  111.  As a quick read of the abstracts on the cover suggests, Microsoft Systems
  112.  Journal wants to explore any development or technology that is of interest
  113.  to the community of professional developers. We plan to cover many diverse
  114.  topics in forthcoming issues. If there are any specific topics that you
  115.  think we should include, please write to us.──Ed.
  116.  
  117.  
  118.  Masthead
  119.  
  120.  JONATHAN D. LAZARUS
  121.  Editor and Publisher
  122.  
  123.  EDITORIAL
  124.  
  125.  BARRY OWEN
  126.  Managing Editor
  127.  
  128.  CHRISTINA G. DYAR
  129.  Associate Editor
  130.  
  131.  GERALD CARNEY
  132.  Staff Editor
  133.  
  134.  DIANA PERKEL
  135.  Editorial Assistant
  136.  
  137.  ART
  138.  
  139.  MICHAEL LONGACRE
  140.  Art Director
  141.  
  142.  VALERIE MYERS
  143.  Associate Art Director
  144.  
  145.  CIRCULATION
  146.  
  147.  WILLIAM B. GRANBERG
  148.  Circulation Manager
  149.  
  150.  L. PERRIN TOMICH
  151.  Assistant to the Publisher
  152.  
  153.  BETSY KAUFER
  154.  Office Manager
  155.  
  156.  Copyright(C) 1987 Microsoft Corporation. All rights reserved; reproduction
  157.  in part or in whole without permission is prohibited.
  158.  
  159.  Microsoft Systems Journal is a publication of Microsoft Corporation, 16011
  160.  NE 36th Way, Box 97017, Redmond, WA 98073-9717. Officers: William H.
  161.  Gates, III, Chairman of the Board and Chief Executive Officer; Jon Shirley,
  162.  President and Chief Operating Officer; Francis J. Gaudette, Treasurer;
  163.  William Neukom, Secretary.
  164.  
  165.  Microsoft Corporation assumes no liabilty for any damages resulting from the
  166.  use of the information contained herein.
  167.  
  168.  Microsoft, the Microsoft logo, MS-DOS and XENIX are registered trademarks
  169.  and CodeView is a trademark of the Microsoft Corporation. IBM is a
  170.  registered trademark of International Business Machines Corporation.
  171.  PageMaker is a registered trademark of Aldus Corporation. PostScript
  172.  is a registered trademark of Adobe Systems, Inc. dBase II is a registered
  173.  trademark of Ashton-Tate. Crosstalk is a registered trademark of Microstuf,
  174.  Inc. WordStar is a registered trademark of MicroPro International. Above is
  175.  a trademark of Intel Corporation. Advantage! is a trademark of AST
  176.  Research, Inc.
  177.  
  178.  ████████████████████████████████████████████████████████████████████████████
  179.  
  180.  IRMA: A 3278 Terminal Emulator for Micro-to-Mainframe Communications
  181.  
  182.  Frank J. Derfler, Jr., and Edward Halbert
  183.  
  184.  Computer products, like plants, evolve in many different ways. Some new
  185.  species of plants mutate from a single root stock, but others are hybridized
  186.  by cross-pollinating different strains. One of the earliest attempts to
  187.  combine an old proven stock with the wild new personal computer resulted in
  188.  a product whose name is now synonymous with micro-to-mainframe
  189.  communications──IRMA.
  190.  
  191.  The developers of the IRMA board took the PC as it was produced by IBM and
  192.  programmed by Microsoft──an ASCII device with only asynchronous
  193.  communications capability──and grafted an EBCDIC-speaking synchronous
  194.  coaxial cable system into it. While designing IRMA, they had to make several
  195.  decisions and solve many technical problems.
  196.  
  197.  
  198.  What IRMA Is
  199.  
  200.  IRMA (the name is not an acronym and was chosen because it is memorable) is
  201.  a circuit board that can be installed in one of the slots in the IBM(R) PC.
  202.  The card has a BNC connector and is attached to the coaxial cable coming
  203.  from a communications controller in front of an IBM mainframe computer.
  204.  IRMA's software allows the PC to emulate IBM 3278 or 3279 terminals and to
  205.  transfer files between a mainframe and a PC.
  206.  
  207.  IRMA is truly integrated into the PC's hardware and operating system. It is
  208.  a standalone coprocessor system hosted by the PC. If you place an IRMA card
  209.  on a table and connect 5 volts and the coaxial cable to it, the IBM 3274
  210.  communications controller talks to it just like any other terminal.
  211.  
  212.  
  213.  IRMA's Lineage
  214.  
  215.  IRMA is now marketed by Digital Communications Associates, Inc. DCA markets
  216.  products for microcomputer communications (the company recently acquired
  217.  Microstuf and its Crosstalk communications package) and large-scale network
  218.  communications. The full DCA product line includes network processors,
  219.  statistical multiplexers, high-speed time-division multiplexers, protocol
  220.  converters, X.25 PADs, integrated software products for network management
  221.  and control, and modems.
  222.  
  223.  DCA acquired IRMA in 1983 as a result of a merger with Technical Analysis
  224.  Corp. (TAC). TAC started engineering design on IRMA in 1982 and released the
  225.  product in 1983.
  226.  
  227.  The man behind IRMA is Andrew Miyakawa. He joined TAC as a programmer in
  228.  1972; in 1978, he became TAC's director of hardware engineering. Miyakawa
  229.  designed the first IRMA printed circuit card. In fact, if you have an old
  230.  IRMA board you can see his signature in the corner, which was later thought
  231.  to be a scratch on the photo negative and removed.
  232.  
  233.  The engineering staff at TAC did a lot of custom work with the IBM coax A
  234.  and coax B interfaces to develop the Agile printer interface for IBM
  235.  systems. The coax A interface uses polling for terminal communications. The
  236.  Agile interface uses a microprocessor to control communications between the
  237.  polled coax system and the dumb printer. There is an obvious parallel
  238.  between this system and the PC, and the engineering team's experience with
  239.  this communications interface was important in the development of IRMA.
  240.  
  241.  The version of the IBM PC that was available in August 1982, when the IRMA
  242.  team started work, was a limited machine with even more limited development
  243.  tools. The PC's eight interrupts and four DMA channels made it difficult to
  244.  enhance the machine──even if most of the interrupts had not already been
  245.  assigned. The PC's character set was, and still is, small, and only the most
  246.  primitive assembler was available. Finally, the 8088 just isnot up to the
  247.  job of responding to the 3270 communications scheme. The coax A polling
  248.  system was designed to interact with terminals using hard-wired logic and
  249.  allows only 5.5 microseconds for a terminal to respond. The interrupt
  250.  latencies on an 8088 don't allow it to recover quickly enough to respond to
  251.  the communications channel.
  252.  
  253.  IRMA's designers forged ahead to make the interface work, almost in spite of
  254.  IBM's product. They decided to add a coprocessor to the PC that is
  255.  considerably faster than the 8088. Having decided upon the design approach,
  256.  the IRMA development team divided the project into four major areas:
  257.  coprocessor hardware, microcode for the hardware, terminal emulation
  258.  software, and the user interface.
  259.  
  260.  The IRMA team spent most of its development time on implementation of the
  261.  interface, rather than on design. TAC received no significant help from IBM
  262.  other than the standard documents available to the general public. IBM's
  263.  Technical Reference manual contained much information about the user
  264.  interfaces, such as the monitor, the keyboard, and memory. This information
  265.  was crucial to the development of the IRMA board.
  266.  
  267.  
  268.  The Hardware
  269.  
  270.  The IRMA board consists of four major components. The Signetics 8X305,
  271.  IRMA's processor, is the large chip on the left side of the board. This
  272.  microprocessor has the power to handle the 3270 protocol and the associated
  273.  polling, data transfer, and handshaking. Even though the chip runs at 4
  274.  MIPS, it can only execute roughly 22 instructions in the 5.5 milliseconds
  275.  allowed for generating a response to the 3274 communications controller.
  276.  
  277.  The IRMA board functions as an intelligent buffer processor and interface
  278.  between the coaxial cable information flow and the IBM PC. Signals from the
  279.  coax travel through the DP8340 and DP8341 3270 Coax Transmitter/Receiver
  280.  Interface, which occurs in the two large chips along the top of the card.
  281.  The serialization and deserialization of data takes place in these two
  282.  chips. The chips also provide the interface between the coaxial cable and
  283.  the microprocessor.
  284.  
  285.  The independence of the IRMA processor in the PC is made possible by the
  286.  Decision Support Interface (DSI). DSI is implemented in the microcode of
  287.  IRMA's processor and support program. DSI allows the 8X305 system to meet
  288.  the requirements of the 3270 protocol by managing streams of buffered data
  289.  and handling all of the timing. The technique that moves the data between
  290.  the processors is called a mailbox structure (see Figure 1).
  291.  
  292.  The mailbox is a 4-byte, dual-ported register array (4 74LS670) located in
  293.  addresses 220H to 223H. The dual port array is the bridge between the 8088
  294.  and the 8X305 processors. Flags are used to detect idle states within the
  295.  DSI structure; an idle state lasts only 5.5 microseconds between messages
  296.  from the controller. When DSI isn't doing anything, it looks to this array
  297.  for any commands that need processing. The command words sent by the IRMA
  298.  program read or write bytes of data in the screen buffer, process
  299.  keystrokes, and call other special features.
  300.  
  301.  Data is transmitted between the two processors through the use of four
  302.  address locations (220H─223H). The processor traffic is controlled by the
  303.  Command Request flag located at a higher address (226H). This flag is
  304.  cleared by either processor when information in the array has been
  305.  successfully read. There is also an Attention Request flag (227H). The
  306.  commands that are passed through these four registers are parameter driven.
  307.  The command is placed in the base address, or word 0, at 220H, and up to
  308.  three arguments can be specified at 221H─223H, depending on the command.
  309.  Before issuing any command on the array, the Command Request flag is set to
  310.  high. The command is then placed in the register(s). After it has been
  311.  picked up by the receiving processor, the request flag is cleared. Address
  312.  locations 224H and 225H are reserved for future use.
  313.  
  314.   There are sixteen commands supported by DSI:
  315.  
  316.    Code          Command Definition
  317.  
  318.      0           Read buffer data
  319.      1           Write buffer data
  320.      2           Read status/cursor position
  321.      3           Clear main status bits
  322.      4           Send keystroke
  323.      5           Light pen transmit
  324.      6           Execute Power-on Reset
  325.      7           Load trigger data and mask
  326.      8           Load trigger address
  327.      9           Load attention mask
  328.     10           Set terminal type
  329.     11           Enable auxiliary relay
  330.     12           Read  terminal   information
  331.     13           No-op
  332.     14           Return revision ID and OEM number
  333.     15           Reserved; Do not use The Video Interface
  334.  
  335.  Even with its standalone power, the IRMA coprocessor board needs the PC as a
  336.  keyboard input and display device. However, the PC lacks many of the
  337.  features and the flexibility found in 3270-series terminals, so the
  338.  designers had to work around these limitations. Special techniques are used
  339.  to move video, characters and colors are substituted on the screen, and
  340.  keyboard emulation alternatives are provided.
  341.  
  342.  The use of an on-board screen buffer is very important for the IRMA/PC video
  343.  interface. Data on the IRMA's video buffer is always active and being
  344.  updated regardless of whether or not the user is looking at it. Only when
  345.  the user switches to the emulation mode is the data moved into the PC's
  346.  video buffer.
  347.  
  348.  The screen buffer is controlled by DSI and occupies a total of 8K of fast
  349.  RAM on the IRMA board. This RAM resides in the four large chips located
  350.  along the bottom edge of the card. The IRMA board supports displays of up to
  351.  132 columns by 43 lines, such as those used by some 3278 models. This
  352.  requirement drove a RAM with a size of 3K for the characters and another 3K
  353.  for the attributes. This is because each character needs 16 bits in memory:
  354.  8 bits for the ASCII code and another 8 bits for the attributes. The
  355.  remaining 2K are used by the 8X305 for local storage.
  356.  
  357.  The attribute characters that are supported by IRMA include protected/
  358.  nonprotected, numeric/alpha/both, light pen detect, tab stops,
  359.  intensified/normal, nondisplay type fields, and modified data tag.
  360.  
  361.  The Extended Attribute Characters control character type (normal, blinking,
  362.  reverse video, underlined), character color, and character set.
  363.  
  364.  Using the 8X305 processor, DSI converts the keystrokes and attribute
  365.  characters into 3270 functions and emulates those same functions on the PC.
  366.  The field attributes are mapped to the colors on the PC, corresponding
  367.  approximately to those on the 3279 terminal. If an IRMA board is in a PC
  368.  with a color display, each color represents a different type of field. The
  369.  attribute characters on the PC translate to the field types in Figure 2.
  370.  
  371.  In order for a field to be unprotected, numeric data only, highlighted, and
  372.  detectable, the program would have to send a 11011000B, D8H to the screen
  373.  buffer. This field attribute continues to be in effect until another field
  374.  type is specified.
  375.  
  376.  IRMA replicates some of the quirks of 3270 functions such as "attribute
  377.  wrap," which occurs when the programmer fails to end a field. The attribute
  378.  for that field not only affects screen locations after it, but also wraps
  379.  around and affects locations before it on the screen. Programmers have to be
  380.  just as careful with IRMA as they are with real 3278/9 terminals.
  381.  
  382.  In the 3278/9 terminals, a specific bit within the attribute byte, known as
  383.  the Modified Data Tag (MDT), is changed whenever a field is modified. If the
  384.  byte had been sent out as D8H and read back as D9H, the program would be
  385.  signaled that the operator has modified the field. The IRMA software must
  386.  interpret the position of the data on the screen of the PC and then convert
  387.  it to the appropriate coordinates for the 3278/9 fields. The Extended
  388.  Attribute Bytes (EAB) are defined in Figure 3.
  389.  
  390.  The IRMA screen-handling software has features designed to allow developers
  391.  to see screen attributes that might not otherwise be displayed. A single
  392.  keystroke shows the attributes of a field. Unprotected fields can be
  393.  temporarily filled to view all of their locations.
  394.  
  395.  Attributes are normally displayed as blanks and take up a byte on the screen
  396.  just as attributes on the 3278/9 terminals do. Addressing for text in the
  397.  IRMA board starts at 50H and increases in increments of 80 for each line up
  398.  to 780H on line 24 during emulation of MOD 2 terminals. Emulation of MOD 3
  399.  terminals requires 32 lines and ends at A00H. Emulation for MOD 4 terminals
  400.  must support 43 lines, and the last line starts at D70H. The storage for the
  401.  status line in the emulation of all models starts at 0H in lower memory, but
  402.  the line is displayed at the bottom of the screen.
  403.  
  404.  
  405.  User Interface
  406.  
  407.  There were some significant problems in the development of the user
  408.  interface for the IRMA board. More than half of the problems involved
  409.  keyboard operation. The major problem was figuring out how to emulate
  410.  special 3270 series terminal keys on the PC's limited keyboard. The PC's
  411.  keyboard has fewer keys to work with, and the PC's BIOS does not allow all
  412.  combinations of keys on the keyboard to generate a scan code. This problem
  413.  was eventually solved by including software that takes control of and
  414.  reprograms the entire keyboard.
  415.  
  416.  The 3270 terminals have 24 PF keys and more than 30 other keys that the PC
  417.  must satisfy. At first, the developers felt it would be best to keep the
  418.  positions of the special function keys to be emulated on the PC as close to
  419.  their positions on a 3278 as possible. Most of the 3270's PF keys were
  420.  originally mapped to the numeric keypad on the right side of the PC's
  421.  keyboard. This was later changed because of conflicts with the use of the
  422.  numeric keypad. The PF keys 1─12 are mapped to Alt-1 through Alt-=, and PF
  423.  keys 13─24 are mapped to Ctrl-1 through Ctrl-=.
  424.  
  425.  Either IRMA's developers had great foresight, or they have set a standard
  426.  for PF keys, because most current PC-to-mainframe applications now map
  427.  PF13-PF24 to the PC's control key sequence. Most of the terminal control
  428.  keys on the left side of the 3278 keyboard have been faithfully duplicated
  429.  on the PC's keyboard for IRMA. In all cases, the normal functions of the
  430.  PC's keyboard are unmodified, and the 3270 emulated functions are reached
  431.  through the Alt and Ctrl keys.
  432.  
  433.  The only keys that were located far from their normal positions on the 3278
  434.  keyboard are the Dup, Field Mark, PA1, and PA2 keys, which are mapped to
  435.  Ctrl-G, Ctrl-H, Ctrl-J, and Ctrl-K, respectively. Other special functions
  436.  not offered by a 3278 terminal are the Show Attribute and Display All
  437.  Unprotected Fields functions. The designers left these in for the benefit of
  438.  users and developers.
  439.  
  440.  Another problem faced by the IRMA design team was screen display emulation.
  441.  The 3278 uses icons for certain actions and messages. These icons cannot be
  442.  recreated with the standard IBM ASCII character set. The designers
  443.  considered drawing these icons with the IBM PC graphic display system, but a
  444.  little experimentation showed that the system would be too slow for good
  445.  emulation. Also, using graphic characters would limit users to graphic
  446.  terminals. The compromise was to use a combination of ASCII characters to
  447.  emulate the special characters available on the IBM terminal. An example of
  448.  some major substitutions are listed in Figure 4.
  449.  
  450.  Through this selection of characters, the designers came up with a
  451.  reasonable facsimile of those used in 3278-type terminals. Screen emulation
  452.  was made easier by the fact that many operators of 3270-series terminals
  453.  recognize characters by where and when they come up as much as by what they
  454.  look like. Thus, almost anything appearing in the same general location on
  455.  the screen at the right time would be interpreted by the operator as the
  456.  same message. The alternative characters developed by the IRMA team have
  457.  become standard and are used by most other manufacturers of terminal
  458.  emulators, including IBM.
  459.  
  460.  Color selection during emulation of the 3279 color terminal was also a
  461.  problem, because the IBM Color Graphics Adapter has a much smaller palette
  462.  than the 3279. Until just recently, the normal protected fields were dark
  463.  blue and hard to read. DCA provided a patch in the setup menu to substitute
  464.  cyan for blue.
  465.  
  466.  
  467.  Converging Limitations
  468.  
  469.  The board was nearly complete when the IRMA team discovered a strange quirk
  470.  in the IBM 3274 controller. The 3274 would not send color or graphic
  471.  characters to the IRMA board. After an extensive investigation, the
  472.  designers discovered that the controller looks for a special color
  473.  convergence test feature found only on the 3279 terminals to determine if
  474.  the terminal at the distant end is really capable of using color. This color
  475.  convergence test allows the user to align the red, blue, and green guns by
  476.  using the cursor keys until they form a single white dot. This dot signifies
  477.  correctly aligned electron guns in the CRT. The presence of data in a
  478.  special convergence test video buffer tells the 3274 that the terminal is
  479.  aligned and ready to accept color graphics. Since the 3274 controller
  480.  couldn't get a color convergence test response from the IRMA board or the
  481.  PC, it would not transmit color graphics.
  482.  
  483.  The design team had to program the IRMA board to fake the controller into
  484.  thinking that it had this test feature. The controller only checks for this
  485.  test at terminal power-up, so the test is faked at the board's
  486.  initialization. The test code is temporarily overlaid in undisplayed video
  487.  buffers.
  488.  
  489.  
  490.  Locked Out
  491.  
  492.  Another problem cropped up in early 3276 controllers. The phase-locked loop
  493.  devices in the 3276 controllers were not consistent, and individual machines
  494.  had significant timing differences during data transmission. At first, IBM
  495.  field technicians consistently blamed problems on improper cable. Since
  496.  changing the length of the cable can adjust the timing of received data,
  497.  this fix often worked. The programming of the IRMA board was modified to
  498.  work around the 3276 problem by addressing the data separator at specific,
  499.  gated times. The bug was fixed on the newer models of the 3276, and
  500.  modifications were developed for older models.
  501.  
  502.  
  503.  User Relations
  504.  
  505.  A major issue in the development of the user interface was user-developed
  506.  software. DCA chose not to copy-protect its software. DCA feels that copy
  507.  protection is just not worth the ill will that it brings.
  508.  
  509.  DCA ships the source code for the terminal emulation and file transfer
  510.  programs with every IRMA board. DCA includes the source code for its
  511.  programs because everyone benefits if someone can improve the code. The user
  512.  manual includes programs written in BASIC so that even the novice programmer
  513.  can see how the board communicates. The BASIC programs were never really
  514.  intended for use in applications because they run very slowly under a BASIC
  515.  interpreter, but many people have tried them anyway.
  516.  
  517.  The design team assumed that sophisticated users would provide their own
  518.  file transfer protocol or use a third-party protocol, but they found that
  519.  they had to supply a more complete system. The file transfer software
  520.  provided for the mainframe link has been updated from the slow FT78X to the
  521.  FTCMS and FTTSO programs. The old FT78X used the VM/CMS Xeditor and just
  522.  copied files byte by byte from disk through a fake keyboard buffer to the
  523.  file on the host. The new program can send large blocks of data and is much
  524.  faster. The new programs are written in PL/1 and require that the PL/1
  525.  transient library installed on the host.
  526.  
  527.  The IRMA package also includes an extensive technical reference manual with
  528.  all the functions and commands documented. Miyakawa speaks for all MS-DOS
  529.  developers when he expresses his wish that someone had defined MS-DOS user
  530.  interfaces or that some of the BIOS responsibility was taken off the
  531.  developer.
  532.  
  533.  DCA has gained much from such large users of the IRMA board as Texaco, Coca
  534.  Cola, and Blue Cross and Blue Shield. Miyakawa feels that user feedback is
  535.  extremely valuable and provides the best information for user interface
  536.  development.
  537.  
  538.  
  539.  IRMA's Future
  540.  
  541.  A new program called IRMAX has been developed to enable the IRMA card to run
  542.  as what IBM calls a Distributive Function Terminal. This allows an IBM PC to
  543.  emulate the 3270 PC, running multiple sessions on a single coax connection.
  544.  This configuration is more desirable because the 3270 PC has had
  545.  compatibility problems running PC software. IRMAX will maintain
  546.  compatibility while allowing the full capabilities of the Distributive
  547.  Function Terminal.
  548.  
  549.  IRMAX functions by removing most of the work from the 3274 controller and
  550.  placing it on the IRMA card. The card carries the SNA, a 3270 display
  551.  support program, and a terminal control program similar to the one in the
  552.  3270 PC.
  553.  
  554.  DCA's commitment to communications will carry it into the LAN market with
  555.  IRMA LAN, which will operate with any manufacturer's version of the network
  556.  basic input/output software (NetBIOS) using the function calls originally
  557.  developed by IBM and Sytek.
  558.  
  559.  IRMA LAN is configured with an IRMA card in a network server, acting as what
  560.  is functionally termed a network gateway. A modified version of the IRMA
  561.  software runs on a PC operating as a network workstation and communicates to
  562.  the IRMA card through the NetBIOS. In an efficient high-speed network, the
  563.  person using the workstation can't tell that the IRMA card is located in a
  564.  remote server.
  565.  
  566.  In some cases (IBM, Sytek, 3COM, and other companies), a "real" NetBIOS lies
  567.  between the networking software and DOS. Other companies, such as Novell and
  568.  Banyan, emulate NetBIOS as part of a complete operating system. In either
  569.  case, the IRMA LAN software relies on NetBIOS to carry data messages to the
  570.  gateway server, where they are addressed to the IRMA card.
  571.  
  572.  In the LAN environment, the IRMA software generates a stream of code called
  573.  a network control block (NCB), which activates NetBIOS and passes data
  574.  through a set of buffers established by NetBIOS and controlled by the NCB.
  575.  The software generates an interrupt 5CH that calls NetBIOS. Other fields of
  576.  the NCB point to a message buffer address and tell NetBIOS where to send the
  577.  information in the buffer. This is a standard implementation of the
  578.  capabilities provided by the NetBIOS communications interface.
  579.  
  580.  
  581.  IRMA Showed How
  582.  
  583.  IRMA is an excellent example of the integration of an advanced
  584.  communications capability into the somewhat limited PC system. The
  585.  development team was innovative and employed sound engineering techniques
  586.  to develop a precedent-setting link between PCs and mainframe computer
  587.  systems.
  588.  
  589.  
  590.  Figure 1:  Mailbox Structure
  591.  
  592.                                                       ┌──COAXIAL─CABLE───
  593.  ┌────────────────────────────────────────────────────│──(TO 327X CONTROLLER)
  594.  │                                           ┌────────┴────┐       │
  595.  │                                           │             │       │
  596.  │                      ╔═══════════╗  ╔═════╧═════╗  ╔════╧══════╗│
  597.  │                      ║  8K RAM   ║  ║   COAX    ║  ║   COAX    ║│
  598.  │╔══════════════╗      ║  BUFFER   ║  ║TRANSMITTER║  ║ RECEIVER  ║│
  599.  │║              ║      ║  MEMORY   ║  ║  DP8340   ║  ║  DP8340   ║│
  600.  │║              ║      ╚══╤═════╤══╝  ╚══╤═════╤══╝  ╚══╤═════╤══╝│
  601.  │║              ║         │     │        │     │        │     │   │
  602.  │║              ╟─────────┘     └────────┘     └────────┘     │   │
  603.  │║MICROPROCESSOR║                                             │   │
  604.  │║    8X305     ║                                             │   │
  605.  │║              ╟───────────────────────┐     ┌───────────────┘   │
  606.  │║              ║                       │     │                   │
  607.  │║              ║                       │     │                   │
  608.  │║              ║                       │     │                   │
  609.  │╚══════════════╝           ╔══════╤════╧══╤══╧═══╤══════╗        │
  610.  │                           ║      │       │      │      ║        │
  611.  │                           ║    DUAL PORTED REGISTER    ║        │
  612.  │                           ║      │       │      │      ║        │
  613.  │                           ╚══════╧════╤══╧══╤═══╧══════╝  IRMA  │
  614.  └───────────────────────────────────────│─────│───────────────────┘
  615.                                        SYSTEM UNIT
  616.                                          │ BUS │
  617.  
  618.  ████████████████████████████████████████████████████████████████████████████
  619.  
  620.  Upgrading Applications For Multi-user Environments
  621.  
  622.  ───────────────────────────────────────────────────────────────────────────
  623.  Also see the related article:
  624.    MS-Net Lock (Function 5CH, Code 00H)
  625.  ───────────────────────────────────────────────────────────────────────────
  626.  
  627.  Robert Cowart
  628.  
  629.  With the appearance of IBM's PC Network and the accompanying software
  630.  modules (MS-DOS 3.x; the Network Program Microsoft-Networks, popularly known
  631.  as MS-Net; and the NetBIOS) as at least semi-stable industry performers,
  632.  software developers who were previously hesitant to release multi-user
  633.  versions of their products now have a clearer upgrade path to follow.
  634.  
  635.  This development raises several questions: What's involved in rewriting
  636.  existing applications for use with MS-DOS 3.x and these new standards, or
  637.  for those many products that adhere to them? Is it worth the effort? How
  638.  long might it take? Do you have to rewrite your entire program? And most
  639.  importantly, if you do end up deciding to go multi-user, how should the
  640.  logistics of implementation be handled?
  641.  
  642.  Obviously, the first point to consider is whether your application stands to
  643.  gain from adaptation to a multi-user environment. There are certainly
  644.  applications that don't lend themselves to multi-user operations. Word
  645.  processors are a case in point; it's hardly advantageous or even advisable
  646.  for more than a single user to be editing a given document. But for such
  647.  applications as database management, accounting systems, manufacturing
  648.  control, or even certain spreadsheet packages, upgrading your product to
  649.  multi-user capability may serve to increase sales and fill a need among
  650.  users, particularly as the market's interest in networking swells. IBM's
  651.  clear commitment to the fully integrated and networked office environment of
  652.  the future reinforces this trend.
  653.  
  654.  
  655.  Single-user Upgrades
  656.  
  657.  In making this decision, you should bear in mind the distinction between
  658.  single-user and multi-user modes of operation on the same PC network.
  659.  Running an existing program in single-user mode allows one or more people to
  660.  load the program into their own computers and run it, thus creating their
  661.  own independent data files. So-called multi-user operation allows users to
  662.  share those same data files. The multi-user program must, in this case,
  663.  offer sufficiently intelligent data management to prevent users from
  664.  accidentally clobbering each other's data.
  665.  
  666.  Most existing single-user applications run without concern for these matters
  667.  if they are in a single-user mode under the current network operating
  668.  systems. More often than not, few, if any, adjustments are necessary to get
  669.  such an application running reasonably well under MS-Net. At the most,
  670.  certain considerations pertaining to file access modes must be taken into
  671.  account to prevent data corruption. This boils down to deciding what
  672.  status──the most frequently used settings are read/write or read-only──each
  673.  file used by a program should be given when a user is running the program on
  674.  a network.
  675.  
  676.  According to Gary Stroud, PC systems programmer for Berkeley, Calif.-based
  677.  Centram Systems West, makers of the TOPS network,
  678.  
  679.  "The first thing to do when adapting a single-user application on a network
  680.  is to set your primary program file and all its overlay files to read-only
  681.  mode. This way, any number of people can run the program, and nobody can
  682.  accidentally erase one of the necessary program files while somebody else is
  683.  in the middle of using them. That can cause a catastrophe. Microsoft Word
  684.  sets its files to read-only automatically, for example. Next, it's
  685.  imperative that, if your application uses temporary scratch files when users
  686.  are creating documents, you ensure that the names for these files are always
  687.  unique. Otherwise MS-DOS will confuse, say, my temporary file and your
  688.  temporary file, and we'd have a mess. Starting with MS-DOS 3.0, there is a
  689.  DOS call for obtaining a unique filename. Also, there is a new DOS file
  690.  create call that will fail if a file of the same name already exists. You
  691.  should use these facilities."
  692.  
  693.  If your current program uses the old-fashioned FCB (file control block)
  694.  method of opening and closing files, you should convert your program to the
  695.  newer (MS-DOS 2.0 or later) calls for file handling. Otherwise, files may
  696.  not be closed properly, which would prevent other people from accessing
  697.  them. This was typical of programs ported over from CP/M or written for MS-
  698.  DOS 1.x. For WordStar(R) and dBASE II(R), for example, the use of FCB was
  699.  the only way to open and close files.
  700.  
  701.  
  702.  Multi-user Upgrades
  703.  
  704.  More-serious philosophical and logistical considerations come into play when
  705.  the developer intends to provide real simultaneous multi-user access to data
  706.  files on the LAN. A deeper understanding of some technical aspects of data
  707.  sharing and real-world LAN usage is necessary. Although MS-DOS 3.1, with its
  708.  extended function calls that are available through Int 21, provides all the
  709.  control necessary for most typical LAN-based data-sharing scenarios, using
  710.  the calls properly and effectively takes some planning and some reading of
  711.  the MS-DOS 3.x Technical Reference manual.
  712.  
  713.  The developer should determine how many users would typically be sharing
  714.  data at one time. How many files would actually be involved? Where might
  715.  bottlenecks crop up in the system? Every command or function of the
  716.  single-user version of the program must be closely scrutinized to determine
  717.  the demands that the multi-user environment will introduce.
  718.  
  719.  To assist in the upward migration process and encourage developers to write
  720.  for MS-Net, Microsoft offers an Independent Software Vendor (ISV) kit for
  721.  $50. The kit includes a variety of published material about MS-Net, along
  722.  with the programmer's reference manual (normally $50) and a selection of
  723.  applications notes.
  724.  
  725.  
  726.  Microrim's R:base(R)
  727.  
  728.  As a case in point, I've investigated the migration of Microrim's R:base
  729.  from single-user PC-DOS 2.x to the multi-user MS-Net environment. (Figure 1
  730.  shows R:base on a multi-user network.) Since Microrim essentially used the
  731.  same network interface module for its System V, the strategies apply to that
  732.  product as well.
  733.  
  734.  As is typical of many database companies, Microrim postponed its multi-user
  735.  implementation until the Microsoft/IBM standard began to take hold and users
  736.  asked for a multi-user, "record-locking" version of R:base. At this point,
  737.  Microrim began the work, which, according to Collin Miller, Microrim's
  738.  director of product development, required about 8 person-months to complete.
  739.  
  740.  The first task was to understand the distinction between true multi-user
  741.  environments and a PC-based LAN, and what impact that would have on product
  742.  design.
  743.  
  744.  "A true multi-user environment employs some central intelligence to control
  745.  traffic flow through a database," says Miller. "The central control, which
  746.  would run in the server, would know which database and record each user was
  747.  using at any given time, on all nodes. On PCs, users are really only sharing
  748.  the same data, not the R:base program itself. R:base is running individually
  749.  in each user's PC."
  750.  
  751.  
  752.  More Intelligence?
  753.  
  754.  Microrim considered writing a central module that would be RAM-resident in
  755.  the server to better arbitrate who was using files and records. "That would
  756.  have given us something more approaching the classical multi-user system,"
  757.  says Miller. "This way we could have established levels of priorities for
  758.  individual user access. We opted not to do that, just on philosophical
  759.  bases."
  760.  
  761.  There were several reasons for this choice. First, writing a multi-user
  762.  system would have added extra levels of complexity for PC users. "PC users
  763.  don't want to get into the degree of complication necessary in really
  764.  sophisticated multi-user databases," says Miller. "We simply wanted
  765.  something that ran as easily as the existing R:base program. Second, this
  766.  meant the single-user version of R:base is essentially identical to the
  767.  multi-user version in both user interface and data file structure, making it
  768.  easier to maintain compatibility for users."
  769.  
  770.  Also, Microrim felt that attempting to write a true multi-user system runs
  771.  counter to the IBM philosophy of PCs──that everybody has his own processor
  772.  and copy of the program running independently. IBM encourages developers to
  773.  keep things as station-independent as possible to prevent unnecessary
  774.  complications.
  775.  
  776.  
  777.  MS-DOS vs. NetBIOS
  778.  
  779.  There are primarily two levels of compatibility to which a network
  780.  application can be written──MS-DOS 3.1 and the NetBIOS (Network Basic Input
  781.  Output System). The DOS level handles most file and record operations
  782.  adequately enough for most purposes and acts on a higher level than the
  783.  NetBIOS, which is the interface between MS-Net/MS-DOS and the particular
  784.  networking card.
  785.  
  786.  "Someone like Microrim has a choice. They can write to DOS level calls, or
  787.  to the lower-level NetBIOS interface," says Dave Melin, product marketing
  788.  manager for networking products at Microsoft. "Both are standards and both
  789.  are popular, but the official recommendation from IBM and Microsoft, as is
  790.  always the case, is to write to the highest possible level in terms of
  791.  programming interfaces. If MS-DOS will do what you need to do, use it. If
  792.  you do need to go below it for some reason such as concurrency or
  793.  proprietary security, then go ahead, but most applications won't require
  794.  it."
  795.  
  796.  It is a common belief that writing to the NetBIOS provides a performance
  797.  advantage. This probably comes from programmers' experience that
  798.  circumventing some other MS-DOS calls by writing directly to hardware does
  799.  increase speed. According to Melin,
  800.  
  801.  "It is true that performance may be improved under the right conditions by
  802.  bypassing the DOS and writing to the NetBIOS directly. By writing to the
  803.  NetBIOS, programmers have more control over network session──related
  804.  parameters. Of course, they must deal with the added complexity of writing
  805.  to a low-level interface. Some people are writing software that doesn't
  806.  require any networking operating system, such as MS-Net, to be present.
  807.  Primarily to minimize memory requirements, they do not want to require the
  808.  presence of all that networking software, such as the redirector. They want
  809.  to provide all the services themselves within their application. In this
  810.  case, they will likely write directly to the NetBIOS."
  811.  
  812.  However, there are hazards involved when writing directly to the NetBIOS,
  813.  particularly because contention management at the NetBIOS level is weak. At
  814.  this level, applications can bump into each other if the NetBIOS is not
  815.  handled very carefully. Considering that this is what DOS is really for, it
  816.  makes sense to employ it whenever possible. "If there is no reason to use
  817.  the NetBIOS, it's not worth it," says Melin. "Besides, the MS-DOS interface
  818.  is what stays stable between revisions, whereas lower-level things have a
  819.  history of changing, jeopardizing your compatibility over time."
  820.  
  821.  In light of these facts, Microrim intentionally worked exclusively on the
  822.  DOS level in implementing the R:base upgrade. Also, it wanted to keep the
  823.  number of extended MS-DOS calls to a minimum. Fewer than five new calls were
  824.  used, according to Miller.
  825.  
  826.  
  827.  Avoiding Corruption
  828.  
  829.  More troublesome to Microrim were concerns over its implementation of file
  830.  and record handling within R:base. Because of the peculiarities of R:base
  831.  files, Microrim faced some unique roadblocks when it was planning this angle
  832.  of its strategy. Unlike dBASE, for example, where database files are
  833.  essentially standalone entities, a series of R:base data files is controlled
  834.  by two more-complex files. The first one (file 1) contains the "schema" or
  835.  database structure, information about the size and layout of each table and
  836.  their relationships to one another in a multitable database. The actual data
  837.  of all related tables is stored in a second, larger file (file 2).
  838.  
  839.  Regulating traffic flow through these two files posed some interesting
  840.  challenges for network implementation on MS-Net. For example, say two people
  841.  attempt to change the structure or information about the number of records
  842.  in a table in the database, that is, modifying file 1. Normally, the
  843.  approach would be to lock the file temporarily for use by the person
  844.  changing it. But this file could bring other users to a screaming halt
  845.  because access to any of the tables in file 2 requires access to file 1
  846.  first. "We wanted to have a bottleneck in file 1, since everyone has to read
  847.  it," says Miller. "We wanted to lock it as little as possible, so we had to
  848.  come up with some other solutions."
  849.  
  850.  Microrim decided that four discrete levels of locking were necessary for the
  851.  amount of flexibility desired and for the type of command a user might be
  852.  issuing.
  853.  
  854.  The first level is the Database lock. It essentially allows a single user to
  855.  have exclusive read/write access to selected files. This lock is necessary
  856.  because it allows for modification of file 1 and packing (eliminating
  857.  unwanted records from) a database. Assurance of single-user access is
  858.  mandatory for these two activities, since the database is often
  859.  significantly reorganized during these periods, and these processes may take
  860.  some time.
  861.  
  862.  The next level is used for a very quick update to the schema (part of
  863.  file 1) and is called a Schema or combination lock. Microrim offers this
  864.  as an alternative to the complete lockout mentioned above. Examples of
  865.  situations in which this lock would be used include defining a new relation,
  866.  joining tables, or doing a database "add" or "subtract," that is, creating
  867.  a new database that is a subset of an existing one. At this level, file 1
  868.  is momentarily locked and then quickly released for use by others. The new
  869.  table(s) being built is/are the only thing(s) locked.
  870.  
  871.  The third level is a Table level lock, allowing for a load or a browse/edit
  872.  on one table in a database. Loading allows a user to make a batch update to
  873.  a table; browse/edit allows a user to edit a screenful of records at one
  874.  time. In either case, other users are allowed read/write access to other
  875.  tables in the database, and may read, but not write to, the locked table.
  876.  
  877.  Finally, Microrim incorporated a Record level lock, which it calls
  878.  concurrency control, for use in managing individual records under multi-user
  879.  table access. (See Figure 2.) Normally, this would be done by using MS-DOS's
  880.  range-locking function.
  881.  
  882.  "MS-DOS 3.1 allows you to lock a record," says Microrim's Collins. "But if
  883.  it's locked, other users have no access to it for any reason, even for
  884.  reading. If you're editing a record in a table and go out to lunch, everyone
  885.  else is locked out of that record. Let's say someone is running a report on
  886.  outstanding orders, while someone else has a record locked in the same order
  887.  table. The report could be inaccurate because one record wasn't accessible.
  888.  
  889.  "You could call this technique collision avoidance, to use networking
  890.  terminology, because it prevents the possibility of collisions. But we
  891.  wanted everyone to always be able to read the database, so we developed a
  892.  collision detection scheme."
  893.  
  894.  Microrim's detection technique operates only if two or more users actually
  895.  do try to alter the same record simultaneously. The scheme keeps track of
  896.  where each user is in the database, allowing access to data records at any
  897.  time. Two people can even be reading or editing the same record at the same
  898.  time. Only for the split second that a user actually writes the change out
  899.  to disk is the record locked. If a change is actually made to the record by
  900.  one user, the second user is then notified that a change has been made since
  901.  his last read. At this point, the second user may want to reread it before
  902.  making his edit.
  903.  
  904.  "Actually, the likelihood of two or more users editing the same record at
  905.  the same time is not that great when you only have five or six users on the
  906.  network," says Miller. "With a small number of users, collision detection is
  907.  fine. But when you try to support a larger number of users, collision
  908.  avoidance would be the better choice, because collision detection begins to
  909.  slow down exponentially with increases in retries."
  910.  
  911.  Microrim's attitude is that collision avoidance comes with a high price tag,
  912.  because it prevents concurrent access to the database records. However,
  913.  Microrim admits that it's a scheme that works well, particularly with a
  914.  large number of users on the network. "By contrast," notes Miller, "our
  915.  [collision detection] scheme has more overhead during a collision. The
  916.  second person has to physically resolve the collision himself, through user
  917.  intervention."
  918.  
  919.  Karl Schulmeisters, software design engineer for systems software at
  920.  Microsoft, says that the exclusive locking procedure Microrim has
  921.  circumnavigated to some degree, is a protective mechanism and intentionally
  922.  designed into MS-DOS to avoid loss of data integrity and to prevent
  923.  incomplete data from being transferred to different users. "There are pros
  924.  and cons to Microrim's solution," Schulmeisters notes. "You really only have
  925.  to lock a record for a brief moment during a read or write of a critical
  926.  area." (See Figure 3.)
  927.  
  928.  
  929.  Problems and Fixes
  930.  
  931.  There are three other more or less generic problems that developers
  932.  have to consider addressing when upgrading their applications. The
  933.  first of these could be called queuing. If a number of users want
  934.  access to a particular resource, whether it is a printer, a file, or
  935.  a modem, some arbitration mechanism may be desirable to handle these
  936.  requests are on a first-come-first-served basis.
  937.  
  938.  Related to the queuing question is the problem of waiting. How do you
  939.  control how long a process waits for an unavailable resource? When
  940.  encountering an error, such as a locked record or file, DOS performs error
  941.  retries only for a short period of time. But say you want to go to lunch and
  942.  let the machine retry a batch update until the resource is available.
  943.  Multi-user R:base allows the user, or a custom program, to retry for as long
  944.  as 4.5 hours, determined by an R:base command, SET WAIT.
  945.  
  946.  Finally, there is the nefarious "deadly embrace." (See Figure 4.) This
  947.  impasse occurs when one user is waiting for resources, such as data tables,
  948.  that are already in exclusive use by the other user. It can result in an
  949.  endless tie-up, in which your program appears to be hung. Microrim solved
  950.  the deadly embrace problem by providing a command, SET LOCK, that the user
  951.  can issue. The command lets a user lock a number of tables in advance of
  952.  beginning a batch process that would involve all those tables. If the tables
  953.  are not all available, the transaction stops there and gives the user a
  954.  message to that effect.
  955.  
  956.  
  957.  Conclusion
  958.  
  959.  There are many points to consider before embarking on any serious rewrite of
  960.  an application for LAN use, not all of which could be discussed here. I have
  961.  tried to bring up the most salient issues. Keep in mind that the planning
  962.  process is the hardest and most critical stage. You may want to begin by
  963.  analyzing how your program works in single-user mode. Then you should
  964.  consider what control mechanisms would be necessary to guarantee lack of
  965.  data corruption and minimize intrusions by users upon each other.
  966.  
  967.  If your application includes a programming language, as R:base does, you
  968.  have to map out each of the commands in the language and analyze what and
  969.  when resources will have to be locked for each command. If your application
  970.  lacks a programming language, things should be that much easier. All this
  971.  planning may well take up the biggest part of your development time, but it
  972.  should pay off in the long run.
  973.  
  974.  For broader system compatibility, try to stick to the MS-DOS 3 function
  975.  calls, unless you are writing a really esoteric application. Finally, the
  976.  MS-DOS 3.1 Technical Reference manual is complete enough to answer most
  977.  questions. According to Microrim's Collins, "We lived and died by the 3.1
  978.  Technical Reference. I don't think we ever had to phone up Microsoft."
  979.  
  980.  
  981.  Figure 1:  This sample network configuration shows several combinations
  982.             typical of networks.
  983.  
  984.  ╔══════════════════ SERVER ╗ ┌────────────────────────────────────────┐
  985.  ║           ┌────────────┐ ║ │  ┌──────────────────────────────┐      │
  986.  ║           │ Database   │ ║ │  │   ╔══════════════════════════│══════│════╗
  987.  ║         ┌─┤ directory  ├───│──┘   ║ ┌────┐ ┌─────────────┐ ┌─┴──┐ ┌─┴──┐ ║
  988.  ║         │ │ c:\dbfiles ├───│──┐   ║ │ a: │ │    c:\      │ │ g: │ │ e: │ ║
  989.  ║         │ │ (g:)       │ ║ │  │   ║ └────┘ │   ┌─┴────┐  │ └────┘ └────┘ ║
  990.  ║         │ └────────────┘ ║ │  │   ║ Local  │ Other    │  │    └──┬──┘    ║
  991.  ║         │ ┌────────────┐ ║ │  │   ║ Floppy │ directory│  │     Remote    ║
  992.  ║         │ │ R:base     │ ║ │  │   ║ Drive  │          │  │     Drives    ║
  993.  ║ ┌─────┐ │ │ System V   │ ║ │  │   ║        │  c:\rbfiles │               ║
  994.  ║ │     │ ├─┤ directory  ├─────┐│   ║        └─────────────┘               ║
  995.  ║ │     │ │ │ c:\rbfiles │ ║ │ ││   ║        Local Hard Drive              ║
  996.  ║ │     │ │ │ (f:)       │ ║ │ ││   ╚═══════════════════════ WORKSTATION 2 ╝
  997.  ║ │ C:\ ├─┤ └────────────┘ ║ │ │└───────────────────────────────────┐
  998.  ║ │     │ │ ┌────────────┐ ║ │ │                                    │
  999.  ║ │     │ │ │ User's     │ ║ │ └─────────────────────────────┐      │
  1000.  ║ │     │ │ │ private    │ ║ │ ┌──────────────────────┐      │      │
  1001.  ║ └─────┘ ├─┤ directory  ├───┘ │    ╔═════════════════│══════│══════│══════╗
  1002.  ║         │ │ c:\user2   │ ║   │    ║ ┌────┐ ┌────┐ ┌─┴──┐ ┌─┴──┐ ┌─┴──┐   ║
  1003.  ║         │ │ (e:)       │ ║   │    ║ │ a: │ │ b: │ │ e: │ │ f: │ │ g: │   ║
  1004.  ║         │ └────────────┘ ║   │    ║ └────┘ └────┘ └────┘ └────┘ └────┘   ║
  1005.  ║         │ ┌────────────┐ ║   │    ║    └──┬──┘       └──────┬─────┘      ║
  1006.  ║         │ │ User's     │ ║   │    ║  Local Floppy         Remote         ║
  1007.  ║         │ │ private    │ ║   │    ║     Drives            Drives         ║
  1008.  ║         └─┤ directory  ├─────┘    ╚═══════════════════════ WORKSTATION 1 ╝
  1009.  ║           │ c:\user1   │ ║
  1010.  ║           └────────────┘ ║
  1011.  ╚══════════════════════════╝
  1012.  
  1013.  
  1014.  Figure 2:  Conflict in Updating. When multiple users are accessing a
  1015.             database, they must take care when updating to avoid conflicts.
  1016.             As shown above, it is not enough just to facilitate multiple
  1017.             access;interlocks must be provided to isolate the entire update
  1018.             process.
  1019.  
  1020.     ┌─────────────┐                                       ┌─────────────┐
  1021.     │   USER 1    │          VALUE IN DATABASE            │    USER 2   │
  1022.     └─────────────┘          ┌───────────────┐            └─────────────┘
  1023.           ┌──────────────────┤               │█
  1024.                Read Value   │     100       │█   Read Value
  1025.   ┌────────────────┐         │               │█─────────────────┐
  1026.   │ Type in change │█        │     100       │█                 │
  1027.   │ (subtract 20)  │█        │               │█                 
  1028.   │  ┌──────────┐  │█        │     100       │█         ┌────────────────┐
  1029.   │  │ Process  │  │█        │  ┌─────────┐  │█         │ Type in change │█
  1030.   │  │ change   │  │█        │  │         │  │█         │ (subtract 10)  │█
  1031.   │  │ (100-20) │  │█───────│  │   80    │  │█         │  ┌──────────┐  │█
  1032.   │  └──────────┘  │█        │  │         │  │█         │  │ Process  │  │█
  1033.   │                │█        │  └─────────┘  │█         │  │ change   │  │█
  1034.   │     Write      │█        │  ┌─────────┐  │█         │  │ (100-10) │  │█
  1035.   │     change     │█        │  │         │  │█         │  └──────────┘  │█
  1036.   └────────────────┘█        │  │   80    │  │█ ───────┤                │█
  1037.     █████████████████        │  │         │  │█         │     Write      │█
  1038.                              │  └─────────┘  │█         │     change     │█
  1039.                              └───────────────┘█         └────────────────┘█
  1040.                                ████████████████           █████████████████
  1041.  
  1042.  
  1043.  Figure 3:  R:base Concurrency Control and Locking
  1044.  
  1045. ╓┌───────────────┌──────────────────┌────────────────────────────────────────╖
  1046.  Command         Control/Lock       Description
  1047.  
  1048.  EDIT form       Concurrency        Allows concurrency control
  1049.  ENTER form                         for other users and items
  1050.                                     unless a table lock is issued.
  1051.  
  1052.  Command         Control/Lock       Description
  1053. 
  1054.  APPEND          Concurrency and    Table lock interrupts
  1055.  CHANGE          Table Lock         concurrency control while
  1056.  DELETE ROWS                        data is modified; table lock
  1057.                                     may also exclude other table
  1058.                                     locks issued after it.
  1059.  
  1060.  EDIT ALL        Table Lock         Table lock set if concurrency
  1061.  ENTER form                         control is not in effect for
  1062.  FROM filespec                      any part of the table. When
  1063.  LOAD                               set, the lock excludes other
  1064.  FORMS                             table locks issued after it.
  1065.  REPORTS
  1066.  SET LOCK ON
  1067.  UNLOAD...
  1068.  FOR tblname
  1069.  VIEW
  1070.  
  1071.  DEFINE          Database Lock      Locks all tables and allows no
  1072.  EXPAND                             other locks to be set while it
  1073.  Command         Control/Lock       Description
  1074. EXPAND                             other locks to be set while it
  1075.  RELOAD                             is in effect. This lock will not
  1076.  REDEFINE                           be set while a table lock is in
  1077.  REMOVE                             effect.
  1078.  REMOVE COLUMNUN
  1079.  UNLOAD
  1080.  
  1081.  BUILD           Table Lock         Used together for some
  1082.  FORMS          Database Lock      operations. While the
  1083.  INTERSECT                          database structure is
  1084.  JOIN                               modified, a database lock is
  1085.  PROJECT                            in effect. After the structure
  1086.  RENAME                             is modified, the database
  1087.  REPORTS                           lock is replaced by a table
  1088.  SUBTRACT                           lock.
  1089.  UNION
  1090.  VIEW
  1091.  
  1092.  
  1093.  Figure 4:  Deadlock can occur when two of more users who have access to some
  1094.             tables wait for access to others. Microrim solved this problem
  1095.             with the SET LOCK command that lets a user lock a number of
  1096.             tables before beginning a batch process.
  1097.  
  1098.                                                      User 2
  1099.                                                      cannot reach
  1100.                               ┌──────────────────┐   CUSTOMERS
  1101.                ┌──────────────┤   CUSTOMERS      │   because user 1
  1102.                │              │   ─────────      │   has locked it.
  1103.                │              │   User 1 locks   │
  1104.                │              │   CUSTOMERS      │  X  ─────────┐
  1105.                │              │   waits for      │               │
  1106.                │              │   TRANSACTIONS   │               │
  1107.                │              └──────────────────┘               │
  1108.                │                                                 │
  1109.                │                                                 │
  1110.                │              ┌──────────────────┐               │
  1111.                │              │   TRANSACTIONS   ├───────────────┘
  1112.                │              │   ────────────   │
  1113.                │              │   User 2 locks   │
  1114.                └────────  X  │   TRANSACTIONS   │
  1115.                               │   waits for      │
  1116.               User 1          │   CUSTOMERS      │
  1117.               cannot reach    └──────────────────┘
  1118.               TRANSACTIONS
  1119.               because user 2
  1120.               has locked it.
  1121.  
  1122.  
  1123.  ───────────────────────────────────────────────────────────────────────────
  1124.  MS-Net Lock (Function 5CH, Code 00H)
  1125.  ───────────────────────────────────────────────────────────────────────────
  1126.  
  1127.  This excerpt opens a file named DATABASE in Deny None mode and locks two
  1128.  portions of it: the first 128 bytes and bytes 1024 through 5119. After some
  1129.  (unspecified) processing, it unlocks the same portions and closes the file.
  1130.  Function 5CH, Code 00H denies all access (read or write) by any other
  1131.  process to the specified region of the file. BX must contain the handle of
  1132.  the file that contains the region to be locked. CX:DX (a 4-byte integer)
  1133.  must contain the offset in the file of the beginning of the region. SI:DI
  1134.  (a 4-byte integer) must contain the length of the region.If another process
  1135.  attempts to use (read or write) a locked region, MS-DOS retries three
  1136.  times; if the retries fail, MS-DOS issues Interrupt 24H for the requesting
  1137.  process. You can change the number of retries with Function 44H, Code 0BH
  1138.  (IOCTL Retry).If a program closes a file that contains a locked region or
  1139.  terminates with an open file that contains a locked region, the result is
  1140.  undefined.Programs should not rely on being denied access to a locked
  1141.  region. A program can determine the status of a region (locked or unlocked)
  1142.  by attempting to lock the region and examining look at the error code.
  1143.  
  1144.    Call
  1145.                AH =  5CH
  1146.                AL =  00H
  1147.                BX    Handle
  1148.                CX:DX Offset of region to be locked
  1149.                SI:DI Length of region to be locked
  1150.  
  1151.    Return
  1152.        Carry set:
  1153.                AX    1 = Invalid function code
  1154.                      6 = Invalid handle
  1155.                     33 = Lock violation
  1156.                     36 = Sharing buffer exceeded
  1157.        Carry not set: No error
  1158.    Macro Definition:
  1159.  
  1160.    OPEN_HANDLE   macro                 path, access
  1161.                  mov                   dx,offset path
  1162.                  mov                   al, access
  1163.                  mov                   ah, 3DH
  1164.                  int                   21H
  1165.                  endm
  1166.    ;
  1167.    WRITE_HANDLE  macro                 handle,buffer,bytes
  1168.                  mov                   bx,handle
  1169.                  mov                   dx,offset buffer
  1170.                  mov                   cx,bytes
  1171.                  mov                   ah,40H
  1172.                  int                   21H
  1173.                  endm
  1174.    ;
  1175.    LOCK          macro                 handle,start,bytes
  1176.                  mov                   bx, handle
  1177.                  mov                   cx, word ptr start+2
  1178.                  mov                   dx, word ptr start
  1179.                  mov                   si, word ptr bytes+2
  1180.                  mov                   di, word ptr bytes
  1181.                  mov                   al, O
  1182.                  mov                   ah, 5CH
  1183.                  int                   21H              endm
  1184.    ;
  1185.    UNLOCK        macro                 handle,start,bytes
  1186.                  mov                   bx,handle
  1187.                  mov                   cx,word ptr start+2
  1188.                  mov                   dx,word ptr start
  1189.                  mov                   si,word ptr bytes+2
  1190.                  mov                   di,word ptr bytes
  1191.                  mov                   al,1
  1192.                  mov                   ah,5CH
  1193.                  int                   21H
  1194.                  endm
  1195.    ;
  1196.    CLOSE_HNDL    macro                 handle
  1197.                  mov                   bx,handle
  1198.                  mov                   ah,3EH
  1199.                  int                   21H
  1200.                  endm
  1201.  
  1202.    stdout        equ                      1
  1203.    ;
  1204.    start1        dd                       0
  1205.    lgthl         dd                     128
  1206.    start2        dd                    1023
  1207.    lgth2         dd                    4096
  1208.    file          db                    "DATABASE", 0
  1209.    op_msg        db                    " opened.",ODH,OAH
  1210.    11_msg        db                    "First 128 bytes locked.",ODH,OAH
  1211.    12_msg        db                    "Bytes 1024-5119 locked.",ODH,OAH
  1212.    u1_msg        db                    "First 128 bytes unlocked.",ODH,OAH
  1213.    u2_msg        db                    "Bytes 1024-5119 unlocked."ODH,OAH
  1214.    c1_msg        db                    "closed.",ODH,OAH
  1215.    handle        dw                    ?
  1216.    ;
  1217.    begin:        open_handle           file,01000010b
  1218.                  jc                    open_error
  1219.                  mov                   handle,ax           ; save handle
  1220.                  write_handle          stdout, file,8
  1221.                  jc                    write error
  1222.                  write_handle          stdout,op_msg,10
  1223.                  jc                    write error
  1224.                  lock                  handle,start1,lgth1
  1225.                  jc                    lock_error
  1226.                  write_handle          stdout,11_msg,25
  1227.                  jc                    write_error
  1228.                  lock                  handle, start2,lgth2
  1229.                  jc                    lock_error
  1230.                  write_handle          stdout,12_msg,25
  1231.                  jc                    write_error
  1232.    ;
  1233.    ;  (Further processing here)
  1234.    ;
  1235.                  unlock                handle,start1,lgth1
  1236.                  jc                    unlock_error
  1237.                  write_handle          stdout,u1_msg,27
  1238.                  jc                    write_error
  1239.                  unlock                handle,start2,lgth2
  1240.                  jc                    unlock_error
  1241.                  write_handle          stdout,u2_msg,27
  1242.                  jc                    write_error
  1243.                  close_handle          handle
  1244.                  jc                    close_error
  1245.                  write_handle          stdout,file,8
  1246.                  jc                    write_error
  1247.                  write_handle          stdout,cl_msg,10
  1248.                  jc                    write_error
  1249.    ;
  1250.    ;  (Further processing here)
  1251.    ;
  1252.    open_error                                             ; routine not shown
  1253.    write error                                            ; routine not shown
  1254.    lock_error                                             ; routine not shown
  1255.    unlock_error                                           ; routine not shown
  1256.    close_error                                            ; routine not shown
  1257.  
  1258.  ████████████████████████████████████████████████████████████████████████████
  1259.  
  1260.  Expanded Memory: Writing Programs That Break the 640K Barrier
  1261.  
  1262.  Marion Hansen, Bill Krueger, and Nick Stuecklen
  1263.  
  1264.  When the size of conventional memory was set at 640K, that seemed like all
  1265.  the memory that anyone with a PC could ever use. But as programs written for
  1266.  DOS grew larger, and the amount of data they could handle increased, what
  1267.  had once seemed inexhaustible pinched like a pair of size 8 shoes on size 10
  1268.  feet. Swapping to disk, or the use of overlays, is a solution, but it often
  1269.  limits performance to unacceptable levels.
  1270.  
  1271.  That's why Lotus Development Corp., Intel Corp., and Microsoft Corp. got
  1272.  together to do something about DOS's 640K memory limit. Together they came
  1273.  up with the Lotus/Intel/Microsoft Expanded Memory Specification (EMS). The
  1274.  programming examples accompanying this article use the EMS and will run
  1275.  under the AST Research Enhanced Expanded Memory Specification (EEMS), a
  1276.  variation of the EMS, as well.
  1277.  
  1278.  Expanded memory is memory beyond DOS's 640K limit. Just as DOS manages
  1279.  conventional memory, the Expanded Memory Manager (EMM) manages expanded
  1280.  memory. The EMM can manage up to 8 megabytes (MB) of expanded memory.
  1281.  Programs that adhere to the EMS can use expanded memory without fear of
  1282.  conflict.
  1283.  
  1284.  Contrary to what you may have heard, you can put code as well as data into
  1285.  expanded memory. Programs can store anything in expanded memory except their
  1286.  stacks, which should reside in conventional memory. While placing the stack
  1287.  in expanded memory is theoretically possible, managing a paged stack is
  1288.  generally very difficult.
  1289.  
  1290.  Expanded memory is implemented in one of two ways. One way is an expanded
  1291.  memory board, where expanded memory physically resides on an add-in board.
  1292.  Intel's Above(TM) Board and AST's Advantage(TM) are examples of expanded
  1293.  memory boards. The other way is a LIMulator, such as the Compaq Deskpro
  1294.  386's CEMM (Compaq Expanded Memory Manager), running on a 386-based system.
  1295.  A LIMulator emulates expanded memory in extended memory (which is memory
  1296.  from 1MB to 16MB) using the 80386 paging hardware.
  1297.  
  1298.  Application programs can't use expanded memory automatically. This article
  1299.  explains how to write programs that take advantage of expanded memory,
  1300.  including programming techniques and examples, and the EMM functions.
  1301.  
  1302.  
  1303.  Expanded Memory
  1304.  
  1305.  In the current DOS environment, code and data can reside in one of three
  1306.  memory locations. Each memory type has advantages and disadvantages.
  1307.  
  1308.  Conventional Memory: Conventional memory is always available, except
  1309.  whatever is used by application programs and resident software, and it's
  1310.  easily accessible. Moving about in conventional memory, whether through code
  1311.  or data, requires very little overhead. Segment register updates (when the
  1312.  software crosses segment boundaries) are the only substantial software
  1313.  overhead. Segment register updates are common to all three types of memory
  1314.  and as such are not a limitation unique to conventional memory. Conventional
  1315.  memory's drawback is its 640K limit. Large application programs, network
  1316.  software, and resident spelling checkers, to name just three types of
  1317.  software a typical user might have, consume prodigious amounts of
  1318.  conventional memory.
  1319.  
  1320.  Disk Memory: There's more than enough room on a disk for any software,
  1321.  but the constant paging in and out of data and code in even the simplest
  1322.  applications creates a great deal of overhead. This makes disk memory
  1323.  undesirable for speed-sensitive applications.
  1324.  
  1325.  DOS is not re-entrant, and you can invoke a terminate-and-stay-resident
  1326.  (TSR) program in the middle of a DOS function. For this reason, TSR programs
  1327.  sometimes have difficulties using DOS for disk I/O.
  1328.  
  1329.  Expanded Memory: Like conventional memory, expanded memory is nearly always
  1330.  available. And with fully populated expanded memory boards, it is sufficient
  1331.  for most applications. Accessing expanded memory requires slightly more
  1332.  overhead than accessing conventional memory but significantly less overhead
  1333.  than accessing disk memory. When an application stays within a single 64K
  1334.  page, expanded memory overhead is comparable to conventional memory
  1335.  overhead.
  1336.  
  1337.  Expanded memory is especially suitable for four types of software: TSR
  1338.  programs, graphics packages, databases, and network software.
  1339.  
  1340.  TSR programs permanently consume the memory they occupy. If a TSR program is
  1341.  large in code or data, it consumes a great deal of conventional memory. A
  1342.  TSR program that is designed to use expanded memory effectively keeps most
  1343.  of its code and data in expanded memory, while maintaining a small kernel in
  1344.  conventional memory for housekeeping chores, such as trapping interrupts,
  1345.  and activating the rest of the TSR program in expanded memory.
  1346.  
  1347.  Drawing and drafting packages frequently have to maintain multiple copies of
  1348.  their graphics bit map. Secondary drawings, double buffers for animations,
  1349.  and additional menus are all stored for later retrieval. Because recall
  1350.  speed is essential, these bit maps must be maintained in memory. Just one
  1351.  monochrome (1 bit per pixel) bit map with 640-by-350 resolution requires
  1352.  nearly 28K of storage. Several such bit map copies can eat up conventional
  1353.  memory, but they are easily accommodated in expanded memory.
  1354.  
  1355.  Database programs sort huge volumes of data, typically much more than
  1356.  conventional memory are able to handle. Expanded memory can be used to
  1357.  store and sort large databases and is much faster than swapping to disk.
  1358.  
  1359.  Network software creates large tables and volumes of resident data.
  1360.  Although network software may be used infrequently──usually just for
  1361.  peripheral sharing and file transfers──it can consume up to 50 percent of
  1362.  available conventional memory. Putting network software in expanded memory
  1363.  frees conventional memory for software that you use more frequently.
  1364.  
  1365.  Using application software efficiently is a trade-off between the
  1366.  convenience of generous amounts of expanded memory and the overhead of
  1367.  paging in 64K blocks of it at a time. You should consider two questions
  1368.  when deciding whether to use expanded or conventional memory for your
  1369.  applications.
  1370.  
  1371.  First, does the code execute a large number of far calls or jumps relative
  1372.  to the time it spends executing other instructions? If it does, put the code
  1373.  in conventional memory. If it doesn't, put the code in expanded memory.
  1374.  
  1375.  Second, does the application's data require segment register initialization
  1376.  each time it is accessed? If it does, use conventional memory. If it doesn't
  1377.  use expanded memory.
  1378.  
  1379.  As a rule of thumb, use expanded memory if both the time spent using data or
  1380.  executing code and the preparation overhead are large.
  1381.  
  1382.  
  1383.  The Page Frame
  1384.  
  1385.  Expanded memory is managed the same way, whether it resides on an add-in
  1386.  board or is emulated in extended memory. The Lotus/Intel/Microsoft EMS
  1387.  defines a 64K segment of memory that resides between 640K and 1MB. This page
  1388.  frame is a window into expanded memory (see Figure 1).
  1389.  
  1390.  Just after the application program starts executing, it allocates a certain
  1391.  number of 16K pages of expanded memory for its own use. Four pages of
  1392.  expanded memory can be mapped into the expanded memory page frame at one
  1393.  time. By mapping pages in and out of the page frame, the program can access
  1394.  any area of the expanded memory that it allocated.
  1395.  
  1396.  The EEMS allows the page frame to reside at any unused memory address
  1397.  between 0K and 1,024K. Theoretically, this allows a page frame length of
  1398.  1MB. Practical considerations, such as DOS and application programs, which
  1399.  use conventional memory, and the BIOS and ROM on add-in boards, which use
  1400.  memory above 640K, restrict the page frame to fewer than the possible 64
  1401.  pages. Generally, in a typical AT system with an EGA, the maximum number of
  1402.  mappable pages that DOS doesn't rely on is six 16K pages.
  1403.  
  1404.  When the EMM software is installed, the user selects where in memory (above
  1405.  640K) the page frame resides. The page frame address is user-selectable, so
  1406.  that if another device uses memory at a particular address, the user can
  1407.  then relocate the page frame.
  1408.  
  1409.  
  1410.  Checking for Memory
  1411.  
  1412.  Before an application program can use expanded memory, it must determine
  1413.  if expanded memory and the EMM are present. There are two methods of
  1414.  determining if the EMM is present: the open-handle technique and the
  1415.  get-interrupt-vector technique.
  1416.  
  1417.  Because the EMM is implemented as a device driver, in the open-handle
  1418.  technique the program issues an open handle command (DOS function 3FH) to
  1419.  determine whether the EMM device driver is present.
  1420.  
  1421.  In the get-interrupt-vector technique, the program issues a get-interrupt-
  1422.  vector command (DOS function 35H) to get the contents of interrupt vector
  1423.  array entry number 67H. The pointer thus obtained accesses information that
  1424.  tells the program whether the EMM is installed. The get-interrupt-vector
  1425.  technique is easier to implement. Most programs can use either technique,
  1426.  but if a program is a device driver or if it interrupts DOS during file
  1427.  system operations, it must use the get-interrupt-vector technique.
  1428.  
  1429.  
  1430.  Residents, Transients
  1431.  
  1432.  Application programs that use expanded memory can be classified as either
  1433.  resident or transient. A transient application program is resident only as
  1434.  long as it executes. When it is finished running, the memory it used is
  1435.  available for other programs. Examples of resident application programs
  1436.  include spreadsheets, word processors, and compilers.
  1437.  
  1438.  A resident application program remains in memory after it executes. Resident
  1439.  application programs are usually invoked by a hardware interrupt, such as a
  1440.  keystroke, or a software interrupt, such as a RAMdisk. Pop-up desktop
  1441.  programs, RAMdisk drives, and print spoolers are examples of resident
  1442.  application programs.
  1443.  
  1444.  Resident programs and transient programs handle expanded memory differently.
  1445.  Resident programs may interrupt transient programs that might be using
  1446.  expanded memory, so resident programs must save and restore the state of the
  1447.  page-mapping registers when they use expanded memory.
  1448.  
  1449.  Transient programs don't interrupt other programs, so they don't need to
  1450.  save and restore state. A resident program typically keeps the EMM handles
  1451.  assigned to it and the expanded memory pages allocated to it by the EMM
  1452.  until the system is rebooted. A transient program, in contrast, should
  1453.  return its handle and pages just before it exits to DOS.
  1454.  
  1455.  
  1456.  EMM Functions
  1457.  
  1458.  The EMM functions, summarized in Figure 2, provide the tools that
  1459.  application programs need to use expanded memory. Functions 1 through 7 are
  1460.  general-purpose functions. Functions 8 and 9 are for interrupt service
  1461.  routines, device drivers, and other memory-resident software. Functions 10
  1462.  and 11 are reserved. Functions 12 through 14 are for utility programs.
  1463.  Finally, Function 15 is for multitasking operating systems, although it can
  1464.  be used for interrupt service routines as easily as Functions 8 and 9.
  1465.  
  1466.  To use expanded memory, programs must perform these steps in the following
  1467.  order:
  1468.  
  1469.    1. Check for the presence of the EMM by using the get-interrupt-vector or
  1470.       open-handle techniques.
  1471.  
  1472.    2. Check whether the EMM's version number is valid (only if the
  1473.       application is EMM version-specific)──Function 7 (Get EMM Version).
  1474.  
  1475.    3. Determine if enough unallocated expanded memory pages exist for the
  1476.       program──Function 3 (Get Unallocated Page Count).
  1477.  
  1478.    4. Save the state of expanded memory hardware (only if it is a resident
  1479.       program)──Function 8 (Save Page Map) or Function 15 (Get/Set Page Map).
  1480.  
  1481.    5. Allocate the number of 16K expanded memory pages needed by the
  1482.       program──Function 4 (Allocate Pages).
  1483.  
  1484.    6. Map the set of expanded memory pages (up to four) into the page
  1485.       frame──Function 5 (Map Handle Page).
  1486.  
  1487.    7. Determine the expanded memory page frame base address──Function 2 (Get
  1488.       Page Frame Address).
  1489.  
  1490.    8. Read/write to the expanded memory segment within the page frame, just
  1491.       as you read or write to conventional memory.
  1492.  
  1493.    9. Deallocate the expanded memory pages when the program is finished using
  1494.       them──Function 6 (Deallocate Pages).
  1495.  
  1496.   10. Restore the state of expanded memory hardware (only if it is a memory-
  1497.       resident program)──Function 9 (Restore Page Map) or Function 15
  1498.       (Get/Set Page Map).
  1499.  
  1500.  Each EMM function's number is passed in register AX. The EMM will return the
  1501.  function's status in the same register.
  1502.  
  1503.  Programs use Int 67 to invoke the EMM. This works like DOS Int 21: preload
  1504.  certain registers and issue an Int 67. All required registers are rigidly
  1505.  specified, and certain conventions exist; for example, the AX register
  1506.  always returns status.
  1507.  
  1508.  
  1509.  Programming
  1510.  
  1511.  The following two examples contain programs that have both code and data in
  1512.  expanded memory. The first example (written in Microsoft C, Version 3.00)
  1513.  illustrates how expanded memory can be used to save and restore data. The
  1514.  main program (see Figure 3) calls a series of subprocedures that allocate
  1515.  one 16K page of expanded memory, save the video RAM area (the user's screen)
  1516.  to expanded memory, clear the screen, and then restore the screen from
  1517.  expanded memory. The program assumes the user has a monochrome display
  1518.  adapter operating in text mode (nongraphics) and video page zero is
  1519.  displayed.
  1520.  
  1521.  The program contains four subprocedures. The detect_emm subprocedure (see
  1522.  Figure 4) determines whether the EMM software is installed. If it is
  1523.  installed, the subprocedure returns to the caller. If the EMM software isn't
  1524.  installed, the subprocedure generates an error message and exits the
  1525.  program.
  1526.  
  1527.  The get_expanded_memory_page subprocedure (see Figure 6) returns a pointer
  1528.  to the expanded memory page and a 16-bit tag or handle associated with that
  1529.  page. The subprocedure uses the EMM to allocate a page of expanded memory.
  1530.  If an unallocated page exists, the procedure allocates it and maps it in and
  1531.  returns the EMM handle that is associated with that page.
  1532.  
  1533.  The check_status subprocedure (see Figure 5) is called after each EMM
  1534.  function to verify that no EMM errors have occurred. The
  1535.  release_expanded_memory_page subprocedure (see Figure 7) releases expanded
  1536.  memory pages by deallocating the handle associated with those pages.
  1537.  
  1538.  The second example illustrates one program loading another program into
  1539.  expanded memory, which is especially applicable for developers of terminate-
  1540.  and-stay-resident (TSR) applications. Both programs are written in Microsoft
  1541.  Macro Assembler, Version 4.0.
  1542.  
  1543.  The first program, expanded_memory_dispatcher_kernel (see Figure 8), loads a
  1544.  set of subprocedures into expanded memory, from where they can be invoked at
  1545.  any time. The set of loaded subprocedures is called a pseudo-overlay. This
  1546.  program loads only one pseudo-overlay and immediately invokes all the
  1547.  subprocedures contained in it. You can easily load as many pseudo-overlays
  1548.  as you want by allocating additional pages in expanded memory, mapping up to
  1549.  four of the newly allocated pages into the page frame, and then loading
  1550.  additional pseudo-overlays.
  1551.  
  1552.  The program has one subprocedure, test_for_EMM (see Figure 9), which
  1553.  determines whether the EMM software is installed and returns the appropriate
  1554.  status.
  1555.  
  1556.  The kernel program loads the program OVERLAY.EXE (see Figure 10) into
  1557.  expanded memory. A pseudo-overlay can't be larger than 64K because of the
  1558.  four-page EMM page frame, so the developer must decompose the program into
  1559.  separate modules that contain code or data no larger than 64K. You can have
  1560.  up to 8MB of expanded memory and, therefore, up to 128 overlays.
  1561.  
  1562.  Although the DOS "load overlay" function (DOS function 4B03H) is used to
  1563.  load the pseudo-overlays, the code and any data loaded remain resident after
  1564.  the load takes place. The subprocedures contained in the pseudo-overlay can
  1565.  be accessed by using the list of pointers returned to the kernel by the
  1566.  initialization code in the pseudo-overlay.
  1567.  
  1568.  The pseudo-overlay program has five subprocedures. If the pseudo-overlay
  1569.  program is invoked from the command line, then the command_line_entry_point
  1570.  subprocedure (see Figure 11) tells the user that this is a pseudo-overlay
  1571.  and thus can't be executed.
  1572.  
  1573.  The initialization subprocedure (see Figure 13) is critical. The kernel
  1574.  calls this subprocedure after the program is loaded. The initialization
  1575.  subprocedure passes back to the kernel the data segment environment, a count
  1576.  of the number of callable subprocedures in the overlay, and a far pointer to
  1577.  each subprocedure.
  1578.  
  1579.  The sum and diff subprocedures are examples of typical applications. The sum
  1580.  subprocedure (see Figure 14) adds the numbers in the AX and DX registers and
  1581.  displays the result, while the diff subprocedure (see Figure 15) subtracts
  1582.  the numbers in the AX and DX registers and displays the result. The
  1583.  display_result procedure (see Figure 16) converts the result into
  1584.  printable ASCII form and then displays it.
  1585.  
  1586.  The pseudo_overlay program places data into expanded memory. The data
  1587.  segment for the pseudo_overlay program is shown in Figure 12. The common
  1588.  data area for both programs is shown in Figure 17.
  1589.  
  1590.  
  1591.  To Get EMS
  1592.  
  1593.  If you're interested in developing application programs that use expanded
  1594.  memory, call Intel for a free copy of the Lotus/Intel/Microsoft Expanded
  1595.  Memory Specification. In the continental United States, but outside Oregon,
  1596.  call (800) 538-3373. In Oregon, Alaska, Hawaii, or outside the United States
  1597.  (except Canada), call (503) 629-7354. In Canada, call (800) 235-0444. For
  1598.  more information on the AST EEMS, contact the AST Product Information Center
  1599.  at (714) 863-1480.
  1600.  
  1601.  
  1602.  Figure 1:  The Lotus/Microsoft EMS defines a 64K segment of memory that
  1603.             resides between 640K and 1MB.
  1604.  
  1605.           16M┌─────────────────────┐
  1606.              │                     │
  1607.              │                     │                       ┌───────────┐
  1608.              │                     │                    ┌──┴─────────┐ │
  1609.              ≈   Extended Memory   ≈                 ┌──┴──────────┐ │ │
  1610.              │                     │             ┌───┴───────────┐ │ │ │
  1611.              │                     │             │    Expanded   │ │ │ │
  1612.            1M├─────────────────────┤            .│    Memory     │ │ │ │
  1613.              │  Reserved by IBM    │         .   │               │ │ │ │
  1614.          896K├─────────────────────┤      .      │               │ │ │ │
  1615.              ├─────────────────────┤   .         │  Available to │ │ │ │
  1616.              │   64K Page Frame    │.            │DOS application│ │ │ │
  1617.              ├─────────────────────┤   .         │    programs   │ │ │ │
  1618.          786K├─────────────────────┤      .      │  adhering to  │ │ │ │
  1619.              │  Reserved by IBM    │         .   │  LIM Expanded │ │ │ │
  1620.          640K├─────────────────────┤            .│     Memory    │ │ ├─┘
  1621.              │    Conventional     │             │ Specification │ ├─┘
  1622.              │       Memory        │             │               ├─┘
  1623.              │                     │             └───────────────┘
  1624.              │    Managed by DOS   │
  1625.            0K└─────────────────────┘
  1626.  
  1627.  
  1628.  Figure 2:  EMM Functions
  1629.  
  1630.  EMM functions provide the tools that application programs need to use
  1631.  expanded memory.
  1632.  
  1633. ╓┌────────┌──────────────┌──────────────────┌────────────────────────────────╖
  1634.  Function  Function       AX                Action
  1635.  Number    Name           Register
  1636.  
  1637.   1        AH: 40         Get Status        Returns a status code to tell you
  1638.                                             whether the EMM is present and
  1639.                                             the hardware/software is working
  1640.  Function  Function       AX                Action
  1641.  Number    Name           Register
  1642.                                            the hardware/software is working
  1643.                                             correctly.
  1644.  
  1645.   2        AH: 41         Get Page Frame    Gives the program the location of
  1646.                           Address           the page frame.
  1647.  
  1648.   3        AH: 42         Get Unallocated   Tells the program the number of
  1649.                           Page Count        unallocated pages and the total
  1650.                                             number of pages in expanded
  1651.                                             memory.
  1652.  
  1653.   4        AH: 43         Allocate Pages    Allocates the number of expanded
  1654.                                             memory pages requested by the
  1655.                                             program; assigns a unique EMM
  1656.                                             handle to the set of pages
  1657.                                             allocated.
  1658.  
  1659.   5        AH: 44         Map Handle Page   Maps the specified logical page
  1660.                                             in expanded memory to the
  1661.  Function  Function       AX                Action
  1662.  Number    Name           Register
  1663.                                            in expanded memory to the
  1664.                                             specified physical page within
  1665.                                             the page frame.
  1666.  
  1667.   6        AH: 45         Deallocate Pages  Deallocates the pages currently
  1668.                                             allocated to an EMM handle.
  1669.  
  1670.   7        AH: 46         Get EMM Version   Returns the version number of the
  1671.                                             EMM software.
  1672.  
  1673.   8        AH: 47         Save Page Map     Saves the contents of the page
  1674.                                             mapping registers of all expanded
  1675.                                             memory boards.
  1676.  
  1677.   9        AH: 48         Restore Page Map  Restores the contents of the page
  1678.                                             mapping registers.
  1679.  
  1680.  10        AH: 49                           Reserved.
  1681.  
  1682.  Function  Function       AX                Action
  1683.  Number    Name           Register
  1684. 
  1685.  11        AH: 4A                           Reserved.
  1686.  
  1687.  12        AH: 4B         Get EMM Handle    Returns the number of active EMM
  1688.                           Count             handles.
  1689.  
  1690.  13        AH: 4C         Get EMM Handle    Returns the number of pages
  1691.                           Pages             allocated to a specific EMM
  1692.                                             handle.
  1693.  
  1694.  14        AH: 4D         Get All EMM       Returns the active EMM handles
  1695.                           Handle Pages      and the number of pages allocated
  1696.                                             to each one.
  1697.  
  1698.  15        AH:4E; AL:00   Get/Set           Saves and restores the mapping
  1699.            AH:4E; AL:01   Page Map          context of the active EMM handle.
  1700.            AH:4E:AL:02
  1701.  
  1702.  
  1703.  Figure 3:  Main Program
  1704.  
  1705.  The main program allocates one 16K page of expanded memory, saves the video
  1706.  RAM area to expanded memory, clears the screen and then restores the screen
  1707.  to expanded memory.
  1708.  
  1709.  #include <dos.h>
  1710.  #include <stdio.h>
  1711.  
  1712.  #define EMM_INT 0x67              /* EMM interrupt number */
  1713.  #define GET_PAGE_FRAME_BASE 0x41  /* EMM func = get page frame base address *
  1714.  #define GET_FREE_COUNT 0x42       /* EMM Func = get unallocated pages count *
  1715.  #define ALLOCATE_PAGES 0x43       /* EMM Func = allocates pages */
  1716.  #define MAP_PAGES 0x44            /* EMM Func = map pages */
  1717.  #define DEALLOCATE_PAGES 0x45     /* EMM Func = deallocate pages */
  1718.  #define GET_INT_VECTOR 0x35       /* DOS func = get interrupt vector */
  1719.  #define DEVICE_NAME_LEN 8         /* Number of chars in device driver name
  1720.                                       field */
  1721.  #define VIDEO_RAM_SIZE 4000       /* Total bytes in video RAM (char/attr) */
  1722.  #define VIDEO_RAM_BASE 0xB0000000 /* Video RAM start address (MDA) */
  1723.  
  1724.  union REGS input_regs, output_regs; /* Regs used for calls to EMM and DOS */
  1725.  struct SREGS segment_regs;
  1726.  unsigned int emm_status;            /* Status returned by EMM */
  1727.  
  1728.  main ()
  1729.  
  1730.  {
  1731.  unsigned int i;
  1732.  long target_time, current_time;
  1733.  char *video_ram_ptr = {VIDEO_RAM_BASE}; /*  Pointer to video RAM  */
  1734.  unsigned int emm_handle;                /*  EMM handle  */
  1735.  char *expanded_memory_ptr;              /*  Pointer to expanded memory */
  1736.  
  1737.  /* Ensure that the Expanded Memory Manager software is installed on the
  1738.     user's system.  */
  1739.  
  1740.     detect_emm();
  1741.  
  1742.  /*  Get a page of expanded memory.  */
  1743.  
  1744.     get_expanded_memory_page (&expanded_memory_ptr, &emm_handle);
  1745.  
  1746.  /*  Copy the current video RAM contents to expanded memory.  */
  1747.  
  1748.     memcpy (expanded_memory_ptr, video_ram_ptr, VIDEO_RAM_SIZE);
  1749.  
  1750.  /*  Clear the screen to nulls.  */
  1751.  
  1752.     memset (video_ram_ptr, '\0', VIDEO_RAM_SIZE);
  1753.  
  1754.  /*  Delay for 1 second so the user can see the blanked screen.  */
  1755.  
  1756.     time (¤t_time);
  1757.     target_time = current_time + 1;
  1758.     while (current_time < target_time)
  1759.      {
  1760.       time (¤t_time);
  1761.      }
  1762.  
  1763.  /*  Restore the video RAM contents from expanded memory.  */
  1764.  memcpy (video_ram_ptr, expanded_memory_ptr, VIDEO_RAM_SIZE);
  1765.  
  1766.  /*  Deallocate the expanded memory page  */
  1767.  
  1768.     release_expanded_memory_page (emm_handle);
  1769.  
  1770.     exit(0);
  1771.  }
  1772.  
  1773.  
  1774.  Figure 4:  Detect_EMM Subprocedure
  1775.  
  1776.  The detect_emm subprocedure determines whether the EMM driver software is
  1777.  installed.
  1778.  
  1779.  detect_emm ()
  1780.  
  1781.  {
  1782.  static char EMM_device_name [DEVICE_NAME_LEN] = {"EMMXXXX0"};
  1783.  char *int_67_device_name_ptr;
  1784.  
  1785.  
  1786.  /*  Determine the address of the routine associated with INT 67 hex.  */
  1787.  
  1788.    input_regs.h.ah = GET_INT_VECTOR;  /*  DOS function  */
  1789.    input_regs.h.al = EMM_INT;         /*  EMM interrupt number  */
  1790.    intdosx (&input_regs, &output_regs, &segment_regs);
  1791.    int_67_device_name_ptr =
  1792.    (segment_regs.es * 65536) + 10;    /*  Create ptr to device name field  */
  1793.  
  1794.  /*  Compare the device name with the known EMM device name.  */
  1795.  
  1796.    if(memcmp(EMM_device_name,int_67_device_name_ptr,DEVICE_NAME_LEN) !=0)
  1797.     {
  1798.       printf ("\x07Abort: EMM device driver not installed\n");
  1799.       exit(0);
  1800.     }
  1801.  }
  1802.  
  1803.  
  1804.  Figure 5:  Check_Status Subprocedure
  1805.  
  1806.  The check_status subprocedure is called after each EMM function to make
  1807.  sure that no EMM errors have occurred.
  1808.  
  1809.  check_status (emm_status)
  1810.  unsigned int emm_status;
  1811.  {
  1812.  static char *emm_error_strings[] = {
  1813.    "no error",
  1814.    "EMM software malfunction",
  1815.    "EMM hardware malfunction",
  1816.    "RESERVED",
  1817.    "Invalid EMM handle",
  1818.    "Invalid EMM function code",
  1819.    "All EMM handles being used",
  1820.    "Save/restore page mapping context error",
  1821.    "Not enough expanded memory pages",
  1822.    "Not enough unallocated pages",
  1823.    "Can not allocate zero pages",
  1824.    "Logical page out of range",
  1825.    "Physical page out of range",
  1826.    "Page mapping hardware state save area full",
  1827.    "Page mapping hardware state save area already has handle",
  1828.    "No handle associated with the page mapping hardware state save area",
  1829.    "Invalid subfunction"
  1830.   };
  1831.  
  1832.  /*  IF EMM error, THEN print error message and EXIT  */
  1833.  
  1834.     if (emm_status != 0)                    /*  IF EMM error...  */
  1835.      {
  1836.        emm_status -= 0x7F;                  /*  Make error code zero-based */
  1837.        printf ("\x07Abort: EMM error = ");  /*  Issue error prefix  */
  1838.        printf ("%s\n", emm_error_strings[emm_status]);
  1839.                                             /*  Issue actual error message */
  1840.        exit(0);                             /*  And then exit to DOS */
  1841.      }
  1842.  }
  1843.  
  1844.  
  1845.  Figure 6:  Get_Expanded_Memory_Page Subprocedure
  1846.  
  1847.  The get_expanded_memory_page subprocedure returns a pointer to the expanded
  1848.  memory page and a 16-bit tag or handle associated with that page.
  1849.  
  1850.  get_expanded_memory_page (expanded_memory_ptr_ptr, emm_handle_ptr)
  1851.  
  1852.  unsigned int *emm_handle_ptr;     /*  16 bit handle returned by EMM  */
  1853.  char *(*expanded_memory_ptr_ptr); /*  Pointer to expanded memory page  */
  1854.  
  1855.  {
  1856.  unsigned int page_frame_base;     /*  Expanded memory page frame base  */
  1857.  unsigned int physical_page = {0}; /*  Physical page number  */
  1858.  
  1859.  /*  Get unallocated pages count.  */
  1860.  
  1861.  input_regs.h.ah = GET_FREE_COUNT;    /*  EMM function  */
  1862.  int86x (EMM_INT, &input_regs, &output_regs, &segment_regs);
  1863.  emm_status = output_regs.h.ah;
  1864.  check_status(emm_status);            /*  Check for errors  */
  1865.  if (output_regs.x.bx < 1)            /*  Check unallocated page count  */
  1866.   {
  1867.    printf ("\x07Abort: insufficient unallocated expanded memory pages\n");
  1868.    exit(0);
  1869.   }
  1870.  
  1871.  /*  Allocate the specified number of pages.  */
  1872.  
  1873.  input_regs.h.ah = ALLOCATE_PAGES;     /*  EMM function  */
  1874.  input_regs.x.bx = 1;                  /*  Number of pages to allocate  */
  1875.  int86x (EMM_INT, &input_regs, &output_regs, &segment_regs);
  1876.  emm_status = output_regs.h.ah;
  1877.  check_status(emm_status);             /*  Check for errors  */
  1878.  *emm_handle_ptr = output_regs.x.dx;   /*  Get EMM handle  */
  1879.  
  1880.  /*  Map the logical page into physical page 0.  */
  1881.  
  1882.     input_regs.h.ah = MAP_PAGES;          /*  EMM function  */
  1883.     input_regs.h.al = 0;                  /*  Logical page number  */
  1884.     input_regs.x.bx = physical_page;      /*  Physical page number  */
  1885.     input_regs.x.dx = *emm_handle_ptr;    /*  EMM handle  */
  1886.     int86x (EMM_INT, &input_regs, &output_regs, &segment_regs);
  1887.     emm_status = output_regs.h.ah;
  1888.     check_status(emm_status);             /*  Check for errors  */
  1889.  
  1890.  /*  Determine the page frame address.   */
  1891.  
  1892.     input_regs.h.ah = GET_PAGE_FRAME_BASE; /*  EMM function  */
  1893.     int86x (EMM_INT, &input_regs, &output_regs, &segment_regs);
  1894.     emm_status = output_regs.h.ah;
  1895.     check_status(emm_status);              /*  Check for errors  */
  1896.     *expanded_memory_ptr_ptr =
  1897.       (output_regs.x.bx * 65536)
  1898.       + (physical_page * 16 * 1024);       /*  Set the expanded memory ptr */
  1899.  }
  1900.  
  1901.  
  1902.  Figure 7:  Release_Expanded_Memory_Page Subprocedure
  1903.  
  1904.  The release_expanded_memory_page subprocedure releases the expanded memory
  1905.  pages by de-allocating the handle associated with those pages.
  1906.  
  1907.  release_expanded_memory_page (emm_handle)
  1908.  
  1909.  unsigned int emm_handle;    /* Handle identifying which page set to
  1910.                                 deallocate */
  1911.  {
  1912.  
  1913.  /*  Release the expanded memory pages by deallocating the handle
  1914.      associated with those pages.  */
  1915.  
  1916.     input_regs.h.ah = DEALLOCATE_PAGES;  /*  EMM function  */
  1917.     input_regs.x.dx = emm_handle;        /*  EMM handle passed in DX  */
  1918.     int86x (EMM_INT, &input_regs, &output_regs, &segment_regs);
  1919.     emm_status = output_regs.h.ah;
  1920.     check_status(emm_status);            /*  Check for errors  */
  1921.  }
  1922.  
  1923.  
  1924.  Figure 8:  Kernel Model
  1925.  
  1926.  The pseudo-overlay is loaded into expanded memory by the kernel.  The
  1927.  kernel then calls the initialization procedure within the pseudo-overlay
  1928.  that returns a data structure to the kernel. The data structure describes
  1929.  the first object that will be located in expanded memory starting at the
  1930.  data and extra segments of the pseudo-overlay, the number of subprocedure
  1931.  entry points in the pseudo-overlay, and a list of far pointers to each of
  1932.  the subprocedures contained in the pseudo-overlay. The developer must
  1933.  establish a convention for the sequence of far pointers and what the
  1934.  procedures they point to do. Other information could be passed in this
  1935.  structure as well, for example, number and types of parameters that are
  1936.  required by the subprocedures in the pseudo-overlay. This example uses a
  1937.  literal to determine the maximum number of far pointers that may be passed.
  1938.  To allocate additional space for a larger number of entries, simply
  1939.  increase the value of max_proc_entries. The example assumes a maximum of 64
  1940.  entries can be returned.
  1941.  
  1942.  CODE    SEGMENT PARA PUBLIC 'CODE'
  1943.  ORG     100h
  1944.  ASSUME  CS:CODE, DS:DATA, ES:NOTHING, SS:STACK
  1945.  
  1946.  max_proc_entries              EQU        64
  1947.  
  1948.  pseudo_over_struct            STRUC
  1949.        proc_data_segment       DW        ?
  1950.        proc_extra_segment      DW        ?
  1951.        proc_entry_count        DW        ?
  1952.        proc_entry_ptr          DD max_proc_entries DUP (?)
  1953.  pseudo_over_struct            ENDS
  1954.  
  1955.  main  PROC  NEAR
  1956.  
  1957.        MOV   AX, DATA                       ; Segment initialization
  1958.        MOV   DS, AX
  1959.  
  1960.  check_for_emm_loaded:
  1961.        CALL  test_for_EMM                   ; Use the "interrupt vector"
  1962.        JE    get_emm_page_frame             ; technique to determine
  1963.        JMP   emm_err_exit                   ; whether EMM is loaded
  1964.  
  1965.  get_emm_page_frame:
  1966.        MOV   AH, 41h                        ; Get the page frame base
  1967.        INT   67h                            ; address from EMM
  1968.        OR    AH, AH
  1969.        JZ    allocate_64K
  1970.        JMP   emm_err_exit
  1971.  
  1972.  allocate_64K:
  1973.        MOV   exp_mem_segment, BX            ; Allocate 4 pages of expanded
  1974.        MOV   AH, 43h                        ; ed memory for this example.
  1975.        MOV   BX, 4                          ; More can be allocated
  1976.        INT   67h                            ; depending on the number of
  1977.        OR    AH, AH                         ; overlays to be loaded.
  1978.        JZ    map_64K                        ; Actually, in this case,
  1979.        JMP   emm_err_exit                   ; only a single page is
  1980.                                             ; required because the example
  1981.                                             ; pseudo-overlay is extremely
  1982.                                             ; small.
  1983.  map_64K:
  1984.        MOV   handle, DX                     ; Map in the first 4 logical
  1985.        MOV   CX, 4                          ; pages at physical pages 0
  1986.  map_pages_loop:                            ; through 3
  1987.        MOV   AH, 44h                        ;    logical page 0 at
  1988.        MOV   BX, CX                         ;       physical page 0
  1989.        DEC   BX                             ;    logical page 1 at
  1990.        MOV   AL, BL                         ;       physical page 1
  1991.        MOV   DX, handle                     ;    logical page 2 at
  1992.        INT   67h                            ;       physical page 2
  1993.        OR    AH, AH                         ;    logical page 3 at
  1994.        LOOPE map_pages_loop                 ;       physical page 3
  1995.        JE    init_load_struct               ; If additional overlays were
  1996.        JMP   emm_err_exit                   ; required, each overlay
  1997.                                             ; would be loaded after
  1998.                                             ; mapping and a new set of
  1999.                                             ; logical pages would be
  2000.                                             ; mapped at the same
  2001.                                             ; physical pages.
  2002.  
  2003.  init_load_struct:
  2004.        MOV   ES, exp_mem_segment            ; Initialize pseudo-overlay
  2005.        MOV   DI, 0                          ; environment and procedure
  2006.        MOV   CX, (SIZE pseudo_over_struct)  ; pointer area. This structure
  2007.        MOV   AL, 0                          ; begins at the page
  2008.        REP   STOSB                          ; frame segment address.
  2009.  
  2010.        MOV   AX, (SIZE pseudo_over_struct)  ; Compute the load address
  2011.        ADD   AX, 000Fh                      ; within expanded memory for
  2012.        AND   AX, 0FFF0h                     ; the overlay. The address is
  2013.        MOV   CX, 4                          ; rounded up to the next
  2014.        SHR   AX, CL                         ; higher paragraph boundary
  2015.        ADD   AX, exp_mem_segment            ; immediately following the
  2016.        MOV   parm_block.load_segment, AX    ; pseudo-overlay environment
  2017.        MOV   parm_block.reloc_factor, AX    ; & procedure pointer
  2018.                                             ; structure. This computation
  2019.                                             ; tion takes into account
  2020.                                             ; the maximum number of
  2021.                                             ; procedure entry points
  2022.                                             ; which the pseudo-overlay
  2023.                                             ; is going to return to
  2024.                                             ; this program.
  2025.  
  2026.        MOV   WORD PTR entry_point[0], 100h  ; Build .COM file entry
  2027.        MOV   WORD PTR entry_point[2], AX    ; point
  2028.  
  2029.        MOV   AH, 4Bh                        ; Load the pseudo-overlay
  2030.        MOV   AL, 03h                        ; using the DOS "load
  2031.        LEA   DX, pseudo_over_name           ; overlay" function
  2032.        PUSH  DS
  2033.        POP   ES
  2034.        LEA   BX, parm_block
  2035.        INT   21h
  2036.        JC    emm_err_exit
  2037.  
  2038.        PUSH  DS                             ; Transfer control to the
  2039.        PUSH  ES                             ; loaded pseudo-overlays
  2040.        CALL  DWORD PTR entry_point          ; initialization code
  2041.        POP   ES
  2042.        POP   DS
  2043.        OR    AH, AH
  2044.        JZ    call_over_procedures
  2045.        JMP   emm_err_exit
  2046.  
  2047.  call_over_procedures:
  2048.        MOV   ES, exp_mem_segment            ; As an example of passing
  2049.        MOV   BX, 0                          ; control to a procedure
  2050.        MOV   DI, 0                          ; existing in expanded
  2051.        MOV   CX, ES:[BX].proc_entry_count   ; memory, each procedure
  2052.        JCXZ  deallocate_exp_memory          ; contained in the overlay will
  2053.                                             ; be called in sequence.
  2054.                                             ; Obviously, a single procedure
  2055.                                             ; could be called just as
  2056.                                             ; easily.
  2057.  
  2058.  pseudo_over_call_loop:
  2059.        PUSH  BX
  2060.        PUSH  CX
  2061.        PUSH  DI
  2062.        PUSH  ES
  2063.        PUSH  DS
  2064.  
  2065.        LDS   AX, ES:[BX+DI].proc_entry_ptr
  2066.        MOV   WORD PTR CS:tp_ent_ptr[0], AX
  2067.        MOV   WORD PTR CS:tp_ent_ptr[2], DS
  2068.  
  2069.        MOV   AX, 123                        ; Pass 2 numbers to
  2070.        MOV   DX, 23                         ; the procedures
  2071.  
  2072.        MOV   DS, ES:[BX].proc_data_segment  ; Set up pseudo-overlays
  2073.        MOV   ES, ES:[BX].proc_extra_segment ; segment environment
  2074.        CALL  DWORD PTR CS:tp_ent_ptr        ; Call each procedure
  2075.  
  2076.        POP   DS
  2077.        POP   ES
  2078.        POP   DI
  2079.        POP   CX
  2080.        POP   BX
  2081.  
  2082.     ADD   DI, 4                             ; Adjust index to the next
  2083.        LOOP  pseudo_over_call_loop          ; procedure (4 bytes long)
  2084.                                             ; pointer & loop till all
  2085.                                             ; have been called
  2086.  
  2087.  deallocate_exp_memory:
  2088.        MOV   AH, 45h                        ; Return the allocated
  2089.        MOV   DX, handle                     ; pages to the expanded
  2090.        INT   67h                            ; memory manager
  2091.        OR    AH, AH
  2092.        JNZ   emm_err_exit
  2093.  
  2094.  exit:
  2095.        MOV   AH, 4Ch                        ; Return a normal exit code
  2096.        MOV   AL, 0
  2097.        INT   21h
  2098.  
  2099.  emm_err_exit:
  2100.        MOV   AL, AH                         ; Display the fact that
  2101.        MOV   AH, 09h                        ; an EMM error occurred
  2102.        LEA   DX, emm_err_msg                ; Go to the normal exit
  2103.        INT   21h
  2104.        JMP   exit
  2105.  
  2106.        tp_ent_ptr              DD        ?  ; CS relative far pointer
  2107.                                             ; used for transfer to the
  2108.  main  ENDP                                 ; procedures in the
  2109.                                             ; pseudo_overlay
  2110.  
  2111.  
  2112.  Figure 9:  Procedure to Test for the Presence of EMM
  2113.  
  2114.  This procedure tests for the presence of the EMM in the system. The carry
  2115.  flag is set if the EMM is present. The carry flag is clear if the EMM is not
  2116.  present.
  2117.  
  2118.  test_for_EMM    PROC NEAR
  2119.  
  2120.         MOV     AX, 3567h              ; Issue "get interrupt vector"
  2121.         INT     21h
  2122.  
  2123.         MOV     DI, 000Ah              ; Use the SEGMENT in ES
  2124.                                        ; returned by DOS, place
  2125.                                        ; the "device name field"
  2126.                                        ; OFFSET in DI.
  2127.  
  2128.         LEA     SI, EMM_device_name    ; Place the OFFSET of the EMM
  2129.                                        ; device name string in SI,
  2130.                                        ; the SEGMENT is already in DS.
  2131.  
  2132.         MOV     CX, 8                  ; Compare the name strings
  2133.         CLD                            ; Return the status of the
  2134.         REPE    CMPSB                  ; compare in the ZERO flag
  2135.         RET
  2136.  
  2137.  test_for_EMM ENDP
  2138.  
  2139.  CODE        ENDS
  2140.  
  2141.  
  2142.  Figure 10:  Pseudo-overlay Module
  2143.  
  2144.  The kernel loads the pseudo-overlay into expanded memory.The kernel calls
  2145.  the initialization procedure within the pseudo-overlay. The initialization
  2146.  procedure returns a data structure to the kernel. The data structure
  2147.  describes the first object that will be located in expanded memory starting
  2148.  at the page frame segment address. It contains the data and extra segments
  2149.  of the pseudo-overlay, the number of subprocedure entry points in the
  2150.  pseudo-overlay, and a list of far pointers to each of the subprocedures
  2151.  contained in the pseudo-overlay.
  2152.  
  2153.  CODE    SEGMENT PARA PUBLIC 'CODE'
  2154.  ASSUME  CS:CODE, DS:DATA
  2155.  ORG     100h
  2156.  
  2157.  actual_proc_entries         EQU         2
  2158.  
  2159.  overlay_entry_struct        STRUC
  2160.          proc_data_segment   DW          ?
  2161.          proc_extra_segment  DW          ?
  2162.          proc_entry_count    DW          ?
  2163.          proc_entry_ptr      DD          actual_proc_entries DUP (?)
  2164.  overlay_entry_struct        ENDS
  2165.  
  2166.  
  2167.  Figure 11:  Procedure to Identify Overlay
  2168.  
  2169.  This procedure merely informs a user that this is the overlay and cannot be
  2170.  executed from the command line.
  2171.  
  2172.  command_line_entry_point        PROC        NEAR
  2173.  
  2174.          MOV     AX, DATA                    ; Set up local data
  2175.          MOV     DS, AX                      ; segment
  2176.  
  2177.          LEA     DX, overlay_err_msg         ; Display overlay error
  2178.          MOV     AH, 09h                     ; message
  2179.          INT     21h
  2180.  
  2181.          MOV     AX, 4C00h                   ; Exit back to DOS
  2182.          INT     21h
  2183.  
  2184.  command_line_entry_point        ENDP
  2185.  
  2186.  
  2187.  Figure 12:  Data Segment for the Pseudo-overlay Module
  2188.  
  2189.  This is the data segment for the pseudo-overlay program.
  2190.  
  2191.  DATA  SEGMENT PARA PUBLIC 'DATA'
  2192.  
  2193.  sum_msg         DB   0Dh, 0Ah, 'Sum of numbers = ', '$'
  2194.  diff_msg        DB   0Dh, 0Ah, 'Difference of numbers = ', '$'
  2195.  overlay_err_msg DB   'Overlay cannot be executed via the command line$'
  2196.  powers_of_ten   DW   10000, 1000, 100, 10, 1
  2197.  
  2198.  DATA  ENDS
  2199.  
  2200.  END   command_line_entry_point
  2201.  
  2202.  
  2203.  Figure 13:  Pseudo-overlay Data Structure Initialization Procedure
  2204.  
  2205.  The initialization subprocedure is called by the kernel after the program is
  2206.  loaded. It passes to the kernel the data segment environment, a count of the
  2207.  number of callable subprocedures in the overlay, and a far pointer to each
  2208.  subprocedure.
  2209.  
  2210.  initialization  PROC    FAR
  2211.  
  2212.  MOV  AX, DATA                                        ; Set up a local
  2213.  MOV  DS, AX                                          ; data segment
  2214.  
  2215.  MOV  AH, 41h                                         ; Get the page
  2216.  INT  67h                                             ; frame segment
  2217.  OR   AH, AH                                          ; address from EMM
  2218.  JNZ  error
  2219.  
  2220.  MOV  ES, BX                                          ; Create pointer
  2221.  MOV  DI, 0                                           ; to the page frame
  2222.                                                       ; segment address
  2223.  
  2224.  MOV  ES:[DI].proc_data_segment, DS                   ; Return local data
  2225.  MOV  ES:[DI].proc_extra_segment, DS                  ; & extra segment
  2226.                                                       ; back to the kernel
  2227.  
  2228.  MOV  WORD PTR ES:[DI].proc_entry_count, 2            ; Return the number
  2229.                                                       ; of callable
  2230.                                                       ; procedures
  2231.  MOV  WORD PTR ES:[DI].proc_entry_ptr[0], OFFSET sum  ; Return
  2232.  MOV  WORD PTR ES:[DI].proc_entry_ptr[2], SEG    sum  ; pointer to each
  2233.  MOV  WORD PTR ES:[DI].proc_entry_ptr[4], OFFSET diff ; local callable
  2234.  MOV  WORD PTR ES:[DI].proc_entry_ptr[6], SEG    diff ; procedure in the
  2235.                                                       ; pseudo-overlay
  2236.                                                       ; back to kernel
  2237.  
  2238.  exit:  MOV   AH, 0                                   ; Set status in AH
  2239.                                                       ; = passed
  2240.  error: RET                                           ; Return status
  2241.                                                       ; in AH
  2242.  
  2243.  
  2244.  Figure 14:  Procedure to Add AX and DX
  2245.  
  2246.  This procedure adds AX and DX and displays the result.
  2247.  
  2248.  sum     PROC    FAR
  2249.          ADD     AX, DX                 ; Add numbers
  2250.          PUSH    AX                     ; Display sum message
  2251.          LEA     DX, sum_msg
  2252.          MOV     AH, 09h
  2253.          INT     21h
  2254.          POP     AX
  2255.          CALL    display_result         ; Display sum
  2256.          RET
  2257.  sum     ENDP
  2258.  
  2259.  
  2260.  Figure 15:  Procedure to Subtract AX and DX
  2261.  
  2262.  This procedure subtracts AX and DX and displays the result.
  2263.  
  2264.  diff    PROC    FAR
  2265.  
  2266.          SUB     AX, DX               ; Subtract numbers
  2267.          PUSH    AX                   ; Display difference message
  2268.          LEA     DX, diff_msg
  2269.          MOV     AH, 09h
  2270.          INT     21h
  2271.          POP     AX
  2272.          CALL    display_result       ; Display difference
  2273.          RET
  2274.  
  2275.  diff    ENDP
  2276.  
  2277.  
  2278.  Figure 16:  Procedure to Display Number in AX in Decimal
  2279.  
  2280.  This procedure displays the number in AX in decimal
  2281.  
  2282.  display_result  PROC    NEAR
  2283.  
  2284.          LEA     DI, powers_of_ten
  2285.          MOV     CX, 5
  2286.  display_loop:
  2287.          XOR     DX, DX            ; Divide the number passed
  2288.          DIV     WORD PTR [DI]     ; in AX by descending powers of ten
  2289.          ADD     AL, '0'           ; Convert digit to ASCII
  2290.  
  2291.          PUSH    DX                ; Output the digit
  2292.          MOV     DL, AL
  2293.          MOV     AH, 02h
  2294.          INT     21h
  2295.          POP     AX
  2296.  
  2297.          ADD     DI, 2
  2298.          LOOP    display_loop
  2299.          RET
  2300.  
  2301.  display_result        ENDP
  2302.  
  2303.  
  2304.  Figure 17:  Data and Stack Segment for the Kernel and the Pseudo-overlay
  2305.  
  2306.  This is the common data area for the kernel and psuedo-overlay programs.
  2307.  
  2308.  DATA            SEGMENT PARA PUBLIC 'DATA'
  2309.  
  2310.  emm_err_msg        DB    'EMM error occurred$' ; EMM diagnostic message
  2311.  pseudo_over_name   DB    'OVERLAY.EXE', 0      ; Name of pseudo-overlay
  2312.  EMM_device_name    DB    'EMMXXXX0'            ; Standard EMM device name
  2313.  exp_mem_segment    DW    ?                     ; Temp for expanded
  2314.                                                 ; memory page frame
  2315.                                                 ; segment address
  2316.  handle             DW    ?                     ; Temp for handle allocated
  2317.                                                 ; to the kernel
  2318.  entry_point  DD          ?                     ; Far pointer to the
  2319.                                                 ; entry point for a .COM
  2320.                                                 ; file
  2321.  parm_block_struct  STRUC                       ; Structure definition
  2322.      load_segment   DW    ?                     ; for a "load overlay"
  2323.      reloc_factor   DW    ?                     ; parameter block
  2324.  parm_block_struct  ENDS
  2325.  parm_block         parm_block_struct <>        ; The actual parameter
  2326.                                                 ; block
  2327.  
  2328.  DATA        ENDS
  2329.  
  2330.  
  2331.  STACK   SEGMENT PARA STACK 'STACK'
  2332.          local_stack     DW 256 DUP ('^^')
  2333.  STACK   ENDS
  2334.  
  2335.  END        main
  2336.  
  2337.  ████████████████████████████████████████████████████████████████████████████
  2338.  
  2339.  Keep Track of Your Windows Memory With FREEMEM
  2340.  
  2341.  Charles Petzold
  2342.  
  2343.  For the beginning Microsoft(R) Windows programmer, even simple, do-nothing
  2344.  Windows programs seem to be forbiddingly long and complex. You may have
  2345.  concluded that all Windows applications are monstrous collections of code.
  2346.  
  2347.  This is not true. Take the program called FREEMEM, for example, a complete
  2348.  and useful Microsoft Windows application in fewer than 100 lines of C code.
  2349.  The FREEMEM program displays the amount of available free memory in an icon
  2350.  at the bottom of the Windows screen. The free memory value is updated every
  2351.  second and is consistent with the value that is shown in the MS-DOS
  2352.  Executive "About" box.
  2353.  
  2354.  The short length of FREEMEM helps to clarify the structure of Windows
  2355.  applications and allows us to discuss extensively some details of Windows
  2356.  programming. And since FREEMEM is certainly an unusual Windows program,
  2357.  we'll explore a few of its tricks as well.
  2358.  
  2359.  
  2360.  Overall Structure
  2361.  
  2362.  FREEMEM.C contains only two functions: WinMain (see Figure 1) and WndProc
  2363.  (see Figure 2). Similar functions are found in most Windows applications.
  2364.  
  2365.  WinMain is the entry point to FREEMEM. WinMain is devoted mainly to
  2366.  performing all of the preliminary initialization chores needed to create and
  2367.  display a window.
  2368.  
  2369.  The WndClass structure passed to the RegisterClass function defines the
  2370.  window class. The most important item in the WndClass structure is
  2371.  lpfnWndProc, which specifies the function within FREEMEM that processes
  2372.  messages from Microsoft Windows. This is the WndProc function shown in
  2373.  Figure 2.
  2374.  
  2375.  The CreateWindow, ShowWindow, and UpdateWindow functions all cause Windows
  2376.  to generate messages to the WndProc function. Within the WndProc function,
  2377.  these messages are identified by names beginning with the letters WM. The
  2378.  names are simply macro identifiers defined in WINDOWS.H that conveniently
  2379.  hide the actual numbered codes.
  2380.  
  2381.  WndProc sorts out the messages through a case statement. Only a few of the
  2382.  many messages that Windows sends to WndProc are handled directly. The rest
  2383.  are sent on to the DefWindowProc function within Windows for default
  2384.  processing.
  2385.  
  2386.  CreateWindow will cause Microsoft Windows to generate a WM_CREATE message.
  2387.  ShowWindow usually generates a whole sequence of messages, among them
  2388.  WM_SIZE and WM_ERASEBKGND. These messages cause the window to be displayed
  2389.  on the screen and the background of the client area that is to be erased.
  2390.  When FREEMEM calls the function UpdateWindow, Microsoft Windows generates a
  2391.  WM_PAINT message, which instructs WndProc to paint the client area of the
  2392.  window.
  2393.  
  2394.  Following the UpdateWindow call, FREEMEM enters a message loop. The
  2395.  GetMessage call retrieves a message from FREEMEM's message queue. If no
  2396.  messages to FREEMEM are available, control can pass to another Windows
  2397.  application. Currently, Windows' nonpreemptive multitasking guarantees that
  2398.  a call to GetMessage is the only time that FREEMEM, in effect, stops
  2399.  running. It regains control when FREEMEM's message queue contains some
  2400.  messages and other applications' message queues are empty. It's really a
  2401.  little more complex than this, but I'll explain it later.
  2402.  
  2403.  TranslateMessage translates keystroke messages into character code messages.
  2404.  Although FREEMEM doesn't care about the keyboard, this translation is
  2405.  necessary to provide a keyboard interface with FREEMEM's system menu.
  2406.  DispatchMessage then sends the message to the WndProc procedure.
  2407.  
  2408.  FREEMEM will terminate when GetMessage retrieves a WM_QUIT message from the
  2409.  queue. The GetMessage function returns a zero in this case, and FREEMEM
  2410.  drops out of the message loop.
  2411.  
  2412.  So far, this routine is normal for most Windows applications, but let's take
  2413.  a look at the details.
  2414.  
  2415.  
  2416.  Creative Use of Icons
  2417.  
  2418.  Unlike most Windows programs, FREEMEM is intended to be displayed only as an
  2419.  icon. Once FREEMEM is running, you can use the keyboard or mouse to open the
  2420.  icon into a regular tiled window, but it won't give you any more information
  2421.  in that form.
  2422.  
  2423.  Most Windows applications have static pictorial icons. The programmer
  2424.  usually creates these icons with the ICONEDIT utility supplied with the
  2425.  Microsoft Windows Software Development Kit. The icon file is referenced by a
  2426.  name in the resource script file. When the window class structure is
  2427.  constructed, the icon is specified by the statement
  2428.  
  2429.    WndClass.hIcon = LoadIcon (hInstance, (LPSTR)szAppName) ;
  2430.  
  2431.  The LoadIcon function loads the icon from the section of the .EXE file where
  2432.  all the resources are stored and assigns a handle to it. (I'm assuming here
  2433.  that the icon has the same name as the application and that szAppName is a
  2434.  pointer to a character string with that name.)
  2435.  
  2436.  If instead you use the following statement:
  2437.  
  2438.    WndClass.hIcon = NULL ;
  2439.  
  2440.  then the application is responsible for drawing the icon. This lets your
  2441.  application create a dynamic icon that you can alter. The CLOCK application
  2442.  included with Microsoft Windows uses this same technique to display the
  2443.  current time even when the window is an icon.
  2444.  
  2445.  From the program's perspective, a NULL icon is just a tiny window that you
  2446.  can draw on in the same way that you draw on the client area of a normal
  2447.  window. If you need to know when your application is becoming an icon, you
  2448.  can find out the information from the WM_SIZE message. For instance, CLOCK
  2449.  takes note of this change so it can eliminate the second hand and update the
  2450.  clock every minute in the iconic form.
  2451.  
  2452.  
  2453.  Forcing the Icon
  2454.  
  2455.  Normally, to execute a Windows application from the MS-DOS Executive, you
  2456.  either press the Enter key while the cursor is on the program name, double-
  2457.  click the program name with the mouse, or select File Run from the menu. If
  2458.  you want to load a program as an icon instead, you just press Shift-Enter
  2459.  when the cursor is on the program name or select File Load from the menu.
  2460.  However, with FREEMEM, it doesn't matter what you do──it always loads as an
  2461.  icon.
  2462.  
  2463.  Most Windows applications execute the function
  2464.  
  2465.    ShowWindow (hWnd, nCmdShow) ;
  2466.  
  2467.  shortly before entering the message loop. The nCmdShow variable is passed to
  2468.  the program as a parameter to WinMain. If you RUN a program from the MS-DOS
  2469.  Executive, nCmdShow is set as equal to the handle of the window that the
  2470.  application is replacing on the display, which is usually the handle of the
  2471.  MS-DOS Executive main window. If you LOAD an application as an icon,
  2472.  nCmdShow is set equal to SHOW_ICONWINDOW. Your application usually doesn't
  2473.  have to figure this out; it simply passes this parameter on to ShowWindow.
  2474.  
  2475.  However, you don't have to use the nCmdShow variable with ShowWindow. In
  2476.  FREEMEM, we use this line instead:
  2477.  
  2478.    ShowWindow (hWnd, SHOW_ICONWINDOW) ;
  2479.  
  2480.  This forces the window to appear as an icon regardless of the value of
  2481.  nCmdShow.
  2482.  
  2483.  You can achieve other results with this technique. If you have an
  2484.  application that you always want to appear first as a "zoomed" full-screen
  2485.  display, you can use
  2486.  
  2487.    ShowWindow (hWnd, SHOW_FULLSCREEN) ;
  2488.  
  2489.  You can even force FREEMEM to occupy a particular icon position at the
  2490.  bottom of the display. If you replace the existing ShowWindow call in
  2491.  FREEMEM with
  2492.  
  2493.    ShowWindow (hWnd, (int) 0xFF8F) ;
  2494.  
  2495.  the icon will be positioned in icon slot 15, all the way over at the right
  2496.  of the display. This syntax is somewhat obscure, but it is documented in the
  2497.  Programmer's Reference manual included with the Software Development Kit.
  2498.  
  2499.  
  2500.  Keeping It an Icon
  2501.  
  2502.  As I mentioned before, you can easily use the keyboard or mouse to open up
  2503.  FREEMEM into a regular tiled window. If you'd like to prevent that and
  2504.  ensure that FREEMEM is always displayed as an icon, just add these two lines
  2505.  to the WndProc function:
  2506.  
  2507.    case WM_QUERYOPEN:
  2508.       break ;
  2509.  
  2510.  A reasonable place for these lines would be right above the line that reads
  2511.  "case WM_DESTROY." Now when you try to use the mouse to open the icon, the
  2512.  icon jumps back to the bottom of the display.
  2513.  
  2514.  These two lines may not seem to be doing very much, but a closer look will
  2515.  reveal what they're up to.
  2516.  
  2517.  Microsoft Windows will send a WM_QUERYOPEN message to a program when it
  2518.  wants to open an icon into a window. The documentation for the WM_QUERYOPEN
  2519.  message states that a windows procedure such as WndProc must return zero to
  2520.  prevent the icon from being opened. Under normal circumstances, the
  2521.  WM_QUERYOPEN message is passed on to the DefWindowProc function, which
  2522.  returns a nonzero value. (The DefWindowProc routine is provided with the
  2523.  Windows Software Development Kit in the file WINDWP.C.) Windows then opens
  2524.  the icon.
  2525.  
  2526.  With the two lines shown above, however, WndProc will return a value of zero
  2527.  for a WM_QUERYOPEN message. So, when Windows asks, "Do you want to be
  2528.  opened?", WndProc answers, "Zero," which in this case means "No thanks."
  2529.  
  2530.  
  2531.  The Timer Messages
  2532.  
  2533.  FREEMEM continues to update its display of free memory even while other
  2534.  applications are running. It performs this through the use of the Microsoft
  2535.  Windows timer, which sends WM_TIMER messages to the window procedure. In
  2536.  this respect, FREEMEM is similar to the CLOCK application. The timer permits
  2537.  a form of multitasking without requiring the application to hog precious
  2538.  processing time.
  2539.  
  2540.  In its WinMain function, FREEMEM requests a timer from Windows with the
  2541.  following statement:
  2542.  
  2543.    if (!SetTimer (hWnd, 1, 1000, NULL))
  2544.       return FALSE ;
  2545.  
  2546.  The third parameter to SetTimer indicates that FREEMEM wants a WM_TIMER
  2547.  message every 1,000 milliseconds, or once a second.
  2548.  
  2549.  If SetTimer returns zero, it means that Windows cannot assign a timer to the
  2550.  program. If you've ever tried to determine how many CLOCK applications can
  2551.  be loaded and running in Windows at the same time, you know that the limit
  2552.  is 15. Any additional SetTimer calls will return a zero. Actually, there is
  2553.  a way to get more than 15 timers out of Windows, but it's more complex.
  2554.  
  2555.  If FREEMEM cannot get a timer, the WinMain function must return a zero value
  2556.  (the value of FALSE), which simply terminates the application. As you'll
  2557.  note from the two other "return FALSE" lines in WinMain, FREEMEM also
  2558.  terminates if a previous instance is already running or if FREEMEM cannot
  2559.  register the window class. While prohibiting multiple instances of FREEMEM
  2560.  from running isn't necessary, there is really no reason to run FREEMEM more
  2561.  than once.
  2562.  
  2563.  If you prefer that FREEMEM tell you why it must terminate when SetTimer
  2564.  returns zero, you can instead use the code shown in Figure 3. This is more
  2565.  informative, if not exactly friendly.
  2566.  
  2567.  The second parameter in the SetTimer call is a "timer ID." When WndProc
  2568.  receives the WM_TIMER message, the wParam parameter contains this value. By
  2569.  using different timer IDs, a Windows application can set multiple timers and
  2570.  do different processing for each one. FREEMEM must also use the timer ID to
  2571.  relinquish the timer with the KillTimer call when WndProc receives a
  2572.  WM_DESTROY message.
  2573.  
  2574.  WM_TIMER messages are not asynchronous. Specifying 1,000 milliseconds in the
  2575.  SetTimer call does not guarantee that the window procedure will receive a
  2576.  WM_TIMER message precisely every second. The WM_TIMER messages are placed in
  2577.  the normal message queue and synchronized with all the other messages.
  2578.  
  2579.  If another application is busy for more than a second, FREEMEM does not get
  2580.  any WM_TIMER messages during that time. FREEMEM receives its next WM_TIMER
  2581.  message from the queue only when the other applications yield control by
  2582.  calling GetMessage, PeekMessage, or WaitMessage.
  2583.  
  2584.  In fact, like WM_PAINT messages, WM_TIMER messages are handled by Microsoft
  2585.  Windows as low-priority items. I said before that control passes to other
  2586.  applications only when a program's message queue is empty. Actually, if a
  2587.  program's message queue contains only WM_PAINT or WM_TIMER messages, and the
  2588.  message queue of another program contains any messages other than WM_PAINT
  2589.  or WM_TIMER, control will pass to the other application anyway.
  2590.  
  2591.  Moreover, Windows does not continue loading up the message queue with
  2592.  multiple WM_TIMER messages if another application is running during this
  2593.  time. In that case, Windows combines several WM_TIMER messages in the
  2594.  message queue into a single message so that the application doesn't get a
  2595.  bunch of them all at once.
  2596.  
  2597.  While Windows' handling of WM_TIMER messages is adequate for a program like
  2598.  FREEMEM, keep these points in mind if you ever do a clock application like
  2599.  the Windows CLOCK. A 1,000-millisecond timer value does not guarantee 3,600
  2600.  WM_TIMER messages over the course of an hour. When you get a WM_TIMER
  2601.  message, you'll want to determine the real time either with a C function or
  2602.  through MS-DOS directly. You can't keep time yourself solely with WM_TIMER
  2603.  messages.
  2604.  
  2605.  
  2606.  Calculating Memory
  2607.  
  2608.  When FREEMEM receives a WM_TIMER message, it must determine the amount of
  2609.  free memory. This little chore was the most difficult part of programming
  2610.  FREEMEM. I knew the information was available because the MS-DOS Executive
  2611.  "About" box showed a free memory value. But Microsoft Windows has around 400
  2612.  function calls, and finding the one you need is sometimes difficult.
  2613.  
  2614.  It turns out that the MS-DOS Executive gets a free memory value by calling
  2615.  GlobalCompact with a parameter of zero. Windows applications normally use
  2616.  GlobalCompact to generate some free memory from the global heap. If
  2617.  necessary, Windows compacts memory and frees up segments marked as
  2618.  discardable when GlobalCompact is called. (The global heap is comprised of
  2619.  memory outside of the program's local data segment.)
  2620.  
  2621.  A close reading of the documentation of GlobalCompact reveals that "if [the
  2622.  parameter] is zero, the function returns a value but does not compact
  2623.  memory." Thus, the value that FREEMEM displays is really a potential free
  2624.  memory size rather than actual free memory. It represents how much memory a
  2625.  Windows application can get from the global heap if it needs it.
  2626.  
  2627.  If you're familiar with the HEAPWALK utility included with the Software
  2628.  Development Kit, you can spend lots of time attempting to reconcile the
  2629.  value displayed by FREEMEM with value displayed by HEAPWALK. It's definitely
  2630.  not obvious. In general, you'll find that GlobalCompact returns
  2631.  approximately the size of the large chunk of free memory in the middle of
  2632.  the memory space maintained by Microsoft Windows, plus some discardable
  2633.  segments above that block of free memory. However, it stops short of the
  2634.  discardable code segment that contains FREEMEM's code. Obviously,
  2635.  GlobalCompact cannot discard FREEMEM's code if FREEMEM is calling the
  2636.  function.
  2637.  
  2638.  
  2639.  Drawing the Icon
  2640.  
  2641.  In FREEMEM, the section of WndProc that handles WM_TIMER messages does not
  2642.  itself update the icon display. Instead, the line
  2643.  
  2644.    InvalidateRect (hWnd, NULL, TRUE) ;
  2645.  
  2646.  notifies Windows that the contents of FREEMEM's window are now invalid and
  2647.  must be repainted. The InvalidateRect function causes Windows to put a
  2648.  WM_PAINT message in the message queue. WndProc actually updates the window
  2649.  only when it receives this WM_PAINT message.
  2650.  
  2651.  There are alternatives to this method. Instead of calling InvalidateRect,
  2652.  WM_TIMER can update the window directly. It would need to retrieve a
  2653.  display context with the statement
  2654.  
  2655.    hDC = GetDC (hWnd) ;
  2656.  
  2657.  and would then call GetClientRect and DrawText, just as in the WM_PAINT
  2658.  logic, but using hDC as the display context rather than ps.hdc. WM_TIMER
  2659.  would finally release the display context with
  2660.  
  2661.    ReleaseDC (hWnd, hDC) ;
  2662.  
  2663.  While this is certainly valid, I chose not to do it this way.
  2664.  
  2665.  WndProc must also process WM_PAINT messages which occur for reasons other
  2666.  than an updated free memory value. For instance, if you use the mouse to
  2667.  move the FREEMEM icon around the screen, Windows will eventually send
  2668.  FREEMEM a WM_PAINT message so that FREEMEM can repaint the icon.
  2669.  
  2670.  So, we'd either have to duplicate the paint code or move it to a separate
  2671.  subroutine. The InvalidateRect call really does this for us by generating
  2672.  the WM_PAINT message. It allows us to use the same paint code for all
  2673.  painting jobs.
  2674.  
  2675.  WM_PAINT messages are considered low priority by Microsoft Windows. They
  2676.  always go to the back of the message queue and are fetched from the queue
  2677.  only when no other message is present. There is a way around this, however.
  2678.  After calling InvalidateRect, WM_TIMER could then call
  2679.  
  2680.    UpdateWindow (hWnd) ;
  2681.  
  2682.  just as we did in WinMain. This instructs Windows to call WndProc directly
  2683.  with a WM_PAINT message, without going through the message queue.
  2684.  
  2685.  But that's really not necessary here. I wanted to keep FREEMEM operating as
  2686.  a low-priority task. If something else was going on in another application,
  2687.  I didn't want FREEMEM to hog time continually by repainting the icon.
  2688.  
  2689.  The DrawText function used by FREEMEM when processing the WM_PAINT message
  2690.  is very convenient for simple word-wrapped text. Note that the line within
  2691.  the DrawText code that reads
  2692.  
  2693.    strlen (strcat (itoa (mem, buffer, 10), "K Free"))
  2694.  
  2695.  does the same thing as the more-common construction
  2696.  
  2697.    sprintf (buffer, "%dK Free", mem)
  2698.  
  2699.  However, sprintf is a big function. By using strlen, strcat, and itoa
  2700.  instead, we can reduce the size of FREEMEM.EXE by about 3K──after all,
  2701.  every little bit helps.
  2702.  
  2703.  
  2704.  Creating FREEMEM
  2705.  
  2706.  Aside from the FREEMEM.C source code, you need two other files to create
  2707.  FREEMEM. The first is the module definition file, which is called
  2708.  FREEMEM.DEF (see Figure 4). The FREEMEM.DEF file contains the standard
  2709.  information that you'll see in definition files for most small Windows
  2710.  applications.
  2711.  
  2712.  The WINSTUB.EXE program specified in the STUB line of FREEMEM.DEF is
  2713.  included with the Software Development Kit. The program runs when you
  2714.  execute FREEMEM outside of Windows. It simply displays the message "This
  2715.  program requires Microsoft Windows."
  2716.  
  2717.  If you want to, you can create a normal MS-DOS program that displays a free
  2718.  memory value comparable to the value that CHKDSK calculates. If you decide
  2719.  to name this normal MS-DOS program DOSFREE.EXE, for instance, you can
  2720.  specify
  2721.  
  2722.    STUB 'DOSFREE.EXE'
  2723.  
  2724.  in the FREEMEM.DEF file. This little trick permits FREEMEM the use of both
  2725.  on the MS-DOS command level and inside Windows. FREEMEM.EXE would thus
  2726.  contain two related, but quite dissimilar, programs in one file.
  2727.  
  2728.  The FREEMEM "make-file" (which is called simply FREEMEM, without an
  2729.  extension) is shown in Figure 5. When you execute
  2730.  
  2731.    MAKE FREEMEM
  2732.  
  2733.  the FREEMEM.C source code will be compiled and linked with the appropriate
  2734.  Windows and C libraries, along with additional information from the
  2735.  FREEMEM.DEF module definition file.
  2736.  
  2737.  
  2738.  No Resource Script?
  2739.  
  2740.  Unlike many sample Windows applications, FREEMEM has no resource script
  2741.  file, which is a file with the extension .RC. FREEMEM doesn't need one.
  2742.  FREEMEM has no menu, no pictorial icon, and no dialog boxes. The only use we
  2743.  would have for a resource script is for storing text strings, but the only
  2744.  text string FREEMEM uses is the one with the word "Free" in it.
  2745.  
  2746.  Of course, for longer programs, the use of the resource script for all text
  2747.  strings is highly recommended since it will eventually make translation of
  2748.  the program into another language easier. But to tell the truth, loading
  2749.  strings stored as resources is a real nuisance for small programs. FREEMEM
  2750.  is short enough that translation is not a big problem, and we'd risk being
  2751.  meretricious by using a resource script for that single text string.
  2752.  
  2753.  
  2754.  Uses in Development
  2755.  
  2756.  I like to keep FREEMEM loaded and running in normal Microsoft Windows use
  2757.  just to give me an idea of how close Windows is getting to a low-memory
  2758.  situation. But it has more value in program development.
  2759.  
  2760.  If you're developing a Windows application, keep a watch on the FREEMEM icon
  2761.  during testing. If the free memory size keeps shrinking, you may be
  2762.  neglecting to free up some allocated memory within the program. You may then
  2763.  want to call on the HEAPWALK utility, also known as Luke Heapwalker, for
  2764.  some assistance in tracking down those orphaned memory blocks.
  2765.  
  2766.  However, don't assume that FREEMEM will display identical values before an
  2767.  application is run and after it terminates. The complexity of Windows'
  2768.  memory management makes this unlikely for most large programs.
  2769.  
  2770.  
  2771.  Figure 1:  WinMain Function of FREEMEM.C
  2772.  
  2773.  The first half of FREEMEM.C contains the WinMain function, which performs
  2774.  necessary initialization and contains the message loop.
  2775.  
  2776.  /*  FreeMem.C -- Windows application that displays free memory */
  2777.  
  2778.  #include <windows.h>    /* all Windows functions */
  2779.  #include <stdlib.h>     /* itoa */
  2780.  #include <string.h>     /* strcat & strlen */
  2781.  
  2782.  long FAR PASCAL WndProc (HWND, unsigned, WORD, LONG) ;
  2783.  
  2784.  int PASCAL WinMain (hInstance, hPrevInstance, lpszCmdLine, nCmdShow)
  2785.      HANDLE  hInstance, hPrevInstance ;
  2786.      LPSTR   lpszCmdLine ;
  2787.      int     nCmdShow ;
  2788.      {
  2789.      static  char szAppName [] = "FreeMem" ;
  2790.      WNDCLASS WndClass ;
  2791.      HWND    hWnd ;
  2792.      MSG     msg ;
  2793.  
  2794.      if (hPrevInstance)
  2795.          return FALSE ;
  2796.  
  2797.      WndClass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
  2798.      WndClass.hIcon         = NULL ;
  2799.      WndClass.cbClsExtra    = 0 ;
  2800.      WndClass.cbWndExtra    = 0 ;
  2801.      WndClass.lpszMenuName  = NULL ;
  2802.      WndClass.lpszClassName = (LPSTR) szAppName ;
  2803.      WndClass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
  2804.      WndClass.hInstance     = hInstance ;
  2805.      WndClass.style         = CS_HREDRAW | CS_VREDRAW;
  2806.      WndClass.lpfnWndProc   = WndProc ;
  2807.  
  2808.      if (!RegisterClass ((LPWNDCLASS) &WndClass))
  2809.          return FALSE ;
  2810.  
  2811.      hWnd = CreateWindow ((LPSTR) szAppName,
  2812.              (LPSTR) szAppName,
  2813.              WS_TILEDWINDOW,
  2814.              0, 0, 0, 0,
  2815.              (HWND)   NULL,
  2816.              (HMENU)  NULL,
  2817.              (HANDLE) hInstance,
  2818.              (LPSTR)  NULL) ;
  2819.  
  2820.      if (!SetTimer (hWnd, 1, 1000, NULL))
  2821.          return FALSE ;
  2822.  
  2823.      ShowWindow (hWnd, SHOW_ICONWINDOW) ;
  2824.      UpdateWindow (hWnd) ;
  2825.  
  2826.      while (GetMessage ((LPMSG) &msg, NULL, 0, 0))
  2827.          {
  2828.          TranslateMessage ((LPMSG) &msg) ;
  2829.          DispatchMessage ((LPMSG) &msg) ;
  2830.          }
  2831.      return (int) msg.wParam ;
  2832.      }
  2833.  
  2834.  
  2835.  Figure 2:  WindProc Function of FREEMEM.C
  2836.  
  2837.  long FAR PASCAL WndProc (hWnd, message, wParam, lParam)
  2838.      HWND    hWnd ;
  2839.      unsigned message ;
  2840.      WORD    wParam ;
  2841.      LONG    lParam ;
  2842.      {
  2843.      static  int mem, lastmem ;
  2844.      char    buffer [20] ;
  2845.      PAINTSTRUCT ps ;
  2846.      RECT    rect ;
  2847.  
  2848.      switch (message)
  2849.          {
  2850.          case WM_TIMER:
  2851.              mem = (int) (GlobalCompact (0L) / 1024) ;
  2852.              if (mem != lastmem)
  2853.                  InvalidateRect (hWnd, NULL, TRUE) ;
  2854.              lastmem = mem ;
  2855.              break ;
  2856.  
  2857.          case WM_PAINT:
  2858.              BeginPaint (hWnd, (LPPAINTSTRUCT) &ps) ;
  2859.              GetClientRect (hWnd, (LPRECT) &rect) ;
  2860.              DrawText (ps.hdc, (LPSTR) buffer,
  2861.                  strlen (strcat (itoa (mem, buffer, 10), "K Free")),
  2862.                  (LPRECT) &rect, DT_WORDBREAK) ;
  2863.              EndPaint (hWnd, (LPPAINTSTRUCT) &ps) ;
  2864.              break ;
  2865.  
  2866.          case WM_DESTROY:
  2867.              KillTimer (hWnd, 1) ;
  2868.              PostQuitMessage (0) ;
  2869.              break ;
  2870.  
  2871.          default:
  2872.              return DefWindowProc (hWnd, message, wParam, lParam) ;
  2873.          }
  2874.      return (long) 0 ;
  2875.      }
  2876.  
  2877.  
  2878.  Figure 3:  Alternate SetTimer Logic
  2879.  
  2880.  This code causes FREEMEM to tell you why it must terminate when SetTimer
  2881.  returns zero.
  2882.  
  2883.  if (!SetTimer (hWnd, 1, 1000, NULL))
  2884.       {
  2885.       MessageBox (hWnd, "Hey! Too many timers!", NULL, MB_OK) ;
  2886.       return FALSE ;
  2887.       }
  2888.  
  2889.  
  2890.  Figure 4:  Module Definition File FREEMEM.DEF
  2891.  
  2892.  The module definition file FREEMEM.DEF is used by LINK4 for information
  2893.  about the program not implied by the source code.
  2894.  
  2895.  NAME    FreeMem
  2896.  
  2897.  DESCRIPTION 'Free Memory Display by Charles Petzold'
  2898.  
  2899.  STUB    'WINSTUB.EXE'
  2900.  
  2901.  CODE    MOVEABLE
  2902.  DATA    MOVEABLE MULTIPLE
  2903.  
  2904.  HEAPSIZE  1024
  2905.  STACKSIZE 4096
  2906.  
  2907.  EXPORTS
  2908.      WndProc @1
  2909.  
  2910.  
  2911.  Figure 5:  FREEMEM Make-File
  2912.  
  2913.  This file, called simply FREEMEM, without an extension, is the "make-file"
  2914.  for generating FREEMEM.EXE from the source code.
  2915.  
  2916.  # Make file for FREEMEM -- assumes Microsoft C 4.0
  2917.  # ------------------------------------------------
  2918.  
  2919.  freemem.obj : freemem.c
  2920.      cl -c -d -Gsw -Os -W2 -Zdp freemem.c
  2921.  
  2922.  freemem.exe : freemem.obj freemem.def
  2923.      link4 freemem, /align:16, /map/line, slibw, freemem
  2924.      mapsym freemem
  2925.  
  2926.  ████████████████████████████████████████████████████████████████████████████
  2927.  
  2928.  CodeView:Debugging Philosophy And Step-by-Step Technique
  2929.  
  2930.  David Norris and Michael J. O'Leary
  2931.  
  2932.  Most MS-DOS programmers at one time or another have had to fire up the MS-
  2933.  DOS debugger DEBUG to patch another program or peek into memory. Some have
  2934.  actually debugged programs with it.
  2935.  
  2936.  All kidding aside, DEBUG has its uses, but it is usually woefully inadequate
  2937.  for doing any serious debugging, as shown by the many MS-DOS debuggers now
  2938.  on the market──we've counted more than a dozen.
  2939.  
  2940.  While debuggers are second only to editors as a computer "religious" issue,
  2941.  arguments often tend to revolve around user-interface issues or "my debugger
  2942.  has more features than your debugger" comparisons. Yet these are secondary
  2943.  to the main consideration: providing facilities that lend themselves well to
  2944.  established debugging techniques. If DEBUG fails, it is because it has no
  2945.  commands that adapt themselves to user queries such as "what is the value of
  2946.  iter?" and no commands like "stop when iter reaches 10."
  2947.  
  2948.  During the design of the Microsoft CodeView(TM) debugger, we surveyed these
  2949.  debugging techniques and created a requirements document for the "ultimate"
  2950.  debugger. By iterating these techniques and requirements, we hope to give
  2951.  you a fuller understanding of the complex world of debugging and aid the
  2952.  future evaluation of debugging tools, thus providing a debugger yardstick.
  2953.  We'll also introduce CodeView's philosophy and evolution, demonstrating its
  2954.  strengths and weaknesses with examples. Our goal is not to teach CodeView
  2955.  commands and syntax (that's what the manual is for), but rather to give you
  2956.  an understanding of the concepts behind the commands.
  2957.  
  2958.  
  2959.  Techniques
  2960.  
  2961.  The different debugging techniques can be categorized into two major groups:
  2962.  internal and external. External debugging implies an inspection of some
  2963.  sort, without specific interaction with the program being debugged. Internal
  2964.  debugging, on the other hand, requires the use of debugging tools that
  2965.  exercise control over the debugged program's environment.
  2966.  
  2967.  External debugging via code reviews by peers can uncover almost three-
  2968.  quarters of all software bugs for some kinds of errors. While the old adage
  2969.  about an ounce of prevention applies here, it has been our experience that
  2970.  scheduling peer code reviews usually plays second fiddle to meeting other
  2971.  product deadlines.
  2972.  
  2973.  Another external technique is simple post-mortem debugging. What did the
  2974.  program output? What should it have output? Simple formatting errors are
  2975.  usually found this way.
  2976.  
  2977.  Embedding debugging aids directly in the program is an anachronism from a
  2978.  time when adequate debugging aids were not available. In this case, the
  2979.  programmer decides where in the code he wants something done, such as
  2980.  testing an assertion (is this pointer valid?) and conditional compilation.
  2981.  
  2982.  None of these external methods requires any specialized debugging tools. On
  2983.  the other hand, internal debugging techniques involve the use of tools that
  2984.  give the user varying degrees of control over his program as it executes.
  2985.  This usually means a debugger, but includes a class of code-flow analysis
  2986.  tools such as profilers, event monitors, and path generators. A debugger can
  2987.  be internal to the program or it can be separate. Generally, the latter
  2988.  approach is used, as it is more desirable to debug the target program by
  2989.  itself, with minimal interference from the debugger.
  2990.  
  2991.  
  2992.  Requirements
  2993.  
  2994.  Let's examine some of the requirements of a debugger. This list contains
  2995.  general ideas about debugging, not specific commands.
  2996.  
  2997.  Size and Speed Efficiency: Obviously, you want a debugger to take up as
  2998.  little space as possible (especially when memory space is at a premium) and
  2999.  to be as fast as possible.
  3000.  
  3001.  Minimum impact on program being debugged: The debugger should not affect the
  3002.  behavior of the program it is debugging. As some have discovered (especially
  3003.  on unprotected systems, such as MS-DOS), the program may crash by itself but
  3004.  run fine when being debugged. This has been called the Heisenberg
  3005.  uncertainty principle when applied to debugging──the result being a
  3006.  "Heisenbug."
  3007.  
  3008.  Execution Control: The user should be provided with a wide range of commands
  3009.  for controlling the execution of his program, mostly in the form of "trap on
  3010.  x." Figure 1 lists the possible event traps.
  3011.  
  3012.  State Manipulation: Users of debuggers need to be able to meaningfully
  3013.  examine the state of a running, suspended, or dead program, including the
  3014.  determination of the cause of suspension, procedure call history,
  3015.  examination and alteration of code and data in its source form, and
  3016.  execution history.
  3017.  
  3018.  User interface: The debugger should be invisible to the user. The use of the
  3019.  debugger should be so comfortable and intuitive that users feel they are
  3020.  "in" the program they are debugging. It should be like driving a car──you
  3021.  don't drive the car around the corner, you just go around the corner.
  3022.  
  3023.  Portability: Programmers have met with various degrees of success in making
  3024.  debuggers portable. Still, it's always nice not to have to learn a new
  3025.  debugger when you move to a new computer system.
  3026.  
  3027.  
  3028.  CodeView Philosophy
  3029.  
  3030.  Microsoft needed a unified debugging tool strategy. The SYMDEB debugger, a
  3031.  symbolic extension of the MS-DOS DEBUG debugger, was partially able to
  3032.  fulfill the need for language independence, but attempts to make it portable
  3033.  or to separate user-interface code from system-dependent code would have
  3034.  been extremely difficult. We realized an entirely new debugger was needed.
  3035.  
  3036.  We knew we wanted the debugger to be language-independent; one hallmark of
  3037.  the Microsoft language series is that it is object-compatible. Therefore, a
  3038.  Microsoft debugger should be able to debug whatever programs users could
  3039.  come up with. We also needed the debugger to have a portable user interface
  3040.  and system-dependent code so that an SDB-compatible debugger could be
  3041.  created for the XENIX operating system. (See "ISLAND Design" diagram below)
  3042.  
  3043.  Above all, we wanted a user interface that everyone could be comfortable
  3044.  with. This proved to be the most difficult design task, as every individual
  3045.  had his own idea about user interfaces. We went through many iterations of
  3046.  the basic screen snapshots before we arrived at the present version.
  3047.  
  3048.  The main problem was presenting the proper information to the user given the
  3049.  limited amount of screen real estate. Thus, there are only two horizontal
  3050.  border bars on the basic screen (the original screen had borders around
  3051.  every window). In fact, the second horizontal bar, called the dialog bar,
  3052.  can be eliminated completely, allowing 22 lines of source information on the
  3053.  screen, and more on the EGA. The registers were placed to minimize the
  3054.  impact on the source window. Also, a pull-down menu system was used because
  3055.  it is a friendly, intuitive interface, and it conserves screen space when it
  3056.  is not used.
  3057.  
  3058.  Finally, we decided that rather than creating a new debugger syntax for
  3059.  people to learn, we would adhere to the syntax of the SYMDEB and DEBUG
  3060.  debuggers in the dialog window. This caused, and still causes, confusion for
  3061.  a number of reasons──mainly because the default radix can change. Our
  3062.  difficulty was that while we wanted compatibility with DEBUG commands
  3063.  (default radix of hex), we also wanted to use an expression evaluator that
  3064.  mirrored C as closely as possible (default decimal radix). To complicate
  3065.  matters, the default radix doesn't affect the dump command, which has its
  3066.  own set of types (byte, word, unsigned, etc.). We felt that programmers who
  3067.  dealt strictly in C might never use the lower-level commands and that
  3068.  assembly-language programmers wouldn't be using C expressions.
  3069.  
  3070.  
  3071.  Introducing CodeView
  3072.  
  3073.  CodeView is currently only available with the Microsoft C Compiler,
  3074.  Version 4.00 and Microsoft FORTRAN Optimizing Compiler, Version 4.0,
  3075.  although versions are planned for other languages and operating systems.
  3076.  After a brief overview of its features we'll go into detail on a few
  3077.  specific functions in an actual debugging session.
  3078.  
  3079.  A sample CodeView screen is shown in Figure 3. Four windows are available
  3080.  for display of information: the source window, the dialog window, the
  3081.  register window, and the watch window.
  3082.  
  3083.  The central and most important window is the source window and can be
  3084.  thought of as a read-only editor attached to debugging functions. For
  3085.  example, the user can scroll and search through source files, and the
  3086.  current line and active breakpoints are shown by a background blue bar and
  3087.  intense video that are imposed on the source text. Navigation through the
  3088.  program, which can be composed of more than one source file, is possible
  3089.  through a number of commands. The user can locate a program label, and the
  3090.  source window loads and displays the source file containing that label.
  3091.  
  3092.  The dialog window was our answer to commands that did not lend themselves to
  3093.  a simpler windowing scheme, such as variable-length data dumps. The dialog
  3094.  window can be thought of as the "glass-teletype" interface to CodeView;
  3095.  indeed, a CodeView switch (/T) forces CodeView to act as its predecessor
  3096.  SYMDEB did, in that a nonwindowing user interface is presented. The dialog
  3097.  window views only a portion of dialog text; scrolling commands that are
  3098.  similar to those in the source window can be used to examine data.
  3099.  
  3100.  The register window simply presents the machine registers. The only command
  3101.  available here is the ability to alter a flag value by clicking on it with
  3102.  the mouse.
  3103.  
  3104.  Finally, the watch window is available for viewing variables and data of
  3105.  interest to the user, so that it doesn't have to typed over and over. The
  3106.  format of the watch window entries is identical to the way they would be
  3107.  displayed in the dialog window; also, the command syntax for variable/data
  3108.  display between these two windows is orthogonal. For example, "?i" displays
  3109.  the variable i in the dialog window, while "w?i" displays it in the watch
  3110.  window.
  3111.  
  3112.  The pull-down menu was designed to mimic the Windows pull-down menu system,
  3113.  including the mouse and the keyboard.
  3114.  
  3115.  Let's look at a debugging session with CodeView in order to demonstrate some
  3116.  debugging concepts.
  3117.  
  3118.  
  3119.  Debugging WHERE
  3120.  
  3121.  WHERE is a utility program written in C (see Figure 2) and compiled with the
  3122.  Microsoft C 4.00 compiler, using the -Zi switch to enable CodeView
  3123.  information. It takes as arguments one or more program names and tries to
  3124.  find them as MS-DOS would try to execute them. Thus, the tool is useful in
  3125.  finding out whether the command "foo" would mean "C:\BIN\FOO.EXE" or
  3126.  "E:\FOO.BAT."
  3127.  
  3128.  Knowing that Real Programmers get it right the first time, we'll run WHERE
  3129.  and hope it doesn't trash our hard disk.
  3130.  
  3131.    A>WHERE WHERE.EXE
  3132.    WHERE.EXE 6224 10-30-86 11:23a
  3133.  
  3134.  Let's verify our program's output by using DIR:
  3135.  
  3136.    A>DIR WHERE.EXE
  3137.    WHERE.EXE 6224 10-30-86 11:23p
  3138.  
  3139.  Obviously, a.m./p.m. is incorrect. A quick glance at the printstats routine
  3140.  verifies that there is a typo; "<=" should have been ">=". Again, since
  3141.  we're Real Programmers, we'll remember to fix the bug later and forget the
  3142.  number of times we have forgotten to fix the bug later.
  3143.  
  3144.  But we haven't fully exercised WHERE. It should be able to find ambiguous
  3145.  names such as "DIR," and perform the name search in the same manner that MS-
  3146.  DOS would: .COM, .EXE, then .BAT. Let's try a simple case:
  3147.  
  3148.    A>WHERE WHERE
  3149.    A>
  3150.  
  3151.  WHERE can't find itself. How about an ambiguous or unambiguous filename
  3152.  somewhere in the path (but not in the current directory)?
  3153.  
  3154.    A>WHERE COMMAND
  3155.    A>
  3156.    A>WHERE COMMAND.COM
  3157.    A>
  3158.  
  3159.  It looks like we have two bugs; we can try to find them at the same time.
  3160.  Let's debug:
  3161.  
  3162.    A>CV WHERE COMMAND
  3163.  
  3164.  Figure 4 shows the initial CodeView screen. The register window is initially
  3165.  off when debugging C programs, and the watch window is initially empty.
  3166.  Typing "t" or clicking on the menu item "Trace!" with the mouse causes a
  3167.  blue highlighted line to appear in the source window on line 17, indicating
  3168.  the current instruction. Tracing one more time makes line 23 the current
  3169.  instruction.
  3170.  
  3171.  You're probably wondering why the current instruction started one line after
  3172.  "main" and why it skipped 5 lines. The C Compiler will only output line
  3173.  numbers for those lines that contain executable code. Lines 18 through 22
  3174.  are declarations that produce no code, while the declaration in line 23 has
  3175.  an assignment. We can show the relationship between source lines and emitted
  3176.  code (see Figure 5) by typing "u main", which instructs CodeView to display
  3177.  both source-code and assembly-language instructions in the source window.
  3178.  Type F3 to return CodeView to source-only mode.
  3179.  
  3180.  
  3181.  Single-stepping
  3182.  
  3183.  As the program is so brief, let's single-step through the program and see
  3184.  what happens. We can put variables of interest in the watch window so we
  3185.  don't have to retype them every time we want to see their values. The first
  3186.  interesting line is line 32; we can execute up to that line by typing "g
  3187.  .32" or by clicking the right mouse button on that line. Single-step by
  3188.  pressing the F10 key. To see the result of the getenv function, type
  3189.  "?envptr". CodeView displays the value 17552:4636; CodeView evaluated
  3190.  "envptr" as a C expression and so returned the value of the pointer (an
  3191.  address). The value of the expressions "&envptr", "envptr", and "*envptr"
  3192.  are different, and CodeView evaluates them properly. We are interested in
  3193.  looking at the null-terminated string pointed to by envptr, so our
  3194.  expression should be
  3195.  
  3196.    >w?envptr,s
  3197.  
  3198.  to put the string in the watch window (see Figure 6). Pointers and pointer
  3199.  expressions in C can be difficult to learn. Inexperienced users may find
  3200.  CodeView confusing in this respect, but no more so than writing in C itself;
  3201.  CodeView is a great learning tool for this purpose.
  3202.  
  3203.  The next two lines alter the variables ptr and namebuf, so let's trace the
  3204.  code and put the variables in the watch window. This can be done all in one
  3205.  line by separating the commands with semicolons:
  3206.  
  3207.    >t 2;w?ptr,s;w?namebuf,s
  3208.  
  3209.  The current line will execute the search_for_file function. We can use the
  3210.  "binary search," or divide-and-conquer, approach to debugging by stepping
  3211.  over the function using the Pstep command:
  3212.  
  3213.    >p
  3214.  
  3215.  The screen now looks like Figure 7. Note that both envptr and ptr are
  3216.  incorrect. The search_for_file function should not have been able to alter
  3217.  these variables, which are local to main. What we need is a way to stop
  3218.  execution when the variables change. CodeView accommodates us with the
  3219.  watchpoint and tracepoint commands.
  3220.  
  3221.  We don't know whether the string pointed to by envptr or envptr itself
  3222.  changed, so we need to trap on both conditions. First, we can reset the
  3223.  state of the program to just before the occurrence of the bug by typing
  3224.  
  3225.    >l;g .40
  3226.  
  3227.  To stop execution when envptr itself changes, we can use the watchpoint
  3228.  command:
  3229.  
  3230.    >wp?main.envptr!=4636
  3231.  
  3232.  We had to specify the function to which envptr belonged since we would be
  3233.  accessing the variable outside its normal scope (that of main).
  3234.  Alternatively, we could have used the Tracepoint command and typed "tpw
  3235.  envptr". In either case, CodeView is somewhat deficient because the user has
  3236.  to know either the initial value of envptr or the size of the pointer. We
  3237.  can break on the second condition by typing
  3238.  
  3239.    >tpb *envptr
  3240.  
  3241.  This command causes a trap when the first character pointed to by envptr
  3242.  changes. Now you continue by clicking on "Go!".
  3243.  
  3244.  After a couple of seconds──CodeView watchpoints and tracepoints can be
  3245.  quite slow, as they are emulated in software, although CodeView can utilize
  3246.  debugging hardware──the CodeView screen returns at line 82. The code just
  3247.  executed was a call to the strcat library routine. Before blaming the run-
  3248.  time library, though, let's take a look at its return value in fullpath:
  3249.  
  3250.    >?fullpath,s
  3251.  
  3252.  Oops (see Figure 8). The search_for_file function has been appending file
  3253.  extensions via strcat ad nauseum; we are overrunning the allocated length of
  3254.  the namebuf array and destroying the contents of other local variables of
  3255.  main. We know that envptr is being changed; unfortunately, the Microsoft C
  3256.  Compiler did not order main's local variables in the order given. In this
  3257.  case they are ordered envptr, ptr, found, namebuf, envpath, then
  3258.  pathbuf──completely different from the declaration order.
  3259.  
  3260.  The bug is easily fixed by removing the previous file extension using the
  3261.  strchr function just before the end of the file extension loop:
  3262.  
  3263.    *strchr(fullpath, '.') = '\0';
  3264.  
  3265.  
  3266.  Summary
  3267.  
  3268.  CodeView is not new. Many of its commands and features have already been
  3269.  seen in other debuggers. What is new about CodeView is the number of
  3270.  commands and degree of integration that it has provided. What is most
  3271.  important is that programmers have a full understanding of the nature of
  3272.  debugging and how they can best utilize the language and debugging tools
  3273.  available to them.
  3274.  
  3275.  
  3276.  ISLAND Design
  3277.  
  3278.  The ISLAND source code is divided into three major sections: user interface,
  3279.  core code, and system-dependent routines. By linking the core code with a
  3280.  given user interface and system-dependent code, a variety of debuggers can
  3281.  be created.
  3282.  
  3283.                                        ┌──────────────────────────┐
  3284.                                        │         CodeView         │
  3285.              User Interface       ┌───┴──────────────────────┬───┘
  3286.                                    │            SDB           │
  3287.                                ┌───┴──────────────────────┬───┘
  3288.                                │                          │
  3289.                                │                          │
  3290.                   Core Code   │          ISLAND          │
  3291.                                │                          │
  3292.                                │                          │
  3293.                                ├──────────────────────────┤
  3294.                                │       DOS 2.x/3.x        │
  3295.                                └───┬──────────────────────┴───┐
  3296.                                    │         XENIX-286        │
  3297.            System Dependent       └───┬──────────────────────┴───┐
  3298.                                        │        XENIX-386         │
  3299.                                        └───┬──────────────────────┴───┐
  3300.                                            │          Others          │
  3301.                                            └──────────────────────────┘
  3302.  
  3303.  
  3304.  Figure 1:  Debugger Event Traps
  3305.  
  3306.  Before/After execution of a specific instruction
  3307.  Before/After successful branch
  3308.  Single-step
  3309.  Data/Code Read/Write
  3310.  Procedure and Function Prologue/Epilogue
  3311.  Cross-Process
  3312.  
  3313.  
  3314.  Figure 2:  WHERE Utility Program
  3315.  
  3316.       /* WHERE - shows where DOS is finding your executable */
  3317.  #include <stdio.h>
  3318.  #include <time.h>
  3319.  #include <sys\types.h>
  3320.  #include <sys\stat.h>
  3321.  #define NUMEXTS     3
  3322.  #define MAXFILELEN   12
  3323.  #define MAXPATHLEN   65
  3324.  struct stat file_stat;
  3325.  char *getenv(char *);
  3326.  char *strchr();
  3327.  main (argc, argv)
  3328.  int argc;
  3329.  char **argv;
  3330.  {
  3331.   int found;
  3332.   char pathbuf[MAXPATHLEN];
  3333.   char namebuf[MAXFILELEN];
  3334.   char *ptr;
  3335.   char *envpath;
  3336.   char *envptr;
  3337.    if (argc < 2) {
  3338.      printf("Usage: where filename[.ext]\n");
  3339.      exit(2);
  3340.    }
  3341.    if (!(envptr = getenv("PATH")))
  3342.      envptr = "";
  3343.    /* Loop thru filenames given */
  3344.    while (ptr = *++argv) {
  3345.      strcpy(namebuf, ptr);
  3346.      /* Try current directory first */
  3347.      if (!(found = search_for_file(namebuf, strchr(namebuf, '.')))) {
  3348.        /* Loop thru paths in path environment variable */
  3349.        envpath = envptr;
  3350.        while (*envpath) {
  3351.          ptr = pathbuf;
  3352.          /* Copy path into buffer */
  3353.          while (*envpath && *envpath != ';')
  3354.            *ptr++ = *envpath++;
  3355.          /* skip path separators */
  3356.          while (*envpath == ';')
  3357.            ++envpath;
  3358.          /* Don't append a '\' if looking thru a root directory */
  3359.          if (*(ptr-1) != '\\')
  3360.            *ptr++ = '\\';
  3361.          *ptr = '\0';
  3362.          strcat(pathbuf, namebuf);
  3363.          if (search_for_file(pathbuf, strchr(namebuf, '.'))) {
  3364.            printstats(pathbuf);
  3365.            break;
  3366.          }
  3367.          }
  3368.        }
  3369.      else printstats(namebuf);
  3370.      }
  3371.    exit(found == 0);
  3372.  }
  3373.  search_for_file (fullpath, specific_file)
  3374.  char *fullpath;
  3375.  int specific_file;
  3376.  {
  3377.   int found, next_ext;
  3378.   static char *extension[NUMEXTS] = {".COM", ".EXE", ".BAT"};
  3379.    if (specific_file)
  3380.      found = !stat(fullpath, &file_stat);
  3381.    else {
  3382.       for (next_ext = 0; next_ext < NUMEXTS; ++next_ext) {
  3383.          /* try .com .exe .bat extensions */
  3384.          strcat(fullpath, extension[next_ext]);
  3385.          if (found = !stat(fullpath, &file_stat))
  3386.            break;
  3387.         }
  3388.       }
  3389.    return(found);
  3390.  }
  3391.  /*
  3392.   * print out fullname from passed string, and stats (size, date..)
  3393.   * from global struct file_stat.
  3394.   */
  3395.  printstats (fullname)
  3396.  char *fullname;
  3397.  {
  3398.    struct tm *tmptr;
  3399.    tmptr = localtime(&file_stat.st_atime);
  3400.    printf("%s \t%ld\t%2d-%2d-%2d\t%d:%02d%c\n",
  3401.        strupr(fullname),
  3402.        file_stat.st_size,
  3403.        tmptr->tm_mon+1,
  3404.        tmptr->tm_mday,
  3405.        tmptr->tm_year,
  3406.        (tmptr->tm_hour < 13 ? tmptr->tm_hour : tmptr->tm_hour-12),
  3407.        tmptr->tm_min,
  3408.        (tmptr->tm_hour <= 12 ? 'p' : 'a'));
  3409.  }
  3410.  
  3411.  ████████████████████████████████████████████████████████████████████████████
  3412.  
  3413.  Page Description Languages: High-Level Languages for Printer Independence
  3414.  
  3415.  ───────────────────────────────────────────────────────────────────────────
  3416.  Also see the following illustrations of Page Description Languages:
  3417.    Adobe System's PostScript
  3418.    A Comparison of Three Page Description Languages
  3419.  ───────────────────────────────────────────────────────────────────────────
  3420.  
  3421.  Steve Rosenthal
  3422.  
  3423.  As printers get steadily smarter, more and more of them are supporting
  3424.  command languages similarly enhanced in power and flexibility. In
  3425.  particular, page description languages (PDLs) are gaining popularity as a
  3426.  preferred way of telling printers how to put images on paper. More than just
  3427.  a simple list of what elements to be printed next, PDLs offer a precise and
  3428.  formalized means of controlling printer output. PDL language statements,
  3429.  which are normally created and sent to the output device invisibly to the
  3430.  end user, can include a combination of operations, objects, dot locations,
  3431.  and references to typographic elements such as fonts.
  3432.  
  3433.  You can, in fact, think of PDLs as high-level languages for printer output,
  3434.  compared to the machine language or assembly language approaches that have
  3435.  until recently been the rule. Not surprisingly, many of the advantages and
  3436.  disadvantages of using a PDL parallel those for using a high-level language
  3437.  in other types of programming, and many of the arguments about which PDL is
  3438.  best echo similar discussions about the relative virtues of computationally-
  3439.  oriented programming languages.
  3440.  
  3441.  Unfortunately, you can't run out and get all those promised advantages on
  3442.  very many devices or from many programs just yet. Of the three major page
  3443.  description languages that are likely to become major forces in the personal
  3444.  computer market in the immediate future──PostScript, Interpress, and
  3445.  Document Description Language (DDL)──only PostScript and Interpress have
  3446.  been implemented on any production output devices so far, and only
  3447.  PostScript has been implemented on hardware and software for personal and
  3448.  desktop computers. So for now, we'll have to settle for an overall
  3449.  discussion of why PDLs might be attractive, what's needed in a PDL and its
  3450.  implementation, and a description of the three major languages that appear
  3451.  to be serious market contenders.
  3452.  
  3453.  
  3454.  Device Independence
  3455.  
  3456.  Backers of PDLs say that the foremost advantage we can expect from them is
  3457.  device independence. Theoretically, an application or system can produce one
  3458.  single set of statements in a page language for output on any output device
  3459.  that supports that page language, rather than having to produce a different
  3460.  version for each brand, resolution level, and technology of output.
  3461.  
  3462.  Using a PDL is like using a single language, such as BASIC, that works on a
  3463.  wide variety of different machines. Even though the internal operation of
  3464.  the various printing devices may be different, that's taken care of during
  3465.  the interpretation of the PDL statements by each individual printer. From
  3466.  the outside, each device seems to be logically equivalent.
  3467.  
  3468.  Yet having a single logical connection between programs and printers affects
  3469.  interfacing in a dramatic way: the number of drivers that must be written is
  3470.  reduced to the sum of the programs and printers at issue rather than their
  3471.  product. Furthermore, an application written to PDL standards continues to
  3472.  support new devices that were not available at the time that the software
  3473.  was created──a feat that is at best difficult with more-traditional
  3474.  approaches.
  3475.  
  3476.  While that's the theory, in practice the results aren't quite as pristine.
  3477.  In all the announced PDLs, there are some device dependencies that sometimes
  3478.  must be considered, just as there often are in programming BASIC or Pascal.
  3479.  You can usually take a PDL file meant for one device and output it on
  3480.  another, but you may have to clean up a few differences or accept some
  3481.  slight artifacts of the retargeting process.
  3482.  
  3483.  Furthermore, every page description language may not be optimized for all
  3484.  new print technologies, so future compatibility cannot really be totally
  3485.  guaranteed. Given that most users replace computer equipment when the
  3486.  available alternative becomes economically more worthwhile, not when the
  3487.  previous generation fails to work any longer, inefficiency can be as much of
  3488.  a limiting factor as inability.
  3489.  
  3490.  Ease of connectability could also vanish as a major advantage unless a
  3491.  single page description language, or at least a small set of languages,
  3492.  becomes the de facto standard. Right now, several different PDLs are
  3493.  competing for market favor, along with several typographic description
  3494.  languages that take a more character-oriented and smaller-region view of
  3495.  the page.
  3496.  
  3497.  In addition, all of these languages compete against the virtual device
  3498.  approach, which has all applications produce their output as a series of
  3499.  calls to functions on a theoretical device. The environment then translates
  3500.  these calls to the actual operations needed by each printer. This last
  3501.  method, by the way, is the one implemented in most operating environments,
  3502.  such as Microsoft's own Windows.
  3503.  
  3504.  Still, given all these cautions, all it takes to convince most people that
  3505.  the PDL approach to universality is worthwhile is one typesetting project on
  3506.  the Macintosh. Because many applications on the Mac (including Microsoft
  3507.  Word and Microsoft Works) can produce output in PostScript, the same files
  3508.  can be run off first inexpensively on the Apple LaserWriter, then, when
  3509.  proofed and ready, can be run in finished form on a true high-quality
  3510.  typesetting machine from Allied Linotype.
  3511.  
  3512.  
  3513.  Low Overhead
  3514.  
  3515.  Economy of description is the second major advantage that page description
  3516.  languages offer. In all current PDLs, an application can describe intended
  3517.  output, where appropriate, as a series of objects rather than characters or
  3518.  individual dots and lines. An object-oriented description often takes much
  3519.  less time to communicate, as well as less memory buffer space and other
  3520.  system resources.
  3521.  
  3522.  For example, if an application wants to draw a circle in the output with a
  3523.  PDL, it generally orders up a circle object of a certain center and radius,
  3524.  with the outline done in the current line width setting. In contrast, if it
  3525.  wants to draw a circle with ordinary dot graphics, the application has to
  3526.  send every point on the circumference to the printer.
  3527.  
  3528.  The same idea applies to boxes, lines, shading, and even typographic fonts.
  3529.  Because the PDL-equipped printer starts with a large store of knowledge
  3530.  about elements of graphics and type, an application usually needs to send
  3531.  only the pointers rather than the objects themselves.
  3532.  
  3533.  In most PDLs, fonts are considered a special class of graphics. All the
  3534.  regular PDL commands that apply to graphics──such as those governing
  3535.  movement, rotation, enlargement, tint, and so on──apply to typographic
  3536.  characters as well. In addition, all PDLs have font-handling commands that
  3537.  take into account the special nature of type.
  3538.  
  3539.  On the flip side, being able to work with regular objects is no help at all
  3540.  for photographic information, scanned images, or other graphic elements
  3541.  that, unlike line drawings, have no simple tonal structure. In fact, if a
  3542.  PDL has to communicate such a graphic as a series of dots that require a lot
  3543.  of overhead for their expression, it can take the PDL more time and space to
  3544.  communicate this type of picture than would a simpler approach.
  3545.  
  3546.  Division of labor is another benefit that PDLs offer. Since page languages
  3547.  describe output in terms of objects, an originating system that contains
  3548.  those objects internally does not have to translate from object to ink
  3549.  dot──a process called rasterizing. On most printer technologies, rasterizing
  3550.  means translating an object into a series of on/off dots placed in a raster
  3551.  pattern of successive lines sweeping across the page. Rasterizing is also a
  3552.  necessary step in printing various sizes, styles, and weights of type.
  3553.  
  3554.  However, rasterizing is computationally intensive, and──if the page creation
  3555.  sequence and timing are to be decoupled from the printing technology──it
  3556.  needs at least one bit of memory for every possible dot placed on the page.
  3557.  Using a PDL permits the main processor to generate the prerasterized image;
  3558.  the printer can then rasterize and store the resulting bit map.
  3559.  
  3560.  PDLs that are full programming languages can also let the printer do some of
  3561.  the computations that precede rasterization. Since PDL statements can
  3562.  describe operations as well as objects, programs have the ability to send
  3563.  complex expressions to the output device; these expressions, once they are
  3564.  reduced to simpler form, will generate either the described object or the
  3565.  parameters of the described object. The output device will then be able to
  3566.  do the computations while the main processor proceeds with other work.
  3567.  
  3568.  
  3569.  Three Main Ingredients
  3570.  
  3571.  Basically, you need three components to implement a PDL in some workable
  3572.  form.
  3573.  
  3574.  The first component is the quality of language itself, which allows it to
  3575.  meet a number of criteria pertaining to function. For example, a page
  3576.  description language must be clearly and completely defined if it is to be
  3577.  used as a consistent standard across multiple machines and systems. It must
  3578.  be sufficiently fast and efficient to make the overhead involved in using it
  3579.  affordable. If it isn't, developers will simply bypass the PDL to write
  3580.  native code drivers for each device.
  3581.  
  3582.  A workable PDL should also have some sort of hierarchical or layered
  3583.  structure. Although not every printer has every feature, users who buy the
  3584.  more capable devices want to put any extra functionality to use; hence a
  3585.  good PDL must be able to take care of most business with a set of core
  3586.  commands and functions, yet still retain the potential to make use of
  3587.  additional capabilities.
  3588.  
  3589.  To be device independent, a PDL must also maintain a universal coordinate
  3590.  system visible to the applications program that is translating the points to
  3591.  the printer's actual coordinate system for output. The coordinate system
  3592.  must be sufficiently large to cover the maximum defined page size at the
  3593.  maximum workable resolution of any supported output device.
  3594.  
  3595.  A successful PDL must also be perceived as acceptable. The primary value of
  3596.  any such language depends in large part on its pervasiveness, so any
  3597.  language must appeal to a large number of software developers and output
  3598.  device manufacturers. Products that are not seen as acceptable, no matter
  3599.  how technically proficient, won't reach the critical mass needed to make
  3600.  their use worthwhile.
  3601.  
  3602.  In most cases, the bid for popularity has led to having the PDL languages
  3603.  themselves placed into the public domain──that is, the actual verbs and
  3604.  structures that comprise the language can be used by anyone, even though the
  3605.  programs that create the code or translate it into printed images are
  3606.  proprietary.
  3607.  
  3608.  
  3609.  The Interpreter
  3610.  
  3611.  The second component of the PDL system is the interpreter and printer
  3612.  controller, which translate the PDL statements into the actual dot
  3613.  information that the printer's marking engine needs to produce the output
  3614.  image.
  3615.  
  3616.  Translation is usually done on the fly by an interpreter (compiling usually
  3617.  isn't worthwhile because documents frequently change between every
  3618.  printing). Strictly speaking, the output of the interpretation process is
  3619.  some result sent back to the host system, but its "side effect" is the
  3620.  creation of the actual sequence of bits needed to control the printer
  3621.  marking engine.
  3622.  
  3623.  The major PDLs for the PC market are all based on threaded stack languages,
  3624.  so the interpreters and languages all have a very Forth-like flavor. They
  3625.  use reverse Polish notation (RPN) and store almost all their data and
  3626.  working values on a push-down stack.
  3627.  
  3628.  Because the computational power needed to make this translation quickly is
  3629.  so great, most of the PDL-equipped printers are more high-powered than the
  3630.  main system for which they are ostensibly peripherals.
  3631.  
  3632.  They also need a lot of memory. All current implementations are for page
  3633.  printers (where the complete page is translated and stored in dot form
  3634.  before the actual marking process begins), and all expect to find a complete
  3635.  bit map in which to store the resulting page image. For the current standard
  3636.  of 300 dots per inch on an 8 1/2- by 11-inch page, that means slightly more
  3637.  than a megabyte.
  3638.  
  3639.  PDL-based printers also need a large ROM space or a great deal of extra
  3640.  memory for downloading the interpreter. A typical PDL interpreter for a
  3641.  laser or similar xerographic page printer takes up several hundred
  3642.  kilobytes.
  3643.  
  3644.  The third component, producing the translation code, is also a huge
  3645.  challenge. Page description languages are intended as a form of
  3646.  communication between program and printer and are not generally designed
  3647.  for ease of human use. Instead, applications are expected to produce the
  3648.  code to be sent to the printer without any direct human intervention.
  3649.  
  3650.  The PDL code emitter that most applications require is somewhat more complex
  3651.  than a standard printer driver, but still within the reach of most serious
  3652.  programmers. For maximum efficiency, the application must specify its output
  3653.  as certain types of objects, and there are similar incentives toward
  3654.  handling type in more compatible ways.
  3655.  
  3656.  It is possible to write your own PDL code emitter, and maybe a dozen or more
  3657.  firms have already included such a facility in current software products.
  3658.  The backers of most PDLs will also write the needed code sections on a
  3659.  contract basis or will recommend a firm that does so.
  3660.  
  3661.  A common code generator can also be shared by multiple applications that run
  3662.  in a common operating environment using a virtual device interface. On the
  3663.  Macintosh, for example, system and applications programs write to the
  3664.  Quickdraw ROM routines, and a single code generator that is loaded as a
  3665.  "printing resource" then changes the ROM calls to PostScript code.
  3666.  
  3667.  Similarly, in Windows and Digital Research's GEM, programs that want to
  3668.  write PostScript don't have to have their own embedded PostScript code. A
  3669.  system driver turns virtual device interface calls into the code needed for
  3670.  a PostScript-based printer.
  3671.  
  3672.  Given all these requirements, it's not surprising that the major page
  3673.  description languages exhibit many similarities. But in the case of these
  3674.  three languages, the resemblance is more than coincidental. PostScript,
  3675.  Interpress, and DDL are all outgrowths of work done at the Xerox Palo Alto
  3676.  Research Center (PARC). So, although the exact syntax and precise list of
  3677.  features characterizing each language have diverged because each language
  3678.  was developed at a different corporate home, the overall spirit and approach
  3679.  are very much the same.
  3680.  
  3681.  
  3682.  PostScript
  3683.  
  3684.  PostScript, from Adobe Corp., was the first page description language to be
  3685.  implemented for personal computer software and peripherals and is still the
  3686.  only one that is being delivered with actual commercial products. Although
  3687.  its first widely available applications were on the Apple Macintosh and the
  3688.  Apple LaserWriter printer, PostScript is now backed by scores of software
  3689.  packages on various machines and operating systems, as well as close to a
  3690.  half dozen or more different printers.
  3691.  
  3692.  PostScript is in the public domain, and there is even a functionally
  3693.  compatible language/interpreter set combination that can be mixed and
  3694.  matched with PostScript sold by Control-C Software of Portland, Oregon.
  3695.  Most implementations so far have been done by Adobe.
  3696.  
  3697.  As a language, PostScript is particularly rich in general programming
  3698.  capabilities as well as in graphic and typographic support elements; more
  3699.  than 250 PostScript verbs cover arithmetic, logical, and control categories,
  3700.  as well as graphics. PostScript is written entirely in ASCII (printable)
  3701.  characters, making it easier to debug final code and to send output
  3702.  descriptions across simple communications links. On the other hand, this
  3703.  slows the transmission of nongeometric images, since they must be translated
  3704.  from bit maps to hex representation and back to be sent as printable
  3705.  characters.
  3706.  
  3707.  PostScript supports outline (vector) fonts and has provision for both
  3708.  built-in fonts and downloadable supplements. All fonts are defined as one
  3709.  single point unit in height and are then scaled to any selected size. The
  3710.  PostScript language definition supports color as well as shading, but so far
  3711.  all the delivered devices have been monochrome. For creating halftone
  3712.  (shaded) images, such as those found in photographs, PostScript includes a
  3713.  number of facilities for creating small regular patterns.
  3714.  
  3715.  PostScript applications can be written to handle documents of most any
  3716.  length, but the language won't supply much built-in help for the more
  3717.  complex projects. Formatting beyond the structure of a single page, not
  3718.  included in the first language definition, is currently implemented as a
  3719.  series of structured comment lines.
  3720.  
  3721.  For debugging or constructing composite projects, PostScript contains a fair
  3722.  number of file and input control statements. The AppleWriter PostScript
  3723.  interpreter, for example, can be used interactively to let you improve
  3724.  various formulations of your procedures.
  3725.  
  3726.  
  3727.  Interpress
  3728.  
  3729.  Interpress is Xerox Corp.'s nomination as a standard page description
  3730.  language. So far, it has been implemented on several larger laser printers
  3731.  and on many Xerox minicomputer applications as part of the Xerox Network
  3732.  Architecture (XNS) system.
  3733.  
  3734.  However, applications in the personal computer field should begin to appear
  3735.  very shortly. Xerox's Ventura Publisher will support Interpress, as will
  3736.  Microsoft Windows. Xerox says that several personal computer printer
  3737.  manufacturers plan to announce Interpress support, starting in the first
  3738.  quarter of 1987. The language itself is also in the public domain.
  3739.  
  3740.  Compared with PostScript, Interpress has more facilities for controlling
  3741.  overall document structure and distribution but slightly less power for
  3742.  general computation. The language can be written in a Forth-like ASCII
  3743.  representation, but the actual code sent to Interpress printers is a binary
  3744.  representation. Bidirectional translators are available for debugging,
  3745.  testing, and learning.
  3746.  
  3747.  Using binary for Interpress files obviously saves space, but perhaps more
  3748.  importantly, it cuts transmission time for complex documents. Because
  3749.  Interpress was explicitly designed for use with networks, transmitting a
  3750.  file more than once was expected to be the rule rather than the exception.
  3751.  
  3752.  The most important document control feature is page independence. In an
  3753.  Interpress output file, each page is dependent only on an initial header and
  3754.  information local to that single page. This feature guarantees that printers
  3755.  that actually image in some order other than first through last will produce
  3756.  the right output, and many of the higher performance printers that do two-
  3757.  sided copying or binding do indeed print documents in various orders.
  3758.  
  3759.  Interpress also features an explicit mechanism for setting imaging priority.
  3760.  On many output devices the order with which overlapping images are laid down
  3761.  matters to the final result; on others, printing can be done faster by using
  3762.  an order that may be different from the order in which the instructions
  3763.  arrive. Hence, Interpress allows the user to specify strict sequencing when
  3764.  needed.
  3765.  
  3766.  As part of an overall network strategy, Interpress also links to a large
  3767.  number of complementary output-related Xerox standards. Many of these
  3768.  define a standard solution to issues that have yet to be addressed in the
  3769.  DOS and Macintosh environments. That includes standards for font handling
  3770.  and naming, encoding of scanned and other raster images, and character
  3771.  coding for extended character sets.
  3772.  
  3773.  
  3774.  DDL
  3775.  
  3776.  Document Description Language (DDL) is Imagen Corp.'s entry into the
  3777.  standard page description language derby. As the latest of the three
  3778.  contenders, and as Imagen's second generation of page description languages,
  3779.  it naturally combines features from the prior languages as well as adding
  3780.  some innovations of its own.
  3781.  
  3782.  DDL devices and programs will not be publicly available until sometime this
  3783.  year, but agreements between Imagen, Hewlett-Packard, and Microsoft ensure
  3784.  the language a substantial launch. It will be Hewlett-Packard's language of
  3785.  choice for sophisticated applications speaking to the HP Laserjet printer
  3786.  and will be supported as an output driver in Microsoft Windows.
  3787.  
  3788.  DDL shares with Interpress an enhanced emphasis on overall document
  3789.  structure as well as on the geometry of each page and perhaps goes even
  3790.  further in this direction. It also makes more-extensive use of caching to
  3791.  reduce the time required in repetitively translating objects from
  3792.  description to dot form.
  3793.  
  3794.  Document control includes page independence and an explicit mechanism for
  3795.  specifying page order for output. The latter feature is particularly
  3796.  important when multiple logical pages are imaged onto each physical page,
  3797.  and the resulting sheets have to be folded and assembled into a complete
  3798.  document.
  3799.  
  3800.  Caching attempts to use all available memory, which is constantly getting
  3801.  cheaper, to increase the speed of performance. Objects, including both fonts
  3802.  and geometric shapes, are held in memory in translated form as long as there
  3803.  is room; they can be reused if called again. That cuts down translation time
  3804.  substantially for repetitive elements.
  3805.  
  3806.  DDL files are transmitted in binary, which makes them more compact and
  3807.  speedier to transmit than an ASCII representation. Compactness is
  3808.  particularly important for scanned images and halftones (photographs), which
  3809.  are normally sent as arrays of dots. For debugging purposes, DDL printers
  3810.  will also accept an ASCII equivalent.
  3811.  
  3812.  DDL can also support both bit-mapped and outline fonts. When scaling bit-
  3813.  mapped fonts, which are made of lists of actual dot positions, the system
  3814.  applies various typographic design rules to produce a more intelligent
  3815.  result than does simple geometric multiplication. Besides the standard fonts
  3816.  found on both computerized and traditional systems, Imagen has licensed
  3817.  several special computer-oriented type families, including a face called
  3818.  Lucida that its originators, Bigelow & Holmes, claim is the first designed
  3819.  explicitly for electronic printing.
  3820.  
  3821.  For times when exact dot placement is important, such as in tiny fonts or in
  3822.  plotter-type output, DDL guarantees that if a target resolution is defined
  3823.  before image creation, dot placement at that same output resolution will
  3824.  follow the original exactly without any errors due to translation back and
  3825.  forth into universal coordinates. Close positioning control will increase
  3826.  greatly in importance when more color devices become available, especially
  3827.  if they are able to intermix colors to provide a wide range of hues and
  3828.  shades ("process color"). Tight dot positioning control also makes it
  3829.  possible to guarantee a close level of correspondence between screen and
  3830.  printed images.
  3831.  
  3832.  Like PostScript, DDL includes a full complement of control structures and is
  3833.  extensible, which makes it possible to write routines that handle both
  3834.  common and special requirements. While a wide variety of programs can be
  3835.  written in DDL, the expected use of this facility is for printer drivers or
  3836.  output filters and formatters.
  3837.  
  3838.  
  3839.  Making a Choice
  3840.  
  3841.  Although end users normally choose a page description language only
  3842.  implicitly through their choice of printers, developers and programmers face
  3843.  a tougher dilemma. They can pick a single PDL and support it as their output
  3844.  language of choice, support multiple PDLs, or rely on an operating
  3845.  environment for PDL support.
  3846.  
  3847.  The problem is made still more difficult by the rising expectations of end
  3848.  users. While the first laser printers that supported graphics and
  3849.  typographic-style fonts initially seemed sufficient, users are now asking
  3850.  for higher performance, faster output, greater detail, and increased ease of
  3851.  use.
  3852.  
  3853.  The makers of PDLs recognize this and are all working on improvements and
  3854.  refinements. But because the interpreters are normally implemented in ROM
  3855.  firmware, updating a PDL is a major market headache. PDLs are not changed
  3856.  very lightly.
  3857.  
  3858.  Furthermore, optimizing the choice of language itself is not the total
  3859.  solution. PDLs are part of a complex web that includes experienced
  3860.  programmers, supporting applications, tools, and output devices. The
  3861.  availability and popularity of resources and collateral material also
  3862.  matter.
  3863.  
  3864.  Which page description language is the best? Perhaps that question won't be
  3865.  settled any more than which programming language should be everyone's
  3866.  choice. At some point, it's not technology but theology.
  3867.  
  3868.  The good news is that developers writing programs for Microsoft Windows do
  3869.  not have to choose. Since Windows provides a device-independent interface, a
  3870.  developer can write an application that can output to a device using a PDL
  3871.  simply by having the appropriate device driver installed. In doing so,
  3872.  Windows permits innovation in PDLs while maintaining a standard application
  3873.  interface. A PostScript driver is already available, and DDL and Interpress
  3874.  drivers are expected to be ready in the near future.
  3875.  
  3876.  ───────────────────────────────────────────────────────────────────────────
  3877.  Adobe System's PostScript
  3878.  ───────────────────────────────────────────────────────────────────────────
  3879.  
  3880.  The "B" cube on page 50 of the printed version was created from the
  3881.  sample of Adobe's PostScript Language which follows.
  3882.  
  3883.    %! PS-Adobe- Adobe Systems Incorporated-Colophon 3 ShowPage Graphics 1986
  3884.    %% DocumentFonts: Palatino-Roman
  3885.    %% Title: B.cube.ps
  3886.  
  3887.    %  B.cube.ps produces a cube drawn with a character on each face.
  3888.    %  To change the font subtitute a chosen font for Palatino-Roman in the
  3889.    %  definition of "masterfont".
  3890.  
  3891.    /cube {
  3892.       /masterfont /Palatino-Roman findfont def
  3893.  
  3894.  % Letter strings that allow you to assign particular letters to each cube
  3895.    face.
  3896.       /front exch def
  3897.       /leftside exch def
  3898.       /back exch def
  3899.       /rightside exch def
  3900.       /top exch def
  3901.       /bottom exch def
  3902.  
  3903.       % The height of the letter is reduced with respect to its angle. (stan)
  3904.       % Height is multiplied by sin/cos.
  3905.       % This helps for perspective.
  3906.  
  3907.       /stan {a sin a cos div sy mul} def
  3908.       /getfont {masterfont [sx 0 stan sy 0 0]
  3909.             makefont setfont}  def
  3910.  
  3911.       % type (s)ize and (a)ngle of oblique
  3912.  
  3913.       gsave
  3914.            /sy 100 def
  3915.            /sx 100 def
  3916.            /a 0A def
  3917.            getfont
  3918.            40 50 moveto
  3919.            .75 setgray
  3920.            back show
  3921.       grestore
  3922.  
  3923.       gsave
  3924.            /a 45 def
  3925.            /sy sy 2 sqrt div def
  3926.            /sx 70 def
  3927.            getfont
  3928.            0 0 moveto
  3929.            .60 setgray
  3930.            bottom show
  3931.       grestore
  3932.  
  3933.       gsave
  3934.            getfont
  3935.            70 0 moveto
  3936.            45 rotate
  3937.            .1 setgray
  3938.            rightside show
  3939.       grestore
  3940.  
  3941.       gsave
  3942.            getfont
  3943.            0 7 moveto
  3944.            45 rotate
  3945.            .40 setgray
  3946.            leftside show
  3947.       grestore
  3948.  
  3949.       gsave
  3950.            /a 0 def
  3951.            /sy 100 def
  3952.            /sx 100 def
  3953.            getfont
  3954.            0 0 moveto
  3955.            .20 setgray
  3956.            front show
  3957.       grestore
  3958.  
  3959.       gsave
  3960.            /sy sy 2 sqrt div def
  3961.            /a 45 def
  3962.            /sx 70 def
  3963.            getfont
  3964.            0 70 moveto
  3965.            top show
  3966.       grestore
  3967.  
  3968.    } def %cube
  3969.  
  3970.    %% EndProlog
  3971.  
  3972.    % To set cube:
  3973.    % gsave
  3974.    % x y translate
  3975.    % x y scale       ...no scale here gives a cube of 100 pts
  3976.    % (bottom) (top) (rightside) (back) (leftside) (front) cube
  3977.    % grestore
  3978.  
  3979.    gsave
  3980.    100 200 translate
  3981.    3 3 scale
  3982.    (B) (B) (B) (B) (B) (B) cube
  3983.    grestore
  3984.  
  3985.  ───────────────────────────────────────────────────────────────────────────
  3986.  A Comparison of Three Page Description Languages
  3987.  ───────────────────────────────────────────────────────────────────────────
  3988.  
  3989.  We asked three PDL vendors to create a sample output consisting of the words
  3990.  "Microsoft Systems Journal" in a box.
  3991.  
  3992.    ■  Postscript Sample
  3993.    ■  DDL Sample
  3994.    ■  Interpress Sample
  3995.  
  3996.  Postscript Sample
  3997.  
  3998.  The PostScript programming from Adobe produces "Microsoft Systems Journal"
  3999.  shown on page 53 of the printed version.(The misspelling of the corporate
  4000.  name was due to creative license on the part of the programmers.)
  4001.  
  4002.  !PS-Adobe-2.0
  4003.    %% Creator: pasteup
  4004.    %% CreationDate: Tue Dec 10 1986
  4005.    %% For: Microsoft Systems Journal
  4006.    %% Pages: 1
  4007.    %% DocumentFonts: Optima
  4008.    %% BeginProcSet: text_tools v1.0 revl.0
  4009.    /box {  % takes relativeX relativeY on stack, draws a box
  4010.        dup 0 rlineto
  4011.        0 3 -1 roll rlineto
  4012.        neg 0 rlineto
  4013.        closepath
  4014.        stroke
  4015.    } bind def
  4016.    % short names for frequently used PostScript operators:
  4017.    /s  /show load def
  4018.    /m  /moveto load def
  4019.    /S  /save load def
  4020.    /RS  { restore save } bind def
  4021.    /R  /restore load def
  4022.  %% EndProcSet
  4023.  %% END Prolog
  4024.  %% Page: 1 1
  4025.    S
  4026.    100 100 moveto
  4027.    35 306 box
  4028.    RS
  4029.    120 110 m
  4030.    /Optima findfont 24 scalefont setfont
  4031.    (MicroSoft Systems Journal) show
  4032.    R
  4033.    showpage
  4034.  %% Trailer
  4035.  
  4036.  
  4037.  DDL Sample
  4038.  
  4039.  The DDL output from Imagen Corporation appears on page 54 of the printed
  4040.  version.
  4041.  
  4042.    Uopbind.ddl (this program uses the standard DDL include file "opbind.ddl")
  4043.    "opbind.ddl" (specify the name of a file for the binding of operators and
  4044.                  constants)
  4045.    :40 (open the specified file and push its source descriptor code on the
  4046.         stack)
  4047.    \srcn ; 3C (assign a name to the source descriptor code of specified file)
  4048.    srcn :3 (interpret the contents of specified source file)
  4049.    srcn :42 (close the specified source file)
  4050.  
  4051.    ; define an operator to convert points to image units
  4052.    { 24 POINTSPERINCH / ImageMetrics 5 index * }
  4053.      \pointstounits =
  4054.    @S (end of preamble and the start of section 1)
  4055.  
  4056.    @Uhelvr (this program uses the font file Helvetica Regular)
  4057.  
  4058.    "helvr" \SymbolStyle = ; set the state variable SymbolStyle to the name of
  4059.                             the font file
  4060.    24 pointstounits \SymbolDesignSize = ; convert 24 points into image units
  4061.  
  4062.    128 array \CompositeMap = ; specify the size of the CompositeMap array
  4063.  
  4064.    33 \i = ; set the starting index for the Composite map array to 33
  4065.    95 {
  4066.            i symbol area\ CompositeMap i = ; store the graphic object for an
  4067.  
  4068.            i 1 + \i =  ; ASCII character and increment the array index
  4069.    } repeat
  4070.  
  4071.    SymbolDesignSize 1 + 3 / relx \ CompositeMap 32 =
  4072.       ;  calculate the width of the ASCII character SP (space)
  4073.  
  4074.    656 \Xvalue = ; set the starting X coordinate for printing specified words
  4075.  
  4076.    600 \Yvalue = ; set the starting Y coordinate for printing specified words
  4077.  
  4078.    Xvalue Yvalue absxy ! ; go to the coordinates Xvalue Yvalue
  4079.  
  4080.    "Microsoft Systems Journal" composite \text =
  4081.        ;  create a  composite object called text
  4082.  
  4083.    text ! ; print the words "Microsoft Systems Journal"
  4084.  
  4085.    text bbox ; calculate the smallest box that can enclose
  4086.                the specified three words
  4087.  
  4088.    \maxy = ; get the maximum Y coordinate of the bounding box from the DDL
  4089.              stack
  4090.    \miny = ; get the minimum Y coordinate of the bounding box from the DDL
  4091.              stack
  4092.    \maxx = ; get the maximum X coordinate of the bounding box from the DDL
  4093.              stack
  4094.    \minx = ; get the minimum X coordinate of the bounding box from the DDL
  4095.              stack
  4096.  
  4097.    maxy miny - \height = ; height of the bounding box
  4098.    maxx minx - \width =  ; width of the bounding box
  4099.  
  4100.    height 2 / \offset = ; calculate the offset from the starting position
  4101.  
  4102.    width height = \width = ; increase the box width by box height
  4103.  
  4104.    Xvalue minx + offset - \Xvalue = ; calculate the starting X and Y
  4105.                                         coordinates
  4106.    Yvalue miny + offset - \Yvalue = ; the printing of a box
  4107.  
  4108.    Xvalue Yvalue absxy ! ; go to the starting coordinates
  4109.  
  4110.    width height height + rectangle line !
  4111.      ; print a box twice as high as the bounding box
  4112.  
  4113.    endimage
  4114.    @E
  4115.  
  4116.  Interpress Sample
  4117.  
  4118.  The Interpress output from Xerox Corporation appears on page 57 of the
  4119.  printed version.
  4120.  
  4121.    ──Date: 8-Dec-86  14:42:23  PST - Object file Name: MICROSOFT2.ip
  4122.  
  4123.    ── Object to Source Conversion parameters:
  4124.    ──    Object File Name: MICROSOFT2.ip,
  4125.    ──    Source File Name: MICROSOFT2.ial.
  4126.    ──    Op codes: Source Only.
  4127.    ──    File Results: SourceOnly,   Sequence Data:
  4128.    Decimal/ASCII.
  4129.    ──    Conversion: Normalized,     Items Per Line:
  4130.     Multiple.
  4131.    ──    Large Arrays: NotSupressed.
  4132.  
  4133.    Interpress/Xerox/2.2
  4134.    BEGIN
  4135.      {
  4136.      Identifier "Xerox" Identifier "XC1-1-1" Identifier
  4137.    "Modern" 3 MAKEVEC
  4138.      FINDFONT
  4139.      99.576 SCALE
  4140.      MODIFYFONT
  4141.      O FSET
  4142.      }
  4143.  
  4144.      {
  4145.      1/11811 SCALE
  4146.      CONCATT
  4147.      375 2950 SETXY
  4148.      O SETFONT
  4149.      String "Microsoft"
  4150.      SHOW
  4151.      40 SETXREL
  4152.      String "Systems"
  4153.      SHOW
  4154.      40 SETXREL
  4155.      String "Journal"
  4156.      SHOW
  4157.      5 15 ISET
  4158.      350 2900 350 3050 MASKVECTOR
  4159.      350 3050 1630 3050 MASKVECTOR
  4160.      1630 3050 1630 2900 MASKVECTOR
  4161.      1630 2900 350 2900 MASKVECTOR
  4162.      }
  4163.  
  4164.    END
  4165.  
  4166.  ████████████████████████████████████████████████████████████████████████████
  4167.  
  4168.  DIAL 2.0 Provides Software Developers with Integrated Support System
  4169.  
  4170.  Barbara Krasnoff
  4171.  
  4172.  Microsoft's on-line DIAL 2.0 support service, which supplies assistance for
  4173.  applications developers, has been recently upgraded and expanded. We asked
  4174.  Sunny Baker, Director of Planning and Marketing, to explain the new
  4175.  developments.
  4176.  
  4177.  
  4178.  MSJ: What exactly is DIAL?
  4179.  
  4180.  SUNNY BAKER: DIAL is an integrated set of on-line support services that use
  4181.  the PC as the interface to our own software. There are three components to
  4182.  DIAL. First, there is the on-line bulletin board system, a knowledge bank
  4183.  that is one of the most sophisticated bulletin board systems in the entire
  4184.  industry. It uses artificial intelligence techniques for keyword search
  4185.  capabilities. It's very fast and powerful.
  4186.  
  4187.  Second, we have an on-line Forum, similar to the ones you get through
  4188.  CompuServe, especially for the ISV (independent software vendor) community.
  4189.  
  4190.  Finally, we offer technical assistance through our TAR (technical assistance
  4191.  request processing) system. We supply both on-line TAR processing and
  4192.  callback for high-priority TARs in order to give people immediate assistance
  4193.  over the telephone. DIAL also transfers both binary and ASCII files, and we
  4194.  do furnish some DIAL software to subscribers.
  4195.  
  4196.  
  4197.  MSJ: When did DIAL first go on-line?
  4198.  
  4199.  BAKER: The first version went on-line at the end of 1984, and, quite
  4200.  honestly, it was not very capable. It was basically a prototype version. At
  4201.  that time, we limited access to DIAL to our OEM customers and some of the
  4202.  specialized ISVs. It wasn't until last year that we really opened DIAL up to
  4203.  the ISV community, and now we have a completely new version.
  4204.  
  4205.  DIAL 2.0 is very similar to what people have been using from the interface
  4206.  standpoint, but functionally and operationally, it's a completely new
  4207.  product. For example, we used to run DIAL on very small machines because of
  4208.  its prototype nature, and now we've moved the actual server for DIAL to a
  4209.  full-sized VAX.
  4210.  
  4211.  MSJ: What are some of the other differences between 2.0 and previous
  4212.  versions?
  4213.  
  4214.  BAKER: The Bulletin Board has been entirely restructured. It has keyword
  4215.  search capabilities, it contains much more information, and the organization
  4216.  and speed of the board are entirely different. The Forum is a totally new
  4217.  feature, and transmission error-checking capabilities have been added as
  4218.  well. The product now uses an ANSI driver rather than a VT52 driver, which
  4219.  is something that our customers asked for. The VT52 was a Microsoft standard
  4220.  that we used in-house, but nobody else uses it, so it was a big problem.
  4221.  
  4222.  MSJ: What kind of service does DIAL contribute to the community? Why is
  4223.  there a need for it?
  4224.  
  4225.  BAKER: There have been complaints in the user community for some time that
  4226.  Windows development has not been supported properly. DIAL is our effort to
  4227.  supply the high level of support that people who are doing software
  4228.  development need. Access to the Bulletin Board gives software developers
  4229.  immediate information for any known problem. We summarize every TAR that's
  4230.  presented to us from whatever source, and place it on the Bulletin Board,
  4231.  giving developers and programmers everywhere full access to bug lists for
  4232.  Microsoft products. There is immediate access to any new software updates;
  4233.  it's the most efficient way to get new tools to users. We also offer example
  4234.  software that they can download and then use in their own applications.
  4235.  
  4236.  From our point of view, the Bulletin Board is the fastest and most efficient
  4237.  way for subscribers to get support from Microsoft, since it contains answers
  4238.  to every question that somebody has asked before. The Bulletin Board is so
  4239.  quick that it's faster than calling somebody or sending in a technical
  4240.  assistance request. We try to get subscribers to DIAL to really use the
  4241.  Bulletin Board actively, especially when they first start developing
  4242.  applications with a Microsoft product.
  4243.  
  4244.  
  4245.  MSJ: Who is the typical DIAL subscriber?
  4246.  
  4247.  BAKER: Right now, our typical users are software developers doing Windows
  4248.  development, OEMs doing adaptations of both DOS and Windows to their own
  4249.  machines, and anybody who uses a Microsoft systems product──especially to
  4250.  create Windows applications. We are in the process of making DIAL available
  4251.  to any person who does systems development using any Microsoft programming
  4252.  language or programming tool kit, with emphasis on our most strategic
  4253.  products: Windows and DOS.
  4254.  
  4255.  MSJ: Approximately how many users do you have?
  4256.  
  4257.  BAKER: We have about 1,200 users today; and we're getting about 120 new
  4258.  users a month. Certainly we would like to penetrate the community even
  4259.  deeper than that. Right now, we know we have about 6,000 Windows Toolkit
  4260.  users out there; our goal is to have at least 75 percent of the active
  4261.  program developers on-line. And there are thousands of C programmers out
  4262.  there who are another target market for us. As our user base expands, the
  4263.  Forums will get better, because there are more and more people exchanging
  4264.  information.
  4265.  
  4266.  
  4267.  MSJ: How much information does Microsoft post on the Bulletin Board?
  4268.  
  4269.  BAKER: We delete things that are obsolete, but otherwise the Bulletin Board
  4270.  is constantly expanding. We now post about 150 items a week. Our goal for
  4271.  next month is to post 1,000 items, and our goal over the next 6-month period
  4272.  is to get up to the point where we will be posting 7,000 items a month,
  4273.  because we're going to support Microsoft's complete product line.
  4274.  
  4275.  MSJ: The Bulletin Board contains information only from Microsoft. Is there
  4276.  any on-line interaction between users?
  4277.  
  4278.  BAKER: That's what the Forum is all about. We just started it in January,
  4279.  and it looks like we're going to have a good quorum.
  4280.  
  4281.  Our Forums are just like the forums on GENIE or CompuServe. Let's say that
  4282.  somebody is looking for a local area network card. That person can ask the
  4283.  community, "Hey, do any of you know a good local area network card?" DIAL
  4284.  people can respond to the Forum and say, "Hey, I know so-and-so." Their
  4285.  anonymity is maintained throughout the Forum.
  4286.  
  4287.  Forum items that we think are noteworthy are also translated into Bulletin
  4288.  Board items, so that everybody can have access to them.
  4289.  
  4290.  The whole point is to make DIAL a one-stop support system that combines
  4291.  everything a user basically needs as far as support from Microsoft, support
  4292.  from his own community, and support from our knowledge base──all integrated
  4293.  into one system.
  4294.  
  4295.  
  4296.  MSJ: When does a subscriber use TAR?
  4297.  
  4298.  BAKER: Any question that relates to the operation of a product or the
  4299.  building of an application with a Microsoft product can be asked over the
  4300.  TAR system.
  4301.  
  4302.  However, to support the complex questions that we get, we really need to see
  4303.  the customer's code. TAR offers an immediate mechanism for transferring that
  4304.  code back and forth. Coupled with our callback on priority questions, we
  4305.  think we've developed the optimum support system for our ISV customers. It
  4306.  lets them have direct contact over the telephone with Microsoft, and gives
  4307.  them the chance to explain the problem in detail in writing, which is
  4308.  usually the best way to handle it.
  4309.  
  4310.  Every TAR is summarized and rewritten to become a Bulletin Board item after
  4311.  it's answered. Of course, the anonymity of the person who sent in the TAR is
  4312.  always maintained.
  4313.  
  4314.  
  4315.  MSJ: How many people do you have on staff right now?
  4316.  
  4317.  BAKER: In the system support area in Microsoft, there are 35 people
  4318.  supporting languages and operating environments. We have just gotten
  4319.  approval to double our operations staff from 10 to 20. We've also introduced
  4320.  a very interesting job rotation plan that allows engineers to work on DIAL
  4321.  and then rotate back into engineering.
  4322.  
  4323.  
  4324.  MSJ: How long does it usually take for a question on TAR to be answered?
  4325.  
  4326.  BAKER: In the past, because of the limited staff, the typical time was 2 or
  4327.  3 days. Right now, our goal is to get it to within 24 hours on all TAR
  4328.  questions.
  4329.  
  4330.  MSJ: How much does DIAL cost?
  4331.  
  4332.  BAKER: $450 a year for unlimited service covering all of the MS-DOS system's
  4333.  products; XENIX Support is $600 per year.
  4334.  
  4335.  
  4336.  MSJ: Are there any arrangements for long-distance callers?
  4337.  
  4338.  BAKER: We have negotiated an arrangement for low-cost service with Telenet
  4339.  so that DIAL users who call long distance can reduce that charge.
  4340.  
  4341.  
  4342.  MSJ: How many lines do you have now?
  4343.  
  4344.  BAKER: We have 8 Telenet lines and 8 long-distance lines. We also monitor
  4345.  the usage at any particular time and add lines as they are needed.
  4346.  
  4347.  
  4348.  MSJ: What is your own frank evaluation of DIAL 2.0?
  4349.  
  4350.  BAKER: It's a good, solid support vehicle that still needs work. In the
  4351.  past, there weren't enough senior people staffed on DIAL. Of course, we're
  4352.  meeting that need now by adding more people.
  4353.  
  4354.  The knowledge base is probably the most outstanding keyword search bulletin
  4355.  board system in the entire computer industry──and we've looked at the
  4356.  systems that DEC and IBM are using as well, so we really have a
  4357.  technological lead with our system.
  4358.  
  4359.  I think that in certain areas, such as actual support delivery, it does need
  4360.  some enhancement, but Microsoft management is being very responsive in
  4361.  giving us the resources to make it a really great world-class system.
  4362.  
  4363.  ████████████████████████████████████████████████████████████████████████████
  4364.  
  4365.  Rich Text Format Standard Makes Transferring Text Easier
  4366.  
  4367.  ───────────────────────────────────────────────────────────────────────────
  4368.  Also see the following related articles:
  4369.    An RTF Sample
  4370.    Preliminary Rich Text Format Specification
  4371.  ───────────────────────────────────────────────────────────────────────────
  4372.  
  4373.  Nancy Andrews
  4374.  
  4375.  Back in the bad old days of punch cards, formatted text was never an issue.
  4376.  There were no formats; you couldn't even use lowercase. But we've come a
  4377.  long way since then. With bit-mapped screens, state-of-the-art word
  4378.  processors can handle numerous character, paragraph, and section formats.
  4379.  Not only can you get lowercase, you can have 16-point Helvetica in a
  4380.  paragraph with a 1/2-inch hanging indent. However, problems arise when you
  4381.  transfer text and formats to other applications. Rich Text Format (RTF)
  4382.  provides a solution to these problems.
  4383.  
  4384.  RTF is a way to encode formatted text. Microsoft is proposing RTF as a
  4385.  standard to make moving text between applications easier. With RTF, users
  4386.  can transfer text from one application to another and not lose any
  4387.  formatting. And developers can save documents in one format, RTF, and know
  4388.  that other applications can read this information without worrying about
  4389.  translating to each application's unique format.
  4390.  
  4391.  
  4392.  Why RTF
  4393.  
  4394.  Microsoft is pushing RTF because of Microsoft Windows. Currently, if you use
  4395.  Window's clipboard to transfer text between applications, you lose all
  4396.  formatting. For example, if you were transferring formatted text from
  4397.  Windows Write into Micrografx Draw, you'd have to reformat the text within
  4398.  Draw. A way to retain the format from one application when it is transferred
  4399.  to another is needed. If both Write and Draw used RTF, the clipboard could
  4400.  transfer the formats along with the text. Microsoft recognizes the need to
  4401.  exchange documents easily among its own products. It sees RTF as a solution
  4402.  for existing and future products.
  4403.  
  4404.  Current format standards such as IBM's Document Content Architecture (DCA),
  4405.  although widely supported, are not adequate. DCA, for example, lacks a good
  4406.  font strategy. It works with pitch only, doesn't consider point sizes, and
  4407.  has only one code to specify proportional spacing. DCA is most efficient for
  4408.  transferring entire documents, as opposed to short strings.
  4409.  
  4410.  Microsoft wanted a standard that could handle all existing formats and both
  4411.  entire documents and the rapid exchange of short formatted strings. This
  4412.  kind of information is usually transferred via the Windows clipboard. When
  4413.  you transfer short strings, you don't want to include the document name,
  4414.  creator, and date of last modification. But when you transfer entire
  4415.  documents, this information is essential. RTF has the flexibility to handle
  4416.  both types of transfer. Also, the standard had to be what Charles Simonyi,
  4417.  author of RTF and Microsoft's chief architect of applications, calls
  4418.  "forward-looking," that is, one that can handle existing applications and
  4419.  leaves room for future enhancements as well.
  4420.  
  4421.  
  4422.  What RTF Is
  4423.  
  4424.  RTF provides a standard format for interchanging text regardless of the
  4425.  output device, operating system, or operating environment. Text and format
  4426.  instructions are saved in 7-bit ASCII format so that they are easy to read,
  4427.  and you can send them over virtually any communications link.
  4428.  
  4429.  RTF uses "control words" to encode the formats. Control words provide the
  4430.  space so that RTF can be expanded later to include macros or additional
  4431.  formats. Control words use this form:
  4432.  
  4433.    \lettersequence
  4434.    <delimeter>
  4435.  
  4436.  If the delimeter is a digit or a hyphen (-), another parameter follows. A
  4437.  nonletter or digit terminates the control word.
  4438.  
  4439.  In addition to control words, RTF uses braces. A left brace ({) marks the
  4440.  beginning of a group of text, and a right brace (}) marks the end. Braces
  4441.  can be used to delineate footnotes, headers, and titles. In RTF, the control
  4442.  words, the control symbol (\), and the braces constitute control
  4443.  information. All other characters in RTF are plain text.
  4444.  
  4445.  A bit of RTF code might look like this:
  4446.  
  4447.    \rtf0\mac{\fonttbl\f1\ fromanBookman;}
  4448.    {\stylesheet {\s0 Normal;}
  4449.    {\s1\i\qj\snext2\f1 Question;}{\s2\qj\f1 Answer;}}
  4450.    {\s0\f1\b\qc Questions and Answers\par }
  4451.    {\s1\i\qj 1. What is the left margin of this document?\par}
  4452.    {\s2\qj\li720\f1 Since no document parameters were specified, the default
  4453.       of 1800 twips (1.25") is used.\par}}
  4454.  
  4455.  Here's what this information means. \rtf0\ indicates that this is an RTF
  4456.  document and the version number is 0. It uses the Macintosh, rather than the
  4457.  PC or ANSI, character set. Next comes information about the current font,
  4458.  which is from font table 1, the roman family, the Times font. After that is
  4459.  style sheet information. Three different styles are assigned. Style 0 is the
  4460.  normal character and paragraph style. Style 1 is italic, the paragraph is
  4461.  justified, it is always followed by a style 2 paragraph, uses font 1, and is
  4462.  called Question. Style 2 is justified, uses font 1, and is called Answer.
  4463.  
  4464.  Next is information about the first paragraph, followed by the text of that
  4465.  paragraph. It is normal text (style 0), uses font 1, and is bold and
  4466.  centered. The second paragraph is a question, uses the question style, and
  4467.  is italic and justified. The last section is the answer, which is based on
  4468.  style 2, is justified, and uses a left indent of 720 twips or 0.5". (Twips
  4469.  are 1/20th of a point; there are 1,440 twips per inch.)
  4470.  
  4471.  To read a stream of RTF you first need to separate the control information
  4472.  from the plain text and then act on the control information. When you
  4473.  encounter a left brace ({), you have to stack the current state and then
  4474.  continue. When you encounter a control symbol (\), collect the control word
  4475.  and parameter, if any, look up the word in the symbol table (a constant
  4476.  table), and follow the directions outlined there. For example, change a
  4477.  formatting property such as adding the bold format. Then continue writing
  4478.  characters to the current destination by using the current formatting
  4479.  properties. When you come to the right brace (}), unstack the current state.
  4480.  Working with RTF is straightforward──much simpler than decoding proprietary
  4481.  file formats of individual applications.
  4482.  
  4483.  
  4484.  Who's Playing
  4485.  
  4486.  RTF, like any standard, is as valuable as the people who support it.
  4487.  Microsoft believes that most Microsoft Windows  applications developers will
  4488.  support RTF, since it is advantageous for them to be able to easily exchange
  4489.  formatted text. George Grayson, president of Micrografx, one of the first
  4490.  Windows applications developers, says that its products, In*a*Vision, Draw,
  4491.  and Graph, will all support RTF by June. Also, Microsoft currently has
  4492.  several Windows applications in development that will definitely support
  4493.  RTF.
  4494.  
  4495.  Aldus, developer of PageMaker for the Mac and PC, has not yet committed to
  4496.  RTF. But Ted Johnson, an Aldus engineer, says that although RTF was too late
  4497.  to be included in Version 1.0 of Aldus's PC PageMaker, versions after 1.0
  4498.  will definitely support RTF for clipboard transfers. PageMaker will also
  4499.  continue to accept formatted text from several word processors using the
  4500.  proprietary file format of each of these word processors. So it looks like
  4501.  RTF is well on its way to acceptance in the Microsoft Windows  world.
  4502.  
  4503.  
  4504.  What Next
  4505.  
  4506.  Greg Slyngstad, program manager in Microsoft's word processing area and
  4507.  current keeper of the RTF standard, claims "this version is complete, but we
  4508.  expect to extend it in the future to meet the needs of Windows application
  4509.  developers (including Microsoft). Any future version will be superset of the
  4510.  current RTF." The goal of RTF is to retain all formatting in transfer. And
  4511.  Slyngstad's goal is to ensure that the RTF standard responds to the needs of
  4512.  developers.
  4513.  
  4514.  
  4515.  ───────────────────────────────────────────────────────────────────────────
  4516.  An RTF Sample
  4517.  ───────────────────────────────────────────────────────────────────────────
  4518.  
  4519.  {\rtf0\mac {\fonttbl{\f0\fswiss Chicago;}{\f3\fswiss Geneva;}{\f4\fmodern
  4520.  Monaco;}{\f13\fnil Zapf Dingbats;}{\f14\fnil Bookman;}{\f15\fnil N Helvetica
  4521.  Narrow;}{\f20\froman Times;}{\f21\fswiss Helvetica;}{\f22\fmodern
  4522.  Courier;}{\f23\ftech Symbol;}}
  4523.  {\stylesheet{\s243\tqc\tx4320\tqr\tx8640 \sbasedon0\snext243
  4524.  footer;}{\sbasedon222\snext0
  4525.  Normal;}}\margl1613\margr1080\margt720\widowctrl\ftnrestart {\headerl
  4526.  \pard\plain 1-4 see auditors notes{\|}\par
  4527.  \par
  4528.  }\sectd \linemod0\linex0\cols1\colsx0\endnhere {\footer \pard\plain
  4529.  {\i\f20\fs18 This document was prepared with Microsoft(R)
  4530.  Word}{\i\scaps\f20\fs18  }{\i\f20\fs18 Version }{\i\scaps\f20\fs18
  4531.  3.0}{\i\f20\fs18  for Apple(R) Macintosh(TM) systems, \par
  4532.  and printed with an Apple LaserWriter(TM).}{\i\scaps\f20\fs18 \par
  4533.  }\pard\plain \s243\tqc\tx4320\tqr\tx8640 {\f150\fs18 \par
  4534.  }}\pard\plain \ri360 {{\pict\macpict\picw73\pich66
  4535.  02b2012500af016700f81101a00082a0008c01000a0000000002d0024051013500b4016700f1
  4536.  5809000000000000000071001e013500c5016200de016000dd013900c501
  4537.                                      ∙
  4538.                                      ∙
  4539.                                      ∙
  4540.  00f3a0621670002a015300eb016000f7015400f3015700ee015c00eb016000ef015f00f60158
  4541.  00f7015300f3015400f3a0008da00083ff}}{\b\f140\fs28 \par
  4542.  \par
  4543.  }\pard \ri187 {\b\f140\fs32 West Associates, Inc.\par
  4544.  }\pard \ri187\sb40\sl320\brdrt\tqr\tx9360 {\f139 Consolidated Balance
  4545.  Sheet}{\f21\fs20 \tab }{\i\f21\fs20 Current as of: December 31st, 1986\par
  4546.  }\pard \ri5947\sa40\brdrb {\f21\fs20 \par
  4547.  }\pard \ri187\brdrth\tx360\tx1080\tx6480\tqr\tx7560\tx8280\tqr\tx9360
  4548.  {\b\f140\fs20 Assets}{\b\f21\fs20 \tab \tab \tab }{\b\i\f21\fs20 1986\tab
  4549.  }{\i\f21\fs20 \tab 1985\par
  4550.  }\pard \ri187\sb40\brdrt\brdrth\tx360\tx1080\tx8280 {\i\f21\fs20 \tab
  4551.  }{\f21\fs20 Current Assets:\par
  4552.  }\pard \ri187\tx360\tx1080\tqr\tx7560\tqr\tx9360 {\f21\fs20 \tab \tab Cash
  4553.  and temporary cash investments}{\b\f21\fs20 \tab $114,888}{\f21\fs20 \tab
  4554.  $143,284\par
  4555.  \tab \tab Accounts receivable}{\b\f21\fs20 \tab 258,238}{\f21\fs20 \tab
  4556.  136,420\par
  4557.  \tab \tab Inventories (at cost)}{\scaps\f21\fs16\up6 1}{\b\f21\fs20 \tab
  4558.  264,619}{\f21\fs20 \tab 142,457\par
  4559.  }\pard \ri187\sb40\tx360\tx1080\tqr\tx7560\tqr\tx9360 {\f21\fs20 \tab \tab
  4560.  Prepaid Income Taxes}{\b\f21\fs20 \tab 26,751}{\f21\fs20 \tab 27,949\par
  4561.  }\pard \ri187\brdrb\tx360\tx1080\tqr\tx7560\tqr\tx9360 {\f21\fs20 \tab \tab
  4562.  Other current assets}{\b\f21\fs20 \tab 23,055}{\f21\fs20 \tab 18,883\par
  4563.  }\pard \ri187\sb40\brdrb\tx360\tx1080\tx1620\tqr\tx7560\tqr\tx9360
  4564.  {\f21\fs20 \tab \tab \tab }{\f138\fs20 Total Current Assets:}{\b\f21\fs20
  4565.  \tab $687,551}{\f21\fs20 \tab $468,993\par
  4566.  }\pard \ri187\tx360\tx1080\tx1620\tqr\tx7560\tqr\tx9360 {\f21\fs20 \tab
  4567.  Property, plant and equipment\tab \par
  4568.  \tab \tab Land and Buildings}{\b\f21\fs20 \tab 24,892}{\f21\fs20 \tab
  4569.  19,993\par
  4570.  \tab \tab Machinery and equipment}{\b\f21\fs20 \tab 68,099}{\f21\fs20 \tab
  4571.  51,445\par
  4572.  \tab \tab Office furniture and equipment}{\b\f21\fs20 \tab 30,575}{\f21\fs20
  4573.  \tab 22,628\par
  4574.  }\pard \ri187\sa60\tx360\tx1080\tx1620\tqr\tx7560\tqr\tx9360 {\f21\fs20 \tab
  4575.  \tab Leasehold improvements}{\scaps\f21\fs16\up6 2}{\b\f21\fs20 \tab
  4576.  26,008}{\f21\fs20 \tab 15,894\par
  4577.  }\pard \ri187\brdrt\tx360\tx1080\tx1620\tqr\tx7560\tqr\tx9360 {\f21\fs20
  4578.  \tab \tab \tab }{\b\f21\fs20 \tab $149,574}{\f21\fs20 \tab $109,960\par
  4579.  }\pard \ri187\tx360\tx1080\tx1620\tqr\tx7560\tqr\tx9360 {\f21\fs20 \tab \tab
  4580.  Accumulated depreciation and amortization}{\b\f21\fs20 \tab
  4581.  (73,706)}{\f21\fs20 \tab (42,910)\par
  4582.                                      ∙
  4583.                                      ∙
  4584.                                      ∙
  4585.  \ri187\sb40\brdrb\brdrdb\tx360\tx1080\tx1620\tqr\tx7560\tqr\tx9360
  4586.  {\f21\fs20 \tab \tab \tab }{\b\f21\fs20 \tab $788,786}{\f21\fs20 \tab
  4587.  $556,579\par
  4588.  }\pard \ri187\brdrdb\tx360\tx1080\tx1620\tqr\tx7560\tqr\tx9360 {\f21\fs20
  4589.  \par
  4590.  }{\f138\fs18 All amounts shown in thousands (000)\par
  4591.  }}
  4592.  
  4593.  ───────────────────────────────────────────────────────────────────────────
  4594.  Preliminary Rich Text Format Specification
  4595.  ───────────────────────────────────────────────────────────────────────────
  4596.  
  4597.  RTF text is a form of encoding for various text formatting properties,
  4598.  document structures, and document properties, using the printable ASCII
  4599.  character set. The main encoding mechanism of "control words" provides a
  4600.  naming convention to expand the realm of RTF to include macros, programming,
  4601.  and so on. Special characters can also be thus encoded, although RTF does
  4602.  not prevent the utilization of character codes outside the ASCII printable
  4603.  set.
  4604.  
  4605.  
  4606.  BASIC INGREDIENTS
  4607.  
  4608.  A file or stream of RTF consists of "plain text" interspersed with control
  4609.  symbols, control words, and braces. Control words follow the format:
  4610.  
  4611.    \<alphabetic string> <delimiter>
  4612.  
  4613.  The delimiter can be a space or any other nonalphanumeric character. When a
  4614.  numeric parameter follows the control word, the first digit of the parameter
  4615.  (or the minus sign, in the case of a negative number) functions as the
  4616.  delimiter of the control word. The parameter is then in turn delimited by a
  4617.  space or any other nonalphanumeric character.
  4618.  
  4619.  Control symbols consist of a \ character followed by a single nonalphabetic
  4620.  character. They require no additional delimiting characters. Control symbols
  4621.  are compact, but there are not too many of them, whereas the number of
  4622.  possible control words is unlimited. The parameter is partially incorporated
  4623.  in control symbols, so a program that does not understand a control symbol
  4624.  can recognize and ignore the corresponding parameter as well.
  4625.  
  4626.  Each group begins with a control word that describes the contents of the
  4627.  group. { indicates the beginning of a text group and } indicates its end.
  4628.  The text grouping is used for formatting and to delineate structural
  4629.  elements of the document, such as the footnotes, headers, title, and so on.
  4630.  All other characters in RTF text constitute "plain text." Since the
  4631.  characters \, {, and } have specific uses in RTF, the control symbols \\,
  4632.  \{, and \} are provided to express the corresponding plain characters.
  4633.  
  4634.  
  4635.  WHAT RTF TEXT MEANS
  4636.  
  4637.  The reader of an RTF stream will be concerned with separating control
  4638.  information from plain text and acting on control information. This is
  4639.  designed to be a relatively simple process, as described below. Some control
  4640.  information just contributes special characters to the plain-text stream.
  4641.  Other control information changes the "program state," which includes
  4642.  properties of the document as a whole and also a stack of "group states"
  4643.  that apply to parts of the document.
  4644.  
  4645.  The file is structured as groups and subgroups. Each group state is defined
  4646.  by a text group (text enclosed in braces). The group state specifies the
  4647.  following:
  4648.  
  4649.    a.  The "destination" or the part of the document that the plain text is
  4650.        building up.
  4651.  
  4652.    b.  The character formatting properties, such as bold or italic.
  4653.  
  4654.    c.  The paragraph formatting properties, such as justification.
  4655.  
  4656.    d.  The section formatting properties, such as number of columns.
  4657.  
  4658.  
  4659.  GROUPS AND SUBGROUPS
  4660.  
  4661.  The overall grouping is the document file as a whole, since the entire
  4662.  document is enclosed in braces, beginning with the control word
  4663.  \rtf<parameter> with the parameter being the version number of the writer.
  4664.  This control word must begin every RTF file, and the entire file must end
  4665.  with the corresponding closing brace.
  4666.  
  4667.  Before any text in the RTF file is entered, the character set for the entire
  4668.  destination may be declared using one of these control words:
  4669.  
  4670.    \ansi            Text is the ansi character used by Microsoft Windows
  4671.                     (default).
  4672.    \mac             Text is the Macintosh character set.
  4673.    \pc              Text is the IBM PC character set.
  4674.  
  4675.  
  4676.  COLOR TABLE
  4677.  
  4678.  The control word \colortbl is used to define the color table, with numeric
  4679.  indexes (starting at 0) for red, green, and blue. These indexes are the same
  4680.  as those used in Windows (the italicized portion represents the variable
  4681.  numeric parameter):
  4682.  
  4683.    \red000          Red index
  4684.    \green000        Green index
  4685.    \blue000         Blue index
  4686.    \cf000           Foreground color
  4687.    \cb000           Background color
  4688.  
  4689.  Each set of color definitions delimited by semicolons defines the next
  4690.  sequential color number. The following example defines color 0 and color 2:
  4691.  
  4692.    {\colortbl\red128\green0\blue64;;\red64\green128\blue0;}
  4693.  
  4694.  
  4695.  FONT TABLE
  4696.  
  4697.  The control word \fonttbl designates the group that is the font table, which
  4698.  assigns the font name and family to the font numbers used. The text is the
  4699.  font name delimited by semicolons. Default is used if no font was assigned
  4700.  and the recipient should use whatever font is considered the default for
  4701.  that particular output device. The font table, if it exists, must occur
  4702.  before the style sheet defintion and any text in the file. Possible font
  4703.  families are:
  4704.  
  4705.    \fnil            Family unknown (default)
  4706.    \froman          Roman family. Proportionally spaced, serif (Times Roman,
  4707.                     Century, Schoolbook, etc.)
  4708.    \fswiss          Swiss family. Proportionally spaced, sans serif
  4709.                     (Helvetica, Swiss, etc.)
  4710.    \fmodern         Fixed pitch, serif or sans serif (Pica, Elite, Courier,
  4711.                     etc.)
  4712.    \fscript         Script family (Cursive, etc.)
  4713.    \fdecor          Decorative fonts (Old English, etc.)
  4714.    \ftech           Technical or symbol fonts
  4715.  
  4716.  Example:
  4717.  
  4718.    {\fonttbl\f0\froman Tms Rmn;\f1\fswiss Helv;\f2\fnil default;}
  4719.  
  4720.  
  4721.  STYLE SHEETS
  4722.  
  4723.  The style sheet for the document is defined by a group beginning with the
  4724.  control word \stylesheet.
  4725.  
  4726.  More precisely, plain text in the group between semicolons is interpreted as
  4727.  style names, which will be defined to stand for formatting properties that
  4728.  are in effect.
  4729.  
  4730.  Example:
  4731.  
  4732.    {\stylesheet{\s0\f3\fs20\qj Normal;}
  4733.    {\s1\f3\fs24\b\qc Heading Level 3;}}
  4734.  
  4735.  This defines Style 0 (given the name "Normal") as 10-point justified type in
  4736.  font 3 (the font is defined in the font table). Style 1 (Heading Level 3),
  4737.  is 12-point font 3, boldface and centered.
  4738.  
  4739.  The following fields may be present if the destination is \stylesheet.
  4740.  
  4741.    \sbasedon000     This defines the style number which the current style is
  4742.                     based on. If this control word is omitted, the style is
  4743.                     not based on any style.
  4744.  
  4745.    \snext000        This defines the next style associated with the current
  4746.                     style. If this control word is omitted, the next style
  4747.                     is itself.
  4748.  
  4749.  
  4750.  PICTURES
  4751.  
  4752.  If the group begins with the control word \pict the plain text within the
  4753.  group is a hex dump of a picture. The following optional parameters may also
  4754.  exist if the group is a picture. If they are not present, the default frame
  4755.  size equals the picture size. A picture must be preceded by the \chpict
  4756.  special character that serves as the anchor point for the picture that
  4757.  follows.
  4758.  
  4759.    \pich000         Defines picture frame height in pixels. The picture
  4760.                     frame is the area set aside for the image. The picture
  4761.                     itself does not necessarily fill the frame (see
  4762.                     \picscaled).
  4763.  
  4764.    \picw000         Picture frame width in pixels
  4765.  
  4766.    \picscaled       Scales the picture up or down to fit within the
  4767.                     specified size of the frame
  4768.  
  4769.    \wmetafile       The picture is a Windows metafile
  4770.  
  4771.    \macpict         The picture is in Mac Quick Draw format
  4772.  
  4773.    \bin000          Special field used to include binary information within
  4774.                     the file (in lieu of hex as expected). The parameter
  4775.                     defines the number of bytes of binary information that
  4776.                     follow. This is useful for the "clipboard" data
  4777.                     exchange where the file is not intended to be
  4778.                     transferred over a communications line.
  4779.  
  4780.  
  4781.  FOOTNOTES
  4782.  
  4783.  If the group begins with the control word \footnote the group contains
  4784.  footnote text. The footnote is anchored to the character that immediately
  4785.  preceds the footnote group. The group may be preceded by the footnote
  4786.  reference characters if automatic footnote numbering is defined. A footnote
  4787.  group may also have the following complements:
  4788.  
  4789.    \ftnsep          Text is a footnote separator.
  4790.    \ftnsepc         Text is a separator for continued footnotes.
  4791.    \ftncn           Text is continued footnote notice.
  4792.  
  4793.  
  4794.  HEADERS AND FOOTERS
  4795.  
  4796.  If the group begins with the control words\header or \footer the group is
  4797.  the header or footer for the current section. The group must precede the
  4798.  first plain text character in the section. The following variant forms may
  4799.  also be used:
  4800.  
  4801.    \headerl         Header on left-hand pages only
  4802.    \headerr         Header on right-hand pages only
  4803.    \headerf         Header on first page only
  4804.    \footerl         Footer on left-hand pages only
  4805.    \footerr         Footer on right-hand pages only
  4806.    \footerf         Footer on first page only
  4807.  
  4808.  
  4809.  INFORMATION
  4810.  
  4811.  The file may also contain an information group, specified by the control
  4812.  word \info. This group is used to store such information as the title. The
  4813.  name of the author, and the subject of the document. The appropriate
  4814.  information is entered as plain text following these control words:
  4815.  
  4816.    \title
  4817.    \subject
  4818.    \author
  4819.    \operator
  4820.    \keywords
  4821.    \comment         Text will be ignored.
  4822.    \version
  4823.    \doccomm         This last control word is for storing comments. It
  4824.                     should not be confused with \comment  which specifies
  4825.                     that the text is to be ignored. Here is an example of
  4826.                     an information block:
  4827.  
  4828.    {\info{\title Unified Field Theory Analysis}{\author A. Finestyne}
  4829.  
  4830.  Other control words allow values to be automatically entered into the
  4831.  information block:
  4832.  
  4833.    \vern000         Internal version number
  4834.    \creatim         Creation time
  4835.    \yr000           Year
  4836.    \mo000           Month
  4837.    \dy000           Day
  4838.    \hr000           Hour
  4839.    \revtim          Revision time
  4840.    \printim         Last print time
  4841.    \buptim          Backup time
  4842.    \edmins          Minutes of editing
  4843.    \nofpages000     Number of pages
  4844.    \nofwords000     Number of words
  4845.    \nofchars000     Number of characters
  4846.    \id000           Internal id number
  4847.  
  4848.  
  4849.  TEXT PROPERTIES
  4850.  
  4851.  Note that a change of destination will reset all text properties to their
  4852.  default values and that changes are only legal at the beginning of a text
  4853.  group.
  4854.  
  4855.  
  4856.  DOCUMENT FORMATTING PROPERTIES
  4857.  
  4858.  The default value (given in square brackets) will occur if the control word
  4859.  is omitted. When the control words are used,000 is replaced by numeric
  4860.  parameters. All measurements are in twips which are twentieths of a point or
  4861.  1/1440th of an inch.
  4862.  
  4863.  Examples:
  4864.    \paperw000       Paper width in twips [12240]
  4865.    \paperh000       Paper height [15840]
  4866.    \margl000        Left margin [1800]
  4867.    \margr000        Right margin [1800]
  4868.    \margt000        Top margin [1440]
  4869.    \margb000        Bottom margin [1440]
  4870.    \facingp         Facing pages (enables gutters and odd/even headers)
  4871.    \gutter000       Gutter width (inside of facing pages) [0]
  4872.    \ogutter000      Outside gutter width [0]
  4873.    \deftab000       Default tab width [720]
  4874.    \widowctrl       Enable widow control
  4875.    \headery000      Header y position from top of page [1080]
  4876.    \footery000      Footer y position from bottom of page [1080]
  4877.    \ftnbj           Footnotes at bottom of page (default)
  4878.    \ftnsep          Text is a footnote separator
  4879.    \ftnsepc         Text is a separator for continued footnotes
  4880.    \endnotes        Footnotes at end of section
  4881.    \ftntj           Footnotes beneath text (top justified)
  4882.    \ftnstart000     Starting footnote number [1]
  4883.    \ftnrestart      Restart footnote numbers each page
  4884.    \pgnstart000     Starting page number [1]
  4885.    \linstart000     Starting line number [1]
  4886.    \landscape000    Printed in landscape format
  4887.  
  4888.  
  4889.  SECTION FORMATTING PROPERTIES
  4890.  
  4891.  Examples:
  4892.    \sectd           Reset to default section properties
  4893.    \sbknone         Section break continuous (no break)
  4894.    \sbkcol          Section break starts a new column
  4895.    \sbkpage         Section break starts a new page (default)
  4896.    \sbkeven         Section break starts an even page
  4897.    \sbkodd          Section break starts odd page
  4898.    \pgnrestart      Restart page numbers at 1
  4899.    \pgndec          Page number format decimal
  4900.    \pgnucrm         Page number format upper case roman numeral
  4901.    \pgnlcrm         Page number lower case roman numeral
  4902.    \pgnucltr        Page number format upper case letter
  4903.    \pgnlcltr        Page number format lower case letter
  4904.    \pgnx000         Page number x pos [720]
  4905.    \pgny000         Page number y pos [720]
  4906.    \linemod000      Line number modulus [1]
  4907.    \linex000        Line number text distance [360]
  4908.    \linerestart     Line number restart at 1 (default)
  4909.    \lineppage       Line number restart on each page
  4910.    \linecont        Line number continued from previous section
  4911.    \vertalt         Vertically align starting at top of page (default)
  4912.    \vertalc         Vertically align in center of page
  4913.    \vertalj         Vertically justify top and bottom
  4914.    \vertal          Vertically align starting at the bottom
  4915.    \cols000         Number of columns (snaking) [1]
  4916.    \colsx000        Space between columns [720]
  4917.    \endhere         Include endnotes in this section
  4918.    \titlepg         Title page is special
  4919.  
  4920.  
  4921.  PARAGRAPH FORMATTING PROPERTIES
  4922.  
  4923.  Examples:
  4924.    \pard            Reset to default paragraph properties
  4925.    \s000            Style; if a style is specified, the paragraph formatting
  4926.                     implied by that style must still be specified with the
  4927.                     paragraph
  4928.    \ql              Quad left (default)
  4929.    \qr              Quad right
  4930.    \qj              Justified
  4931.    \qc              Centered
  4932.    \fi000           First line indent [0]
  4933.    \li000           Left indent [0]
  4934.    \ri000           Right indent [0]
  4935.    \sb000           Space before [0]
  4936.    \sa000           Space after [0]
  4937.    \sl000           Space between lines (If no \sl is specified, default is
  4938.                     12 points. If \sl000 is specified, line spacing is
  4939.                     automatically determined by the tallest font on the
  4940.                     line).
  4941.    \keep            Keep
  4942.    \keepn           Keep with next paragraph
  4943.    \sbys            Side by side
  4944.    \pagebb          Page break before
  4945.    \noline          No line numbering
  4946.    \tx000           Tab position (this places a vertical bar at the
  4947.                     specified position for the height of the paragraph)
  4948.    \tqr             Flush right tab (last specified position)
  4949.    \tqc             Centered tab
  4950.    \tqdec           Decimal aligned tab
  4951.    \brdrt           Border top
  4952.    \brdrb           Border bottom
  4953.    \brdrl           Border left
  4954.    \brdrr           Border right
  4955.    \box             Border all around
  4956.    \brdrs           Single thickness border
  4957.    \brdrth          Thick border
  4958.    \brdrsh          Shadow border
  4959.    \brdrdb          Double border
  4960.    \tldot           Leader dots
  4961.    \tlhyph          Leader hyphens
  4962.    \tlul            Leader underscore
  4963.    \tlth            Leader thick line
  4964.  
  4965.  
  4966.  CHARACTER FORMATTING PROPERTIES
  4967.  
  4968.  Examples:
  4969.    \plain           Reset to default text properties
  4970.    \b               Boldface
  4971.    \i               Italic
  4972.    \strike          Strikethrough
  4973.    \outl            Outline
  4974.    \shad            Shadow
  4975.    \scaps           Small caps
  4976.    \caps            All caps
  4977.    \v               Invisible text
  4978.    \f000            Font number
  4979.    \fs000           Font size in half points [24]
  4980.    \expnd000        Expansion or compression of space between characters in
  4981.                     quarter points (negative value indicates compression).
  4982.    \ul              Continuous Underline
  4983.    \ulw             Word underline
  4984.    \uld             Dotted underline
  4985.    \uldb            Double underline
  4986.    \ulnone          Cancels all underlining
  4987.    \up000           Superscript in half points [6]
  4988.    \dn000           Subscript in half points [6]
  4989.  
  4990.  A zero following the character formatting instruction turns off the format.
  4991.  
  4992.  For example:
  4993.    \b0              Turns off the boldface
  4994.    \i0              Turns off the italic
  4995.  
  4996.  
  4997.  SPECIAL CHARACTERS
  4998.  
  4999.  If a character name is not recognized by a reader, it will simply be
  5000.  ignored. Other characters may be added for interchange with other programs.
  5001.  
  5002.  These are examples of special characters:
  5003.    \chpgn           Current page number (as in headers)
  5004.    \chftn           Auto footnote references (footnote to follow in a
  5005.                     group.)
  5006.    \chdate          Current date (as in headers)
  5007.    \chtime          Current time (as in headers)
  5008.    \|               Formula character
  5009.    \~               Nonbreaking space
  5010.    \-               Discretionary, or soft, hyphen
  5011.    \_               Nonbreaking hyphen
  5012.    \'hh             Any hex value (can be used to identify 8-bit value)
  5013.    \page            Required page break
  5014.    \line            Required line break (no paragraph break)
  5015.    \par             End of paragraph
  5016.    \sect            End of section and end of paragraph
  5017.    \tab             Same as ASCII 9
  5018.  
  5019.  For simplicity of operation, ASCII 9 will be accepted as tab. The control
  5020.  code \<10> (backslash followed by ASCII 10) and \<13> will both be accepted
  5021.  as \par. ASCII 10 and ASCII 13 without the backslashes will be ignored. They
  5022.  may be used to include carriage returns for easier readability but will have
  5023.  no effect on the interpretation as long as they do not occur within a
  5024.  control word. It's a good idea to insert carriage returns every 255
  5025.  characters or less to facilitate transmission via E-mail systems.
  5026.  
  5027.  ████████████████████████████████████████████████████████████████████████████
  5028.  
  5029.  Ask Dr. Bob!
  5030.  
  5031.  DOS Path
  5032.  
  5033.  Dear Dr. Bob,
  5034.  
  5035.  I need to use the MS-DOS path to search for the overlays, initialization
  5036.  files, and data files my program needs. I can't figure out how to get this
  5037.  from MS-DOS. Can you give me a clue about where to get this information?
  5038.  ──Searching
  5039.  
  5040.  Dear Searching,
  5041.  
  5042.  It's true that MS-DOS has no usable support for path information. But you
  5043.  can use the C library getenv ("PATH") function. It returns a pointer to the
  5044.  current path string. At that point it's up to your program to decode and use
  5045.  that information, which is in the same format you see when you type "path"
  5046.  after the MS-DOS prompt. You need to parse the string and then scan
  5047.  individual directories for the files that you will need.
  5048.  
  5049.  
  5050.  Redundan-C
  5051.  
  5052.  Dear Dr. Bob,
  5053.  
  5054.  My programs (like most everyone's these days) use some C modules and some
  5055.  MASM modules. It seems so redundant to type the same declarations in C
  5056.  format and then in MASM format. Often, I make a change in one and then
  5057.  forget to incorporate it into the other. Is there a way to get around this?
  5058.  ──Forgetful
  5059.  
  5060.  Dear Forgetful,
  5061.  
  5062.  Actually, there is. The trick is to use C's preprocessor phase. What you can
  5063.  do is make a header file that produces the definitions required for both C
  5064.  and MASM. Then you write one declaration file using generic rather than C-
  5065.  or MASM-specific declarations. When you make a change, all you need to do is
  5066.  change the generic header file, and it will automatically change both the C
  5067.  and MASM declarations.
  5068.  
  5069.  Here's how it works. First you make a header file like the one in Figure 1,
  5070.  called C_OR_ASM.HDR. It takes information from a generic program declaration
  5071.  file and turns it into the format required by either the C compiler or MASM.
  5072.  
  5073.  Next, put the declarations for your program in a file like the one shown in
  5074.  Figure 2. In this case, we have called our file SHARED.HDR. As you can see,
  5075.  instead of using either C or MASM's declaration formats, what you use are
  5076.  generic formats. C_OR_ASM.HDR then takes this file and turns it into the
  5077.  required format.
  5078.  
  5079.  The last piece you need to finish the puzzle is a way of telling the
  5080.  compiler or assembler about these files. Here's where you use C's
  5081.  preprocessor. You use both the /D and /EP switches. The /D switch tells the
  5082.  preprocessor which set of definitions to use in C_OR_ASM. /EP tells the
  5083.  compiler to stop after this preprocessor stage. If you're assembling a MASM
  5084.  module, use this line:
  5085.  
  5086.    msc shared.hdr
  5087.    /DMASM_STYLE /EP;
  5088.    >shared.inc
  5089.  
  5090.  It takes SHARED.HDR and produces the declarations required in MASM format in
  5091.  a file called SHARED.INC.
  5092.  
  5093.  For C declarations use the following line:
  5094.  
  5095.    msc shared.hdr
  5096.    /DC_STYLE /EP;
  5097.    >shared.h
  5098.  
  5099.  It produces the declarations in C format in a file called SHARED.H.
  5100.  
  5101.  Now you have declaration files in both formats. All you need to do is
  5102.  include them with your .C or .ASM files when you compile. If you make any
  5103.  changes, the only file you need to change is SHARED.HDR.
  5104.  
  5105.  
  5106.  Taking Out Mouse Garbage
  5107.  
  5108.  Dear Dr. Bob,
  5109.  
  5110.  I am currently writing a QuickBASIC program that uses some of the new hi-res
  5111.  EGA modes──SCREEN 8, 9, and 10. But whenever I move the mouse around while
  5112.  my program is writing to the screen, I get garbage. Is there a fix for this?
  5113.  ──Mystified
  5114.  
  5115.  
  5116.  Dear Mystified,
  5117.  
  5118.  You're in trouble. Because the EGA has write-only registers, in order for
  5119.  two programs to write to the screen at the same time, they need to
  5120.  cooperate. Microsoft QuickBASIC doesn't cooperate with anyone. So you can
  5121.  just avoid using the mouse, or, if the mouse is critical, disable it while
  5122.  you're drawing on the screen. For additional information you can call
  5123.  Microsoft Technical Support at (206) 882-8089 and ask for "Writing EGA
  5124.  Software for the IBM PC."
  5125.  
  5126.  
  5127.  Hexing Windows' Calculator
  5128.  
  5129.  Dear Dr. Bob,
  5130.  
  5131.  I used to have a terminate-and-stay-resident calculator I used all the time.
  5132.  When I started using Microsoft Windows, I found I really needed all my
  5133.  memory for Windows and the applications I run with it. I've tried using the
  5134.  Windows calculator. Although it has the basics, what I need is the ability
  5135.  to display the hex equivalent of decimal numbers. Is there any way I can get
  5136.  the Windows calculator to do this?──Refiguring
  5137.  
  5138.  Dear Refiguring,
  5139.  
  5140.  You're in luck. It's quick and easy. Just hold down the H key and the number
  5141.  currently displayed will temporarily change to its hexidecimal equivalent.
  5142.  
  5143.  
  5144.  Standard Memory Error
  5145.  
  5146.  Dear Dr. Bob,
  5147.  
  5148.  I'm running Microsoft Windows and I'm using standard applications. When I
  5149.  quit one of the standard applications, Windows doesn't always seem to
  5150.  relinquish all of its memory. Occasionally I get a "Not enough memory..."
  5151.  error when I think I shouldn't.──Puzzled
  5152.  
  5153.  Dear Puzzled,
  5154.  
  5155.  This can happen because of the way Windows manages memory for standard
  5156.  applications. You need to follow a couple of basic guidelines when working
  5157.  with several standard applications and Windows. First, load the largest
  5158.  standard application. When Windows starts the second standard application,
  5159.  it swaps the first out and the second in. Loading the largest first ensures
  5160.  that there will be enough room. Then if you quit the largest application the
  5161.  second will still have as much memory as was allotted for the largest one.
  5162.  You can fix this by choosing the About... command in the MS-DOS Executive
  5163.  System menu. In addition to displaying the amount of memory, it also does
  5164.  garbage collection and will reclaim and reorganize the unused memory.
  5165.  
  5166.  
  5167.  BitBlt Limits
  5168.  
  5169.  Dear Dr. Bob,
  5170.  
  5171.  I'm working on a Windows app. I tried to use BitBlt to copy a large portion
  5172.  of the screen, but it didn't work. What's wrong?──Frustrated
  5173.  
  5174.  Dear Frustrated,
  5175.  
  5176.  An undocumented limitation of existing versions of Windows is that bitmaps
  5177.  can't be larger than 64K. One way you can get around this is to use
  5178.  "banding"──splitting the block that you want to move into rectangles and
  5179.  then calling BitBlt on each of the smaller rectangles.
  5180.  
  5181.  
  5182.  Stock Bitmaps
  5183.  
  5184.  Dear Dr. Bob,
  5185.  
  5186.  The Windows Software Development Kit documentation for CreateCompatible
  5187.  display context says that "GDI automatically selects a monochrome stock
  5188.  bitmap" for the new memory display context. What does this mean? Is there
  5189.  something special about a "stock bitmap"?──Wondering
  5190.  
  5191.  Dear Wondering,
  5192.  
  5193.  There is nothing special about a monochrome stock bitmap. It is simply a
  5194.  bitmap with one plane and one bit per pixel. By using the CreateCompatibleDC
  5195.  function a display context is set up whose "display surface" is simply a
  5196.  bitmap allocated from system memory──it is not actually displayed anywhere.
  5197.  Any GDI function may be used to draw into this bitmap. BitBlt can quickly
  5198.  move images between itself and another DC. When a memory DC is created,
  5199.  Windows allocates a monochrome bitmap for it as a default, even if your
  5200.  original had color. If you have all color information, you can switch to a
  5201.  color bitmap by using CreateBitmap and SelectObject.
  5202.  
  5203.  
  5204.  Scaling Text
  5205.  
  5206.  Dear Dr. Bob,
  5207.  
  5208.  I'm confused by Windows' mapping modes. My graphics get scaled because I
  5209.  specify anisotropic mode, but my text doesn't. Is there a way to scale text?
  5210.  ──Confused
  5211.  
  5212.  Dear Confused,
  5213.  
  5214.  Maybe. You can tell whether a given device supports character scaling with
  5215.  GetDeviceCaps (hDC,TEXTCAPS). Certain bits of the return value tell what
  5216.  sorts of text display tricks are supported. If the device does support some
  5217.  text scaling, be sure to use DrawText, instead of TextOut, because TextOut
  5218.  doesn't scale.
  5219.  
  5220.  
  5221.  Interrupt Routines
  5222.  
  5223.  Dear Dr. Bob,
  5224.  
  5225.  I'm writing a Windows app that uses a special interrupt-driven I/O board.
  5226.  Obviously, it's not supported by Windows. Ideally, I would like my interrupt
  5227.  service routine to send messages to the Windows app. It would be notified by
  5228.  GetMessage, just as when you press a key or move the mouse. Am I dreaming?
  5229.  ──Dreamer
  5230.  
  5231.  Dear Dreamer,
  5232.  
  5233.  Yes. First of all, you can't call any Windows functions from within your
  5234.  interrupt service routine because right now Windows is not re-entrant. You
  5235.  might have better luck when DOS and Windows are truly multitasking. Windows
  5236.  handles the keyboard and mouse in a special way and does not allow you to
  5237.  define your own messages that would work like the keyboard and mouse. The
  5238.  best you can do is use SetTimer to generate WM_TIMER messages for your
  5239.  Windows application periodically. You can have your interrupt routine set
  5240.  flags, which are polled in the WM_TIMER code. Obviously, this means that you
  5241.  can't count on a quick response since you'll be at the mercy of the Windows
  5242.  scheduler and the other applications to return your program control
  5243.  occasionally.
  5244.  
  5245.  
  5246.  Windows Color Support
  5247.  
  5248.  Dear Dr. Bob,
  5249.  
  5250.  The EGA hardware supports 16 colors. To my surprise, I discovered Windows
  5251.  displays only eight. Say it ain't so, Bob.──Surprised
  5252.  
  5253.  Dear Surprised,
  5254.  
  5255.  When Microsoft wrote the EGA driver, it traded colors for speed. The EGA
  5256.  card has four planes, allowing a possibility of 16 colors. Microsoft chose
  5257.  to use only three planes for color and reserved the fourth plane for the
  5258.  mouse cursor and the other cursor. If Microsoft had given this plane to
  5259.  color rather than the cursor, each time the mouse moved you would need to
  5260.  restore the screen in the old cursor position, save the screen in the new
  5261.  cursor position, and draw the cursor. All three operations would have to
  5262.  process all four planes. This would be too slow, especially on an 8088 PC.
  5263.  
  5264.  By dedicating one plane to the cursor, you don't have to save a copy of the
  5265.  screen under the cursor. Moving the cursor is simply erasing it from the old
  5266.  position and drawing it at the new position. This tradeoff seems reasonable.
  5267.  After all, Windows can dither the dots and get 64 shades for each of the
  5268.  eight colors that are supported.
  5269.  
  5270.  
  5271.  Using Windows and SYMDEB Together
  5272.  
  5273.  Dear Dr. Bob,
  5274.  
  5275.  I do a lot of traveling. I like to use my time on the airplane to work on my
  5276.  hobby──debugging Windows apps. How can I have my Windows app and SYMDEB
  5277.  running on the screen at the same time?──Jetsetter
  5278.  
  5279.  Dear Jetsetter,
  5280.  
  5281.  First, fasten your seatbelt, and then relax. It has been reported that you
  5282.  can run Windows and SYMDEB on the same screen some of the time. But this
  5283.  doesn't work reliably, and in fact, Dr. Bob hasn't been able to get it to
  5284.  work at all.
  5285.  
  5286.  In any event you'll most likely find it easier between landings and takeoffs
  5287.  to use a second monitor or a serial terminal. That way you can see your
  5288.  Windows app running on one screen and SYMDEB on the other.
  5289.  
  5290.  
  5291.  Getting the Same Image
  5292.  
  5293.  Dear Dr. Bob,
  5294.  
  5295.  I'm using the same series of steps for output to the screen and the printer,
  5296.  but the images are different. Is there a way to guarantee that the output
  5297.  will be the same on both devices?──Curious
  5298.  
  5299.  Dear Curious,
  5300.  
  5301.  You can't always guarantee the images will be the same because the images
  5302.  that are produced depend on the capability of the output device. For
  5303.  example, monitors differ in number of colors and resolution as well as
  5304.  support from the device driver. To get the same image on different devices,
  5305.  you must use SelectObject to give each device the same pen, brush, and font.
  5306.  Then it's up to Windows to match what you send to the capabilities of the
  5307.  device.
  5308.  
  5309.  
  5310.  Self-Modifying Code
  5311.  
  5312.  Dear Dr. Bob,
  5313.  
  5314.  Let's say that you have a program that manipulates its own code. How then
  5315.  can it function with only one code segment? Also, is there an objective
  5316.  reality?──Philosophical
  5317.  
  5318.  
  5319.  Dear Philosophical,
  5320.  
  5321.  Well, if you must write self-modifying code... be sure that the modified
  5322.  code can be relocated, and specify that it is not discardable. If Windows
  5323.  discards and reloads, then some can be lost. To prevent this, state that it
  5324.  isn't moveable in the definition file. Windows uses the same code segment
  5325.  when you load multiple instances of the same program. To force Windows to
  5326.  load a fresh copy of the code specify:
  5327.  
  5328.    CODE MULTIPLE MOVEABLE
  5329.  
  5330.  in the Module Definition File, passed to the linker.
  5331.  
  5332.  As for objective reality, Dr. Bob defers to his colleague Dr. Science of San
  5333.  Francisco, Calif.
  5334.  
  5335.  
  5336.  Figure 1:
  5337.  
  5338.  #ifdef    MASM_STYLE
  5339.     #define CONSTANT(name,value)     name = value
  5340.     #define INTVAR(name)             name DW ?
  5341.     #define INTARRAY(name,size)      name DW size dup(?)
  5342.     #define STRUCVAR(name,strc)      name DB (size strc)dup(?)
  5343.     #define DEFSTRUC(name)           name STRUC
  5344.     #define ENDSTRUC(name)           name ENDS
  5345.  #endif
  5346.  
  5347.  #ifdef    C_STYLE
  5348.     #define CONSTANT(name,value)    #define name (value)
  5349.     #define INTVAR(name)            int  name;
  5350.     #define INTARRAY(name,size)     int  name[size];
  5351.     #define STRUCVAR(name,strc)     struct strc name;
  5352.     #define DEFSTRUC(name)          struct  name {
  5353.     #define ENDSTRUC(name) };
  5354.  #endif
  5355.  
  5356.  
  5357.  Figure 2:
  5358.  
  5359.  #include "c_or_asm.hdr"
  5360.  
  5361.  CONSTANT (ScreenHt,    200)
  5362.  CONSTANT (ScreenWidth, 640)
  5363.  CONSTANT (MaxNumPts,   1024)
  5364.  
  5365.  DEFSTRUC (Point)
  5366.  INTVAR (x)
  5367.  INTVAR (y)
  5368.  ENDSTRUC (Point)
  5369.  
  5370.  STRUCVAR (startPt,Point)
  5371.  INTVAR     (counter)
  5372.  INTARRAY (data,MaxNumPts)
  5373.  
  5374.  
  5375.  Figure 3:
  5376.  
  5377.  $cursor dd  0b8000000h  ;for MONO adapter use 0b0000000h
  5378.  $hexdig db  '0123456789ABCDEF'
  5379.  
  5380.  ;------ Display character in AL and advance cursor:
  5381.  $showc: push  es,di
  5382.           les di,cs:$cursor     ; Get current cursor
  5383.                                 ; (segment & offset)
  5384.           mov ah,07h            ; Set attribute
  5385.           cld
  5386.           stosw                 ; Write char to screen
  5387.           mov word ptr cs:$cursor,di ; Save csr (offset only)
  5388.           pop di,es
  5389.           ret
  5390.  
  5391.  ;------ Display value in DX as a hex number:
  5392.  $showx: push  f,ax,bx,cx
  5393.          mov cx,12             ; Shift by 12,8,4, and 0 bits
  5394.  $sx1:   mov bx,dx             ; Get number from DX
  5395.          shr bx,cl             ; Get next nybble to display
  5396.          and bx,000fh
  5397.          mov al,cs:$hexdig[bx] ; Map nybble to hex digit
  5398.          call  $showc          ; Show hex digit
  5399.          sub cl,4
  5400.          jns $sx1              ; Keep going until shift count
  5401.                                ; is neg.
  5402.          pop cx,bx,ax,f
  5403.          ret
  5404.  
  5405.  ;------ Display string stored in code segment just after CALL to
  5406.  ;------ this routine:
  5407.  $shows: push  f,ax,bx,bp
  5408.          mov bp,sp
  5409.          mov bx,[bp+8]         ; Get return addr -
  5410.                                ; it points to string
  5411.  $ss1:   mov al,cs:[bx]        ; Get next char from string
  5412.          inc bx                ; Advance string pointer
  5413.          or  al,al             ; Reached 0 end of string?
  5414.          jz  $ss2              ; Exit if so
  5415.          call  $showc          ; Display char
  5416.          jmp $ss1              ; Back for more
  5417.  $ss2:   mov [bp+8],bx         ; reset return address
  5418.          pop bp,bx,ax,f
  5419.          ret
  5420.  
  5421.  ;------ Macro to display a string.
  5422.  ;------ Example: SHOWS 'Booga booga!'
  5423.  SHOWS   macro str
  5424.          call  $shows
  5425.          db  str,0
  5426.  endm
  5427.  ;------ Macro to display a register. Example:  SHOWR BX
  5428.  SHOWR   macro reg
  5429.          SHOWS ' ®='
  5430.          push dx
  5431.          mov dx,reg
  5432.          call  $showx
  5433.          pop dx
  5434.  endm
  5435.  
  5436.  ████████████████████████████████████████████████████████████████████████████
  5437.  
  5438.  Carl's Toolbox
  5439.  
  5440.  Carl Warren
  5441.  
  5442.  Starting a new column in a new magazine can be a tricky business. Often you
  5443.  aren't sure who the reader is or what the interest level might be. Luck and
  5444.  a great deal of experience seem to be with MSJ. We have a pretty good idea
  5445.  of who you are and what you want.
  5446.  
  5447.  Even though the column is called "Carl's Toolbox," the fact is, it's your
  5448.  toolbox. In amateur radio days we called this our junkbox. That was the old
  5449.  wooden or paperboard box under the bench that contained odds and ends, a few
  5450.  chassis and boards that we picked up at a junkyard or swap meet and from
  5451.  which we all vowed to create a masterpiece of ham radio design.
  5452.  
  5453.  One old friend of mine used this ploy to smuggle in his brand-new Collins S
  5454.  line──old-time hams know this was the good stuff. He had convinced his wife
  5455.  that all those old military chassis, tubes, and other collected junk would
  5456.  soon be transformed into a work of art──which, some six months and about
  5457.  $5,600 (equipment was much cheaper in 1959) later, it was.
  5458.  
  5459.  Times have changed and so has the equipment. Today, prices are a little
  5460.  better, and yes, you may decide that building is far better than buying,
  5461.  especially when the tool you need doesn't really exist.
  5462.  
  5463.  I'm going to tell you not only about the tools you can buy, but about those
  5464.  that you can build yourself. Since I'll be taking a hands-on approach, I'll
  5465.  tell you how well various pieces of equipment work, and perhaps some
  5466.  interesting ways to use them.
  5467.  
  5468.  Of course, not all the tools and equipment that I'll be talking about come
  5469.  from manufacturers. Every now and then a few friends and I sit down and try
  5470.  to noodle our way through a problem, and occasionally we develop something
  5471.  useful. For example, not too long ago my friend Keith dropped by my office
  5472.  for a chat. I was complaining about all the problems you run into when you
  5473.  add a new board to the system or try to get a new device driver to work.
  5474.  "It's like a continual series of reboots," I said.
  5475.  
  5476.  Keith, who spends most of his time writing specialized code for various
  5477.  vendors, asked, "Why don't you do it in software?"
  5478.  
  5479.  He explained that by using the "A" mini-assembler command in DEBUG, you can
  5480.  enter the following four instructions that will reboot the system:
  5481.  
  5482.    MOV AX,40
  5483.    MOV DS,AX
  5484.    MOV WORD PTR [0072],1234
  5485.    JMP FFFF:0000
  5486.  
  5487.  The first two instructions (MOV AX,40 and MOV DS,AX) put the hex value 0040
  5488.  into the 8086 DS register. This points the 8086 at the area of memory in
  5489.  which the IBM PC's ROM I/O, reset, and power-on routines are located.
  5490.  
  5491.  The next instruction (MOV WORD PTR [0072],1234) stores the hex value 1234 to
  5492.  the IBM PC ROM's Reset flag data field. The ROM's power-on routines use this
  5493.  flag to determine when a Ctrl-Alt-Del reset is in progress or whether a
  5494.  power-on reset is occurring. The 1234 value transfers control directly to
  5495.  the power-on routines, and the memory and I/O checks are skipped. If the
  5496.  Reset flag contains garbage, as it would during power-up, all the checks are
  5497.  performed. Of course, if you want the checks, change the 1234 to 0000. When
  5498.  the program is executed, a full I/O and memory check takes place.
  5499.  
  5500.  That's not a bad little piece of utility software for those of you who
  5501.  collect routines. I have a few others that will be published in future
  5502.  issues.
  5503.  
  5504.  Keeping Track of Changes
  5505.  
  5506.  IBM Personally Developed Software: SuperC
  5507.  Donald M. Ludlow and Randy Black, IBM Personally Developed Software; P.O.
  5508.  Box 3280; Wallingford, CT 06494; (800) 426-7279
  5509.  Price: $29.95
  5510.  
  5511.  Whenever I review a product from IBM, I'm always afraid that if I don't like
  5512.  it, someone will accuse me of being biased against IBM. So, when I was asked
  5513.  to test-drive IBM's Personally Developed Software package, SuperC, I heard
  5514.  an anxious voice in the back of my mind say, "What if it's junk?" After all,
  5515.  the software in this series has ranged from excellent to mediocre, so I
  5516.  wasn't exactly sure what to expect.
  5517.  
  5518.  First of all, SuperC is neither a C-language development package nor a
  5519.  tutor. It is, however, a powerful and useful utility that allows
  5520.  programmers, writers, editors, and anyone who deals with stored information
  5521.  that changes to keep track of those changes.
  5522.  
  5523.  In operation, you can find changes in source code, binary code, or just
  5524.  plain ASCII words. For example, suppose in updating a document you
  5525.  accidentally insert a large space or new word in a sentence, something that
  5526.  just shouldn't be there or that changes your meaning. SuperC uses its word-
  5527.  compare feature to notify you that a change has occurred.
  5528.  
  5529.  Of course, in order to compare changes, both an old and new file must exist.
  5530.  In the case of WordStar users, where a backup copy is kept, the new copy is
  5531.  compared with the old through the use of simple syntax:
  5532.  
  5533.    A>SuperC
  5534.      [d:][path]newfile
  5535.      [d:][path]oldfile
  5536.  
  5537.  In some cases, the only thing you would be interested in is whether or not
  5538.  there are changes in files. When a change is detected by using the file-
  5539.  compare feature, the program reports that there are indeed differences. At
  5540.  this time, you can determine whether to search for word, line, or byte
  5541.  differences.
  5542.  
  5543.  Within SuperC is the tagged-as-is delta-file (Tasdf) file format, which is
  5544.  intended for experienced programmers. It generates a special output file
  5545.  similar to a delta listing with a prefix tagged onto each line, including T)
  5546.  for title record, I) for inserted line, D) for deleted line, RN for
  5547.  reformatted line, S) for summary record, and E) for end record.
  5548.  
  5549.  Although it appears to be just a simple compare program, SuperC includes
  5550.  capabilities such as bypassing BASIC line numbers, processing assembler
  5551.  comments and blank lines, and even substitution for nondisplayable
  5552.  characters. Also, you can specify what line(s) should be ignored in the
  5553.  process.
  5554.  
  5555.  Don't be concerned about space, either; SuperC manages just about any length
  5556.  of file. Recently, I compared a long ASCII file in a standard document of
  5557.  over 70K in length with no difficulty.
  5558.  
  5559.  SuperC offers on-line help at the touch of a function key (F1). All
  5560.  documentation is on-line and can be printed out.
  5561.  
  5562.  Since the next several columns will address various programming methods,
  5563.  I'll be using the listing function in SuperC, which prints up to 132 columns
  5564.  wide.
  5565.  
  5566.  Getting All the Fax
  5567.  
  5568.  GammaLink
  5569.  GammaFAX; 2452 Embarcadero Way; Palo Alto, CA 94303; (415) 856-7421
  5570.  Price: $995
  5571.  Includes a synchronous modem board that fits in one slot on an IBM PC, XT,
  5572.  or AT, or a similar machine, and the GammaFAX software package. The GammaFAX
  5573.  board provides bidirectional facsimile transmission CCITT group III to and
  5574.  from PCs, as well as PC-to-PC communications with full data compression and
  5575.  error checking up to 9,600 bits per second.
  5576.  
  5577.  As more and more technology comes into the office, the desktop of the future
  5578.  is getting quite cluttered. The stacks of paper and in-and-out baskets have
  5579.  been pushed to one side to make room for IBM PCs, printers and facsimile
  5580.  machines, mice, and whatever else that manufacturers can dream up. At least
  5581.  one of these desktop items, the facsimile machine, can be moved off the desk
  5582.  and back to the store room. GammaLink's GammaFAX board and software can take
  5583.  over the chores once relegated to the standalone facsimile.
  5584.  
  5585.  Besides fitting into an open slot on the PC backplane, the GammaFAX board
  5586.  can also be plugged into any available phone jack with an RJ-11 receptacle.
  5587.  By doing this you can probably reclaim at least 2 square feet of your desk.
  5588.  
  5589.  The GammaFAX system is capable of taking existing text files or even saved
  5590.  picture files and shooting them out to a facsimile machine anywhere in the
  5591.  world. And since facsimile is a two-way street, so is GammaFAX: the machine
  5592.  can be set to work in an answer facsimile mode or to communicate with other
  5593.  PCs for very rapid file transfer.
  5594.  
  5595.  The GammaFAX software is flexible and smart. By entering the DEBUG option,
  5596.  you can have the program report the status of the operation, thus ensuring a
  5597.  great deal of confidence in the operation of the line as well as the modem.
  5598.  
  5599.  A series of menus facilitates use of the program. For example, on start-up
  5600.  you are greeted with a menu that asks what you want to do: send, receive,
  5601.  convert programs for facsimile transmission, or set up a string of files for
  5602.  sending or receiving. You make your choices with the function keys, which
  5603.  direct you to another level of menus. F10 always takes you back to the
  5604.  previous level.
  5605.  
  5606.  The GammaFAX requires the undivided attention of the machine, which is fine
  5607.  unless you have to get some other work done while sending. The store and
  5608.  forward function does allow you to set times for a transfer to begin, but
  5609.  does not let you use the machine.
  5610.  
  5611.  I set up a Windows definition file for GammaFAX, so it could be called from
  5612.  Microsoft Windows, and there were no ill effects on either application. It
  5613.  would be nice, though, if the GammaFAX people would do some Windows
  5614.  compatibility rewriting so that it can be called as a windowed task and work
  5615.  in the background.
  5616.  
  5617.  You can use GammaFAX with a PC, XT, or AT. I strongly recommend the AT
  5618.  because it lets you use all the speed that the board has. The manual,
  5619.  although poorly written and in need of a good layout job, contains lots of
  5620.  information──you just have to search hard for it. Clever programmers will be
  5621.  able to build a better software mousetrap for communications by using the
  5622.  board and the information contained in the manual.
  5623.  
  5624.  The GammaFAX board is essentially a facsimile machine, so software
  5625.  developers will find it ideal for creating desktop publishing images.
  5626.  GammaFAX makes it possible to obtain pictures from sources other than
  5627.  scanners. Scanners are becoming an important tool for desktop publishing, as
  5628.  they allow photographs or other complex pictures to be scanned in.
  5629.  
  5630.  There is a problem, though. GammaFAX stores pictures in CCITT group III
  5631.  format, which is good for facsimile, but not so good for most of the
  5632.  formats used with bit-image file formats. GammaFAX does provide the
  5633.  utilities necessary to scan images in with a Canon scanner and convert
  5634.  them for facsimile transmission.
  5635.  
  5636.  In the group III format, the least significant bit of the first byte in the
  5637.  file is the first bit transmitted during the group III phase of the in
  5638.  message of a facsimile call. The most significant bit of the last byte of
  5639.  the file is the last bit transmitted. A standard-resolution facsimile page
  5640.  has 1,728 dots across and 1,075 lines in the vertical dimension. A high-
  5641.  resolution picture, such as a half-tone, is 1,728 by 2,150. The FAX virtual
  5642.  page program, supplied with the board, lets you convert the binary image
  5643.  format into a bit-image format for use with such programs as Media
  5644.  Cybernetics's Dr. Halo II.
  5645.  
  5646.  Since the image does have specific characteristics, it can be manipulated
  5647.  into other forms. The Tag Image File Format (TIFF), a newly proposed format
  5648.  developed by Aldus Corp., Dest Corp., and Microsoft Corp., provides a
  5649.  structured format for digital data. TIFF is based on CCITT group III and IV
  5650.  formats, but it adds additional information for the digital data, including
  5651.  color and detail about individual pixels, so that a full picture can be
  5652.  saved.
  5653.  
  5654.  
  5655.  ════════════════════════════════════════════════════════════════════════════
  5656.  
  5657.  
  5658.  Vol. 2 No. 2 Table of Contents
  5659.  
  5660.  
  5661.  Microsoft Operating System/2: A Foundation for the Next Generation
  5662.  
  5663.  Microsoft's joint effort with IBM to develop an operating system for Intel
  5664.  80286- and 80386-based computers resulted in Operating System/2. Using the
  5665.  286's protected mode, OS/2 not only breaks the 640K memory barrier, it also
  5666.  provides multitasking capability and inter-process communication.
  5667.  
  5668.  
  5669.  OS/2 Windows Presentation Manager: Microsoft Windows on the Future
  5670.  
  5671.  Included as a standard part of the new Operating System/2, the Windows
  5672.  presentation manager will provide OS/2 with the same advantages that
  5673.  Microsoft Windows brings to MS-DOS, such as a standard user interface and a
  5674.  rich independent graphic application environment.
  5675.  
  5676.  
  5677.  OS/2 DOS Environment: Compatibility And Transition for MS-DOS Programs
  5678.  
  5679.  An important OS/2 feature is its MS-DOS compatibility environment, a mode
  5680.  that emulates DOS 3.x and runs most existing programs. Discussed are the
  5681.  restrictions of the compatibility mode and the considerations in the
  5682.  construction of programs designed to run with both MS-DOS and OS/2.
  5683.  
  5684.  
  5685.  OS/2 Multitasking: Exploiting the Protected Mode of the 286
  5686.  
  5687.  The Microsoft OS/2 scheduler, the dispatcher, and the protected mode of the
  5688.  286 are the keys to OS/2's multitasking operation. This article considers
  5689.  the system developers view of the fundamental elements of multitasking:
  5690.  processes, threads, and screen groups.
  5691.  
  5692.  
  5693.  OS/2 Memory Management
  5694.  
  5695.  
  5696.  OS/2 Inter-Process Communication: Semaphores, Pipes, and Queues
  5697.  
  5698.  Multitasking operations need fast and reliable communication between
  5699.  multiple processes and threads. To offer the system developer a wide range
  5700.  of facilities to choose from, OS/2 depends on shared memory as well as
  5701.  system semaphores, pipes, and queues for rapid exchange of information.
  5702.  
  5703.  
  5704.  The MS OS/2 LAN Manager
  5705.  
  5706.  
  5707.  A Complete Guide to Writing Your First OS/2 Program
  5708.  
  5709.  Discussions of program development for OS/2 introduce a new vocabulary.
  5710.  Dynamic linking or module definition files may at first seem forbidding,
  5711.  but they, like OS/2, are variants of familiar concepts. We offer a sample
  5712.  program complete with multitasking and inter-process communication.
  5713.  
  5714.  
  5715.  Turning Off the Car to Change Gears: An Interview with Gordon Letwin
  5716.  
  5717.  With more than 350,000 lines of code, OS/2 is unquestionably the most
  5718.  complex software that Microsoft has designed. Microsoft Systems Journal
  5719.  spoke with Gordon Letwin, the chief architect of OS/2, to learn more about
  5720.  the challenges of designing Operating System/2.
  5721.  
  5722.  
  5723.  A Simple Windows Application For Custom Color Mixing
  5724.  
  5725.  Our regular programming article presents COLORSCR, a deceptively simple
  5726.  program. On the surface, it provides three scroll bars to adjust the red,
  5727.  green, and blue components of GDI color specification. The elegance of the
  5728.  design is its use of child windows to manage the screen area.
  5729.  
  5730.  
  5731.  Ask Dr. Bob
  5732.  
  5733.  
  5734.  EDITOR'S NOTE
  5735.  
  5736.  The recently announced IBM Personal Series/2 computers and the first product
  5737.  of Microsoft's joint development agreement with IBM, Microsoft Operating
  5738.  System/2, advance personal computing to new levels of sophistication and
  5739.  improved performance. For the end user, this translates into greater
  5740.  ease of use. For the software developer, however, improvements in
  5741.  technology often mean increasingly complex procedures that bring new
  5742.  challenges and probably a few frustrations as well.
  5743.  
  5744.  To help the software developer unravel the intricacies of OS/2, this issue
  5745.  of Microsoft Systems Journal provides an overview of the new operating
  5746.  system as well as a detailed look at OS/2's basic features──multitasking,
  5747.  inter-process communication, and memory management. And MSJ doesn't stop
  5748.  there──the issue continues to explore OS/2 with articles on the MS-DOS
  5749.  compatibility environment, the Windows presentation manager, and an
  5750.  interview with Microsoft's chief architect of OS/2, Gordon Letwin.
  5751.  
  5752.  What will OS/2 mean to the PC world? Although no one disagrees that the
  5753.  impact will be deep, standards are not made overnight. Because MSJ is
  5754.  committed to the concerns and interests of the software developer, we are
  5755.  planning to bring you a great deal more on OS/2, but certainly not to the
  5756.  exclusion of MS-DOS. MS-DOS will continue to be important for a long time
  5757.  to come.
  5758.  
  5759.  A reminder to DIAL subscribers: Code samples in this and any previous issue
  5760.  are available on-line. Keep an eye on the DIAL Bulletin Board for more
  5761.  information on this.
  5762.  
  5763.  We've said it before, but it's worth repeating. We want MSJ to be your
  5764.  journal, your forum for exchanging ideas. Write to us with your suggestions
  5765.  for articles, your reactions to this and previous issues.──Ed.
  5766.  
  5767.  
  5768.  Masthead
  5769.  
  5770.  JONATHAN D. LAZARUS
  5771.  Editor and Publisher
  5772.  
  5773.  EDITORIAL
  5774.  
  5775.  TONY RIZZO
  5776.  Technical Editor
  5777.  
  5778.  BARRY OWEN
  5779.  Managing Editor
  5780.  
  5781.  CHRISTINA G. DYAR
  5782.  Associate Editor
  5783.  
  5784.  JOANNE STEINHART
  5785.  Production Editor
  5786.  
  5787.  GERALD CARNEY
  5788.  Staff Editor
  5789.  
  5790.  DIANA E. PERKEL
  5791.  Editorial Assistant
  5792.  
  5793.  ART
  5794.  
  5795.  MICHAEL LONGACRE
  5796.  Art Director
  5797.  
  5798.  VALERIE MYERS
  5799.  Associate Art Director
  5800.  
  5801.  CIRCULATION
  5802.  
  5803.  WILLIAM B. GRANBERG
  5804.  Circulation Manager
  5805.  
  5806.  L. PERRIN TOMICH
  5807.  Assistant to the Publisher
  5808.  
  5809.  BETSY KAUFER
  5810.  Administrative Assistant
  5811.  
  5812.  Copyright(C) 1987 Microsoft Corporation. All rights reserved; reproduction
  5813.  in part or in whole without permission is prohibited.
  5814.  
  5815.  Microsoft Systems Journal is a publication of Microsoft Corporation, 16011
  5816.  NE 36th Way, Box 97017, Redmond, WA 98073-9717. Officers: William H.
  5817.  Gates, III, Chairman of the Board and Chief Executive Officer; Jon Shirley,
  5818.  President and Chief Operating Officer; Francis J. Gaudette, Treasurer;
  5819.  William Neukom, Secretary.
  5820.  
  5821.  Microsoft Corporation assumes no liability for any damages resulting from
  5822.  the use of the information contained herein.
  5823.  
  5824.  Microsoft, the Microsoft logo, MS-DOS, and XENIX are registered trademarks
  5825.  and CodeView is a trademark of Microsoft Corporation. Macintosh is a
  5826.  trademark of Apple Computer, Inc. IBM is a registered trademark and
  5827.  Personal System/2 is a trademark of the International Business Machines
  5828.  Corporation. PageMaker is a registered trademark of Aldus Corporation.
  5829.  UNIX is a registered trademark of AT&T. Compaq is a registered trademark
  5830.  of Compaq Computer Corporation. Intel is a registered trademark of Intel
  5831.  Corporation. Paintbrush is a registered trademark of ZSoft Corporation.
  5832.  
  5833.  ████████████████████████████████████████████████████████████████████████████
  5834.  
  5835.  Microsoft Operating System/2: A Foundation For the Next Generation
  5836.  
  5837.  ───────────────────────────────────────────────────────────────────────────
  5838.  Also see the related articles:
  5839.    Evolution and History of MS-DOS
  5840.    CMD.EXE-OS/2 Command Processor
  5841.    System Configuration (CONFIG.SYS)
  5842.  ───────────────────────────────────────────────────────────────────────────
  5843.  
  5844.  Tony Rizzo
  5845.  
  5846.  After much waiting, speculating, and hoping, the word from Microsoft and
  5847.  IBM is out, and that word is Operating System/2 (OS/2), their new single-
  5848.  user, multitasking operating environment. OS/2 is the successor to MS-DOS
  5849.  (and PC-DOS) and is the first product of Microsoft and IBM's Joint
  5850.  Development Agreement, which is especially relevant to MSJ's readers.
  5851.  
  5852.  At the same time that the software announcements were made, IBM launched
  5853.  its next generation of personal computers, the Personal System/2 series.
  5854.  On April 2, 1987, a host of new IBM hardware and software offerings were
  5855.  introduced, but the focus was decidedly on the new IBM machines: an entry-
  5856.  level 8086 PC and new 80286- and 80386-based machines, built mainly around
  5857.  in-house VLSI technology and offering substantial price/performance
  5858.  improvements over the old hardware.
  5859.  
  5860.  Very briefly, the new systems boast switchless planar (or self-configuring
  5861.  system) boards with on-board video and disk controllers, greatly expanded
  5862.  RAM capabilities, 3-1/2" floppies, and new IBM hard files with a 1-to-1
  5863.  interleave factor. The new 286 and 386 machines also usher in IBM's 16/32-
  5864.  bit Micro Channel architecture, a new bus structure that greatly improves
  5865.  data throughput, and a new Video Graphics Array (VGA) display subsystem.
  5866.  VGA offers built-in, 640-by-480 resolution with 16 colors, which is
  5867.  necessary for advanced graphics─based software.
  5868.  
  5869.  For software developers, however, the big news is the new Microsoft OS/2
  5870.  environment, which will run on the new 286 and 386 machines, as well as on
  5871.  the older PC AT and compatibles.
  5872.  
  5873.  ───────────────────────────────────────────────────────────────────────────
  5874.  The Parallel Growth of Hardware and Software. As hardware continues to
  5875.  provide enhanced and more sophisticated services, the MS-DOS world will
  5876.  slowly give way to OS/2 and new multi-applications systems.
  5877.  
  5878.  ┌───────────┬─────────────┬────────────────┬───────────────┬──────────────┐
  5879.  │           │    8088     │      8086      │     80286     │      80386   │
  5880.  ├───────────┼─────────────┼────────────────┼───────────────┼──────────────┤
  5881.  │MS-DOS 1.x │≡≡≡≡≡≡≡≡≡≡≡≡≡│≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡│═══════════════│──────────────│
  5882.  │           │             │                │               │              │
  5883.  │           │             │                │               │              │
  5884.  │MS-DOS 2.x │≡≡≡≡≡≡≡≡≡≡≡≡≡│≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡│═══════════════│──────────────│
  5885.  │           │             │                │               │              │
  5886.  │           │             │                │                              │
  5887.  │MS-DOS 3.x │≡≡≡≡≡≡≡≡≡≡≡≡≡│≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡│════════REAL MODE ONLY═══════>│
  5888.  │           │             │                │                              │
  5889.  │           │             │                │               │              │
  5890.  │MS-OS/2    │             │                │≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡│≡≡≡≡≡≡≡≡≡≡≡≡≡>│
  5891.  │           │             │                │               │              │
  5892.  │           │             │                │               │              │
  5893.  │MS-OS/2 386│             │                │               │≡≡≡≡≡≡≡≡≡≡≡≡≡>│
  5894.  └───────────┴─────────────┴────────────────┴───────────────┴──────────────┘
  5895.  ───────────────────────────────────────────────────────────────────────────
  5896.  
  5897.  The Connection
  5898.  
  5899.  In contrast to the current situation in which IBM offers PC-DOS and other
  5900.  manufacturers offer the slightly different MS-DOS, what IBM calls
  5901.  "Operating System/2 Standard Edition" and what Microsoft calls "Operating
  5902.  System/2" are in fact one and the same operating system.
  5903.  
  5904.  Perhaps more important is that the Windows presentation manager, the MS
  5905.  OS/2 version of Microsoft Windows, is an integral part of the new
  5906.  operating system. Both MS OS/2 and the Windows presentation manager are in
  5907.  compliance with IBM's recently announced Systems Application Architecture
  5908.  (SAA), a set of common specifications created to promote "consistent"
  5909.  applications development across the entire line of IBM System/370,
  5910.  System/3x, and personal computer systems.
  5911.  
  5912.  To date, every version of MS-DOS, including the newly announced Version
  5913.  3.3, has been a single-process operating system. That is, they can only
  5914.  execute one application at a time. This restriction is a consequence of
  5915.  the 8086/8088 architecture, which is limited to 1Mb of memory and offers
  5916.  no hardware support for protecting memory──any application can access at
  5917.  will any memory location at any time.
  5918.  
  5919.  Unrestricted memory and device access by software may very well be a boon
  5920.  to those creative programmers who look to squeeze every bit of performance
  5921.  out of the MS-DOS, 8086/8088 environment, but it poses serious problems
  5922.  for those trying to run more than one application at the same time.
  5923.  Furthermore, the memory map of the MS-DOS, 8086/8088 environment leaves
  5924.  only 640Kb of main memory available for both applications and the
  5925.  operating system.
  5926.  
  5927.  Though MS-DOS has been continually refined─especially in its file
  5928.  management, device support, background processing (e.g., print spooling),
  5929.  network support, and very rudimentary attempts at multitasking──it has
  5930.  remained fundamentally a single-user, single-application design.
  5931.  
  5932.  
  5933.  Almost the Promised Land
  5934.  
  5935.  Without hardware-based protection mechanisms, an operating system would
  5936.  have a very difficult time managing more than one application; preventing
  5937.  applications from accessing conflicting memory locations becomes almost
  5938.  impossible. Intel addressed this problem by including a "protected mode"
  5939.  of operation in the design of its 80286 processor. On account of this,
  5940.  expectations for an enhanced operating system were high when IBM
  5941.  introduced the 286-based PC. The 286 processor's protected mode of
  5942.  operation, its ability to address up to 16Mb of memory, and the fact that
  5943.  the 286 also provided a "real" mode fully compatible with the 8086/8088
  5944.  suggested the potential for a more powerful operating system.
  5945.  
  5946.  Unfortunately, the 286 was designed before the importance of the IBM PC
  5947.  (8088) standard was established. As a result, the 286 cannot
  5948.  simultaneously support both real and protected modes─a major drawback.
  5949.  Adding to the problem, the 286 real mode has every deficiency of the 8086
  5950.  environment: the 640Kb barrier, no memory protection, and, as a result, no
  5951.  multiple-application operation.
  5952.  
  5953.  Because of these 286 shortcomings, it was not feasible to build a
  5954.  protected-mode operating system with MS-DOS compatibility before the
  5955.  release of the PC AT. So, rather than take a half-step, Microsoft and IBM
  5956.  decided to invest in a new, extensive development effort that would focus
  5957.  on a protected-mode operating system (see "Turning Off the Car to Change
  5958.  Gears: An Interview with Gordon Letwin,"). The new operating
  5959.  system would take advantage of the hardware, break the 640Kb memory
  5960.  barrier, allow a multi-application, multitasking environment, and still
  5961.  permit total compatibility with MS-DOS applications.
  5962.  
  5963.  The result of this effort is MS OS/2.
  5964.  
  5965.  ───────────────────────────────────────────────────────────────────────────
  5966.  Block Diagram of the MS OS/2 Environment. MS OS/2 is a complicated set of
  5967.  interrelated modules working together to provide a complete multitasking,
  5968.  virtual memory environment.
  5969.  
  5970.                                           Application
  5971.              Application               (or Library Routine)
  5972.              │   │     │  Dynamic Link   │      │   │
  5973.              │   │     │    Function     │      │   │
  5974.              │   │     │ Call Interface  │      │   │
  5975.              │   │                            │   │
  5976.              │   │  ┌─System Session Manager─┐  │   │
  5977.              │   │  │                       │  │   │
  5978.              │   │  │Console Device Interface│  │   │
  5979.                                               
  5980.          ╔══════════════════════════════════════════════╗
  5981.          ║                  Kernel                      ║
  5982.          ║ ┌───────────┐       Process Management       ║
  5983.          ║ │File system│       Memory Management        ║
  5984.          ║ └───────────┘       Program Management       ║
  5985.          ║                     Time Management          ║
  5986.          ║                     I/O Management           ║
  5987.          ╚══════════════════════════════════════════════╝
  5988.                                 
  5989.          ░░░░░░░░░░░░░░░░ Device Drivers ░░░░░░░░░░░░░░░░
  5990.                                 
  5991.          ░░░░░░░░░░░░░░░░░░░ Hardware ░░░░░░░░░░░░░░░░░░░
  5992.  
  5993.  ───────────────────────────────────────────────────────────────────────────
  5994.  
  5995.  The Real Thing
  5996.  
  5997.  MS OS/2 is a priority-based, pre-emptive, multitasking environment which
  5998.  includes a new application interface that isolates applications from low-
  5999.  level hardware differences between machines running MS OS/2. OS/2 utilizes
  6000.  the protected mode of the 286 chip to allocate an exclusive address space
  6001.  to each application. OS/2 controls access to memory, so that different
  6002.  applications cannot corrupt each other's data and program areas.
  6003.  
  6004.  In protected mode, the 286 uses the contents of the segment register to
  6005.  access a local descriptor table (LDT) containing information about each
  6006.  application. The contents of the segment register serve as an index into
  6007.  the descriptor table. The operating system also accesses a global
  6008.  descriptor table (GDT), which contains information on code and data that
  6009.  is shared by all executing applications (see Figure 1). Both of these
  6010.  tables are exclusively managed by the operating system, and together they
  6011.  allow up to the entire 16Mb of real memory to be mapped to up to 1
  6012.  gigabyte of virtual memory.
  6013.  
  6014.  Also, application programs are normally prevented from writing directly to
  6015.  the underlying hardware. The 286 architecture provides for discrete I/O
  6016.  privilege levels (IOPL) to be assigned to applications; this capability
  6017.  enables the operating system to control access to hardware. Although it is
  6018.  possible for an application to obtain hardware control if it can gain
  6019.  access to an appropriate privilege level, the operating system generally
  6020.  does not explicitly make that privilege available to an application.
  6021.  Protected mode and IOPL are the basic, 286 hardware──based underpinnings of
  6022.  MS OS/2 (for a detailed discussion of privilege levels and how OS/2
  6023.  performs multitasking, see "OS/2 Multitasking: Exploiting the Protected
  6024.  Mode of the 286,").
  6025.  
  6026.  
  6027.  Memory Management
  6028.  
  6029.  In protected mode OS/2 can simultaneously run many applications, any one
  6030.  of which may cause the amount of physical memory available to the system
  6031.  to be exceeded. When an application's memory requirements exceed real
  6032.  memory (referred to as "memory overcommit"), OS/2 will swap memory
  6033.  segments to disk as required (see Figure 3). OS/2 dynamically expands and
  6034.  contracts allocated memory and collects fragmented memory allocations
  6035.  (known as "memory compaction"), which provides efficient control of
  6036.  available memory.
  6037.  
  6038.  Microsoft has gone to great lengths to protect the enormous investment in
  6039.  pre-MS OS/2 software. To achieve this goal, OS/2 provides a compatibility
  6040.  mode in which real-mode MS-DOS applications can run (see Figure 3). The
  6041.  compatibility mode offers an 8086 environment in which most MS-DOS
  6042.  programs will run. However, a small number of programs, especially those
  6043.  that directly access hardware or with timing dependence, won't run in
  6044.  compatibility mode.
  6045.  
  6046.  Compatibility mode allows for one MS-DOS program to execute in the
  6047.  foreground; background, light protected-mode applications are suspended
  6048.  when in this mode. A program executing in the compatibility mode can only
  6049.  run in the foreground─such a program is suspended if one of the background
  6050.  protected-mode applications is selected for foreground operation.
  6051.  
  6052.  
  6053.  Command Processors
  6054.  
  6055.  Microsoft OS/2 comes with two command processors, COMMAND.COM and CMD.EXE.
  6056.  The former is the real-mode command processor, similar to the MS-DOS
  6057.  command processor, with its familiar initialization, resident, and
  6058.  transient parts. As in MS-DOS, COMMAND.COM is used to start programs when
  6059.  in compatibility mode.
  6060.  
  6061.  CMD.EXE is the MS OS/2 command processor, which offers an extended command
  6062.  syntax. Most important, however, is that CMD.EXE is both shareable and
  6063.  swappable. This means that multiple copies of CMD.EXE can be executed, but
  6064.  only one copy of CMD.EXE is actually kept in memory, and that copy is
  6065.  shared by all protected-mode applications. The only additional memory used
  6066.  is that which is unique to any given application.
  6067.  
  6068.  
  6069.  Presentation Manager
  6070.  
  6071.  OS/2 consists of two fundamental, interrelated parts. The first is the
  6072.  kernel, which provides for the major internal workings of the operating
  6073.  system. Integrated into the MS OS/2 system is the Windows presentation
  6074.  manager. Like Microsoft Windows for MS-DOS, it is a consistent, graphic
  6075.  user interface and a device-independent applications environment.
  6076.  
  6077.  The MS OS/2 Windows presentation manager is derived from the current
  6078.  Microsoft Windows, Version 1.03, software. The MS OS/2 Windows
  6079.  presentation manager, however, has been significantly enhanced; it has
  6080.  been designed to use multiple overlapping windows and to conform to the
  6081.  Common User Access component of IBM's SAA.
  6082.  
  6083.  The key word is consistency. Microsoft will soon release Windows, Version
  6084.  2.0, for the MS-DOS environment with the same user interface as the
  6085.  Windows presentation manager. The visual interface will be consistent in
  6086.  both MS OS/2 and MS-DOS. Also, because the Windows presentation manager is
  6087.  consistent with SAA, this new standard is expected to eventually find its
  6088.  way into a large number of computer systems, from micros to mainframes.
  6089.  
  6090.  For the end user, the Windows presentation manager offers
  6091.  
  6092.    ■  the ability to run graphics-oriented programs
  6093.    ■  a simple and consistent user interface
  6094.    ■  access to the session manager
  6095.    ■  enhanced mouse and keyboard control
  6096.  
  6097.  And, for the software developer, the Windows presentation manager
  6098.  provides
  6099.  
  6100.    ■  access to the MS OS/2 multitasking and memory management kernel
  6101.       functions
  6102.  
  6103.    ■  access to intertask communication and data interchange facilities
  6104.  
  6105.    ■  a device-independent output architecture
  6106.  
  6107.    ■  a rich and sophisticated graphics library
  6108.  
  6109.  
  6110.  The Session Manager
  6111.  
  6112.  The new Windows environment gives the user full control over the operating
  6113.  environment. The windows environment accomplishes this by offering users a
  6114.  session manager, which allows users to control the status of concurrently
  6115.  running applications, as well as that of any application running in
  6116.  compatibility mode. The user requests access to the session manager with
  6117.  the SysReq key, and the session manager responds with a list of active
  6118.  applications (see Figure 4). The user can then bring any process to the
  6119.  foreground, start any number of new processes, or terminate any number of
  6120.  existing processes. There is a lot more to the session manager, however,
  6121.  than meets the eye.
  6122.  
  6123.  
  6124.  The New Frontier
  6125.  
  6126.  When the session manager is active, it switches between different screen
  6127.  groups. To understand screen groups we need to take a close, if somewhat
  6128.  brief, look at the activity underlying executing applications. MS OS/2
  6129.  applications are made up of one or more processes, which are in turn made
  6130.  up of one or more threads. A thread is an "atomic unit," a unique entity
  6131.  that the MS OS/2 scheduler can allocate CPU cycles to, that is, it is
  6132.  actually executable by the CPU. A process can have any number of
  6133.  executable threads, each of which can be in one of three states: blocked,
  6134.  ready to execute, or executing.
  6135.  
  6136.  Processes are, in turn, assigned to screen groups. Screen groups are
  6137.  collections of processes that write to a common virtual display and
  6138.  receive input from a virtual keyboard. As an example, a screen group might
  6139.  be a collection of related windows, each of which is running its own
  6140.  process and each of which was started inside a parent window (screen).
  6141.  
  6142.  The screen group writes to a virtual display and reads from a virtual
  6143.  keyboard because it is possible to execute any number of screen groups,
  6144.  each with its own screen and keyboard needs. Since there is only one
  6145.  physical screen and keyboard, there must be some system to prevent
  6146.  applications from conflicting with each other. MS OS/2's solution maps
  6147.  each screen group to its own unique virtual screen, eliminating any
  6148.  possible conflicts.
  6149.  
  6150.  When a user requests access to an active application, the session manager
  6151.  maps that application's screen group to the physical screen─more commonly
  6152.  known as the foreground. MS OS/2 manages the mapping of virtual screens to
  6153.  the physical screen with the session manager (see Figure 5). The user has
  6154.  complete control over which applications MS OS/2 will bring to the
  6155.  foreground, and over initiation or termination of new applications (and
  6156.  their associated screen groups).
  6157.  
  6158.  There is a lot going on here. Obviously, OS/2 has provided the user with a
  6159.  great deal of flexibility. But what has the software developer gained?
  6160.  
  6161.  
  6162.  API and FAPI
  6163.  
  6164.  MS OS/2 offers a rich application program interface that allows software
  6165.  developers to fully exploit the resources of the operating system. For
  6166.  example, the Windows presentation manager provides a large set of
  6167.  sophisticated graphics functions, giving the software developer the
  6168.  building blocks necessary to create very sophisticated screen images.
  6169.  
  6170.  MS OS/2 provides a substantial set of CALL-based functions──189 of them to
  6171.  be exact──to assist in the development of programs that will run in a
  6172.  multitasking environment. These functions, which handle file and device
  6173.  management, memory management, and task management, are known as the
  6174.  kernel, or operating system Application Programming Interface (API).
  6175.  
  6176.  The API includes facilities to write device drivers, known as the Device
  6177.  Driver Interface. Device drivers handle device initialization, transfer of
  6178.  data to and from devices, and device error control, and they provide
  6179.  access to low-level BIOS functions. High-level languages such as C can
  6180.  call these functions directly. These callable functions completely
  6181.  eliminate the need to depend on interrupts (for example, Interrupt 21
  6182.  function calls) and the need to write directly to hardware.
  6183.  
  6184.  API also provides functions to deal with the complexities of inter-
  6185.  process communication (IPC). Processes and threads communicate through
  6186.  semaphores, queues, pipes, and shared memory (see "OS/2 Inter-Process
  6187.  Communication: Semaphores, Pipes, and Queues,"). API's functions
  6188.  make such complex programming significantly easier to write.
  6189.  
  6190.  Microsoft will support all of these functions as a standard. There will be
  6191.  a substantial improvement in the ability to port software to future
  6192.  releases of MS OS/2, as well as an enhanced ability for the products of
  6193.  different software developers to interact with each other. Microsoft
  6194.  suggests that applications conforming to the API standard will run within
  6195.  the MS OS/2 environment and future releases of OS/2. This creates an
  6196.  opportunity to standardize coding practices throughout the entire PC
  6197.  environment.
  6198.  
  6199.  Realistically, OS/2 won't become as pervasive as MS-DOS for some time yet.
  6200.  It will most likely be at least 3 years before 286- and 386-based machines
  6201.  take over the major share of the PC market. MS-DOS, Version 3.3, will
  6202.  remain with us for the foreseeable future, so developers will want the
  6203.  ability to write software that can move between MS-DOS and MS OS/2.
  6204.  
  6205.  This need, however, has already been anticipated and resolved; Microsoft
  6206.  guarantees to support a subset of the API function set that will execute
  6207.  in every available mode of the two operating environments. Dubbed the
  6208.  Family Application Programming Interface (FAPI), this set of 91
  6209.  functions──25 of them are somewhat restricted──will permit programs to
  6210.  execute under MS-DOS 3.3, within the compatibility mode of MS OS/2, and as
  6211.  an actual, although limited, MS OS/2 application (see Figure 6).
  6212.  
  6213.  API/FAPI represents a unique opportunity for software developers to create
  6214.  applications that can deal with current limitations while incorporating
  6215.  future advances, thus providing users to run state-of-the-art applications
  6216.  on older machines.
  6217.  
  6218.  
  6219.  Software Assistance
  6220.  
  6221.  By August, Microsoft plans to release a Software Development Kit (SDK)
  6222.  that will contain the full specifications of MS OS/2─as well as a beta-
  6223.  release version of the operating system kernel. The toolkit will come with
  6224.  C Version 4.5, MASM Version 4.5, and the CodeView debugger.
  6225.  
  6226.  The new compiler and assembler will be able to generate code to be used
  6227.  with the new Microsoft Dynamic Linker. The linker will allow code to be
  6228.  linked with FAR calls, creating external references to various libraries.
  6229.  These external references eliminate the need to link library routines into
  6230.  each object (or load) module. The Dynamic Link Editor services program
  6231.  needs by loading library routines only when necessary.
  6232.  
  6233.  
  6234.  The LAN Manager
  6235.  
  6236.  The LAN Manager will play an important role in the MS OS/2 environment and
  6237.  can be fully integrated into MS OS/2. It promises an exciting new
  6238.  applications environment in which different applications, such as
  6239.  spreadsheets, word processors, and database systems, will be able to
  6240.  communicate with each other not only application-to-application (e.g.,
  6241.  spreadsheet to word processor) within one processor, but also among
  6242.  processors connected to a network. The LAN API component allows developers
  6243.  to take full advantage of the capabilities of the LAN Manager so as to
  6244.  build truly distributed applications. It will also include site-to-site
  6245.  electronic-mail capabilities and resource sharing. (For more information
  6246.  on the LAN API, see "The MS OS/2 Lan Manager,").
  6247.  
  6248.  
  6249.  Help from the 286
  6250.  
  6251.  The 386 chip provides much faster speed, 1 gigabyte of addressable real
  6252.  memory, and a huge virtual address space─64 terabytes! It is also capable
  6253.  of running "virtual" 8086 machines, obviating the need for a compatibility
  6254.  mode. Users can run multiple MS-DOS sessions within the multitasking
  6255.  environment.
  6256.  
  6257.  Microsoft estimates that only 10 percent of the MS OS/2 code will have to
  6258.  be modified or rewritten, primarily in the memory management component, to
  6259.  utilize the 386. This code modification will be completely transparent to
  6260.  applications written under the MS OS/2 API on the 286 chip; MS OS/2 has
  6261.  been standardized in this respect. With the next release of MS OS/2,
  6262.  software developers may gain access to additional tools, but they won't
  6263.  have to rewrite any code because of changes made to the existing API. This
  6264.  means, of course, that software developers can begin now to create on the
  6265.  286 applications that can easily be ported to the 386──a substantial
  6266.  development advantage.
  6267.  
  6268.  
  6269.  A New Philosophy
  6270.  
  6271.  MS OS/2 is obviously a greatly improved and sophisticated piece of
  6272.  software. Software development under this new environment will require a
  6273.  considerable effort to understand just how all of the pieces fit together.
  6274.  It will take some time before developers can claim mastery of all the
  6275.  tools currently available.
  6276.  
  6277.  There will also be a shift in emphasis from programming around the
  6278.  operating system to programming with it. Instead of hunting for
  6279.  undocumented interrupts that perform unsupported services and bypassing
  6280.  the operating system to get to the hardware, developers will learn to
  6281.  integrate all of the tools now at their disposal. With new hardware that
  6282.  virtually eliminates performance bottlenecks and a new operating system
  6283.  that takes full advantage of that hardware, developers can focus directly
  6284.  on their applications.
  6285.  
  6286.  
  6287.  Conclusion
  6288.  
  6289.  Although they are not exactly right around the corner, it won't be long
  6290.  before we will have applications "systems" that interact with each other,
  6291.  work together across networks, and share resources efficiently in
  6292.  environments spanning micros, minis, and mainframes.
  6293.  
  6294.  What will become of the "under-the-hood" programmer? What about all of
  6295.  those who still long to hack, to get their fingers dirty by directly
  6296.  driving hardware, manipulating code and data segments, and fooling with
  6297.  undocumented system calls? Well, MS OS/2 is undoubtedly full of mysterious
  6298.  code to decipher, but the real challenge will be fully utilizing the many
  6299.  tools and facilities that are now available to create state-of-the-art
  6300.  applications. For the serious microcomputer software developer the future
  6301.  is MS OS/2, and the future is now.
  6302.  
  6303.  
  6304.  ───────────────────────────────────────────────────────────────────────────
  6305.  Evolution and History of MS-DOS
  6306.  ───────────────────────────────────────────────────────────────────────────
  6307.  
  6308.  IN AUGUST 1981 Microsoft introduced MS-DOS, Version 1.0, a simple
  6309.  operating system providing support for a new IBM machine and a fledgling
  6310.  industry. Over the last six years the machine and the industry have grown
  6311.  up, and MS-DOS has evolved into MS OS/2, a very sophisticated,
  6312.  multitasking operating environment.
  6313.  
  6314.  Presented here is a brief but complete history of MS-DOS. It is hoped that
  6315.  the reader will find it entertaining, nostalgic, and perhaps revealing.
  6316.  How many readers know, for example, that the original design of the File
  6317.  Allocation Table (FAT) can be traced to Mr. Gates himself?
  6318.  
  6319.  1974: Intel introduces the 8 bit 8080 processor.
  6320.  
  6321.  January 1975: MITS introduces the $400 Altair computer; it has no
  6322.  keyboard, no monitor, no disk, and no operating system.
  6323.  
  6324.  February 1975: Paul Allen and Bill Gates develop and sell their own
  6325.  version of BASIC to MITS for the Altair.
  6326.  
  6327.  February 1976: Paul Allen, now working for MITS, asks Bill Gates to write
  6328.  a disk-based version of BASIC for the Altair.
  6329.  
  6330.  Bill Gates creates a working model of his disk BASIC in 10 days. He
  6331.  designs a disk layout and file structure based on a centralized File
  6332.  Directory and File Allocation Table (FAT). He also includes a rudimentary
  6333.  set of file services in the disk BASIC he is developing.
  6334.  
  6335.  1976-1978: Microsoft Disk BASIC is ported to all major 8 bit personal
  6336.  computers. An assembler and linker are developed for 8080- and Z80- based
  6337.  systems.
  6338.  
  6339.  April 1978: Intel announces the 8086, a 16 bit processor.
  6340.  
  6341.  January 1979: Tim Paterson of Seattle Computer Products begins work on a
  6342.  plug-in 8086 processor card to bring the power of the 8086 to the S-100
  6343.  bus.
  6344.  
  6345.  June 1979: Microsoft and Tim Paterson show Microsoft's BASIC running on
  6346.  Paterson's 8086 card at the National Computer Conference in New York.
  6347.  
  6348.  April 1980: Delays hold up the delivery of CP/M 86. Tim Paterson decides
  6349.  to write his own "Quick and Dirty" OS, which becomes known as 86-DOS. He
  6350.  incorporates the FAT structure first designed by Bill Gates for Disk-
  6351.  BASIC, and some features and techniques underlying MS-DOS.
  6352.  
  6353.  August 1980: IBM takes its first step towards producing the IBM PC,
  6354.  planning to use readily available, off-the-shelf 8 bit hardware. IBM
  6355.  visits Microsoft, asking if Microsoft can write a ROM-based BASIC for the
  6356.  computer IBM is developing.
  6357.  
  6358.  Microsoft suggests that IBM consider the 16 bit architecture. IBM's
  6359.  "Project Chess" goes on to become the 8088 (8086-based) IBM PC.
  6360.  
  6361.  The first working version of 86-DOS runs on Tim Paterson's 8086 card. This
  6362.  is essentially the birth of what will become known as MS-DOS.
  6363.  
  6364.  September 1980: IBM asks Microsoft to provide COBOL, FORTRAN and Pascal
  6365.  for their personal computer. Microsoft suggests to IBM that an operating
  6366.  system would be necessary to develop the additional languages.
  6367.  
  6368.  October 1980: Microsoft submits a proposal to IBM that includes MS-DOS.
  6369.  
  6370.  November 1980: The proposal is accepted by IBM. A prototype machine
  6371.  arrives at Microsoft and a small DOS team begins a concentrated period of
  6372.  work.
  6373.  
  6374.  February 1981: 86-DOS runs on the prototype for the first time. Over the
  6375.  next half year the OS is refined and becomes MS-DOS, Version 1.0.
  6376.  
  6377.  August 1981: IBM introduces the IBM PC, and announces three operating
  6378.  systems: MS-DOS, CP/M 86, and the P System. For several months MS-DOS is
  6379.  the only OS available. It is also priced substantially lower than CP/M.
  6380.  
  6381.  MS-DOS incorporates a number of advanced features:
  6382.  
  6383.    ■  Greater device independence.
  6384.    ■  Greater data integrity.
  6385.    ■  Simplified handling of peripheral devices.
  6386.    ■  A replaceable COMMAND.COM processor.
  6387.  
  6388.  February-April 1982: Microsoft introduces for the MS-DOS environment:
  6389.  
  6390.    ■  The Macro Assembler.
  6391.    ■  The FORTRAN Compiler.
  6392.    ■  The COBOL Compiler.
  6393.  
  6394.  June 1982: MS-DOS, Version 1.1, is announced, providing support for
  6395.  double-sided, eight sector diskettes on the IBM PC.
  6396.  
  6397.  March 1983: IBM announces the PC XT.
  6398.  
  6399.  Microsoft announces the release of MS-DOS, Version 2.0, which includes:
  6400.  
  6401.    ■  Supports for hard disks including a XENIX(c)-like hierarchical
  6402.       directory structure.
  6403.  
  6404.    ■  File Handles, allowing programs to reference files anywhere they
  6405.       have been loaded.
  6406.  
  6407.    ■  The ability to redirect the I/O of a program to any file or device,
  6408.       and implements pipes and filters.
  6409.  
  6410.    ■  Support  for nine-sectored diskettes, increasing storage to 360 Kb.
  6411.  
  6412.    ■  Provision for installable device drivers, promoting logical device
  6413.       independence.
  6414.  
  6415.    ■  Background printing through PRINT.COM.
  6416.  
  6417.    ■  ANSI.SYS device driver, providing support for serial "stream"
  6418.       processing to give cursor positioning and color control information to
  6419.       the monitor.
  6420.  
  6421.  March 1984: IBM introduces the PCjr with half-height disk drives, and MS-
  6422.  DOS, Version 2.11, is introduced, providing support for it.
  6423.  
  6424.  August 1984: IBM announces the 80286 based PC AT, with a 20 Mb hard disk
  6425.  and 1.2 megabyte high density diskette drive.
  6426.  
  6427.  Microsoft introduces MS-DOS, Version 3.0, which includes:
  6428.  
  6429.    ■  A rewritten MS-DOS kernel and a new standard set of I/O calls.
  6430.  
  6431.    ■  ISO Open System Interconnect (ISO) based model for networking.
  6432.  
  6433.    ■  Network redirector and file sharing support for the IBM PC Network
  6434.       Adapter Card.
  6435.  
  6436.  November 1984: Microsoft introduces MS-DOS, Version 3.1, and  Microsoft
  6437.  Networks (MS-Net) including:
  6438.  
  6439.    ■  Redirector and file sharing services for non-IBM network cards.
  6440.    ■  Transport and server functions to all MS-DOS systems.
  6441.    ■  Network spooled printing.
  6442.    ■  The basic structure for Installable File Systems.
  6443.  
  6444.  June 1985: Microsoft, Intel, and Lotus establish the Lotus-Intel-Microsoft
  6445.  Extended Memory Specification, allowing programs to access memory beyond
  6446.  the MS-DOS limit of 640 Kb of RAM.
  6447.  
  6448.  August 1985: IBM and Microsoft enter into a Joint Development Agreement.
  6449.  
  6450.  January 1986: Microsoft introduces MS-DOS, Version 3.2, which includes
  6451.  support for 3.5" diskettes.
  6452.  
  6453.  April 1987: IBM announces the Personal System/2 series of computers.
  6454.  
  6455.  Microsoft announces MS OS/2, with an integrated Windows presentation
  6456.  manager, MS-DOS, Version 3.3, Windows 2.0, and a new LAN Manager.
  6457.  
  6458.  
  6459.  ───────────────────────────────────────────────────────────────────────────
  6460.  CMD.EXE-OS/2 Command Processor
  6461.  ───────────────────────────────────────────────────────────────────────────
  6462.  
  6463.  OS/2 provides the user with a new command processor that extends the
  6464.  functionality of the command language and provides enhanced batch file
  6465.  support.
  6466.  
  6467.  Command Line Operators
  6468.  
  6469.  The following shows both the old operators as well as the new, from
  6470.  highest to lowest precedence.
  6471.  
  6472.  command.com           cmd.exe         Function
  6473.  
  6474.                           ^            Lexical Escape Charactor
  6475.                           ()           Command Grouper
  6476.      <                    <            I/O Redirector -+  Same
  6477.      >                    >            I/O Redirector  |->Precedence
  6478.      >>                   >>           I/O Redirector -+     Level
  6479.      |                    |            Pipe Operator
  6480.                           &&           And Operator
  6481.                           ||           Or Operator
  6482.                           &            Command Separator
  6483.  
  6484.  The operators common to both environments work the same way they
  6485.  previously did.
  6486.  
  6487.  The Lexical Escape Character allows the precedence operators to be used as
  6488.  regular charactors. Refer to the following,
  6489.  
  6490.    1. echo go home > kid
  6491.    2. echo go home ^> kid
  6492.  
  6493.  In the first case a file called kid is created with the line "go home" as
  6494.  its contents. In the second, "go home > kid" is echoed to the screen.
  6495.  
  6496.  The Command Grouper works in exactly the same way parentheses work in
  6497.  equations, to establish explicit precedence. Refer to the following,
  6498.  
  6499.  directory a: contains the files temp1, temp2 and temp3
  6500.  
  6501.    temp1 contains "a"
  6502.    temp2 contains "b"
  6503.    temp3 contains "c"
  6504.  
  6505.    1. dir a:*.* >> temp1 & (temp1 >> temp2 & temp2 >> temp3)
  6506.    2. (dir a:*.* >> temp1 & temp1 >> temp2) & temp2 >> temp3
  6507.  
  6508.  After execution of the first command line temp1, temp2 and temp3 will look
  6509.  as follows
  6510.  
  6511.    temp1 contains a,temp1,temp2,temp3
  6512.    temp2 contains b,a
  6513.    temp3 contains c,b,a
  6514.  
  6515.  If the second line had been executed instead, temp1, temp2 and temp3 would
  6516.  look as follows
  6517.  
  6518.    temp1 contains a,temp1,temp2,temp3
  6519.    temp2 contains b,a,temp1,temp2,temp3
  6520.    temp3 contains c,b,a,temp1,temp2,temp3
  6521.  
  6522.  The parentheses determine the order of execution. Additionally, lower
  6523.  precedence operators can be executed prior to those of higher precedence.
  6524.  
  6525.  The Pipe and Redirection operators work in the same way they previously
  6526.  did.
  6527.  
  6528.  The And operator performs the command to its right if and only if the
  6529.  command to its left successfully executes. For example, in
  6530.  
  6531.    dir exist.txt && copy exist.txt lpt1
  6532.  
  6533.  if the DIR command is successful, then exist.txt is printed. If DIR was
  6534.  not successful the command on the right will not be executed.
  6535.  
  6536.  The Or operator performs one of two commands depending on the success or
  6537.  failure of the first command given. For example, in the following
  6538.  
  6539.    more < document.txt || more < document.doc
  6540.  
  6541.  more will either output the contents of document.txt and then exit, or, if
  6542.  document.txt doesn't exist, will output the contents of document.doc.
  6543.  
  6544.  The Command Separator allows more than one command to be typed on a
  6545.  command line. For example,
  6546.  
  6547.    dir a:\*.* > temp & dir b:\*.* >> temp
  6548.  
  6549.  places the directory listing of the root directory of a: in a file called
  6550.  temp (which is either overwritten if it exists or is created if it doesn't
  6551.  exist) and then immediately executes the next command, which appends the
  6552.  contents of the root directory of b: to temp.
  6553.  
  6554.  BATCH FILE ENHANCEMENTS
  6555.  
  6556.  OS/2 supports all of the old batch file commands and adds four new ones.
  6557.  They are
  6558.  
  6559.    CALL
  6560.    EXTPROC
  6561.    SETLOCAL
  6562.    ENDLOCAL
  6563.  
  6564.  The Call command finally provides a means for users to chain together
  6565.  batch files so that when a called batch file ends it returns control to
  6566.  the calling batch file.
  6567.  
  6568.  Extproc allows a user to run batch files designed for command processors
  6569.  other than CMD.EXE. The command is issued as the first line of the batch
  6570.  file and defines the alternate command processor.
  6571.  
  6572.  Setlocal allows the user to define local drive, directory and environment
  6573.  variables for the current batch file. When the command is issued it saves
  6574.  the existing drive, directory and environment information and replaces it
  6575.  with the user specified information.
  6576.  
  6577.  In conjunction with the setlocal command, the endlocal command restores
  6578.  the drive, directory and environment information previously saved through
  6579.  setlocal, and allows additional setlocal calls.
  6580.  
  6581.  If a setlocal call is issued and the batch file issuing the setlocal call
  6582.  terminates without issuing an endlocal call, the operating system restores
  6583.  the original drive, directory and environment information, insuring that
  6584.  the state previously known to CMD.EXE still exists.
  6585.  
  6586.  
  6587.  ───────────────────────────────────────────────────────────────────────────
  6588.  System Configuration (CONFIG.SYS)
  6589.  ───────────────────────────────────────────────────────────────────────────
  6590.  
  6591.  CONFIG.SYS is the file that contains the commands used to configure the
  6592.  system. Only a single CONFIG.SYS file is needed to configure the system
  6593.  for both real-mode and protected-mode operations.
  6594.  
  6595.  During system initialization, OS/2 opens and reads the CONFIG.SYS file in
  6596.  the root directory of the drive from which it was started and interprets
  6597.  the commands within the file. If the file is not found, OS/2 assigns
  6598.  default values for the configuration commands.
  6599.  
  6600.  The following list summarizes the configuration commands for OS/2:
  6601.  
  6602.    buffers        Determine the number of buffers to allocate for disk I/O.
  6603.  
  6604.    country        Select the format for country-dependent information.
  6605.  
  6606.    device         Specify path and filename of a device driver to be
  6607.                   installed.
  6608.  
  6609.    iopl           Specify whether I/O privilege is to be granted or not.
  6610.  
  6611.    maxwait        Set the time limit for calculating CPU starvation.
  6612.  
  6613.    memman         Select memory management options.
  6614.  
  6615.    priority       Disable dynamic priority variation in scheduling regular
  6616.                   class processes.
  6617.  
  6618.    protectonly    Select the modes of operation.
  6619.  
  6620.    protshell      Specify path and filename of the OS/2 top-level command
  6621.                   processor.
  6622.  
  6623.    run            Start a system or daemon process during system
  6624.                   initialization.
  6625.  
  6626.    swappath       Specify the location of the swap file.
  6627.  
  6628.    timeslice      Set the time slice values for process scheduling.
  6629.  
  6630.    threads        Set the maximum number of threads in the system.
  6631.  
  6632.    trace          Select system trace.
  6633.  
  6634.    tracebuf       Set the system trace buffer size.
  6635.  
  6636.  The following commands are ignored:
  6637.  
  6638.    files
  6639.    lastdrive
  6640.  
  6641.  The following commands apply only to the configuration for real-mode and
  6642.  only if the MODE command specifies that old applications will be run.
  6643.  These real-mode-only commands are documented after the OS/2 configuration
  6644.  commands.
  6645.  
  6646.    break          Check for Ctrl-Break.
  6647.    fcbs           Determine file control block management information.
  6648.    rmsize         Select the amount of memory for real-mode applications.
  6649.    shell          Load and start the top-level command processor.
  6650.  
  6651.  
  6652.  Figure 1:  Virtual/Real Memory Mapping. The global descriptor table (GDT)
  6653.             and every local descriptor table (LDT) can each map 8191 64K
  6654.             segments or .5 Gb (8191 x 64K bytes= .5 Gb). There is one GDT for
  6655.             the entire MS OS/2 system, shared by every application. Each
  6656.             specific application has its own private LDT. Together, the
  6657.             shared GDT and the private LDT provide a virtual address space of
  6658.             up to one Gb for each application. The virtual addresses are
  6659.             managed by the operating system, and are mapped to a real memory
  6660.             space of up to 16Mb.
  6661.  
  6662.        Application Address Spaces
  6663.  
  6664.         Application n-┌──────────┐
  6665.                     ┌─┴─────────┐│
  6666.                   ┌─┴──────────┐││
  6667.                  ┌┴──────────┐ │││    1Gb      Real
  6668.    Application 1-│ ─ ─ ─ ─ ─ │ │││     |      Memory
  6669.       ┌───────── │_ _Code_ _ │ │││     |     ┌───────┐16M      Secondary
  6670.       │          │   Data    │ │││     |     ≈       ≈          Storage
  6671.    Unique        ├ ─ ─ ─ ─ ─ ┤ │││     |     ├ ─ ─ ─ ┤         ┌────────┐
  6672.    to Each       ≈           ≈ │││    LDTs   │ Code  │╔═══════╗│   ..   │
  6673.    Application   │           │ │├┘     |     ├ ─ ─ ─ ┤║Virtual║├─ ─ ─ ─ ┤
  6674.       │          ├─ ─ ─ ─ ─ ─┤ ├┘      |     │ Code  │╣Memory ╠│  Code  │
  6675.       │          │   Data    ├─┘       |     ├ ─ ─ ─ ┤║ Mgt   ║├ ─ ─ ─ ─┤
  6676.       ╞════════  ├─ ─ ─ ─ ─ ─┤ ─ ─ ─ ─5Gb─ ─ │ Data  │╚═══════╝│  Data  │
  6677.       │          ≈           ≈         |     ├ ─ ─ ─ ┤         ├─ ─ ─ ─ ┤
  6678.    Shared        ├─ ─ ─ ─ ─ ─┤         |     ├ ─ ─ ─ ┤         │   ..   │
  6679.                  │System Code│        GDT    │ System│         └────────┘
  6680.       │          │ and Data  │         |     └───────┘
  6681.       └──────────┴───────────┘         |
  6682.  
  6683.  
  6684.  Figure 2:  Protected Mode Memory Map
  6685.  
  6686.                        ╔═════════════╗
  6687.                        ║ Real Memory ║
  6688.                        ╚═════════════╝
  6689.                   16M╔═══════════════════╗ ───┐
  6690.                      ║  New Application  ║    │ Moveable
  6691.                      ≈   code and data   ≈    ├─Swappable or
  6692.                      ║     segments      ║    │ Non-swappable
  6693.                    1M╟───────────────────╢ ───┘
  6694.                      ║                   ║
  6695.                      ║  BIOS and video   ║      Fixed
  6696.                      ║     buffers       ║
  6697.                  640K╟───────────────────╢ ───┐
  6698.                      ║                   ║    │
  6699.                      ║  New Application  ║    │ Moveable
  6700.                      ≈   code and data   ≈    ├─Swappable or
  6701.                      ║     segments      ║    │ Non-swappable
  6702.                      ║                   ║    │
  6703.                      ╟─ ─ ─ ─ ─ ─ ─ ─ ─ ─╢ ───┘
  6704.                      ║       OS/2        ║      Fixed
  6705.                      ║                   ║
  6706.                     0╚═══════════════════╝
  6707.  
  6708.  
  6709.  Figure 3:  Compatibility Mode Memory Map
  6710.  
  6711.                          ╔═════════════╗
  6712.                          ║ Real Memory ║
  6713.                          ╚═════════════╝
  6714.                    16M╔═══════════════════╗ ───┐
  6715.                       ║  New Application  ║    │ Moveable
  6716.                       ≈   code and data   ≈    ├─Swappable or
  6717.                       ║     segments      ║    │ Non-swappable
  6718.                       ╟─ ─ ─ ─ ─ ─ ─ ─ ─ ─╢ ───┘
  6719.                       ║       OS/2        ║  Fixed
  6720.                     1M╟───────────────────╢ ───
  6721.                       ║  BIOS and video   ║  Fixed
  6722.                       ║     buffers       ║
  6723.                   640K╟───────────────────╢
  6724.                       ╟─ ─ ─ ─ ─ ─ ─ ─ ─ ─╢ ───┐
  6725.                       ║      Single       ║    │
  6726.                       ≈ "old" Application ≈    │
  6727.                       ║                   ║    ├─Variable size
  6728.                       ╟───────────────────╢    │
  6729.                       ║   "MS-DOS 3.x"    ║    │
  6730.                       ╟───────────────────╢ ───┘
  6731.                       ║       OS/2        ║      Fixed
  6732.                       ║                   ║
  6733.                      0╚═══════════════════╝
  6734.  
  6735.  
  6736.  Figure 5:  OS/2 maps multiple virtual I/O to the
  6737.             physical screen and keyboard.
  6738.  
  6739.                              Screen Groups
  6740.      ┌─────────┐             ┌─────────┐                 ┌─────────┐
  6741.      │         │             │▓▓▓ ┌──┐ │                 │         │
  6742.      │    C>   │             │▓▓ │/\│ │                 │    ?    │
  6743.      │         │             │    └──┘ │                 │         │
  6744.      └─────────┘             └─────────┘                 └─────────┘
  6745.     Character User        Presentation Manager          Special Purpose
  6746.       Interface        Graphics Desktop Publishing           │
  6747.        Light WP                    │                         │
  6748.        Database      ┌─────────────────────────────┐        │
  6749.       Spreadsheet ──│ Visual Console I/O Management│───────┘
  6750.                      └──────────────────────────────┘
  6751.                            ╔════════════════╗   ┌────────────────┐
  6752.                            ║ ░░░░░░░░░░░░░░ ║█  │ Physical Video │
  6753.                            ║ ░░░░░░░░░░░░░░ ║█  │    Manager     │
  6754.                            ║ ░░░░░░░░░░░░░░ ║█  └────┬───────────┘
  6755.                            ║ ░░░░░░░░░░░░░░ ║█───────┘
  6756.                          ╔═╝────────────────╚═╗
  6757.                          ║   ║█
  6758.                          ╚════════════════════╝█
  6759.                            ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
  6760.  
  6761.  
  6762.  Figure 6:  FAPI assures the software developer that an application will
  6763.             run under both MS-DOS 3.x and MS OS/2.
  6764.  
  6765.                                16M ┌──────────────────────────┐
  6766.                                    │  ┌────────────┐          │
  6767.                                    │  │   OS/2     │          │
  6768.                                    │  │ ┌──────────┴───┐      │
  6769.                                    │  │ │      OS/2    │      │
  6770.                                    │  │ │  ┌───────────┴────┐ │
  6771.                                    │  │ │  │      OS/2      │ │
  6772.                                    │  └─┤  │    Protect     │ │
  6773.                                    │    │  │     Mode       │ │
  6774.                                    │    └──┤    Full New    │ │
  6775.                                    │       │      API       │ │
  6776.                                    │       └────────────────┘ │
  6777.                MS-DOS 3.n          │     ┌────────────────┐   │
  6778.        ┌──────────────────────┐    │     │  "Family API"  │   │
  6779.        │       Reserved       │───┼────│   Applications │   │
  6780.        ├──────────────────────┤  1M├ ─ ─ └────────────────┘ ─ ┤
  6781.        │        DOS 3.n       │    │         Reserved         │
  6782.        │     Compatibility    │640K├ ─ ─ ┌────────────────┐ ─ ┤
  6783.        │      Environment     │────┼────│     DOS 3.n    │   │
  6784.        └──────────────────────┘    │     │  Compatibility │   │
  6785.                                    │     │   Environment  │   │
  6786.                                    │     └────────────────┘   │
  6787.                                  0 └──────────────────────────┘
  6788.  
  6789.  ████████████████████████████████████████████████████████████████████████████
  6790.  
  6791.  The OS/2 Windows Presentation Manager: Microsoft Windows On the Future
  6792.  
  6793.  Manny Vellon
  6794.  
  6795.  Last April, Microsoft announced a series of new products, including a new
  6796.  operating system, Microsoft Operating System/2 (OS/2). An important part
  6797.  of this announcement was the inclusion of Microsoft Windows as a standard
  6798.  part of the OS/2 operating system. Currently, Microsoft Windows must be
  6799.  purchased separately and installed under MS-DOS.
  6800.  
  6801.  The Windows presentation manager will be tightly integrated with OS/2 and
  6802.  will provide the same benefits that Microsoft Windows provides to MS-DOS:
  6803.  a windowed, graphical user interface and support for a variety of input
  6804.  and output devices. Through the presentation manager, OS/2 will replace
  6805.  the well-known A> prompt of MS-DOS with window-based screens.
  6806.  
  6807.  This union of Windows with OS/2 strengthens the role of Windows in future
  6808.  system and application products and also addresses the ease-of-use issues
  6809.  that have been associated with MS-DOS and IBM PC software. Windows
  6810.  provides a more intuitive interface that allows novice users to learn
  6811.  products more quickly.
  6812.  
  6813.  Also important is IBM's support of Microsoft Windows in its new products.
  6814.  This support ensures that Windows will become a standard part of the
  6815.  operating environment and encourages other hardware manufacturers to
  6816.  support it as well.
  6817.  
  6818.  
  6819.  Protected Mode
  6820.  
  6821.  The new IBM PS/2 series, the IBM PC AT, the Compaq 386, and other Intel
  6822.  286- and 386-based computers are capable of running in real mode or
  6823.  protected mode. In real mode, they operate much like the Intel 8088-based
  6824.  IBM PC: they are limited to using 1 megabyte (Mb) of memory, they can
  6825.  usually run only one program at a time, and they are vulnerable to
  6826.  crashing when a program goes awry. In protected mode, the 286- and 386-
  6827.  based computers don't have those limits.
  6828.  
  6829.  Under OS/2's protected-mode operating system, programs are no longer
  6830.  limited to 640K; applications can take advantage of up to 16 Mb of real
  6831.  memory and up to 1 gigabyte of virtual memory. OS/2 is able to perform
  6832.  multitasking──it can run several programs simultaneously by quickly
  6833.  switching among them. OS/2 is more robust than real-mode operating
  6834.  systems; programs run independently of each other, and if one crashes
  6835.  it is less likely to affect the others.
  6836.  
  6837.  Currently, Microsoft Windows can also do many of these functions. By
  6838.  performing a variety of sophisticated functions, Windows can take
  6839.  advantage of extended memory if available and can provide rudimentary
  6840.  multitasking. With the OS/2 Windows presentation manager, however, these
  6841.  functions are much easier to implement since the underlying operating
  6842.  system kernel includes these capabilities.
  6843.  
  6844.  
  6845.  User Interface
  6846.  
  6847.  In addition to this symbiosis "under the hood," OS/2 Windows is integral
  6848.  to the operating system at the user level. The Windows presentation
  6849.  manager will be the standard user interface for the new operating system.
  6850.  Users won't have to learn about disk directories, filenames, and cryptic
  6851.  commands; executing programs and managing the OS/2 file system with the
  6852.  OS/2 Windows presentation manager will be intuitive and fast.
  6853.  
  6854.  Although the new OS/2 Windows bears a strong resemblance to today's
  6855.  Microsoft Windows, there are some substantial differences between the two.
  6856.  Most significant are the differences in user interfaces. To eliminate
  6857.  these differences so as to produce a common user interface, Microsoft has
  6858.  announced a new version of Microsoft Windows──Version 2.0 (see Figure 2).
  6859.  This version, while still a real-mode version for MS-DOS, employs the same
  6860.  user interface to be used by the OS/2 Windows presentation manager.
  6861.  
  6862.  When Microsoft Windows was first developed, it used tiled windows (see
  6863.  Figure 1).
  6864.  Overlapped windows were considered too slow and unusable with
  6865.  low-resolution displays. In order to respond to customer requests for
  6866.  overlapping windows, and because optimized graphics algorithms and
  6867.  improved processing speeds have eliminated performance bottlenecks that
  6868.  are found in older technology, the new Windows uses overlapped windows
  6869.  instead of tiled windows.
  6870.  
  6871.  New Windows products will also have improved keyboard interfaces. Although
  6872.  Windows is best used with a mouse, it is possible, and sometimes
  6873.  preferable, to use it solely through the keyboard. Changes in the user
  6874.  interface will make this easier.
  6875.  
  6876.  First, the keyboard interface has been improved in order to allow direct
  6877.  access to items in dialog boxes. In Figure 4, for example, the user can
  6878.  type Alt-F to quickly position the cursor in the filename field of the
  6879.  dialog box. Access to menus from the keyboard has also been enhanced by
  6880.  allowing the developer to select any letter from a menu command to execute
  6881.  the item rather than being limited to the first letter, as in the case of
  6882.  Windows Version 1.03 and Version 1.04. This helps to make it easier for
  6883.  software developers to provide meaningful command names while still
  6884.  providing fast keyboard access to commands.
  6885.  
  6886.  The second significant change affects mouse operation. Currently, Windows
  6887.  employs pull-down menus. You click down on the menu bar to make visible a
  6888.  pop-up menu; then, while holding down the mouse button, you drag down to
  6889.  the desired command in the pop-up menu and let go of the button. This
  6890.  technique is fast, but it is prone to accidental selections and requires
  6891.  considerable manual dexterity. The new interface allows you to click and
  6892.  let go on the menu bar to make the pop-up menu visible and then click down
  6893.  on the desired command within the given menu.
  6894.  
  6895.  Finally, the new interface employs some new terminology (for example,
  6896.  minimize and maximize, instead of icon and zoom) and some additional
  6897.  keyboard operations.
  6898.  
  6899.  Users who are accustomed to the old keyboard interface won't be forced to
  6900.  learn the new one. Microsoft Windows, Version 2.0, will work in both the
  6901.  old and new styles to help users get accustomed to the new interface.
  6902.  However, the OS/2 Windows presentation manager will use this new interface
  6903.  only.
  6904.  
  6905.  ───────────────────────────────────────────────────────────────────────────
  6906.  Progression for Windows-based Applications
  6907.  
  6908.                            Windows 1.03
  6909.                                  
  6910.  
  6911.                           Optional Changes
  6912.           Edit Resource Files        Enhanced Printer Interfaces
  6913.           Implement Mult Doc Facil   Error Condition Support
  6914.                                  
  6915.  
  6916.                             Windows 2.0
  6917.           Overlapped Applications    EMS/EEMS Support
  6918.           New User Interface         Display Performance
  6919.                      Multiple Document Facilities
  6920.                                  
  6921.  
  6922.           Mechanical API Changes     Mode Rules
  6923.           GDI-to-GPI                 Access to OS/2 Facilities
  6924.                      Conform to Protected
  6925.                                  
  6926.  
  6927.                            OS/2 Windows
  6928.           Overlapped Applications    Preemptive Multitasking
  6929.           New User Interface         Large Memory (16Mb)
  6930.                      GPI and New Device Drivers
  6931.  
  6932.  ───────────────────────────────────────────────────────────────────────────
  6933.  
  6934.  API
  6935.  
  6936.  Although Microsoft Windows, Version 2.0, and the OS/2 Windows presentation
  6937.  manager appear to be similar and have many common functions, programs
  6938.  written for Microsoft Windows will have to be modified to work with the
  6939.  OS/2 Windows presentation manager. OS/2 restricts several operations in
  6940.  programs; for example, software interrupts are not allowed. Programs
  6941.  written for MS-DOS will also have to be modified to work in OS/2.
  6942.  
  6943.  Besides the changes required by the new operating system, such as
  6944.  replacing software interrupts with operating system calls, other changes
  6945.  have been made to the OS/2 Windows Application Program Interface (API) in
  6946.  order to standardize coding practices, improve error handling, and exploit
  6947.  new graphics capabilities. Fortunately, many of these changes are
  6948.  "mechanical" in nature. To follow the new coding practices, for example,
  6949.  all window manager calls are preceded with "Win": "WinCreateWindow",
  6950.  "WinShowWindow", etc. Other changes entail reordering parameters.
  6951.  Applications that make heavy use of graphics will require the most
  6952.  changes; the OS/2 Windows presentation manager contains a new graphics
  6953.  library that requires a variety of changes to be made to the API.
  6954.  
  6955.  
  6956.  The Future
  6957.  
  6958.  OS/2 is not intended to replace MS-DOS. Microsoft expects that real-mode
  6959.  (MS-DOS) and protected-mode (OS/2) products will continue to co-exist
  6960.  until Intel 286- and 386-based computers predominate. MS-DOS and Microsoft
  6961.  Windows will continue to be developed and marketed for low-end machines,
  6962.  while OS/2 addresses newer, more-powerful computers. The Windows user
  6963.  interface will serve as a bridge for users, allowing them to operate each
  6964.  of these classes of machines regardless of the underlying hardware.
  6965.  
  6966.  In time, Microsoft expects Intel 386-based machines to become the standard
  6967.  hardware environment for powerful new business and engineering
  6968.  applications. Microsoft will extend OS/2 to exploit the additional
  6969.  capabilities of the Intel 386 and will upgrade OS/2 in the future. Once
  6970.  more, the Windows presentation manager interface will help to keep these
  6971.  changes invisible to the user.
  6972.  
  6973.  
  6974.  Figure 2:  Comparison of Current and Future Windows Versions
  6975.  
  6976. ╓┌───────────────────────────────┌────────────────┌──────────────┌───────────╖
  6977.                                   Microsoft
  6978.                                   Windows         Microsoft      MS-OS/2
  6979.                                   1.03 & 1.04     Windows 2.0    Windows
  6980.  
  6981.  Presentation Spaces              Tiled           Overlapped     Overlapped
  6982.  
  6983.  More Consistent User                ---          Yes            Yes
  6984.  and Keyboard Interface
  6985.  
  6986.  Processor Environments           8088, 8086,     8088, 8086,    286, 386
  6987.                                   286, 386        286, 386
  6988.  
  6989.  Large Memory Support                ---          EMS/EEMS       16Mb
  6990.  
  6991.  Multitasking                     Non             Non            Fully
  6992.                                   Preemptive      Preemptive     Preemptive
  6993.  
  6994.  Enhanced Display Performance        ---          Yes            Yes
  6995.                                   Microsoft
  6996.                                   Windows         Microsoft      MS-OS/2
  6997.                                   1.03 & 1.04     Windows 2.0    Windows
  6998. Enhanced Display Performance        ---          Yes            Yes
  6999.  
  7000.  Runs Existing Windows (1.03)     Yes             Yes            Yes
  7001.  Application
  7002.  
  7003.  Graphics API                     GDI             GDI            GDI
  7004.  
  7005.  Multiple Document Interface         ---          Yes            Yes
  7006.  
  7007.  Device Drivers                      ---          Enhanced       New Model
  7008.  
  7009.  Old Application Support             ---          Improved       Improved
  7010.  
  7011.  Integral Part of OS                 ---          ---            Yes
  7012.  
  7013.  Protected Mode Execution            ---          ---            Yes
  7014.  Applications Execution
  7015.  
  7016.                                   Microsoft
  7017.                                   Windows         Microsoft      MS-OS/2
  7018.                                   1.03 & 1.04     Windows 2.0    Windows
  7019. 
  7020.  New Development API                 ---          ---            Yes
  7021.  
  7022.  New User "Shell"                    ---          ---            Yes
  7023.  and Keyboard Interface
  7024.  
  7025.  
  7026.  Figure 3a:  Microsoft Windows API. Programs written for MS-DOS will
  7027.              have to be modified to work in OS/2. Many of these changes
  7028.              are mechanical in nature──for example, all window manager
  7029.              calls are preceeded with "Win". Compare the above Windows 2.0
  7030.              code with that shown in Figure 3b below.
  7031.  
  7032.                                      ∙
  7033.                                      ∙
  7034.                                      ∙
  7035.  int PASCAL WinMain( hInstance, hPrevInstance, lpszCmdLine, cmdShow )
  7036.  HANDLE hInstance, hPrevInstance;
  7037.  LPSTR lpszCmdLine;
  7038.  int cmdShow;
  7039.  {
  7040.          MSG   msg;
  7041.          HWND  hWnd;
  7042.          NPWNDCLASS       pHelloClass;
  7043.                                      ∙
  7044.                                      ∙
  7045.                                      ∙
  7046.       /* Allocate and initialize class data structure pHelloClass */
  7047.                                      ∙
  7048.                                      ∙
  7049.                                      ∙
  7050.           /* Create a window class */
  7051.          if (!RegisterClass( (LPWNDCLASS)pHelloClass ) )
  7052.                  return FALSE;
  7053.  
  7054.          /* Create an application window */
  7055.          hWnd = CreateWindow((LPSTR)"Class", (LPSTR) "Title",
  7056.                              WS_TILEDWINDOW, 0, 0, 0, 0, (HWND)NULL,
  7057.                              (HMENU)NULL, (HANDLE)hInstance,
  7058.                              (LPSTR)NULL );
  7059.                                      ∙
  7060.                                      ∙
  7061.                                      ∙
  7062.          /* Process messages*/
  7063.          while (GetMessage((LPMSG)&msg, NULL, 0, 0)) {
  7064.                  TranslateMessage((LPMSG)&msg);
  7065.                  DispatchMessage((LPMSG)&msg);
  7066.                  }
  7067.  
  7068.                  return (int)msg.wParam;
  7069.  }
  7070.  
  7071.  CAPTION: OS/2 Windows Presentation Manager Program
  7072.                                      ∙
  7073.                                      ∙
  7074.                                      ∙
  7075.  
  7076.  
  7077.  Figure 3b: OS/2 Windows presentation manager API (see figure 3a)
  7078.  
  7079.  int cdecl main(argc, argv)
  7080.  int argc;
  7081.  char *argv[];
  7082.  {
  7083.          QMSG qmsg;
  7084.          HAB    hab;
  7085.          HMQ    hmq;
  7086.          HWND   hwnd, hwndFrame;
  7087.  
  7088.          /* get an anchor block */
  7089.          hab = WinInitialize();
  7090.  
  7091.          /* create a message queue for the application */
  7092.          hmq = WinCreateMsgQueue(hab, 0);
  7093.  
  7094.          /* create a window class */
  7095.          if (!WinRegisterClass(  (LPCH) "Class",
  7096.                                  WndProc,
  7097.                                  WS_SYNCPAINT | WS_CLIPSIBLINGS |
  7098.                                  WS_SIZEREDRAW,
  7099.                                  0,
  7100.                                  NULL)) return FALSE;
  7101.  
  7102.          /* create an application window */
  7103.          hwndFrame =
  7104.                  WinCreateStdWindow(NULL,
  7105.                                     FS_MENU | FS_TITLEBAR | FS_MINMAX |
  7106.                                     FS_SIZEBORDER | FS_SYSMENU,
  7107.                                     (LPSTR) "Class",
  7108.                                     (LPSTR) "Title", 0L, NULL,
  7109.                                     IDM_APPMENU,
  7110.                                     (HWND far *)&hwnd);
  7111.                                      ∙
  7112.                                      ∙
  7113.                                      ∙
  7114.          /* process messages */
  7115.          while (WinGetMsg(hab, (LPQMSG)&qmsg, NULL, 0, 0))
  7116.                  WinDispatchMsg(hab, (LPQMSG)&qmsg);
  7117.  
  7118.          /* destroy the resources used by the application */
  7119.          WinDestroyWindow(hwndFrame);
  7120.          WinDestroyMsgQueue(hmq);
  7121.          WinFinalize(hab);
  7122.  }
  7123.  
  7124.  ████████████████████████████████████████████████████████████████████████████
  7125.  
  7126.  The OS/2 MS-DOS Environment:Compatibility and Transition for MS_DOS Programs
  7127.  
  7128.  ───────────────────────────────────────────────────────────────────────────
  7129.  Also see the related article:
  7130.    Some Programming Don'ts for OS/2
  7131.  ───────────────────────────────────────────────────────────────────────────
  7132.  
  7133.  Joel Gillman
  7134.  
  7135.  Microsoft's multitasking, virtual memory operating system has finally been
  7136.  unveiled. Now that MS OS/2 is a reality, programmers may be wondering with
  7137.  some trepidation whether they'll have to rewrite all their MS-DOS
  7138.  programs.
  7139.  
  7140.  The good news is that nearly all of your existing code, both commercial
  7141.  products and custom-made utilities, will run just fine in OS/2's MS-DOS
  7142.  compatibility environment, an operating mode that emulates MS-DOS 3.3.
  7143.  Programs that need low-level access will not run in the compatibility
  7144.  mode, but these are exceptions. Most spreadsheets, word processors, and
  7145.  many other applications programs will work just fine.
  7146.  
  7147.  However, once you start writing code to run in the multitasking protected
  7148.  mode of OS/2, you will have to do some things differently. There's a
  7149.  silver lining in this, because you will end up with better code once
  7150.  you've unlearned some of the bad habits you picked up writing in MS-DOS.
  7151.  Multitasking implies resource sharing, so you can't be overly greedy with
  7152.  system resources anymore. But what you may lose in resource access you
  7153.  gain in the power of multitasking.
  7154.  
  7155.  After booting OS/2, the user is greeted by the session manager, from which
  7156.  the user can start any number of protected-mode applications or enter
  7157.  compatibility mode. For each protected-mode program, the session manager
  7158.  creates a new protected mode execution environment, called a screen group.
  7159.  Each screen group has a command processor (CMD.EXE, which corresponds to
  7160.  the MS-DOS 3.x COMMAND.COM), a virtual screen buffer, a virtual keyboard,
  7161.  and a virtual mouse, along with a virtual memory space of up to 16
  7162.  megabytes (Mb) depending on the total number of screen groups.
  7163.  
  7164.  
  7165.  Compatibility Mode
  7166.  
  7167.  The MS-DOS compatibility mode in OS/2 uses the 80286 chip's real mode,
  7168.  which is engaged by a technique called mode switching (see "OS/2: Turning
  7169.  Off the Car to Change Gears,"). Mode switching emulates a full
  7170.  system reset without disrupting operation, allowing the processor to
  7171.  switch from protected to real mode. The compatibility mode gives the
  7172.  system an 8086 interface with 1 Mb of address space and emulates MS-DOS
  7173.  3.x with the MS-DOS SHARE utility installed. OS/2 needs up to 500 Kb of
  7174.  system memory in a typical configuration──substantially more than the 50 Kb
  7175.  required by MS-DOS 3.x.
  7176.  
  7177.  The compatibility mode recognizes all of the documented MS-DOS services. A
  7178.  number of undocumented interrupt 21H services also exist under MS-DOS 3.x,
  7179.  but since OS/2 does not recognize most of the undocumented MS-DOS
  7180.  services, programs that use them won't run in the compatibility mode.
  7181.  
  7182.  The compatibility mode supports a ROM data area and accepts service
  7183.  interrupts, but you cannot call ROM services directly by address. You must
  7184.  use the interrupt 10 through interrupt 1A services instead (see Figure 1).
  7185.  Applications can call any hardware interrupt except the CMOS
  7186.  clock/calendar interrupt or an interrupt already taken over by any OS/2
  7187.  device driver other than the keyboard. The compatibility mode will issue
  7188.  interrupt 28h (spooler interrupt)──so you can still run SideKick(R)──and
  7189.  interrupt 24h (critical error handler).
  7190.  
  7191.  A program running in the MS-DOS compatibility mode freezes up if the
  7192.  system places it in the background by switching to the protected mode.
  7193.  Programs frozen in the background receive no CPU service or interrupts. A
  7194.  program that determines the time of day by counting clock ticks, for
  7195.  example, will generate inaccurate times if it goes into the background.
  7196.  
  7197.  Version-specific applications won't run in the compatibility mode, because
  7198.  the MS-DOS version number of OS/2 is 10. A way around this is to modify
  7199.  the code so that it calls DOSGETVERSION and, if it gets back 10,
  7200.  determines which mode it's executing by calling DOSGETMACHINEMODE. The
  7201.  program can then take the appropriate action.
  7202.  
  7203.  
  7204.  Handling Devices
  7205.  
  7206.  Most of your old device drivers will not run in compatibility mode. OS/2
  7207.  doesn't support any of the MS-DOS block device drivers, such as those used
  7208.  with disk or tape drives. The only character device drivers it supports
  7209.  are VDI (video display interface) and CON (console) drivers, since
  7210.  character device drivers will work only if they are polled rather than
  7211.  interrupt-driven. The system supports all of the device driver command
  7212.  packets shown in Figure 2.
  7213.  
  7214.  When you run a driver, only programs running in the compatibility mode can
  7215.  use its device. The device isn't available to protected-mode applications.
  7216.  Device drivers cannot call user code, because they operate at a higher
  7217.  privilege level than the user program.
  7218.  
  7219.  Drivers are installed the same way as in MS-DOS(R), using the configuration
  7220.  command
  7221.  
  7222.    device = driver filename
  7223.  
  7224.  MS-DOS device drivers are loaded and initialized in compatibility mode.
  7225.  Initialization is in most respects the same as in MS-DOS, except that no
  7226.  interrupt 21h (hardware-independent) functions can be performed from the
  7227.  initialization code.
  7228.  
  7229.  Compatibility mode restricts which devices programs can manipulate. Sound-
  7230.  generating programs that need a higher frequency time base for more
  7231.  precise pitch control can remap the 8253 clock/timer chip, that is, assign
  7232.  different numbers to its system interrupts. Remapping the 8259 interrupt
  7233.  controller is not allowed. Such programs, which must remap the 8259 in
  7234.  order to trap keystrokes, will not run. Applications can still hook
  7235.  keystrokes after OS/2 gets them, however.
  7236.  
  7237.  Programs that need low-level disk I/O access for copy protection purposes
  7238.  cannot remap the disk controller. Programs do have direct access to the
  7239.  disk controller via interrupt 13h (floppy disk services), interrupt 25h
  7240.  (absolute disk read), and interrupt 26h (absolute disk write). Note that
  7241.  interrupt 13h and interrupt 26h are not allowed for fixed media such as
  7242.  hard disks.
  7243.  
  7244.  High-speed communications applications that must remap the DMA controller
  7245.  won't run because the operating system remaps the controller. Applications
  7246.  can remap the COM, AUX, and parallel ports, although using one of these
  7247.  ports in the compatibility mode makes it unavailable to protected mode
  7248.  programs and vice versa.
  7249.  
  7250.  
  7251.  80286 Restrictions
  7252.  
  7253.  Programmers writing applications for MS-DOS developed some programming
  7254.  techniques and coding shortcuts to enhance performance, even though the
  7255.  books and manuals tended to discourage using them. Many of these
  7256.  techniques won't work in OS/2 because of differences between the 8086 or
  7257.  8088 chip and the 80286. You'll have to observe several restrictions if
  7258.  you want to run your applications in the compatibility environment or in
  7259.  protected mode.
  7260.  
  7261.  First, don't use wrap-around segment offsets. The 8086 and 8088
  7262.  microprocessors translate an out-of-range address value into something
  7263.  recognizable, but the 80286 doesn't. You cannot address beyond the
  7264.  allocated size of a segment; the system aborts the program if an offset
  7265.  larger than the segment descriptor's limit value is used to reference that
  7266.  segment.
  7267.  
  7268.  The 80286 doesn't allow writable code segments. One of the bits in the
  7269.  segment descriptor identifies the segment as either code or data. A code
  7270.  segment's descriptor doesn't have a read/write bit, so only valid code
  7271.  segments can be placed in the CS (code segment selector) register, and a
  7272.  program may not write into valid code segments. However, an alias can be
  7273.  used to make a data segment into a code segment to be executed.
  7274.  
  7275.  Since different machines use different timing speeds, don't count on the
  7276.  CPU clock as a timing constant. This is a typical problem in copy-
  7277.  protected programs.
  7278.  
  7279.  Don't allow a division-trap handler to resume execution in the original
  7280.  code stream unless it is able to detect and understand differences between
  7281.  the 8086 or 8088 and the 80286. After a division-error trap, the 80286
  7282.  points to the division instruction (including prefixes) and doesn't change
  7283.  the register values. The 8086 and 8088 point to the instruction following
  7284.  the division instruction and may change the DX:AX or AH:AL register sets
  7285.  as well.
  7286.  
  7287.  The 80286 CL (low-order loop/shift/repeat count) registers won't permit
  7288.  shift counts greater than 31. On the 80286, the shift and rotate counts
  7289.  are masked to 5 bits.
  7290.  
  7291.  When executing the PUSH SP (push stack pointer onto stack) instruction,
  7292.  the 80286 pushes the SP value onto the stack before incrementing the
  7293.  value, while the 8086 and 8088 push the SP value after incrementing it.
  7294.  Few programs use this particular code sequence, but for those that do,
  7295.  Microsoft offers this way around the problem:
  7296.  
  7297.    MOV AX,SP
  7298.    PUSH AX
  7299.  
  7300.  these two lines of code can be implemented by a macro.
  7301.  
  7302.  The PUSHF instruction followed by a POPF may change the contents of the
  7303.  flag register in the 80286, since more flag bits are defined in the 80286
  7304.  flag word than in those of the 8086 or 8088. Also, because of a bug in the
  7305.  80286, the POPF may change the contents of the flag register in the
  7306.  80286. You should use flag-specific instructions to set or test flag
  7307.  register values instead of setting or testing for explicit bit patterns.
  7308.  
  7309.  
  7310.  FAPI
  7311.  
  7312.  In order to permit you to write new programs guaranteed to run in MS-DOS
  7313.  and in OS/2's protected mode (both current and future versions), Microsoft
  7314.  has defined a set of system calls that are guaranteed to support both
  7315.  environments. This set of system calls, the Family Application Program
  7316.  Interface (FAPI), is a subset of the full OS/2 Application Program
  7317.  Interface (API).
  7318.  
  7319.  Five types of code will run on OS/2: old programs designed for MS-DOS 3.x
  7320.  that run on OS/2 in the compatibility mode; presentation manager programs,
  7321.  FAPI programs that run on OS/2 in the compatibility mode; FAPI programs
  7322.  that run on OS/2 in protected mode; and new programs that run on OS/2 only
  7323.  in the protected mode. Figure 3 summarizes the characteristics valid for
  7324.  each of these program types.
  7325.  
  7326.  FAPI allows a program to be linked to run in both modes and includes
  7327.  system calls in all categories except those specific to protected mode,
  7328.  such as multitasking, run-time linking, and device monitors. Some
  7329.  restrictions apply to using the FAPI calls in the compatibility mode,
  7330.  which are discussed in detail in the OS/2 Programmer's Guide.
  7331.  
  7332.  The protected mode does not give you direct access to the screen, as you
  7333.  would have with a MS-DOS 3.x BIOS call because of its memory protection.
  7334.  Instead, you have access to a virtual screen buffer within each screen
  7335.  group. The screen buffer paints the screen only when that screen group is
  7336.  active. For bimodal compatibility, FAPI provides a subset of the video
  7337.  input/output (VIO) calls for creating and writing to a virtual screen
  7338.  buffer.
  7339.  
  7340.  Just using FAPI calls, however, won't guarantee protected mode
  7341.  compatibility. Basically, you want a well-behaved program; it shouldn't
  7342.  sneak past the operating system to the hardware or get too clever with the
  7343.  segment registers. To write code that operates in protected mode, you give
  7344.  up sovereignty over the hardware. Multitasking requires that the operating
  7345.  system, rather than your program, allocate hardware and system resources.
  7346.  
  7347.  If you've done any programming in XENIX or UNIX, you're used to this.
  7348.  However, if you've only worked in MS-DOS, you'll want to adjust your
  7349.  thinking a little.
  7350.  
  7351.  OS/2 uses an indirect segment addressing scheme: a segment number points
  7352.  to a table entry, called a segment decriptor, which in turn points to the
  7353.  memory space. OS/2's memory management service will change the memory
  7354.  pointer in the descriptor as the total system memory allocation changes,
  7355.  so there is no way of knowing where a given segment number actually puts
  7356.  you in physical memory. Thus, a well-behaved program doesn't try to
  7357.  interpret segment numbers or calculate other segment locations from a
  7358.  given segment number.
  7359.  
  7360.  You can't assume that any given segments overlap or don't overlap, nor can
  7361.  you assume any particular relationship between segment:offset combinations
  7362.  and physical memory. The segment number is nothing more than a segment ID,
  7363.  with no particular significance apart from that.
  7364.  
  7365.  The segment registers are intended for the storage of valid segment
  7366.  numbers. If you store invalid numbers there──for example, by using the
  7367.  segment registers as scratch-pad memory (the 8086 doesn't seem to have
  7368.  enough registers to suit some people)──your program will crash.
  7369.  
  7370.  Your program cannot issue a CLI (clear interrupt) instruction in protected
  7371.  mode because this causes a protection trap. When in compatibility mode,
  7372.  IRET (interrupt return) restores the previous value of IFLG (interrupt
  7373.  flag), but IRET has no effect in protected mode. Protected mode programs
  7374.  can interact directly with hardware only by linking to a special I/O
  7375.  Privilege Level Segment. This allows access to the 80286 processor's Ring
  7376.  2 security level.
  7377.  
  7378.  
  7379.  Bimodal Device Drivers
  7380.  
  7381.  OS/2 supports bimodal device drivers that run in either mode, obviating
  7382.  the need for the system to switch modes to process interrupts.
  7383.  
  7384.  Unlike a MS-DOS 3.x device driver, a bimodal OS/2 driver has to support
  7385.  multiple synchronous and asynchronous requests. However, the basic
  7386.  structure remains pretty much the same: the driver contains a strategy
  7387.  routine and an interrupt routine. In addition, some device drivers may
  7388.  have to include a routine to trap ROM BIOS software interrupts from
  7389.  compatibility mode.
  7390.  
  7391.  An application program uses a request packet to call the strategy routine,
  7392.  just as with a MS-DOS 3.x driver. The strategy routine determines whether
  7393.  a request is valid and places valid requests in a queue to the device,
  7394.  using the DevHlp functions to manage the queue. If the device is idle, the
  7395.  strategy routine starts it and returns to the operating system, which
  7396.  suspends the thread until the request has been executed.
  7397.  
  7398.  When the device completion interrupt occurs, the interrupt routine sets
  7399.  the return status in the request packet and removes that packet from the
  7400.  queue. It then calls a DevHlp routine, DevDone, to tell OS/2 that the
  7401.  request is complete.
  7402.  
  7403.  The strategy routine should disable interrupts when checking to see if the
  7404.  device is active and when examining the queue. This protects the interrupt
  7405.  routine from other driver request interrupts. When interrupts are
  7406.  reenabled, the interrupt routine will receive only ones of higher
  7407.  priority.
  7408.  
  7409.  This is only a sample of some design considerations in writing OS/2
  7410.  bimodal device drivers. The subject is treated at greater length in the
  7411.  OS/2 Device Driver Guide.
  7412.  
  7413.  
  7414.  New Tools
  7415.  
  7416.  A new C compiler that runs in both modes is included in the OS/2 Software
  7417.  Development Kit. However, executable files written in Microsoft C
  7418.  Compiler, Version 4.0, should have no trouble running in the compatibility
  7419.  mode, as long as they meet the general criteria outlined earlier for
  7420.  operation in OS/2. You don't need to recompile or relink them with the new
  7421.  OS/2 C run-time library.
  7422.  
  7423.  The new C library is nearly identical to the Version 4.0 library, which is
  7424.  designed for single-thread execution only. Most of the functions are not
  7425.  re-entrant and therefore will not work in a multithread process. Figure 4
  7426.  lists the re-entrant functions that may be used in multithread programs.
  7427.  
  7428.  All the routines in Figure 4 will work properly in programs that use a far
  7429.  data model (compact and large model). However, in near-data-model-memory
  7430.  (small- and medium-model) programs, only some of these routines are
  7431.  guaranteed to function properly. The others have model-dependent pointers
  7432.  in their interfaces and have the potential to allocate stack space outside
  7433.  the default segment (SS!=DS).
  7434.  
  7435.  The kit also contains a new macro assembler that will run in protected
  7436.  mode. Again, existing MASM executable files will run in the compatibility
  7437.  mode, subject to the general restrictions described earlier.
  7438.  
  7439.  The MS-DOS 3.2 network function calls aren't supported in the
  7440.  compatibility mode, but the new Microsoft OS/2 LAN Manager will allow
  7441.  networking in protected mode.
  7442.  
  7443.  
  7444.  Device Monitors
  7445.  
  7446.  One problem that has plagued MS-DOS programmers is that when several
  7447.  terminate-and-stay-resident (TSR) programs are loaded into memory,
  7448.  they tend to step on each other in order to catch a reactivate keystroke
  7449.  from the keyboard. When you put a TSR like Borland's SideKick, Rosesoft's
  7450.  ProKey, or North Edge Software's TimeSlips into the background, the
  7451.  program calls interrupt 21h function 31h (Terminate and Stay Resident) and
  7452.  remains in memory even though you return to the system prompt. The program
  7453.  is still watching the keyboard, trapping keystrokes before the system gets
  7454.  them. When you hit a hot key combination, such as Ctrl-Alt, the program
  7455.  pops back onto the screen. The problem is that when two or more pop-up
  7456.  programs reside in the background at the same time, each wants to trap the
  7457.  keyboard interrupt before any other program does (see "Moving Toward an
  7458.  Industry Standard for Developing TSRs," MSJ, Vol. 1, No. 2).
  7459.  
  7460.  
  7461.  OS/2 solves this problem in protected mode by giving each TSR its own
  7462.  keyboard device monitor for reading keystrokes. Suppose you want to have
  7463.  several TSRs written by different vendors available within a given screen
  7464.  group. Each one will have its own keyboard device monitor. The monitors
  7465.  receive keystrokes in the order of monitor registration, which is set when
  7466.  the programs are first run. Keystrokes are passed on to the first monitor
  7467.  registered, which can trap the keystroke and generate a response or pass
  7468.  the keystroke on the next monitor registered (see Figure 5).
  7469.  
  7470.  Obviously, in this scheme, no two TSRs can use the same reactivate-key
  7471.  sequence. You can't reactivate a pop-up program in one screen group from a
  7472.  different screen group. By definition, a screen group is made up of a
  7473.  virtual screen, virtual mouse, virtual keyboard, and virtual memory
  7474.  space, and screen groups can't talk to each other.
  7475.  
  7476.  
  7477.  The Tradeoff
  7478.  
  7479.  The constraints that OS/2 imposes on the programmer may seem at first
  7480.  rather strict. With MS-DOS, you are pretty much free to use the system
  7481.  services or to leave them alone and go directly to the CPU instead. But in
  7482.  a multitasking system, you just can't do that. OS/2 takes over the CPU and
  7483.  the hardware, granting access only in certain instances.
  7484.  
  7485.  On the other hand, a lot of old problems go away. Applications will no
  7486.  longer have to fight each other for system resources, because OS/2
  7487.  allocates resources among them on a priority basis. Applications will have
  7488.  a common program interface (API) to work with, ensuring future
  7489.  compatibility. What you lose in resource access you gain in the new
  7490.  opportunities of multitasking, such as the potential for real-time
  7491.  multithread applications.
  7492.  
  7493.  Similarly, the memory access restrictions are compensated for in protected
  7494.  mode by the greatly increased size of the memory space that you can work
  7495.  with and the addition of memory management capabilities. The only
  7496.  limitation is that you must use system calls for access rather than
  7497.  stuffing numbers directly into the segment registers. And the 80286 chip
  7498.  is fast enough that efficiency issues aren't as critical as they were on
  7499.  the original PCs and XTs, so programming shortcuts such as segment games
  7500.  just aren't necessary anymore.
  7501.  
  7502.  
  7503.  Figure 1:  Hardware/Operating Environment Compatibility Chart. Hardware
  7504.             compatibility and support is different for DOS 3.3 and OS/2.
  7505.             OS/2 seeks to insulate applications software from the hardware.
  7506.  
  7507.                                             ┌──────────OS/2─────────┐
  7508.                                  DOS 3.3    Compatibility      New
  7509.                                              Environment     Programs
  7510.   Supported Hardware              8088            --             --
  7511.                                   8086            --             --
  7512.                                    286           286
  7513.                                    386           386
  7514.  
  7515.   Available Memory                640K           840K           16MB
  7516.   Can overcommit Memory            --             --             YES
  7517.   True Multitasking                --             --             YES
  7518.   Use Software Interrupts         YES            YES              NO
  7519.   Use Hardware Interrupts         YES            YES              NO
  7520.   Use undocumented DOS            YES           Some              NO
  7521.     Interfaces
  7522.   Have direct access to Hardware  YES            YES             YES
  7523.   Can run in the Background        NO             NO             YES
  7524.   Obey 286 Segment Rules           NO             NO             YES
  7525.  
  7526.  
  7527.  Figure 2:  Device Driver Commands. Shown here are the device driver commands
  7528.             supported under OS/2.
  7529.  
  7530.  Code      Command
  7531.  
  7532.    0       Init
  7533.    3       IOCtl Input
  7534.    4       Input (Read)
  7535.    5       Non-Destructive Input No Wait
  7536.    6       Input Status
  7537.    7       Input Flush
  7538.    8       Output
  7539.    9       Output with Verify
  7540.   10       Output Status
  7541.   11       Output Flush
  7542.   12       IOCtl Output (Write)
  7543.   13       Device Open
  7544.   14       Device Close
  7545.   15       Generic IOCtl
  7546.  
  7547.  
  7548.  Figure 3:  Software Compatibility Chart. Programs may run in one or more
  7549.             modes depending on how they behave.
  7550.  
  7551. ╓┌──────────────────────┌─────────────┌─────────────┌────────────┌───────────╖
  7552.                          ┌─ DOS ──┐    ┌────────────────── OS/2 ───────────┐
  7553.                         Old Programs  ┌─────FAPI Programs─────┐  New Programs
  7554.  
  7555.  Start With             COMMAND.COM   COMMAND.COM   COMMAND.EXE  CMD.EXE
  7556.  
  7557.  Can Run in OS/2
  7558.  Compatibility Box      Yes           Yes           No           No
  7559.  
  7560.  Can Run in Background  No            No            Yes          Yes
  7561.  
  7562.                          ┌─ DOS ──┐    ┌────────────────── OS/2 ───────────┐
  7563.                         Old Programs  ┌─────FAPI Programs─────┐  New Programs
  7564. 
  7565.  Permit Old-Style INT
  7566.  21H Dos 3.x
  7567.  Interrupts             Yes           No            No           No
  7568.  
  7569.  Permit Undocumented
  7570.  Dos Interfaces         No            No            No           No
  7571.  
  7572.  Have IOPL              Yes           Via FAPI      Via FAPI     Via OS/2
  7573.  
  7574.  Obey 286 Segment
  7575.  Rules                  No            Yes           Yes          Yes
  7576.  
  7577.  Can Overcommit
  7578.  Memory                 No            No            Yes          Yes
  7579.  
  7580.  Addressable Memory     640K          640K          16M          16M
  7581.  
  7582.  Permit Software
  7583.                          ┌─ DOS ──┐    ┌────────────────── OS/2 ───────────┐
  7584.                         Old Programs  ┌─────FAPI Programs─────┐  New Programs
  7585. Permit Software
  7586.  Interrupts             Yes           Via FAPI      No           No
  7587.  
  7588.  Permit Hardware
  7589.  Interrupts             Yes           No            No           No
  7590.  
  7591.  Program Residence      Below         Below         Above        Above
  7592.                         Boundary      Boundary      Boundary     Boundary
  7593.  
  7594.  Permit Multitasking    No            Yes           Yes          Yes
  7595.  
  7596.  
  7597.  Figure 4:  OS/2 API Calls
  7598.  
  7599.  Family API (FAPI) functions are highlighted
  7600.  
  7601.  * Indicates that FAPI support is limited (certain restrictions are
  7602.    imposed)
  7603.  
  7604. ╓┌─────────────────────┌─────────────────────────────────────────────────────╖
  7605.  API Function Name     Description
  7606.  
  7607.  BadDynLink            Bad Dynamic Link
  7608.  DosAllocHuge*         Allocate Huge Memory
  7609.  DosAllocShrSeg        Allocate Shared Segment
  7610.  DosBeep               Generate Sound From Speaker
  7611.  DosBufReset           Commit File Cache Buffers
  7612.  DosCaseMap*           Perform Case Mapping on String of
  7613.                        Binary Characters
  7614.  DosChdir              Change Current Directory
  7615.  DosChgFilePtr         Change File Read/ Write Pointer
  7616.  DosClose              Close File Handle
  7617.  DosCloseQueue         Close Queue
  7618.  DosCloseSem           Close System Semaphore
  7619.  DosCreateCSAlias      Create CS Alias
  7620.  DosCreateSem          Create System Semaphore
  7621.  DosCreateThread       Create another thread of execution
  7622.  DosCreateQueue        Create Queue
  7623.  DosCWait*             Wait for child termination
  7624.  DosDelete             Delete File
  7625.  API Function Name     Description
  7626. DosDelete             Delete File
  7627.  DosDevConfig          Get Device Configuration
  7628.  DosDevIOCtl*          I/O Control for Devices
  7629.  DosDupHandle          Duplicate File Handle
  7630.  DosEnterCritSec       Enter Critical Section of Execution
  7631.  DosError*             Enable Hard Error Processing
  7632.  DosExecPgm*           Execute Program
  7633.  DosExit*              Exit Program
  7634.  DosExitCritSec        Exit  Critical Section of Execution
  7635.  DosExitList           Routine List for Process Termination
  7636.  DosFileLocks*         File Lock Manager
  7637.  DosFindClose*         Close Find Handle
  7638.  DosFindFirst*         Find First Matching File
  7639.  DosFindNext*          Find Next Matching File
  7640.  DosFlagProcess        Set Process External Event Flag
  7641.  DosFreeModule         Free  Dynamic-Link Module
  7642.  DosFreeSeg            Free Segment
  7643.  DosGetCtryInfo*       Get Country-Dependent Formatting Information
  7644.  DosGetDateTime        Get Current Date and Time
  7645.  DosGetDBCSev          Get DBCS Environmental Vector
  7646.  API Function Name     Description
  7647. DosGetDBCSev          Get DBCS Environmental Vector
  7648.  DosGetEnv             Get Address of Process
  7649.  DosGetHugeShift       Get Shift Count
  7650.  DosGetInfoSeg         Get Address of System VariablesSegment
  7651.  DosGetMachineMode     Return Current Mode of Processor
  7652.  DosGetMessage         Get System Message and Insert Text Strings
  7653.  DosGetModHandle       Get Dynamic-Link Module Handle
  7654.  DosGetModName         Get Dynamic-Link Module Name
  7655.  DosGetProcAddr        Get Dynamic-Link Procedure Address
  7656.  DosGetPrtyGet         Process Priority
  7657.  DosGetShrSeg          Access Shared Segment
  7658.  DosGetVersion         Get Dos Version Number
  7659.  DosGiveSeg            Give  Access to Segment
  7660.  DosHoldSignal*        Disable/Enable Signal
  7661.  DosInsMessage         Insert Variable Text Strings In Message
  7662.  DosIOAccess           Request I/O Access to Device
  7663.  DosKillProcess        Terminate Process
  7664.  DosLoadModule         Load  Dynamic-Link Module
  7665.  DosMakePipe           Create Pipe
  7666.  DosMkdir              Make Subdirectory
  7667.  API Function Name     Description
  7668. DosMkdir              Make Subdirectory
  7669.  DosMonClose           Close Connection to OS/2 Device Driver
  7670.  DosMonOpen            Open  Connection to OS/2 Device Driver
  7671.  DosMonRead            Read  Input from Monitor Structure
  7672.  DosMonReg             Register Set of Buffers as Monitor
  7673.  DosMonWrite           Write Output to Monitor Structure
  7674.  DosMove               Move File or Subdirectory
  7675.  DosMuxSemWait         Wait  for one of n semaphores to be cleared
  7676.  DosNewSize            Change File Size
  7677.  DosOpen*              Open File
  7678.  DosOpenQueue          Open Queue
  7679.  DosOpenSem            Open Exisiting Semaphore
  7680.  DosPeekQueue          Peek Queue
  7681.  DosPurgeQueue         Purge Queue
  7682.  DosPutMessage         Output Message Text To Handle
  7683.  DosQCurDir            Query Current Directory
  7684.  DosQCurDisk           Query Current Disk
  7685.  DosQFHandState        Query File Handle State
  7686.  DosQFileInfo*         Query File Information
  7687.  DosQFileMode          Query File Mode
  7688.  API Function Name     Description
  7689. DosQFileMode          Query File Mode
  7690.  DosQFSInfo            Query File System Information
  7691.  DosQHandType          Query Handle Type
  7692.  DosQueryQueue         Query Queue
  7693.  DosQVerify            Query Verify Setting
  7694.  DosRead               Read from File
  7695.  DosReadAsync          Asynchronous Read from File
  7696.  DosReadQueue          Read  from Queue
  7697.  DosReAllocHuge*       Change Huge Memory Size
  7698.  DosReAllocateSeg*     Change segment Size
  7699.  DosResumeThread       Restart Thread
  7700.  DosRmdir              Remove Subdirectory
  7701.  DosSelectDisk         Select Default Drive
  7702.  DosSemClear           Clear (release) Semaphore
  7703.  DosSemRequest         Request Semaphore
  7704.  DosSemSet             Set Semaphore Owned
  7705.  DosSemSetWait         Set Semaphore and wait for next Clear
  7706.  DosSemWait            Wait for Semaphore to be Cleared
  7707.  DosSetDateTime        Set Current Date and Time
  7708.  DosSetFHandState*     Set File Handle State
  7709.  API Function Name     Description
  7710. DosSetFHandState*     Set File Handle State
  7711.  DosSetFileInfo        Set File Information
  7712.  DosSetFileMode        Set File Mode
  7713.  DosSetMaxFH           Set Maximum File Handles
  7714.  DosSetPrty            Set Process Priority
  7715.  DosSetSigHandler*     Handle Signal
  7716.  DosSetVec             Establish Handler For Exception Vector
  7717.  DosSetVerify          Set/Reset Verify Switch Delay ProcessExecution
  7718.  DosSubAlloc           Suballocate Memory within Segment
  7719.  DosSubFree            Free Memory Suballocated within Space
  7720.  DosSubSet             Initialize or Set Allocated Memory
  7721.  DosSuspendThread      Suspend Thread Execution
  7722.  DosSystemService      Dos System Process Services
  7723.  DosTimerAsync         Start Asynchronous Timer
  7724.  DosTimerStart         Start Periodic Interval Timer
  7725.  DosTimerStop          Stop  Asynchronous or Interval Timer
  7726.  DosPTrace             Interface for Program Debugging
  7727.  DosWrite              Synchronous Write to File
  7728.  DosWriteAsync         Asynchronous Write to File
  7729.  DosWriteQueue         Write to Queue
  7730.  API Function Name     Description
  7731. DosWriteQueue         Write to Queue
  7732.  KbdCharIn             Read Character Scan Code
  7733.  KbdFlushBuffer        Flush Keyboard Buffer
  7734.  KbdGetStatus          Get Keyboard Status
  7735.  KbdPeek*              Peek at Character-Scan Code
  7736.  KbdRegister           Register keyboard Subsystem
  7737.  KbdSetStatus          Set Keyboard Status
  7738.  KbdStringIn           Read Character String
  7739.  MouClose              Close Mouse Device For Current Screen
  7740.  GroupMouDrawPtr       Release Screen Area For Device Driver Use
  7741.  MouGetDevStatus       Get Current Pointing Device Driver Status Flags
  7742.  MouGetEventMask       Get Current Pointing Device One- Word Event Mask
  7743.  MouGetNumButtons      Get Number of Buttons
  7744.  MouGetNumMickeys      Get Number of Mickeys-Per-Centimeter
  7745.  MouGetNumQueel        Get Current Status for Pointing  Device Event Queue
  7746.  MouGetScaleFact       Sets  Scale Factors for Current Pointing Device
  7747.  MouOpen               Open  Mouse Device For Current Screen Group
  7748.  MouReadEventQue       Read  Pointing Device Event Queue
  7749.  MouRegister           Register Mouse Subsystem
  7750.  MouRemovePtr          Reserve Screen Area For Application Use
  7751.  API Function Name     Description
  7752. MouRemovePtr          Reserve Screen Area For Application Use
  7753.  MouSetEventMask       Assign New Event Mask To Current Pointing Device
  7754.  MouSetHotKey          Set System Hot Key
  7755.  MouSetPtrShape        Set Pointer Shape and Size
  7756.  MouSetScaleFact       Set Scale Factors for Current Positioning Device
  7757.  VioEndPopUp           Deallocate a Pop-up Display Screen
  7758.  VioGetAnsi            Get ANSI State
  7759.  VioGetBuf             Get Logical Video Buffer
  7760.  VioGetCurPos          Get Cursor Position
  7761.  VioGetCurType         Get Cursor Type
  7762.  VioGetPhysBuf         Get Physical Video Buffer
  7763.  VioPopUp              Allocate Pop-up DisplayScreen
  7764.  VioPrtScreen          Print Screen
  7765.  VioReadCellStr        Read Character-Attribute String
  7766.  VioReadCharStr        Read Character String
  7767.  VioRegister           Register Video Subsystem
  7768.  VioSavReDrawWait      Screen Save RedrawWait
  7769.  VioScrLock*           Lock Screen
  7770.  VioScrollDn           Scroll Screen Down
  7771.  VioScrollLf           Scroll Screen Left
  7772.  API Function Name     Description
  7773. VioScrollLf           Scroll Screen Left
  7774.  VioScrollRt           Scroll Screen Right
  7775.  VioScrollUp           Scroll Screen Up
  7776.  VioScrUnLock          Unlock Screen
  7777.  VioSetAnsi            Set ANSI On or Off
  7778.  VioSetCurPos          Set Cursor Position
  7779.  VioSetCurType         Set Cursor Type
  7780.  VioSetMode            Set Display Mode
  7781.  VioShowBuf            Display Logical Buffer
  7782.  VioWrtCellStr         Write Character-Attribute String
  7783.  VioWrtCharStr         Write Character String
  7784.  VioWrtCharStrAttr     Write Character String With Attribute
  7785.  VioWrtNAttr           Write N Attributes
  7786.  VioWrtNCell           Write N Character-Attributes
  7787.  VioWrtNChar           Write N Characters
  7788.  VioWrtTty             Write TTY String
  7789.  
  7790.  
  7791.  Figure 5:  When TSR programs are run in protected mode, OS/2 assigns each a
  7792.             keyboard device monitor, which reads keystrokes from the keyboard.
  7793.             This keeps the TSRs from fighting each other to be the first to
  7794.             trap keystrokes.
  7795.  
  7796.  ╔═══════════╗  ╔═══════════╗  ╔═══════════╗  ╔═══════════╗  ╔═══════════╗
  7797.  ║     1     ║  ║     2     ║  ║     3     ║  ║     4     ║  ║     5     ║
  7798.  ║           ║  ║           Pop-Up Applications           ║  ║           ║
  7799.  ╚═══════════╝  ╚═══════════╝  ╚═══════════╝  ╚═══════════╝  ╚═══════════╝
  7800.                                                                
  7801.  ┌─────╨─────┐  ┌─────╨─────┐  ┌─────╨─────┐  ┌─────╨─────┐  ┌─────╨─────┐
  7802.  │     1     │  │     2     │  │     3     │  │     4     │  │     5     │
  7803.  │           │  │         Keyboard Device Monitors        │  │           │
  7804.  └────────╥──┘  └────────╥──┘  └────────╥──┘  └────────╥──┘  └───────────┘
  7805.           ║             ║             ║             ║        
  7806.  ┌────────║────────║─────║─────────║────║─────────║────║────────║────────┐
  7807.  │        ╚════════╝     ╚═════════╝    ╚═════════╝    ╚════════╝        │
  7808.  ├ ─ ─ ─ ─ ┐                                                             │
  7809.  │Interrupt│              Keystroke Distribution                         │
  7810.  └────────┴────────────────────────────────────────────────────────╥────┘
  7811.       ╚═══════╗                                             ╔═══════╝
  7812.       ┌───────╨────────────────────────────────────────────────────┐
  7813.       │                   Keyboard Device Driver                    │
  7814.       └────────────────────────────────────────────────────────────┘
  7815.           ║
  7816.  
  7817.  
  7818.  ───────────────────────────────────────────────────────────────────────────
  7819.  Some Programming Don'ts for OS/2
  7820.  ───────────────────────────────────────────────────────────────────────────
  7821.  
  7822.  When writing programs for either the compatibility mode or protected mode
  7823.  in OS/2, you must observe a number of restraints that weren't necessary in
  7824.  writing for MS-DOS. Many of these restrictions arise from differences
  7825.  between the 8086 or 8088 and the 80286 microprocessors.
  7826.  
  7827.    ■ Don't depend on segment overlap or lack of it.
  7828.  
  7829.    ■ Don't depend on any relationship between segment:offset combinations and
  7830.      physical memory.
  7831.  
  7832.    ■ Don't use wrap-around address offsets.
  7833.  
  7834.    ■ Don't use the segment registers for anything but valid segment numbers.
  7835.  
  7836.    ■ Never address beyond the allocated size of a segment.
  7837.  
  7838.    ■ If you have to play with the I/O ports, use only the appropriate dynamic
  7839.      link routines (FAPI).
  7840.  
  7841.    ■ Don't mix code and data in the same segment.
  7842.  
  7843.    ■ Don't use undefined opcodes.
  7844.  
  7845.    ■ Don't use the PUSH SP instruction.
  7846.  
  7847.    ■ Don't use the POPF instruction.
  7848.  
  7849.    ■ Don't use shift counts greater than 31.
  7850.  
  7851.    ■ Don't use IDIV operands to produce a most-negative number.
  7852.  
  7853.    ■ Don't resume execution in the original code stream after a division
  7854.      trap.
  7855.  
  7856.    ■ Don't use redundant prefix bytes.
  7857.  
  7858.    ■ Don't use CLI instructions (in protected mode).
  7859.  
  7860.    ■ Don't use CPU speed as a timing constant.
  7861.  
  7862.    ■ Don't examine or set explicit flag register values.
  7863.  
  7864.    ■ Don't single-step interrupt instructions in debuggers.
  7865.  
  7866.    ■ Don't write self-modifying code.
  7867.  
  7868.  ████████████████████████████████████████████████████████████████████████████
  7869.  
  7870.  OS/2 Multitasking: Exploiting the Protected Mode of the 80286
  7871.  
  7872.  ───────────────────────────────────────────────────────────────────────────
  7873.  Also see the related article:
  7874.    Configuring the OS/2 Multitasker
  7875.  ───────────────────────────────────────────────────────────────────────────
  7876.  
  7877.  Ray Duncan
  7878.  
  7879.  Multitasking is the technique of dividing CPU time between multiple tasks
  7880.  so that they appear to be running simultaneously. Of course, the
  7881.  microprocessor is only executing one sequence of machine instructions
  7882.  within one task at any given time, but the switching from one task to
  7883.  another is invisible to both the user and the programs themselves. The
  7884.  operating system is responsible for allocating system resources such as
  7885.  memory to the various executing tasks and for resolving contending
  7886.  requests to such peripherals as video displays and disk drives.
  7887.  
  7888.  The part of the operating system that allocates CPU time between tasks is
  7889.  called the scheduler or dispatcher, and the rotation from one task to
  7890.  another, or from a task to a module of the operating system, is called a
  7891.  context switch. When a context switch occurs, the dispatcher must save the
  7892.  current state of the task that was executing, including its registers and
  7893.  program counter, load the registers and program counter belonging to the
  7894.  next task to run, and transfer control to that task at the point where it
  7895.  was previously suspended.
  7896.  
  7897.  There are two basic strategies for task scheduling that are used by modern
  7898.  multitasking operating systems: event-driven and pre-emptive.
  7899.  
  7900.  Event-driven schedulers rely on each task to be "well-behaved" and yield
  7901.  control at frequent enough intervals so that every program has acceptable
  7902.  throughput and none will be "starved" for CPU cycles. This yielding may be
  7903.  explicit (the program calls a specific operating system function to give
  7904.  up control) or implicit (the program is suspended when it requests the
  7905.  operating system to perform I/O on its behalf and regains control only
  7906.  after the I/O is completed and other tasks have in turn yielded control).
  7907.  This strategy is quite efficient in transaction-oriented systems where
  7908.  there is a great deal of I/O and not much computation, but the system can
  7909.  be brought to its knees by a single compute-bound task, such as an in-
  7910.  memory sort.
  7911.  
  7912.  A pre-emptive scheduler relies on the presence of an external signal
  7913.  generated at regular intervals, typically a hardware interrupt triggered
  7914.  by a real-time clock. When the interrupt occurs, the operating system
  7915.  gains control from whatever task was executing, saves its context,
  7916.  evaluates the list of programs that are ready to run, and gives control to
  7917.  (dispatches) the next program. This approach to multitasking is often
  7918.  called "time-slicing"; this term is derived from the mental image of
  7919.  dividing the sweep of a second hand around a clock face into little wedges
  7920.  and doling out the pieces to all the eligible programs.
  7921.  
  7922.  Modern mainframe and minicomputers, and the more powerful microcomputers,
  7923.  such as the Intel 80286 and Motorola 68020, include hardware features
  7924.  specifically designed to make multitasking operating systems more
  7925.  efficient and robust. These include privilege levels and memory
  7926.  protection.
  7927.  
  7928.  In the simplest use of privilege levels, the CPU is either in kernel mode
  7929.  or user mode at any given time. In kernel mode, which is reserved for the
  7930.  operating system, any machine instruction can be executed, and any
  7931.  location in memory can be accessed. As part of the mechanism of
  7932.  transferring control from the operating system to an application program,
  7933.  the CPU is switched into user mode. In this CPU state, any attempt to
  7934.  execute certain reserved instructions, such as writing to an I/O port,
  7935.  causes a hardware interrupt and returns control to the operating system.
  7936.  (Although the Intel 80286 microprocessor actually supports four privilege
  7937.  levels, OS/2 ordinarily uses only the highest and lowest of these.)
  7938.  Similarly, the hardware memory protection mechanisms detect any attempt by
  7939.  an application program to access memory that does not belong to it, and
  7940.  generate a hardware interrupt that allows the operating system to abort
  7941.  the offending task.
  7942.  
  7943.  Microsoft OS/2 is designed around a pre-emptive, priority-based
  7944.  multitasking scheduler. Understanding OS/2 multitasking requires a grasp
  7945.  of three distinct, but related, concepts or system objects: processes,
  7946.  threads, and screen groups.
  7947.  
  7948.  ───────────────────────────────────────────────────────────────────────────
  7949.  Three types of systems objects are involved in OS/2 multitasking: processes,
  7950.  threads, and screen groups. A process represents an application program
  7951.  accessing system resources such as files, memory, and inter-process
  7952.  communication facilities. A process can contain multiple concurrent points
  7953.  of execution called threads; each thread has its own priority and stack.
  7954.  Processes are collected into screen groups that read and write a virtual
  7955.  display and keyboard; the Session Manager is used to select a screen group.
  7956.  
  7957.                     ┌──────────────────────────────────┐
  7958.                     │                                  │█
  7959.                     │                                  │█
  7960.                     │                                  │█
  7961.                     │         Physical Screen          │█
  7962.                     │                                  │█
  7963.                     │                                  │█
  7964.                     │                                  │█
  7965.                     │                                  │█
  7966.                     └──────────────────────────────────┘█
  7967.                       ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
  7968.                                       
  7969.                              Session manager maps
  7970.                       virtual screen for a group to the
  7971.                               physical display.
  7972.                     ┌ ─ ─ ─ ─ ─ ─ ─ ─ ┴──────────────────┐
  7973.                     │                                    │
  7974.          ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐              ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
  7975.                                 █                                    █
  7976.          │                     │█             │                     │█
  7977.            Virtual screen for   █               Virtual screen for   █
  7978.          │   screen group 1    │█             │   screen group 2    │█
  7979.                                 █                                    █
  7980.          └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘█             └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘█
  7981.           ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀              ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
  7982.                                                              
  7983.            │                │                     │             │
  7984.    ┌───────┴───────┐ ┌──────┴───────────────┐ ┌───┴────┐ ┌──────┴────────┐
  7985.    │   │      │    │ │   │      │      │    │ │   │    │ │   │      │    │
  7986.    │   │      │    │ │   │      │      │    │ │   │    │ │   │      │    │
  7987.    │   │      │    │ │   │      │      │    │ │   │    │ │   │      │    │
  7988.    │   │      │    │ │   │      │      │    │ │   │    │ │   │      │    │
  7989.    │ Thread Thread │ │ Thread Thread Thread │ │ Thread │ │ Thread Thread │
  7990.    │   A1     A2   │ │   B1     B2     B3   │ │   C1   │ │   D1     D2   │
  7991.    └───────────────┘ └──────────────────────┘ └────────┘ └───────────────┘
  7992.        PROCESS A            PROCESS B          PROCESS C     PROCESS D
  7993.  ───────────────────────────────────────────────────────────────────────────
  7994.  
  7995.  Processes
  7996.  
  7997.  The simplest case of an OS/2 process is very similar to an application
  7998.  program loaded for execution under MS-DOS 2.x and 3.x. A process is
  7999.  started (whether by a shell or command processor or by another
  8000.  application) by a call to the OS/2 service DOSEXECPGM. OS/2 initiates a
  8001.  new process by allocating memory segments to hold that process's code,
  8002.  data, and stack, and then initializing the memory segments from the
  8003.  contents of the program's .EXE disk file.
  8004.  
  8005.  Once it is running, a process can obtain access to additional system
  8006.  resources such as files, pipes, semaphores, queues, and additional memory
  8007.  with various OS/2 function calls (see Figure 1). A process terminates
  8008.  itself with a call to the OS/2 function DOSEXIT and can also be aborted by
  8009.  its parent process, by an unrecoverable hardware error, or by a memory
  8010.  protection fault. In any case, OS/2 will automatically release all
  8011.  resources belonging to the process when it terminates.
  8012.  
  8013.  Ordinarily, processes are only aware of themselves, OS/2 services, and any
  8014.  child processes they start directly. They cannot directly access memory
  8015.  space or resources belonging to another process, including a child
  8016.  process, without the cooperation of that process.
  8017.  
  8018.  
  8019.  Threads
  8020.  
  8021.  The MS OS/2 scheduler, however, knows nothing about processes; it
  8022.  partitions the available CPU cycles among dispatchable entities known as
  8023.  threads. A thread has a point of execution, a priority, a stack pointer,
  8024.  and general register contents (see Figure 1). At any given time, a thread
  8025.  is either blocked (waiting for I/O or some other event), ready to execute,
  8026.  or actively executing (it has control of the CPU).
  8027.  
  8028.  Each process has a primary thread that receives control from OS/2 when the
  8029.  process is started; it begins executing at the program's designated entry
  8030.  point. However, that primary thread can start up additional threads within
  8031.  the same process. Multiple threads within a process execute asynchronously
  8032.  to one another, can have different priorities, and can manipulate one
  8033.  another's priorities.
  8034.  
  8035.  Although the threads within a process have separate stacks, they share the
  8036.  same near data segment (DGROUP) and thus the same local heap. Carefully
  8037.  designing code to use the stack for local variables allows procedures to
  8038.  be shared between threads──this happens naturally in C programs. Access to
  8039.  static variables or other data structures must be coordinated between
  8040.  threads through the use of RAM semaphores or similar mechanisms. The
  8041.  threads also share all the other system resources owned by the process as
  8042.  a whole──open files, system semaphores, queues, and pipes──but OS/2
  8043.  generally provides automatic serialization of operations on these
  8044.  resources.
  8045.  
  8046.  When a thread within an application program is executing, the system is in
  8047.  user mode; the thread can only access the resources owned by the process
  8048.  that contains it and cannot execute certain machine instructions. A clock
  8049.  tick, another hardware interrupt, or a call by the application for an OS/2
  8050.  function causes a transition back into kernel mode, so that OS/2 can
  8051.  service the interrupt or provide the requested operation.
  8052.  
  8053.  When OS/2 is ready to exit kernel mode, the scheduler receives control and
  8054.  examines its list of active threads. The thread with the highest priority
  8055.  that is ready to execute gains control of the machine. If the thread that
  8056.  was just interrupted is one of several eligible threads and has not used
  8057.  up its time-slice, it receives preference. If a thread becomes starved for
  8058.  CPU cycles because other threads with higher priorities are getting all
  8059.  the attention, OS/2 will temporarily bump that thread's priority to a
  8060.  higher value.
  8061.  
  8062.  
  8063.  Screen Groups
  8064.  
  8065.  Processes are in turn members of screen groups, which is what the average
  8066.  user perceives as being OS/2 multitasking. When the user presses the
  8067.  SysReq key, he exits from the current session, or screen group, to a menu
  8068.  displayed by the Session Manager. He can then select a command processor
  8069.  or other program already executing in another screen group or establish a
  8070.  new screen group by loading a new copy of the protected-mode command
  8071.  processor.
  8072.  
  8073.  OS/2 maintains a separate virtual screen for each screen group, which
  8074.  receives the output of all the processes in that group. The virtual screen
  8075.  is mapped to the physical display whenever that screen group is selected
  8076.  by the user with the Session Manager. New processes are added to a screen
  8077.  group by being "spawned" by the command processor or another process
  8078.  already executing within the group.
  8079.  
  8080.  By convention, only one of the processes within a screen group should be
  8081.  in the foreground at any given time, that is, writing to the screen and
  8082.  reading from the keyboard. The OS/2 DETACH command allows programs to be
  8083.  placed in the background from the command processor level. However, since
  8084.  OS/2 does not place any restrictions on access to the virtual display by
  8085.  the various processes within a screen group, DETACHing normal programs is
  8086.  not usually too useful since it just results in chaotic displays. Programs
  8087.  intended to be used as background tasks must be specially designed to use
  8088.  the OS/2 services for pop-up windows and keyboard monitors, so that they
  8089.  do not disrupt the displays or otherwise interfere with the proper
  8090.  operation of the foreground task within their group.
  8091.  
  8092.  A screen group, or session, is removed from the system by first
  8093.  terminating any active application programs within that group and
  8094.  returning to the command processor prompt. Then, entry of the EXIT
  8095.  command terminates the command processor itself. The Session Manager
  8096.  regains control and displays a menu of the remaining screen groups.
  8097.  
  8098.  
  8099.  OS/2 Programming
  8100.  
  8101.  OS/2 offers a diverse set of services to application programs that allow
  8102.  the creation of complex and powerful multitasking applications (see
  8103.  Figure 2). These services include
  8104.  
  8105.    ■  starting and stopping child processes
  8106.  
  8107.    ■  obtaining the return code of a child process
  8108.  
  8109.    ■  starting, suspending, and destroying threads
  8110.  
  8111.    ■  altering the priorities of threads
  8112.  
  8113.    ■  inter-process communication (see "OS/2 Inter-Process Communication:
  8114.       Semaphores, Pipes, and Queues,")
  8115.  
  8116.  OS/2 DOSEXECPGM is used by the parent process to load and execute the
  8117.  child process. This function is analogous to, but considerably more
  8118.  powerful than, the EXEC function (Int 21h Function 4Bh) that was available
  8119.  in MS-DOS 2.x and 3.x. The child process can, in turn, load other
  8120.  processes and so on until system limits on threads or open files are
  8121.  exhausted or the system runs out of swap space on the disk.
  8122.  
  8123.  The OS/2 DOSEXECPGM function is called with
  8124.  
  8125.    ■  the address of the filename of the child process to be executed
  8126.  
  8127.    ■  a flag controlling whether the child process is synchronous or
  8128.       asynchronous
  8129.  
  8130.    ■  the addresses of an argument string block and an environment block to
  8131.       be passed to the child process (Each of these blocks consists of a
  8132.       series of null-terminated (ASCIIZ) strings; the block is terminated
  8133.       by an extra null byte. The environment block corresponds exactly to
  8134.       the environment block you are familiar with in MS-DOS 2.x and 3.x,
  8135.       while the simplest case of the argument block is simply a copy of the
  8136.       command line that invoked a process.)
  8137.  
  8138.    ■  addresses of buffers to receive the process ID of the child and other
  8139.       information
  8140.  
  8141.  When a child process executes synchronously, execution of the thread in
  8142.  the parent process that made the DOSEXECPGM call is suspended until the
  8143.  child process terminates, either intentionally or owing to an error
  8144.  condition. When the thread in the parent resumes execution, it is supplied
  8145.  with the return code of the child and a termination code that indicates
  8146.  whether the child terminated normally or was aborted by the operating
  8147.  system.
  8148.  
  8149.  If the child process is asynchronous, all threads in the parent process
  8150.  continue to execute while the child is running. Any thread in the parent
  8151.  process can later use the DOSCWAIT call to resynchronize with the child
  8152.  process by suspending itself until the child terminates and then obtaining
  8153.  its return code. As an alternative, the parent can use the process ID of
  8154.  the child, which is supplied by OS/2 on return from the original
  8155.  DOSEXECPGM call, to unilaterally abort execution of the child process with
  8156.  the DOSKILLPROCESS call at any time. (A special usage of the asynchronous
  8157.  option of DOSEXECPGM, together with the DOSPTRACE function, allows the
  8158.  child program to be traced, inspected, and modified under the parent
  8159.  program's control. This combination of OS/2 services allows the creation
  8160.  of program debuggers that are compatible with 80286 memory protection.)
  8161.  
  8162.  The child process automatically inherits access to certain resources of
  8163.  the parent process. These resources include the handles for any open files
  8164.  (unless the parent process explicitly opened the files with the
  8165.  noninheritance flag), handles to any open pipes, handles to any open
  8166.  system semaphores (but not ownership of the semaphores), and a copy of the
  8167.  parent's environment block (unless the parent goes to the trouble of
  8168.  creating a new block and passes a pointer to it).
  8169.  
  8170.  Figure 3 is a demonstration of the DOSEXECPGM function──a synchronous
  8171.  execution of CHKDSK as a child process. The various options for
  8172.  asynchronous execution, coupled with several options that are available
  8173.  with the DOSCWAIT function and the DOSKILLPROCESS function, allow for very
  8174.  flexible execution-time relationships between parent and child processes.
  8175.  
  8176.  
  8177.  Managing Threads
  8178.  
  8179.  OS/2 has a rich repertoire of function calls for control of multiple
  8180.  threads of execution within a process. The use of multiple threads is
  8181.  particularly appropriate for cases in which a process must manage several
  8182.  I/O devices with vastly different I/O rates (for example, the keyboard,
  8183.  disk, and video display) and remain rapidly responsive to the needs of
  8184.  each. Multiple threads are also appropriate for cases in which a process
  8185.  needs to run in multiple instances, but the instances do not require
  8186.  separate data segments or resources, since multiple threads are started
  8187.  much more quickly than multiple processes and have less system overhead.
  8188.  
  8189.  As mentioned previously, each process has a single primary thread that is
  8190.  known to the OS/2 dispatcher when it is started up, and this thread's
  8191.  initial point of execution is the program's designated entry point. The
  8192.  primary thread can use the MS OS/2 function DOSCREATETHREAD to start up
  8193.  additional threads within the process, and those new threads can also
  8194.  start up threads and so on. Each thread is initially entered through a far
  8195.  call from OS/2 and can terminate through a far return or by issuing the
  8196.  OS/2 function call DOSEXIT. (A process is terminated when the sole
  8197.  remaining active thread in a process issues DOSEXIT, or when any thread
  8198.  issues DOSEXIT with a special parameter that indicates that all threads
  8199.  should be immediately terminated.)
  8200.  
  8201.  A thread can use the function DOSSLEEP to suspend itself for a programmed
  8202.  period of time, or it can block on a semaphore to await reactivation by
  8203.  another thread or process triggering that same semaphore. Alternatively, a
  8204.  thread can use the functions DOSSUSPENDTHREAD or DOSRESUMETHREAD to
  8205.  suspend or reactivate other threads within the same process without their
  8206.  cooperation. Similarly, a thread can use the functions DOSGETPRTY or
  8207.  DOSSETPRTY to inspect or modify the execution priority of itself or other
  8208.  threads in accordance with execution-time requirements.
  8209.  
  8210.  Because a thread can be unilaterally suspended by another thread without
  8211.  its knowledge or cooperation, the functions DOSENTERCRITSEC and
  8212.  DOSEXITCRITSEC are provided in order to protect a thread from interruption
  8213.  while it is executing a critical section of its code.
  8214.  
  8215.  Figure 4 demonstrates the use of DOSCREATETHREAD by one thread to start up
  8216.  another thread that emits ten beeps at one-second intervals and then
  8217.  terminates. Although this is a trivial use of multiple threads, it gives
  8218.  an inkling of the enormous power of this concept and the ease with which
  8219.  asynchronous processing can be incorporated into an OS/2 application.
  8220.  
  8221.  
  8222.  Summary
  8223.  
  8224.  OS/2 uses a time-slicing, pre-emptive, priority-based multitasking
  8225.  strategy. The Intel 80286 microprocessor's support for privilege levels
  8226.  and memory protection are fully exploited by OS/2 in order to run multiple
  8227.  concurrent tasks and to protect those tasks from damaging each other or
  8228.  the operating system.
  8229.  
  8230.  A process represents the execution of an application and the ownership of
  8231.  any resources──files, memory, etc.──associated with that execution.
  8232.  Processes can spawn other processes and can exert certain control over
  8233.  those child processes, but sharing of data between two processes is
  8234.  possible only with the cooperation of both processes. OS/2 has a wealth of
  8235.  facilities for inter-process communication, which are discussed in "OS/2
  8236.  Inter-Process Communication: Semaphores, Pipes, and Queues."
  8237.  
  8238.  The thread is the dispatchable element used by OS/2 to track execution
  8239.  cycles of the processor. Threads can start, stop, and influence the
  8240.  execution of other threads within the same process. Sharing of data
  8241.  between threads is natural and, in fact, difficult to avoid, since all
  8242.  threads in a process share access to the same memory, files, pipes,
  8243.  queues, and semaphores. In essence, the OS/2 loader knows about processes,
  8244.  but the OS/2 scheduler knows about threads.
  8245.  
  8246.  
  8247.  Figure 1:  Thread- and Process-specific information maintained by OS/2.
  8248.  
  8249.  Per-Process Information
  8250.  
  8251.  Process ID (PID)
  8252.  Disk-swapping information
  8253.  Local Descriptor Table (LDT) pointer
  8254.  System resources owned or opened:
  8255.     Files
  8256.     Pipes
  8257.     Queues
  8258.     System Semaphores
  8259.     Device monitors
  8260.     Memory
  8261.  Child processes
  8262.  
  8263.  Per-Thread Information
  8264.  
  8265.  Thread ID
  8266.  Thread priority
  8267.  Thread state: blocked, ready to execute, active
  8268.  Time-slice
  8269.  Instruction pointer
  8270.  Processor state (registers and flags)
  8271.  Stack pointer
  8272.  
  8273.  
  8274.  Figure 2:  OS/2 multitasking services at a glance.
  8275.  
  8276.  Process Control
  8277.  
  8278.  DOSEXECPGM        Load and execute a child process
  8279.  DOSCWAIT          Wait for child process to terminate
  8280.  DOSKILLPROCESS    Unconditionally terminate another process.
  8281.  DOSPTRACE         Inspect/modify/trace a child process
  8282.  
  8283.  Threads Controlling Threads
  8284.  
  8285.  DOSCREATETHREAD   Create another execution thread within the sameprocess
  8286.  DOSSUSPENDTHREAD  Suspend the execution of a thread
  8287.  DOSRESUMETHREAD   Reactivate a thread
  8288.  DOSEXIT           Terminate current thread or all threads in process
  8289.  
  8290.  Read/Alter Thread Priorities
  8291.  
  8292.  DOSGETPRTY        Get the priority of specified thread
  8293.  DOSSETPRTY        Set the priority of specified thread
  8294.  
  8295.  Inter-Thread Protection
  8296.  
  8297.  DOSENTERCRITSEC   Disable other threads in same process
  8298.  DOSEXITCRITSEC    Re-enable other threads in same process
  8299.  
  8300.  
  8301.  Figure 3:  This sample code fragment demonstrates the use of the OS/2 system
  8302.             DOSEXECPGM to run CHKDSK as a synchronous child process
  8303.  
  8304.                                      ∙
  8305.                                      ∙
  8306.                                      ∙
  8307.             push  ds                      ; address of object buffer
  8308.             push  offset DGROUP:ObjName
  8309.             push  ObjNameLen              ; length of object buffer
  8310.             push  0                       ; execute synchronously
  8311.             push  ds                      ; address of argument block
  8312.             push  offset DGROUP;ArgBlk
  8313.             push  0                       ; address of envir.block
  8314.             push  0                       ; (OL=inherit parent's)
  8315.             push  ds                      ; address to receive return
  8316.             push  offset DGROUP:RetCode   ;   and termination codes
  8317.             push  ds                      ; address of name of child
  8318.             push  offset DGROUP :PgmName
  8319.             call  DOSEXECPGM              ; transfer to 286DOS
  8320.             or    ax,ax                   ; was EXEC successful?
  8321.             jnz   error                   ; jump if EXEC failed...
  8322.                                      ∙
  8323.                                      ∙
  8324.                                      ∙
  8325.  ObjName    db   64 dup (0)          ; receives name of dynamic link
  8326.  ObjNameLen equ  $-ObjName           ; causing EXEC failre
  8327.  
  8328.  ArgBlk     db   'chkdsk *.*',0      ; block of argument strings for child...
  8329.             db   0                   ; extra null byte terminates block
  8330.  
  8331.                                      ; receives return codes from child...
  8332.  RetCode    dw   0                   ; termination code for child
  8333.             dw   0                   ; result code from child's DOSEXIT
  8334.  
  8335.  PgmName    db   'chkdsk.eve',0      ; name of child program to run
  8336.  
  8337.  
  8338.  Figure 4:  An example of the use of multiple threads for asynchronus
  8339.             execution of tasks within a single process. The main line of
  8340.             execution allocates memory for a new stack and then starts up
  8341.             another thread called beeper. The new thread uses the OS/2 service
  8342.             DOSBEEP to emit ten short tones at one second intervals and then
  8343.             terminates.
  8344.  
  8345.  stksiz    equ   1024                     ; size of new thread's stack
  8346.                                      ∙
  8347.                                      ∙
  8348.                                      ∙
  8349.  Selector  dw    ?                        ; selector from DOSALLOCSEG
  8350.  
  8351.  BeeperlD  dw    ?                        ;  Thread ID for new thread
  8352.                                           ; named 'beeper'
  8353.                                      ∙
  8354.                                      ∙
  8355.                                      ∙
  8356.            push  stksiz                   ; size of new segment
  8357.            push  ds                       ; address of variable
  8358.            push  offset DGROUP:Selector   ; to receive new selector
  8359.            push  0                        ; non-shared segment
  8360.            call  DOSALLOCSEG              ; TRANSFER TO 286DOS
  8361.            or    ax,ax
  8362.            jnz   error                    ; jump if alloc failed
  8363.  
  8364.            push  cs                       ; execution address of
  8365.            push  offset_TEXT:Beeper       ; new thread
  8366.            push  ds                       ; address to receive new
  8367.            push  offsetDGROUP;BeeperID    ; Thread ID
  8368.            push  Selector                 ; address of base of
  8369.            push  stksiz                   ; new thread's Stack
  8370.            call  DOSCREATETHREAD          ; transfer to 286DOS
  8371.            or    ax,ax
  8372.            jnz   error                    ; jump if create failed
  8373.                                      ∙
  8374.                                      ∙
  8375.                                      ∙
  8376.  beeper    proc  far                      ; entry point for thread
  8377.  
  8378.            mov   cx,10                    ; emit ten beeps...
  8379.  
  8380.  beep1:    push  440                      ; sound a 440 Hz tone
  8381.            push  100                      ; for 100 milliseconds
  8382.            call  DOSBEEP                  ; transfer to 286DOS
  8383.  
  8384.            push 0                         ; now pause for one sec.
  8385.            push  1000
  8386.            call  DOSSLEEP                 ; transfer to 286DOS
  8387.  
  8388.            loop  beep1                    ; loop ten times
  8389.            ret
  8390.  
  8391.  beeper    endp
  8392.                                      ∙
  8393.                                      ∙
  8394.                                      ∙
  8395.  
  8396.  ───────────────────────────────────────────────────────────────────────────
  8397.  Configuring the OS/2 Multitasker
  8398.  ───────────────────────────────────────────────────────────────────────────
  8399.  
  8400.  There are four different directives that can be placed in the system
  8401.  CONFIG.SYS file to influence the operation of OS/2 multitasking. These
  8402.  are
  8403.  
  8404.    THREADS=n
  8405.    MAXWAIT=seconds
  8406.    PRIORITY=ABSOLUTE|DYNAMIC
  8407.    MAXWAIT=x[,y]
  8408.  
  8409.  The THREADS directive controls how many threads can be simultaneously
  8410.  created in the system. The parameter n may be 16-255, with a default of
  8411.  16, which is sufficient for OS/2 and a few simple processes. The upper
  8412.  limit of 255 cannot be expanded.
  8413.  
  8414.  When a thread is denied the CPU for the number of seconds specified by
  8415.  MAXWAIT because of other higher-priority threads using up all the time-
  8416.  slices, the starved thread receives a temporary increase in its priority
  8417.  for one time-slice.
  8418.  
  8419.  The PRIORITY directive activates OS/2's mechanisms for dynamically
  8420.  altering the priority of each thread based on the activity of other
  8421.  threads within the system. When PRIORITY=ABSOLUTE, the MAXWAIT directive
  8422.  has no effect whatsoever.
  8423.  
  8424.  The TIMESLICE directive controls the length of the time-slices used by the
  8425.  OS/2 scheduler. The x parameter represents the normal length of a thread's
  8426.  time-slice in milliseconds. If a thread uses up its entire time-slice and
  8427.  must be pre-empted, its next time-slice will be one tick longer, up to the
  8428.  limit given by the y parameter. This strategy is used by OS/2 to reduce
  8429.  the amount of context-switching overhead when several compute bound
  8430.  threads are running.
  8431.  
  8432.  ████████████████████████████████████████████████████████████████████████████
  8433.  
  8434.  OS/2 Memory Management
  8435.  
  8436.  In a multitasking system, random access memory (RAM) must be administered
  8437.  as a critical, finite system resource akin to disk storage or a line
  8438.  printer. The well-bred application program uses the host operating
  8439.  system's facilities to request additional memory politely when necessary,
  8440.  releases unneeded memory, and abstains from meddling with memory that
  8441.  doesn't belong to it.
  8442.  
  8443.  In the MS-DOS environment, such genteel programs are few and far between.
  8444.  Most popular application programs place the goal of high performance above
  8445.  all else, and a disposition to grab all available memory, capture
  8446.  interrupt vectors, and directly access the video controller's refresh
  8447.  buffer for fast screen displays is the rule rather than the exception.
  8448.  This behavior causes many headaches for those system software vendors who
  8449.  seek to graft multitasking abilities onto MS-DOS with such new user
  8450.  interfaces as Windows, TopView, and DESQview.
  8451.  
  8452.  The 80286 microprocessor, which is the heart of the IBM PC AT and
  8453.  compatible computers, can execute in either of two modes: real mode and
  8454.  protected mode. When the 80286 is running in real mode, as it does under
  8455.  MS-DOS, it essentially functions as a fast 8086/88 processor with a few
  8456.  added machine instructions. The segment registers contain physical
  8457.  addresses, which can be manipulated directly, and the total amount of
  8458.  memory that can be addressed is 1 megabyte (Mb) (see Figure 1). On the
  8459.  8086, and the 80286 running in real mode, there is no way for the
  8460.  operating system to intervene when a program does not use memory properly
  8461.  or to prevent one program from writing into another's memory space.
  8462.  
  8463.  Under OS/2, however, the rules are changed. OS/2 runs the 80286 processor
  8464.  in protected mode, providing hardware support for memory protection and
  8465.  memory access privilege levels, and presenting a very different hardware
  8466.  architecture to the programmer. The contents of segment registers are now
  8467.  logical selectors rather than physical addresses (see Figure 2). Selectors
  8468.  are indexes into descriptor tables that contain the actual physical
  8469.  address and length of the corresponding chunk of memory, and the
  8470.  descriptor tables themselves are typically not accessible to application
  8471.  programs at their privilege level. Each segment has an attribute that
  8472.  determines how it can be accessed: executable, read-only data, or read-
  8473.  write data.
  8474.  
  8475.  Memory protection between programs is enforced by each process's local
  8476.  descriptor table (LDT), a map of the memory segments that can be accessed
  8477.  by that process. If a task tries to load a segment register with a
  8478.  selector that is not valid for its own descriptor table or for the global
  8479.  descriptor table (GDT), a hardware interrupt is generated (see Figure 3).
  8480.  The operating system then recovers control, terminates the offending
  8481.  process, and displays diagnostic information on the system console.
  8482.  
  8483.  Thus, applications running under OS/2 must be civil to survive: there is
  8484.  no alternative to using the host system's services for managing memory.
  8485.  Only the operating system is entrusted with the privilege level and the
  8486.  knowledge of physical memory layout, which is necessary to create and
  8487.  destroy memory segments through manipulation of descriptor tables.
  8488.  Fortunately, OS/2 provides applications with a complete repertoire of
  8489.  memory management services, in three major groups: local heap management,
  8490.  conventional allocation and release of global memory segments, and "huge"
  8491.  global block management (logically contiguous memory spaces that are
  8492.  larger than 64Kb).
  8493.  
  8494.  In exchange for good behavior, the 80286's protected mode provides a
  8495.  reward──virtual memory (see Figure 4). A task can request, and own, more
  8496.  bytes of RAM memory than physically exist in the system. The combination
  8497.  of hardware support for paging and a specialized module of the operating
  8498.  system called the swapper present to the task the illusion that all of its
  8499.  segments are simultaneously present and accessible.
  8500.  
  8501.  When all of physical memory is in use and a process attempts to allocate
  8502.  additional memory, the least recently used segment is copied to a swap
  8503.  file on the disk, and the memory it occupied is freed up. A special bit in
  8504.  the descriptor table entry for the swapped-out segment is set to indicate
  8505.  that it is nonresident. When a selector for a swapped-out segment is used
  8506.  in a memory access, a hardware interrupt called a page fault occurs. The
  8507.  virtual memory manager (swapper) gains control, reads the needed segment
  8508.  from the disk into physical memory (possibly writing another segment out
  8509.  first to make room), updates the descriptor table entry for the selector
  8510.  to correspond to the new physical address, and restarts the process's
  8511.  instruction that was trying to access the segment.
  8512.  
  8513.  Thus, virtual memory means that the presence or absence in physical memory
  8514.  of any particular segment is completely transparent to the process that
  8515.  owns it. If an address that is not present is accessed, it will be
  8516.  invisibly loaded on demand. Correspondingly, the amount of memory that can
  8517.  be committed by all the processes in the system combined is limited only
  8518.  by the amount of swapping space on the disk (although limits on the number
  8519.  of available segment selectors do exist, they are of no practical
  8520.  significance owing to the sizes of today's fixed disks).
  8521.  
  8522.  The 80286's support for protected, virtual memory facilitates two other
  8523.  OS/2 features: shared text (code) segments and dynamic linking. Segments
  8524.  containing machine instructions are marked with the executable attribute
  8525.  and cannot be modified. Thus, when the same program is loaded for
  8526.  execution in two or more sessions, each instantiation can share the same
  8527.  memory-resident piece of code, instead of requiring separate, distinct
  8528.  copies. The protected-mode command processor called CMD.EXE is a good
  8529.  example: the overhead of multiple screen groups is small, since only one
  8530.  copy of the machine code in CMD.EXE is needed. Adding another screen group
  8531.  with its own command processor requires only the creation of another data
  8532.  segment and virtual screen buffer.
  8533.  
  8534.  Dynamic link libraries are simple extensions of the concepts of virtual
  8535.  memory and shared text segments. Under MS-DOS, when a program was linked
  8536.  to a run-time library, the actual machine code for the library routines
  8537.  became a permanent part of the application. Under OS/2, the linker and the
  8538.  .EXE file header have been extended to allow run-time binding (Figure 5)
  8539.  of library routines. When a program containing dynamic links is loaded for
  8540.  execution, OS/2 examines the file header and loads any modules from
  8541.  dynamic link libraries that are required, then updates the FAR CALL
  8542.  addresses within the application. Naturally, these dynamically linked
  8543.  routines can be shared between multiple processes and will be discarded
  8544.  and reloaded as needed by the memory manager. Much of OS/2 itself is
  8545.  implemented in the form of dynamic link libraries.
  8546.  
  8547.  
  8548.  Figure 1:  Real Address Mode Segment Selector Interpretation. The selector
  8549.             is used to identify segments in real memory.
  8550.  
  8551.     ┌─────────────────────────────────────────────────────────────────┐
  8552.     │                                                                 │█
  8553.     │  ╔══════════════╤══════╗              ╔══════════╗ ───┐         │█
  8554.     │  ║   Selector   │ 0000 ║              ║          ║    │         │█
  8555.     │  ╚══════════╤═══╧══════╝              ║          ║    │         │█
  8556.     │             │                      ┌─ ╟──────────╢   1M         │█
  8557.     │      Segment and Base              │  ║          ║   Physical   │█
  8558.     │          Address                  64K ║  Seg 1   ║   Address    │█
  8559.     │             │                      │  ║          ║   Space      │█
  8560.     │             └──────────────────────┴─╟──────────╢    │         │█
  8561.     │                                       ║          ║    │         │█
  8562.     │                                       ║          ║    │         │█
  8563.     │                                       ╚══════════╝ ───┘         │█
  8564.     │                                                                 │█
  8565.     └─────────────────────────────────────────────────────────────────┘█
  8566.       ██████████████████████████████████████████████████████████████████
  8567.  
  8568.  
  8569.  Figure 2:  In protected mode the selector does not specify a segment's
  8570.             location in physical memory. Instead, it uniquely identifies
  8571.             (or names) any one of 16K possible segments──the selector uses
  8572.             14 bits to address the segments in a given task's virtual address
  8573.             space. Because each segment can address 64K, the total virtual
  8574.             space available is Gb.
  8575.  
  8576.                            ╔════════════╗ ──┐
  8577.                            ╠═ Seg 3fff ═╣   │
  8578.                            ╠═ Seg 3ffe ═╣   │
  8579.                            ╠═ Seg 3ffd ═╣   │
  8580.                    ┌──────╠═ Seg 3ffc ═╣   │
  8581.                    │       ╠═ Seg 3ffb ═╣   │
  8582.               ┌────┴───┐   ║            ║   │
  8583.               │Selector│   <            <   1 G Virtual
  8584.               └────────┘   >            >   Address Space
  8585.                            ║            ║   │
  8586.                            ╠══ Seg 4  ══╣   │
  8587.                 1 to 64K───╠══ Seg 3  ══╣   │
  8588.                            ╠══ Seg 2  ══╣   │
  8589.                            ╠══ Seg 1  ══╣   │
  8590.                            ╠══ Seg 0  ══╣   │
  8591.                            ╚════════════╝ ──┘
  8592.  
  8593.  
  8594.  Figure 3:  The GDT maintains system-wide information while the LDT maintains
  8595.             task-specific information. There is one GDT for any given system
  8596.             that is shared by every task or process. Each task's virtual local
  8597.             space, managed bu the LDT, is isolated from that of any other.
  8598.             OS/2 itself resolves virtual addresses to map tasks to real
  8599.             memory.
  8600.  
  8601.                   CPU                       │   Memory   │
  8602.      ┌─────────────────────────┐            ├────────────┤──┐
  8603.      │                         │      ┌─┬──├────────────┤  │
  8604.      │         ┌15─────────0┐  │      │     ├────────────┤  │
  8605.      │         │ GDT Limit  ├─────────┘ │   │      ∙     │  │
  8606.   GDT│  ┌23────┴────────────┤  │            │      ∙     │ GDT
  8607.      │  │     GDT Base      ├─────────┐ │   │      ∙     │  │
  8608.      │  └───────────────────┘  │      │     ├────────────┤  │
  8609.      ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤      │ │   ├────────────┤  │
  8610.      │         ┌15─────────0┐  │      │     ├────────────┤  │
  8611.      │         │LDT Selector│  │      └─┴──├────────────┤──┘
  8612.      │         └────────────┘  │            │            │
  8613.      │                         │            │   LDT{1}   │
  8614.      │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │      ┌─┬──├────────────┤──┐
  8615.   LDT│         ┌15─────────0┐  │      │     ├────────────┤  │
  8616.      │ │       │ LDT Limit  ├┼────────┘ │   ├────────────┤  │
  8617.      │  ┌23────┴────────────┤  │            │      ∙     │  │
  8618.      │ ││     LDT Base      ├┼────────┐ │   │      ∙     │ Current
  8619.      │  └───────────────────┘  │      │     │      ∙     │ LDT
  8620.      │ │                     │ │      │ │   ├────────────┤  │
  8621.      │    Program Invisible    │      │     ├────────────┤  │
  8622.      │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │      │ │   ├────────────┤  │
  8623.      └─────────────────────────┘      └────├────────────┤──┘
  8624.                                             │            │
  8625.                        TASK 1               │   LDT{n}   │
  8626.                        VIRTUAL ADDRESS─┐    ├────────────┤
  8627.                              SPACE     │    ├────────────┤
  8628.                     ┌──────────────┐   │    ├────────────┤
  8629.                     │ ╔══════════╗ │   │    │      ∙     │
  8630.                     │ ║TASK 1    ║ │   │    │      ∙     │
  8631.   TASK 3            │ ║LOCAL     ║ │   │    │      ∙     │
  8632.   VIRTUAL ADDRESS   │ ║ADDRESS   ║ │   │    ├────────────┤
  8633.          SPACE ┐    │ ║SPACE     ║ │──┘    ├────────────┤
  8634.                    │ ╚══════════╝ │        ├────────────┤
  8635.       ┌─────────────│──────────────│┐       ├────────────┤
  8636.       │ ╔══════════╗│┌─────────────│───────────────┐
  8637.       │ ║TASK 3    ║││ ╔════════╗  ││ ╔══════════╗ │
  8638.       │ ║LOCAL     ║││ ║GLOBAL  ║  ││ ║TASK 2    ║ │
  8639.       │ ║ADDRESS   ║││ ║ADDRESS ║  ││ ║LOCAL     ║ │
  8640.       │ ║SPACE     ║││ ║SPACE   ║  ││ ║ADDRESS   ║ │
  8641.       │ ╚══════════╝││ ╚════════╝  ││ ║SPACE     ║ │
  8642.       └─────────────└──────────────┘┘ ╚══════════╝ │
  8643.                      └─────────────────────────────┘
  8644.                                    
  8645.                                    └TASK 2
  8646.                                     VIRTUAL ADDRESS
  8647.                                     SPACE
  8648.  
  8649.  
  8650.  Figure 4:  80826 Virtual Address Space. OS/2 provides one GDT that is shared
  8651.             by each task or process. Hardware support for paging and the OS/2
  8652.             swapper together provide the illusion that all segments are
  8653.             simultaneously present and accessible.
  8654.  
  8655.                                                 Task B Address Space
  8656.        Task A Private Address Space         Task B Private Address Space
  8657.  
  8658.                       ┌───────┐ 65535                      ┌───────┐ 65535
  8659.                       │       │                           │       │   
  8660.                       │  SEG. │ Offset                     │  SEG. │ Offset
  8661.                       │       │   │                        │       │   │
  8662.      ┌───────┬───────└───────┘ 0        ┌───────┬───────└───────┘ 0 
  8663.      │       │            ∙               │       │            ∙
  8664.      │ LDT A │            ∙               │ LDT B │            ∙
  8665.      │       │            ∙               │       │            ∙
  8666.      └───────┴──┐     ┌───────┐ 65535     └───────┴──┐     ┌───────┐ 65535
  8667.                 │     │       │                     │     │       │   
  8668.                 │     │  SEG. │ Offset               │     │  SEG. │ Offset
  8669.                 │     │       │   │                  │     │       │   │
  8670.                 └────└───────┘ 0                   └────└───────┘ 0 
  8671.  
  8672.        Task C Private Address Space              Shared Address Space
  8673.  
  8674.                       ┌───────┐ 65535                      ┌───────┐ 65535
  8675.                       │       │                           │       │   
  8676.                       │  SEG. │ Offset                     │  SEG. │ Offset
  8677.                       │       │   │                        │       │   │
  8678.      ┌───────┬───────└───────┘ 0        ┌───────┬───────└───────┘ 0 
  8679.      │       │            ∙               │       │            ∙
  8680.      │ LDT C │            ∙               │  GDT  │            ∙
  8681.      │       │            ∙               │       │            ∙
  8682.      └───────┴──┐     ┌───────┐ 65535     └───────┴──┐     ┌───────┐ 65535
  8683.                 │     │       │                     │     │       │   
  8684.                 │     │  SEG. │ Offset               │     │  SEG. │ Offset
  8685.                 │     │       │   │                  │     │       │   │
  8686.                 └────└───────┘ 0                   └────└───────┘ 0 
  8687.  
  8688.  
  8689.  Figure 5:  To invoke run-time dynamic linking, the application uses the
  8690.             following system calls.
  8691.  
  8692.  DOSLOADMODULE       Load Dynamic Link Module
  8693.  DOSFREEMODULE       Free Dynamic Link Module
  8694.  DOSGETPROCADDR      Get Dynamic Link Procedure Address
  8695.  DOSGETMODHANDLE     Get Dynamic Link Module Handle
  8696.  DOSGETMODNAME       Get Dynamic Link Module Name
  8697.  
  8698.  ████████████████████████████████████████████████████████████████████████████
  8699.  
  8700.  OS/2 Inter-Process Communication: Semaphores, Pipes, and Queues
  8701.  
  8702.  Ray Duncan
  8703.  
  8704.  Operating System/2 multitasking services allow applications to create
  8705.  multiple concurrent threads within a process or to create child processes.
  8706.  However, as we have seen earlier (see "OS/2 Multitasking: Exploiting the
  8707.  Protected Mode of the 80286,"), the multitasking functions provide
  8708.  a parent process with only a very limited ability to influence and
  8709.  communicate with a child process. The parent process can pass information
  8710.  to the child at load time in the form of command strings and the
  8711.  environment block, it can obtain the child process's exit (return) code
  8712.  and a code describing the child process's method of termination, and
  8713.  unilaterally abort a child process.
  8714.  
  8715.  To fill the need for rapid exchange of all kinds of information between
  8716.  concurrent threads and related or unrelated processes, OS/2 includes a
  8717.  rich set of system services called inter-process communication (IPC)
  8718.  functions. Computer literature is teeming with different mechanisms for
  8719.  inter-process communication, and more are being added every day. Still,
  8720.  there is broad agreement on three basic methods of IPC, all of which are
  8721.  supported by OS/2: semaphores, pipes, and queues.
  8722.  
  8723.  In this article, we'll briefly discuss each of these three IPC mechanisms,
  8724.  summarize the relevant OS/2 functions, and supply a brief coding example.
  8725.  Since there are many options available in the IPC services, we can't
  8726.  provide a comprehensive description of their capabilities here; the
  8727.  program fragments are only intended to demonstrate the most common usage.
  8728.  More detailed information can be found in the OS/2 Programmer's Reference
  8729.  manual.
  8730.  
  8731.  While reading about the IPC functions, bear in mind the distinctions
  8732.  between processes and threads. When a process opens or creates a
  8733.  semaphore, pipe, or queue, a handle is returned that can be used by any
  8734.  thread within that process and is inherited by any child processes. On the
  8735.  other hand, when a thread issues an OS/2 function call that waits on a
  8736.  semaphore or performs a synchronous read or write to a pipe or queue, only
  8737.  the calling thread is suspended──other threads in the process continue to
  8738.  run as they did before. When multiple threads in the same process issue
  8739.  requests against the same IPC object, any necessary serialization of these
  8740.  requests is taken care of within the operating system.
  8741.  
  8742.  
  8743.  Semaphores
  8744.  
  8745.  A semaphore can be viewed as a simple object with two states: set (owned)
  8746.  or cleared (not-owned). OS/2 provides a complete battery of system
  8747.  services to test, set, and clear semaphores; a summary of these functions
  8748.  is found in Figure 1. Semaphores are a high-performance mechanism of
  8749.  inter-process communication, because they are always resident in memory
  8750.  and the OS/2 services that manipulate them are relatively fast.
  8751.  
  8752.  The classical use of semaphores, and a usage fully supported by OS/2, is
  8753.  to control access to a nonreentrant routine or a serially reusable
  8754.  resource (SRR). An SRR is a file, peripheral device, or a memory object
  8755.  that is damaged or produces unpredictable results if it is accessed by
  8756.  more than one thread or process at a time. With semaphores, mutual
  8757.  exclusion on such a resource is easy to arrange between cooperating
  8758.  processes. A semaphore is established that represents the resource, and a
  8759.  thread or process refrains from accessing the resource unless it "owns"
  8760.  the corresponding semaphore.
  8761.  
  8762.  An alternative use of semaphores in OS/2 is to provide for synchronization
  8763.  or signaling between threads or processes. In this usage, one or more
  8764.  threads can suspend themselves by using an OS/2 function to "block" or
  8765.  wait on a semaphore. When the semaphore is cleared by another thread in
  8766.  response to some event, all of the threads that were blocking on that
  8767.  semaphore will wake up and run. When a semaphore is used for signaling and
  8768.  no resource that can be corrupted is involved, any thread that knows about
  8769.  the semaphore can set, clear, or test the semaphore at any time.
  8770.  
  8771.  To provide for the slightly different requirements of inter-thread and
  8772.  inter-process communication, OS/2 supports two types of semaphores: RAM
  8773.  semaphores and system semaphores.
  8774.  
  8775.  RAM semaphores are used for signaling or resource control between multiple
  8776.  threads in a single process. Each RAM semaphore requires the allocation of
  8777.  a double word of data storage; the double word should be initialized to
  8778.  zero when the process is started. The handle for a RAM semaphore is simply
  8779.  its 32-bit address (selector and offset).
  8780.  
  8781.  System semaphores are named objects that are used for signaling or
  8782.  synchronization between processes; the name always takes the form:
  8783.  
  8784.    \SEM\name.ext
  8785.  
  8786.  The extension (.ext) is optional. The storage for a system semaphore is
  8787.  allocated by the operating system somewhere outside the creating
  8788.  processes' memory space.
  8789.  
  8790.  Access to a system semaphore is obtained by creating or opening it by
  8791.  name; OS/2 returns a 32-bit handle that can be used for subsequent access
  8792.  to the semaphore. When the process no longer requires access to the system
  8793.  semaphore, it can use the handle to close it as it would close a file. The
  8794.  three functions that control access to a system semaphore, DOSCREATESEM,
  8795.  DOSOPENSEM, and DOSCLOSESEM, are described in more detail below.
  8796.  
  8797.  Creating a system semaphore. The function DOSCREATESEM is called with the
  8798.  address of a null-terminated (ASCIIZ) semaphore name, the address of a
  8799.  double-word variable that will receive the semaphore handle, and a flag
  8800.  that indicates whether ownership of the semaphore will be "exclusive"──in
  8801.  which case the state of the semaphore cannot be altered by a process that
  8802.  does not own it (see Figure 2). If the create operation is successful, the
  8803.  initial state of the semaphore will be not-owned (cleared).
  8804.  
  8805.  Opening a system semaphore. The function DOSOPENSEM is called with the
  8806.  address of a null-terminated (ASCIIZ) semaphore name of an existing
  8807.  semaphore and the address of a double word of storage that will receive a
  8808.  handle. Opening a semaphore does not establish ownership, test, or change
  8809.  the value of the semaphore.
  8810.  
  8811.  When a process creates or opens system semaphores, the handles to those
  8812.  semaphores are inherited by any child processes that are started with
  8813.  DOSEXECPGM. However, the child process does not own the semaphores even if
  8814.  the parent owned them; only one process at a time can own a given
  8815.  semaphore.
  8816.  
  8817.  Closing a system semaphore. The function DOSCLOSESEM is called with a
  8818.  handle and terminates access to a system semaphore. If a process
  8819.  terminates with open semaphores, they are closed automatically.
  8820.  
  8821.  However, if any of these semaphores were owned by the process, any threads
  8822.  in other processes that were waiting on the semaphore are awakened and
  8823.  receive an error code that indicates that the owning process may have
  8824.  terminated abnormally and the resource controlled by the semaphore may be
  8825.  in an indeterminate state. The semaphore itself ceases to exist when
  8826.  processes that use the semaphore have terminated or called DOSCLOSESEM.
  8827.  
  8828.  ───────────────────────────────────────────────────────────────────────────
  8829.  RAM Semaphores: A thread (Tx) issues the DosSemRequest call to claim access
  8830.  to the semaphore controlling a given SRR. If the semaphore is currently
  8831.  unused, DosSemRequest gives the calling thread ownership of the semaphore.
  8832.  All other threads are locked out until the owning thread clears the semaphore
  8833.  through the DosSemClear and relinquishes ownership of both the SRR and the
  8834.  semaphore.
  8835.  
  8836.  
  8837.               T1    D      Tx enters and owns
  8838.  Multiple            o      the SRR (semaphore        D
  8839.  Concurrent          s      not available)            o
  8840.  Threads             S          │                     s
  8841.  (T1...Tn)    T2    e          │                     S          Tx exits and
  8842.  desire              m                               e          relinquishes
  8843.  access to           R     Tx       Tx       Tx    m     Tx  ownership of
  8844.  a Serially          e                               C          the SRR and
  8845.  Reusable    ...    q                    │           l          the semaphore
  8846.  Resource            u                    │           e
  8847.  (SRR)               e       Serially Reuseable       a
  8848.                      s       Resource                 r
  8849.               Tn    t
  8850.  
  8851.                                                
  8852.                             SEMAPHORE AVAILABLE
  8853.  
  8854.  ───────────────────────────────────────────────────────────────────────────
  8855.  
  8856.  Mutual Exclusion
  8857.  
  8858.  The two OS/2 functions that are associated with the classical use of a
  8859.  semaphore are DOSSEMREQUEST and DOSSEMCLEAR.
  8860.  
  8861.  Claiming a semaphore. The OS/2 function DOSSEMREQUEST is used by a thread
  8862.  to establish ownership of a semaphore and, by inference, ownership of the
  8863.  resource associated with the semaphore. DOSSEMREQUEST is called with the
  8864.  handle of a system or RAM semaphore and a time-out parameter. If the
  8865.  specified semaphore is currently unowned, the function sets it as owned
  8866.  and returns a success code. If the semaphore is already owned by another
  8867.  thread, the system either suspends the thread indefinitely until the
  8868.  semaphore becomes available, waits for a specified interval and then
  8869.  returns with an error code, or returns immediately with an error code,
  8870.  depending on the time-out parameter.
  8871.  
  8872.  Recursive requests for system semaphores are supported by means of a use
  8873.  count, maintained by OS/2, of the number of times the semaphore owner has
  8874.  issued DOSSEMREQUEST without a corresponding DOSSEMCLEAR. However,
  8875.  recursive use of RAM semaphores is not supported.
  8876.  
  8877.  Releasing a semaphore. The function DOSSEMCLEAR is called with a semaphore
  8878.  handle and releases the thread's ownership of that semaphore. An error
  8879.  code is returned if the handle is invalid or if the semaphore was created
  8880.  with the exclusive option and is not currently owned by the calling
  8881.  thread. If the semaphore is already cleared, then no error is returned.
  8882.  
  8883.  When multiple threads are waiting on the same semaphore with
  8884.  DOSSEMREQUEST, it can be difficult to predict which thread will be
  8885.  awakened and acquire the semaphore when it is released by the current
  8886.  owner. The thread selected depends on an interaction of the priorities of
  8887.  the waiting threads, the position of the threads in the scheduler's list,
  8888.  and the activity of other threads in the system.
  8889.  
  8890.  
  8891.  Signaling
  8892.  
  8893.  Semaphores are used as signals when only one thread is in a position to
  8894.  detect an event but other threads may wish to take action based on this
  8895.  event. For example, a thread in a detached process utility that is
  8896.  monitoring the keyboard data stream for a hot key might wish to trigger
  8897.  other threads in the same process to display a pop-up screen or write to a
  8898.  file. The MS OS/2 functions that support signaling include DOSSEMSET,
  8899.  DOSSEMCLEAR, DOSSEMWAIT, DOSSEMSETWAIT, and DOSMUXSEMWAIT.
  8900.  
  8901.  Setting a semaphore. The OS/2 function DOSSEMSET is called with the handle
  8902.  of a system or RAM semaphore and unconditionally sets that semaphore (see
  8903.  Figures 3 and 4). The function fails if the handle is invalid or if a
  8904.  system semaphore was created with the exclusive option and is currently
  8905.  owned by another process (a semaphore used for signaling would not
  8906.  ordinarily be created with the exclusive option).
  8907.  
  8908.  Clearing a semaphore. The function DOSSEMCLEAR is called with the handle
  8909.  of a RAM or system semaphore and unconditionally clears that semaphore. As
  8910.  with DOSSEMSET, the function fails if the semaphore handle is invalid or
  8911.  if the semaphore was created with the exclusive option and is currently
  8912.  owned by another process. If any threads were blocked on the semaphore,
  8913.  they are restarted.
  8914.  
  8915.  Waiting on a semaphore. OS/2 contains several functions that allow a
  8916.  thread to suspend itself until one or more semaphores are cleared. The
  8917.  function DOSSEMWAIT is called with a semaphore handle and a time-out
  8918.  value. The calling thread is suspended until the indicated semaphore is
  8919.  cleared by another thread or process with DOSSEMCLEAR; it regains control
  8920.  immediately if the semaphore is not set. The function returns an error if
  8921.  the handle is invalid, no-wait was specified, and the semaphore is
  8922.  currently set; if a finite wait was specified and timed out; or if the
  8923.  semaphore was created with the exclusive option and is currently owned by
  8924.  another process.
  8925.  
  8926.  The function DOSSEMSETWAIT works just like DOSSEMWAIT, except that it sets
  8927.  the semaphore if it was not already set and then suspends the current
  8928.  thread until the semaphore is cleared by another thread or process or the
  8929.  indicated time-out expires.
  8930.  
  8931.  DOSSEMWAIT and DOSSEMSETWAIT are level-triggered, not edge-triggered. This
  8932.  means that it is possible for a thread that is blocking on a semaphore to
  8933.  miss a quick clearing and resetting of the semaphore, depending on the
  8934.  thread's priority and position in the scheduler's list and the activity of
  8935.  the other threads in the system.
  8936.  
  8937.  The function DOSMUXSEMWAIT is called with a list of up to 16 semaphores
  8938.  and an optional time-out interval. The calling thread is suspended until
  8939.  any of the indicated semaphores are cleared or the indicated interval has
  8940.  elapsed. Unlike DOSSEMWAIT and DOSSEMSETWAIT, the function DOSMUXSEMWAIT
  8941.  is edge-triggered; the waiting thread is awakened when one of the
  8942.  semaphores in the list changes state even if that semaphore gets set
  8943.  (armed) again right away by another process or another thread.
  8944.  
  8945.  
  8946.  Pipes
  8947.  
  8948.  Pipes are a means of inter-process communication midway in power between
  8949.  semaphores and queues. Like semaphores, the level of performance of pipes
  8950.  is relatively high, because the information is always kept resident in
  8951.  memory; like queues, pipes can be used to pass any type of data between
  8952.  processes. Physically, a pipe is simply a chunk of memory that is used as
  8953.  a ring buffer, with In and Out pointers maintained by the system. From a
  8954.  process's point of view, reading and writing a pipe are equivalent to
  8955.  reading and writing a file, except that a pipe is much faster.
  8956.  
  8957.  Pipes are not named entities. A process creates a pipe by calling the OS/2
  8958.  function DOSMAKEPIPE with the addresses of two variables to receive the
  8959.  read and write handles for the pipe, and a maximum pipe size (up to 65,504
  8960.  bytes). The function fails if there is not enough memory to create the
  8961.  pipe or if no handles are available.
  8962.  
  8963.  The two handles for reading and writing the pipe are assigned out of the
  8964.  same numeric sequence as those used for access to files. Any child
  8965.  processes automatically inherit the handles and have access to the pipe.
  8966.  We have already encountered the two major restrictions on the use of
  8967.  pipes: they can only be used for communication between closely related
  8968.  processes, and the maximum amount of data that a pipe can contain at one
  8969.  time is relatively small.
  8970.  
  8971.  Threads read and write a pipe with the usual file DOSREAD and DOSWRITE
  8972.  calls, supplying a handle, buffer address, and record length (see
  8973.  Figure 5). If a thread attempts to write to a pipe and the pipe is full,
  8974.  that thread is suspended until another thread or process removes enough
  8975.  data from the pipe so that the write can be completed. If a thread
  8976.  requests a synchronous read from a pipe and the pipe is empty, that thread
  8977.  is suspended until some other thread writes enough data to the pipe to
  8978.  satisfy the read request. There are no control or permission mechanisms or
  8979.  checks performed on operations to pipes.
  8980.  
  8981.  A pipe vanishes from the system when all the processes using the pipe
  8982.  either close the pipe handles or terminate. If two processes are
  8983.  communicating with pipes and the process reading the pipe ends, the
  8984.  process writing the pipe receives an error code.
  8985.  
  8986.  The most common usage of pipes by a process──and the way that the system's
  8987.  command interpreter uses them──is to arrange for the pipe's handles to be
  8988.  substituted for the standard input and standard output handles. Any child
  8989.  processes that are started then automatically communicate with the parent
  8990.  process for their input and output instead of the keyboard and screen,
  8991.  without any special knowledge or action on the child's part.
  8992.  
  8993.  ───────────────────────────────────────────────────────────────────────────
  8994.  Communicating with a Pipe: Pipes are a high performance mechanism for
  8995.  communication between closely related processes (such as a parent process and
  8996.  multiple child processes). Pipes are read and written like a file, and may
  8997.  contain variable-length messages with any content.
  8998.  
  8999.                    ╔═════════╗      PIPES      ╔══════════╗
  9000.                    ║ NEXT IN ║  ╔═══════════╗  ║ NEXT OUT ║
  9001.                    ╚═════════╝  ║  aaaaaa   ║  ╚═════════╝
  9002.                            █   ║  aaaaaa   ║     │    │
  9003.       ╔═════════════╗  █    █   ╟───────────╢     │    │     ╔═════════════╗
  9004.       ║  PROCESS A  ╟─    █   ║  eeeeee   ║     │    └────║  PROCESS X  ║
  9005.       ╚═════════════╝  █    █   ║  eeeeee   ║     │          ╚═════════════╝
  9006.                        █    █   ╟───────────╢     │
  9007.       ╔═════════════╗  █    █   ║   bbbbb   ║─────┘
  9008.       ║  PROCESS B  ╟─    █   ╟───────────╢
  9009.       ╚═════════════╝  █    █   ║   ggggg   ║
  9010.                        █    █   ║   ggggg   ║
  9011.       ╔═════════════╗  █    █   ║   ggggg   ║
  9012.       ║  PROCESS C  ╟─    █   ╟───────────╢
  9013.       ╚═════════════╝  █    █   ║   ccccc   ║
  9014.                             █   ╠═══════════╣
  9015.                             █─ ╠═══════════╣
  9016.                             █   ╠═══════════╣
  9017.                                 ╚═══════════╝
  9018.  ───────────────────────────────────────────────────────────────────────────
  9019.  
  9020.  Queues
  9021.  
  9022.  Queues are the most powerful inter-process communication service provided
  9023.  by OS/2, and consequently the most complex to use. Queues are slower than
  9024.  pipes, and much slower than semaphores, but they are also far more
  9025.  flexible.
  9026.  
  9027.    ■  Queues are named objects and can be opened and written by any
  9028.       process.
  9029.  
  9030.    ■  The size of a queue is limited only by the amount of free memory plus
  9031.       swap space on the disk.
  9032.  
  9033.    ■  Each record in a queue is a separately allocated block of memory
  9034.       storage and can be as large as 65,536 bytes.
  9035.  
  9036.    ■  The records in a queue may be ordered by the first-in-first-out
  9037.       (FIFO), last-in-first-out (LIFO), or priority methods.
  9038.  
  9039.    ■  The queue owner can examine records in the queue and remove them
  9040.       selectively, in any order.
  9041.  
  9042.    ■  Data in the queue is not copied from place to place by the operating
  9043.       system; instead, the data is passed in shared memory segments.
  9044.  
  9045.  The name of a queue will always take the form of \QUEUES\name.ext where
  9046.  the extension (.ext) is optional. When a queue is created or opened, OS/2
  9047.  returns a handle that is used for subsequent access to the queue. A
  9048.  summary of OS/2 queue services can be found in Figure 6.
  9049.  
  9050.  Creating a queue. The function DOSCREATEQUEUE is used to originate a new
  9051.  queue in the system. The function is called with a null-terminated
  9052.  (ASCIIZ) string that names the queue, a parameter that specifies the queue
  9053.  ordering (FIFO, LIFO, or priority), and the address of a variable to
  9054.  receive the queue handle (see Figure 7). An error code is returned if a
  9055.  queue already exists with the same name, the given name is invalid, or
  9056.  there is not enough free memory in the system to establish the queue's
  9057.  supporting data structure. Only the process that creates the queue, called
  9058.  the queue owner, can remove records from the queue, but all threads in the
  9059.  owning process can access the queue with equal authority.
  9060.  
  9061.  Opening a queue. Any process can open an existing queue by calling the
  9062.  OS/2 function DOSOPENQUEUE with the name of the queue and the address of
  9063.  two variables to receive the queue handle and the PID of the queue owner.
  9064.  The handle is used for subsequent writes of records to the queue.
  9065.  
  9066.  Writing a queue. Adding records to a queue is much more involved than
  9067.  writing a record to a pipe. The writer must first allocate a chunk of
  9068.  global shared memory of appropriate size with the function DOSALLOCSEG and
  9069.  copy the data for the queue record into it. Next, the writer must use the
  9070.  function DOSGIVESEG to obtain a new selector for the memory segment that
  9071.  can be passed to another process. Finally, the writer calls DOSWRITEQUEUE
  9072.  with a queue handle, a priority, the address (from DOSGIVESEG) and length
  9073.  of the data to be added to the queue (see Figure 8). The queue write
  9074.  proper fails if the queue handle is invalid or if there is insufficient
  9075.  system memory to expand the supporting structure of the queue. Obviously,
  9076.  the sequence can also fail at an earlier point (in DOSALLOCSEG or
  9077.  DOSGIVESEG) if the system runs out of memory (the physical memory plus
  9078.  swap space on the disk is exhausted) or selectors.
  9079.  
  9080.  Reading a queue. The function DOSREADQUEUE is used by the queue owner to
  9081.  remove a record from a queue; it is called with a queue handle and several
  9082.  other selection parameters and returns the address of a queue element
  9083.  (selector and offset) and its length. The owner can choose a record from
  9084.  the queue based on its position or priority or simply take the next record
  9085.  in line. The read operation can also be synchronous (the calling thread is
  9086.  suspended until a record is available) or asynchronous (the calling thread
  9087.  gets control back immediately, and a semaphore is set when a record is
  9088.  available).
  9089.  
  9090.  The function DOSPEEKQUEUE works similarly to DOSREADQUEUE, but the record
  9091.  is not removed from the queue when it is retrieved. This allows the queue
  9092.  owner to scan through the data waiting in the queue and decide on a
  9093.  processing strategy, without disturbing the data structure or being forced
  9094.  to copy the records to its own memory.
  9095.  
  9096.  Miscellaneous operations. The function DOSQUERYQUEUE allows any process
  9097.  that has a valid handle for a queue to obtain the current number of
  9098.  elements in that queue. DOSPURGEQUEUE discards all records that are
  9099.  currently in the queue; this function can only be called by the queue
  9100.  owner.
  9101.  
  9102.  Closing a queue. The function DOSCLOSEQUEUE is called with a queue handle
  9103.  and informs OS/2 that the calling process will not require further access
  9104.  to the queue. If the closing process is also the queue owner, the queue is
  9105.  purged and destroyed──any further attempts to write records to the queue
  9106.  by other processes will fail.
  9107.  
  9108.  ───────────────────────────────────────────────────────────────────────────
  9109.  Communicating with a Queue: Queues are the most flexible and powerful
  9110.  mechanism of inter-process communication supported by OS/2. A queue is
  9111.  basically an ordered list of shared memory segments; each segment contains
  9112.  a separate message and may be as large as 64K. The messages may be ordered
  9113.  in the queue by First-In-First-Out, Last-In-Last-Out, or priority, and may
  9114.  be inspected or removed from the queue by the server process in any order
  9115.  whatsoever.
  9116.  
  9117.                          ╔═════════════╗
  9118.                          ║ Queues Data ╠════════════╗
  9119.                      █  ║  Structure  ║            ║
  9120.      ╔═══════════╗   █   ╚═══╦═════════╝            ║    ╔═══════════╗
  9121.      ║ Process D ║                               ╚═══║ Process Y ║
  9122.      ╚═══════════╝   █   ╔════════╦════════════╗         ╚═══════════╝
  9123.      ╔═══════════╗   █   ║ MSG 'H'║            
  9124.      ║ Process E ║     ╚════════╝        ╔════════╦═════════════╗
  9125.      ╚═══════════╝   █                     ║ MSG 'K'║             
  9126.      ╔═══════════╗   █                     ╚════════╝        ╔════════╗
  9127.      ║ Process F ║                       ╔═════════════════╣ MSG 'J'║
  9128.      ╚═══════════╝   █                                      ╚════════╝
  9129.                      █                ╔════════╗
  9130.                                ╔══════╣ MSG 'N'║
  9131.                                      ╚════════╝
  9132.                           ╔════════╗
  9133.                           ║ MSG 'M'║
  9134.                           ╚════════╝
  9135.  ───────────────────────────────────────────────────────────────────────────
  9136.  
  9137.  Summary
  9138.  
  9139.  OS/2 supports three methods of inter-process communication that are used
  9140.  to pass messages between threads or processes: semaphores, pipes, and
  9141.  queues.
  9142.  
  9143.  Semaphores are used to symbolize ownership of a resource or signal
  9144.  occurrence of an event; they can be thought of as simple flags that can be
  9145.  set, cleared, or tested.
  9146.  
  9147.  Pipes are a high-performance means of passing variable-length, variable-
  9148.  content data between two closely related processes; however, pipes have
  9149.  the relative disadvantage that their maximum size is fixed at the time
  9150.  that they are created and they can never hold more than 64K.
  9151.  
  9152.  Queues allow a vast amount of prioritized data to be passed from multiple
  9153.  client processes to a server process; each message in the queue can be as
  9154.  large as 64K, and the total amount of data in the queue is limited only by
  9155.  the system's virtual memory. The selection of a particular IPC technique
  9156.  for a given application must be made carefully on the basis of performance
  9157.  requirements and the character of the messages to be exchanged between the
  9158.  interested threads or processes.
  9159.  
  9160.  
  9161.  Figure 1:  A summary of OS/2 semaphore support functions. Semaphores can be
  9162.             used to coordinate access to a resource or for signaling between
  9163.             threads or processes.
  9164.  
  9165.  Access to system semaphores
  9166.  
  9167.  DOSCREATESEM   Create a system semaphore
  9168.  DOSOPENSEM     Open an existing system semaphore
  9169.  DOSCLOSESEM    Close a system semaphore
  9170.  
  9171.  Semaphore functions for resource control
  9172.  
  9173.  DOSSEMREQUEST  Establish ownership of a semaphore
  9174.  DOSSEMCLEAR    Release ownership of a semaphore
  9175.  
  9176.  Semaphore functions for signaling
  9177.  
  9178.  DOSSEMSET      Unconditionally set a semaphore
  9179.  DOSSEMCLEAR    Unconditionally clear a semaphore
  9180.  DOSSEMWAIT     Wait until semaphore is cleared
  9181.  DOSSEMSETWAIT  Unconditionally set semaphore and wait
  9182.                 until it is cleared by another thread
  9183.  DOSMUXSEMWAIT  Wait for any of several semaphores to be cleared
  9184.  
  9185.  
  9186.  Figure 2:  A code fragment that illustrates the creation or opening of a
  9187.             system semaphore.
  9188.  
  9189.            push 1                    ; exclusive ownership not desired
  9190.            push ds                   ; variable to receive semaphore handle
  9191.            push offset DGROUP:shandle
  9192.            push ds                   ; address of semaphore name
  9193.            push offset DGROUP:sname
  9194.            call DOSCREATESEM         ; transfer to OS/2
  9195.            or   ax,ax                ; create successful?
  9196.            jz   continue             ; jump if it was...
  9197.            cmp  ax,err_sem_exists    ; semaphore already exists?
  9198.            jnz  error                ; jump if some other error
  9199.  
  9200.                                      ; semaphore exists, open it instead
  9201.            push ds                   ; variable to receive handle
  9202.            push offset DGROUP:shandle
  9203.            push ds                   ; address of semaphore name
  9204.            push offset DGROUP:sname
  9205.            call DOSOPENSEM           ; transfer to OS/2
  9206.            or   ax,ax                ; was open successful?
  9207.            jnz  error                ; if can't open and can't create,
  9208.                                      ; something is terribly wrong
  9209.                                      ∙
  9210.                                      ∙
  9211.                                      ∙
  9212.  
  9213.  sname     db   '\SEM\RESOURCE.LCK',0
  9214.  
  9215.  shandle   dd   0
  9216.  
  9217.  
  9218.  Figure 3:  A code sample that demonstrates setting a system semaphore.
  9219.             System semaphores are best suited for "between" processes.
  9220.  
  9221.                                      ∙
  9222.                                      ∙
  9223.                                      ∙
  9224.                                        ; open the semaphore
  9225.  
  9226.            push ds                     ; variable to receive handle
  9227.            push offset DGROUP:shandle
  9228.            push ds                     ; address of semaphore name
  9229.            push offset DGROUP:sname
  9230.            call DOSOPENSEM             ; transfer to O/S2
  9231.            or   ax,ax                  ; open successful?
  9232.            jnz  open_err               ; jump if open failed
  9233.                                      ∙
  9234.                                      ∙
  9235.                                      ∙
  9236.                                        ; set the semaphore:
  9237.                                        ; push semaphore handle...
  9238.            push word ptr shandle+2
  9239.            push word ptr shandle
  9240.            call DOSSEMSET              ; transfer to O/S2
  9241.            or   ax,ax                  ; was operation successful?
  9242.            jnz  set_err                ; jump if set failed
  9243.                                      ∙
  9244.                                      ∙
  9245.                                      ∙
  9246.  sname     db   '\SEM\RESOURCE.LCK',0
  9247.  
  9248.  shandle   dd   0                      ; handle for system semaphore
  9249.  
  9250.  
  9251.  Figure 4:  A code fragment that demonstrates setting a RAM semaphore. RAM
  9252.             semaphores are best suited for use "within" processes.
  9253.  
  9254.                                      ∙
  9255.                                      ∙
  9256.                                      ∙
  9257.                                         ; set the RAM semaphore
  9258.            push ds                      ; push its address
  9259.            push offset DGROUP:my_sem
  9260.            call DOSSETSEM               ; transfer to O/S2
  9261.            or   ax,ax                   ; was set successful?
  9262.            jnz  set_err                 ; jump if set failed
  9263.                                      ∙
  9264.                                      ∙
  9265.                                      ∙
  9266.  mysem     dd   0                       ; storage for RAM semaphore
  9267.  
  9268.  
  9269.  Figure 5:  Establishing Pipes
  9270.                                      ∙
  9271.                                      ∙
  9272.                                      ∙
  9273.            push ds                        ; address to receive handle
  9274.            push offset DGROUP:prdh                 ; for reading pipe
  9275.            push ds                        ; address to receive handle
  9276.            push offset DGROUP:pwriteh              ; for writing pipe
  9277.            push 0                         ; max pipe size = default
  9278.            call DOSMAKEPIPE               ; transfer to O/S2
  9279.            or   ax,ax                     ; was pipe created?
  9280.            jnz  error                     ; jump if create failed.
  9281.                                      ∙
  9282.                                      ∙
  9283.                                      ∙
  9284.                                           ; in here we spawn a "child"
  9285.                                           ; process which inherits handles...
  9286.  
  9287.                                           ; now send a message to child
  9288.                                           ; process through the pipe...
  9289.  
  9290.            push pwriteh                   ; pipe write handle
  9291.            push ds                        ; address of message
  9292.            push offset DGROUP:message
  9293.            push message_length            ; length of message
  9294.            push ds                        ; address of variable to
  9295.            push offset DGROUP:status               ; receive bytes written
  9296.            call DOSWRITE                  ; transfer to O/S2
  9297.            or   ax,ax                     ; did write succeed?
  9298.            jnz  error                     ; jump if write failed.
  9299.                                      ∙
  9300.                                      ∙
  9301.                                      ∙
  9302.  preadh    dw   ?    : handle to read pipe
  9303.  
  9304.  pwriteh   dw   ?                         ; handle to write pipe
  9305.  
  9306.  status    dw   ?                         ; receives length from DOSWRITE
  9307.  
  9308.  
  9309.  Figure 6:  Substantial control over queues is provided by the OS/2 queue
  9310.             management functions.
  9311.  
  9312.  Queue access
  9313.  
  9314.  DOSCREATEQUEUE Create a queue (process is owner, can read or write
  9315.                   messages to queue)
  9316.  DOSOPENQUEUE   Open a previously existing queue (can only write
  9317.                   messages to queue)
  9318.  DOSCLOSEQUEUE  Close queue (queue is destroyed when closed by owner)
  9319.  
  9320.  Queue input/output
  9321.  
  9322.  DOSWRITEQUEUE  Write message to queue (either queue owner or any process
  9323.                   that has opened queue)
  9324.  DOSREADQUEUE   Read message from queue (queue owner only)
  9325.  DOSPEEKQUEUE   Nondestructive read of queue message (queue owner only)
  9326.  
  9327.  Queue information
  9328.  
  9329.  DOSQUERYQUEUE  Find number of messages currently in queue (queue owner or
  9330.                   any process that has opened queue)
  9331.  DOSPURGEQUEUE  Discard all messages currently in queue (queue owner only)
  9332.  
  9333.  
  9334.  Figure 7:  Creating a queue, waiting for a message to be written into it, and
  9335.             then closing the queue (destroying it).
  9336.  
  9337.                                      ∙
  9338.                                      ∙
  9339.                                      ∙
  9340.                                       ; first create queue...
  9341.            push ds                    ; address to receive handle
  9342.            push offset DGROUP:qhandle
  9343.            push 0                     ; queue ordering = FIFO
  9344.            push ds                    ; address of queue name
  9345.            push offset DGROUP:qname
  9346.            call DOSCREATEQUEUE        ; transfer to OS/2
  9347.            or   ax,ax                 ; was create successful?
  9348.            jnz  error                 ; jump if create failed
  9349.  
  9350.                                       ; now read from queue
  9351.            push qhandle               ; queue handle
  9352.            push ds                    ; address to receive PID
  9353.            push offset DGROUP:qident          ; and event code
  9354.            push ds                    ; receives message length
  9355.            push offset DGROUP:qmsglen
  9356.            push ds                    ; receives message pointer
  9357.            push offset DGROUP:qmsgptr
  9358.            push 0                     ; 0= read first element
  9359.            push 0                     ; 0= synchronous read
  9360.            push ds                    ; receives message priority
  9361.            push offset DGROUP:qmsgpri
  9362.            push ds                    ; handle for RAM semaphore
  9363.            push offset DGROUP:qsem         ; (not used for synch reads)
  9364.            call DOSREADQUEUE          ; transfer to OS/2
  9365.            or   ax,ax                 ; was read successful?
  9366.            jnz  error                 ; jump if read failed
  9367.  
  9368.            les  bx,qmsgptr            ; let ES:BX point to queue msg
  9369.  
  9370.            push es                    ; now release queue message's
  9371.            call DOSFREESEG            ; shared memory segment
  9372.            or   ax,ax                 ; was release successful?
  9373.            jnz  error                 ; jump if released failed
  9374.  
  9375.            push qhandle               ; now close the queue
  9376.            call DOSCLOSEQUEUE         ; also destroying it
  9377.            or   ax,ax                 ; was close successful?
  9378.            jnz  error                 ; jump if close failed
  9379.                                      ∙
  9380.                                      ∙
  9381.                                      ∙
  9382.  qhandle   dw   ?                     ; receives handle from DosCreateQueue
  9383.  
  9384.  qname     db   '\QUEUES\MYQUEUE',0        ; ASCIIZ queue name
  9385.  
  9386.  qident    dw   0,0                   ; writer's PID, event code
  9387.  
  9388.  qmsglen   dw   ?                     ; length of queue msg received
  9389.  
  9390.  qmsgptr   dd   0                     ; address of queue msg received
  9391.  
  9392.  qmsgpri   dw   ?                     ; priority of queue msg
  9393.  
  9394.  
  9395.  Figure 8:  Opening a queue, writing a message into it, and then closing it.
  9396.                                      ∙
  9397.                                      ∙
  9398.                                      ∙
  9399.            push ds                         ; address to receive PID of
  9400.            push offset DGROUP:qowner       ; queue's owner
  9401.            push ds                         ; address to receive handle
  9402.            push offset DGROUP:qhandle
  9403.            push ds                         ; address of queue's name
  9404.            push offset DGROUP:qname
  9405.            call DOSOPENQUEUE               ; transfer to OS/2
  9406.            or   ax,ax                      ; was open successful?
  9407.            jnz  error                      ; jump if open failed
  9408.  
  9409.  
  9410.            push qmsglen                    ; length of shared memory segment
  9411.            push ds                         ; receives selector for segment
  9412.            push offset DGROUP:qselc
  9413.            push 1                          ; 1 = segment will be shared
  9414.            call DOSALLOCSEG                ; transfer to OS/2
  9415.            or   ax,ax                      ; was allocation successful?
  9416.            jnz  error                      ; jump if memory allocate failed
  9417.  
  9418.  
  9419.            mov  si,offset qmsg             ; copy our queue message
  9420.            mov  es,qselc                   ; to shared segment
  9421.            xor  di,di
  9422.            mov  cx,qmsglen
  9423.            cld
  9424.            rep movsb
  9425.  
  9426.  
  9427.            push qselc                      ; get a new selector for the
  9428.            push qowner                     ; shared segment that can
  9429.            push ds                         ; be used by receiver of
  9430.            push qselr                      ; the queue message
  9431.            call DOSGIVESEG                 ; transfer to OS/2
  9432.            or   ax,ax                      ; did function succeed?
  9433.            jnz  error                      ; jump if can't give it away
  9434.  
  9435.  
  9436.            push qhandle                    ; handle for queue
  9437.            push 0                          ; 0= event data
  9438.            push qmsglen                    ; length of queue message
  9439.            push qselr                      ; selector for queue data
  9440.            push 0                          ; (offset)
  9441.            push 0                          ; queue element priority
  9442.            call DOSWRITEQUEUE              ; transfer to OS/2
  9443.            or   ax,ax                      ; did write succeed?
  9444.            jnz  error                      ; jump if queue write failed
  9445.  
  9446.  
  9447.            push qselc                      ; release shared memory that
  9448.            call DOSFREESEG                 ; contains the queue message
  9449.            or   ax,ax                      ; did release succeed?
  9450.            jnz  error                      ; jump if release failed
  9451.  
  9452.  
  9453.            push qhandle                    ; now close the queue
  9454.            call DOSCLOSEQUEUE              ; transfer to OS/2
  9455.            or   ax,ax                      ; did close succeed?
  9456.            jnz  error                      ; jump if close failed
  9457.                                      ∙
  9458.                                      ∙
  9459.                                      ∙
  9460.  qhandle   dw   ?                ; receives handle from DosOpenQueue
  9461.  
  9462.  qowner    dw   ?                ; receives PID of queue owner
  9463.  
  9464.  qname     db   '\QUEUES\MYQUEUE',0        ; ASCIIZ queue name
  9465.  
  9466.  qselc     dw   ?                ; shared memory selector for caller
  9467.  
  9468.  qselr     dw   ?                ; shared memory selector for receiver
  9469.  
  9470.  qmsg      db   'This is my queue message'
  9471.  qmsglen   equ  $-qmsg
  9472.  
  9473.  ████████████████████████████████████████████████████████████████████████████
  9474.  
  9475.  The MS OS/2 LAN Manager
  9476.  
  9477.  System connectivity has become an increasingly significant issue in the
  9478.  development of today's personal computing systems. As a result, it is
  9479.  becoming substantially more important for operating systems to provide
  9480.  expanded support for Local Area Networking.
  9481.  
  9482.  The design of Microsoft OS/2 recognizes this fact by providing substantial
  9483.  networking support in the form of the Microsoft OS/2 LAN Manager.
  9484.  
  9485.  Basically, the MS OS/2 LAN Manager is a protected mode version of
  9486.  Microsoft Networks, and it is compatible with existing versions of MS-Net,
  9487.  XENIX Net and the IBM PC Local Area Network Program.
  9488.  
  9489.  The LAN Manager is a separately "installable" component of MS OS/2; once
  9490.  installed, it works with the operating system to seamlessly solve
  9491.  networking requirements.
  9492.  
  9493.  The LAN Manager is designed to facilitate the traditional network file-
  9494.  and printer-sharing services. But it goes far beyond this by providing for
  9495.  a new class of communication facilities, which will allow software
  9496.  developers to increase the sophistication of network services
  9497.  substantially and will make distributed processing practical.
  9498.  
  9499.  The LAN Manager uses the protected mode, multitasking facilities of MS
  9500.  OS/2 to implement non-dedicated servers with extensive network
  9501.  administration and security services, as well as transparent file- and
  9502.  printer-sharing capabilities.
  9503.  
  9504.  The LAN Manager allows an OS/2 based system to be added to existing MS-
  9505.  Net-based networks. The new system can function within the network as a
  9506.  workstation or a server or both.
  9507.  
  9508.  
  9509.  Protected Mode NetBios
  9510.  
  9511.  The multitasking environment in which the LAN Manager must function
  9512.  requires a new, protected-mode version of the MS-Net NetBios. The NetBios
  9513.  is the equivalent of the transport layer of the OSI model, and provides
  9514.  for virtual circuit and datagram services.
  9515.  
  9516.  The standardized NetBios API allows network drivers to access low-level
  9517.  hardware (for such tasks as checking the status of a network card). The
  9518.  NetBios is designed not only to understand the hardware at one end
  9519.  (network hardware comes with a customized Hardware/NetBios layer), but
  9520.  also to understand API calls from network drivers at the other end.
  9521.  Applications access NetBios services via the network drivers.
  9522.  
  9523.  The LAN Manager must deal with several multitasking issues. First,
  9524.  applications will most likely need to share access to more than one
  9525.  network driver. Second, there must be a way for network drivers to share
  9526.  access to the NetBios services.
  9527.  
  9528.  Central to the LAN Manager is the Microsoft Net Kernel, which provides
  9529.  applications with the support necessary to simultaneously access one or
  9530.  more network drivers. Very briefly, the kernel serves as a dispatcher for
  9531.  applications' requests to access network drivers, and controls access to
  9532.  those network drivers by treating each driver as a named, installable
  9533.  device driver that can be opened or closed.
  9534.  
  9535.  To insure that network drivers can interface to the net kernel and
  9536.  therefore share access to the NetBios, developers must adhere to the
  9537.  system interface specifications (and the protected mode NetBios API)
  9538.  published as part of the Microsoft OS/2 LAN Manager. These specifications
  9539.  will ease the effort that is necessary for OEMs and third-party network
  9540.  software developers to design their hardware and software applications to
  9541.  support the new multitasking, distributed processing environment.
  9542.  
  9543.  
  9544.  Distributed Processing
  9545.  
  9546.  From a software developer's point of view, the LAN Manager can be seen as
  9547.  providing a set of extensions to the MS OS/2 Inter-process Communications
  9548.  API. Just as MS OS/2 provides semaphores, pipes, and queues for
  9549.  communication between different processes running on the same machine, the
  9550.  LAN Manager provides named pipes and mailslots for communication between
  9551.  processes running on different machines on the network.
  9552.  
  9553.  Named pipes and mailslots are the building blocks of distributed
  9554.  processing. These remote inter-process communication facilities provide
  9555.  the ability to establish communications channels between tasks, allowing
  9556.  for sharing of data──not only between a server machine and its clients,
  9557.  but also among peer machines residing on a given network.
  9558.  
  9559.  Data-base applications provide a clear example of the distinction between
  9560.  file service and distributed proessing. Using current LAN technology, it
  9561.  is typical to search a file resident on another machine by reading a
  9562.  virtual file that is mapped to the real file by the MS-Net redirector.
  9563.  
  9564.  In the distributed processing model, the requesting workstation passes a
  9565.  request to the server, which then conducts the search and passes back the
  9566.  results. The only information that travels on the network is the original
  9567.  request and the reply. This requires a level of inter-machine
  9568.  communication not possible with file- or record-oriented facilities.
  9569.  
  9570.  The LAN Manager API is analogous at the network level to the MS OS/2
  9571.  Inter-process Communication API that allows thread-to-thread and process-
  9572.  to-process communication at the operating system level. At any given
  9573.  moment, LAN API functions can block, be blocked, or can be in a state of
  9574.  execution (i.e., reading, writing or querying either named pipes or
  9575.  mailslots).
  9576.  
  9577.  At the applications level, the LAN API provides CALL-based functions that
  9578.  can be incorporated into high-level language code. Programs written to the
  9579.  specifications of the API will be able to communicate across a network.
  9580.  
  9581.  
  9582.  Virtual Circuits
  9583.  
  9584.  The LAN API supports communications at the "Network and Transport Levels"
  9585.  of the Open System Interconnect (OSI) model. Stream-oriented and
  9586.  optionally, message-oriented information is communicated through a
  9587.  mechanism called the named pipe. Message-oriented information can also be
  9588.  communicated via the high-speed mechanism called a named mailslot.
  9589.  
  9590.  To understand the difference between named pipes and mailslots, one needs
  9591.  to understand two concepts basic to communications: those of virtual
  9592.  circuits and datagrams.
  9593.  
  9594.  Virtual circuits are network-level data channels that guarantee an error-
  9595.  free and continuous communication channel, where each packet of routed
  9596.  information is delivered in order. Regardless of how a channel is
  9597.  established between two points (it could, for example, be a very complex
  9598.  switching network in a phone system), the two communicating ends perceive
  9599.  the channel as a direct communications link. This is, in essence, how
  9600.  named pipes work.
  9601.  
  9602.  Datagrams are network-level channels that accept isolated messages
  9603.  (specific packets of information) from the transport layer and attempt to
  9604.  deliver them without regard for order or timeliness, much the same way as
  9605.  a postal system, in which the sender mails letters without knowing exactly
  9606.  when they will arrive, or in what order (message B, sent after message A,
  9607.  might arrive before message A). Named mailslots work in this fashion.
  9608.  
  9609.  Across the network, either of these mechanisms lets processes
  9610.  transparently establish remote or local communications through symbolic
  9611.  names that follow MS OS/2 file naming conventions. Additionally, it is
  9612.  possible to impose access restrictions on named pipes and mailslots, just
  9613.  as can be done with files.
  9614.  
  9615.  
  9616.  Named Pipes
  9617.  
  9618.  Every reader of MSJ should be familiar with MS-DOS pipes. These pipes
  9619.  function as a transparent holding area for the output of one program to
  9620.  become the input of another. For example, DIR | SORT routes the output
  9621.  from DIR to a temp file that is then read by SORT.
  9622.  
  9623.  Named pipes essentially extend this facility to provide direct
  9624.  communication between processes running on a network. Named pipes are two-
  9625.  way (or full duplex) channels that can be written at either end and read
  9626.  at the opposite end (that is, what is written at one end cannot be read
  9627.  from that same end).
  9628.  
  9629.  To create a named pipe a process executes the DosMakeNmPipe function call
  9630.  (see Figure 1), which then opens a pipe as either a byte stream or a
  9631.  message stream. DosMakeNmPipe returns an identifying handle for that end
  9632.  of the pipe, which is called the serving end of the pipe. DosOpen (a
  9633.  regular OS/2 API call) opens the other end of the pipe, known as the
  9634.  client end, and similarly returns a handle.
  9635.  
  9636.  Once a named pipe has been established it is controlled through the
  9637.  facilities provided by the LAN Manager. It can be read, written to and
  9638.  queried for activity or information. Named pipes can, at this point be
  9639.  utilized across a network, and can be serially closed and opened by other
  9640.  processes.
  9641.  
  9642.  Named pipes are therefore extremely flexible. Once created they can be
  9643.  accessed just like any sequential file, from beginning to end. Because
  9644.  rewinding of pipes is not possible (information comes and goes), the
  9645.  contents of a pipe can only be accessed once, unless the original
  9646.  information is again sent through the pipe.
  9647.  
  9648.  Local and remote procedure call dialogues between processes can be
  9649.  efficiently implemeted because named pipes support transaction I/O calls.
  9650.  
  9651.  Named pipes can also be accessed by applications running on MS DOS 3.x,
  9652.  machines serving as workstations on a given network, allowing those
  9653.  programs to communicate with server-based applications.
  9654.  
  9655.  Thus, DosMakeNmPipe can allow multiple instances of a pipe, each with the
  9656.  same name. This insures that multiple clients DosOpening to that name will
  9657.  obtain separate and distinct pipes to the serving process.
  9658.  
  9659.  
  9660.  Named Mailslots
  9661.  
  9662.  Named pipes must insure that a full duplex communication channel exists
  9663.  between processes. There is, of necessity, a considerable amount of system
  9664.  overhead necessary to maintain such a connection.
  9665.  
  9666.  Certain types of application-specific transactional messages to be
  9667.  exchanged between processes do not require a full duplex, error-free
  9668.  channel of communication. A named mailslot provides the optimal means for
  9669.  handling messages under these conditions. Note that byte streams cannot
  9670.  take advantage of named mailslots. Byte streams need the support
  9671.  mechanisms built into named pipes to ensure error-free transmission.
  9672.  
  9673.  Mailslots are inherently fast because they need not be opened or closed by
  9674.  processes. Once a named mailslot has been established through the
  9675.  DosMakeMailslot function call processes running on different machines
  9676.  across a network simply write to it by name.
  9677.  
  9678.  The function call used to write to a named mailslot, DosWriteMailslot,
  9679.  supports both "class" and "priority" settings for messages. Priority
  9680.  controls which messages DosReadMailslot reads first.
  9681.  
  9682.  Class is usually used with remote mailslots. Messages can be assigned
  9683.  either a first- or second-class status. First-class mail is reliable in
  9684.  that DosWriteMailslot blocks until a message is delivered or an error
  9685.  occurs, insuring that the state of the message will be known. Second-class
  9686.  mail is sent strictly without error checking. Although delivery will
  9687.  probably succeed, if it fails an error will not be reported.
  9688.  
  9689.  Between high-speed named mailslots and highly reliable named pipes, the
  9690.  LAN Manager provides the necessary communications facilities necessary to
  9691.  meet most sophisticated network communications needs. Software developers
  9692.  now have a base on which to develop complete, distributed applications.
  9693.  The opportunity to take advantage of a programming environment in which
  9694.  applications can access remote sites, and in which workstations can
  9695.  offload work to a server, will drive the next generation of software.──T.R.
  9696.  
  9697.  
  9698.  Figure 1:  DosMakeNmPipe Function
  9699.  
  9700.  int FAR PASCAL
  9701.  
  9702.  DosMakeNmPipe(name, handle, omode, pmode, size1, size2, timeout)
  9703.  char far * name;         asciz pipe name
  9704.  int far * handle;        place for handle to be returned
  9705.  unsigned int omode;      DOS open mode
  9706.  unsigned int pmode;      pipe open mode
  9707.  unsigned int size1;      hint of outgoing buffer size
  9708.  unsigned int size2;      hint of incoming buffer size
  9709.  long      timeout;       time out for DosWaitNmPipe
  9710.  Name:     Asciz name of pipe. Pipes are named
  9711.            \PIPE\NAME.
  9712.  
  9713.  Handle:   Handle of the named pipe that is created.
  9714.  
  9715.  Omode:    Open mode mask as for DosOpen call. The following bits are
  9716.            defined:
  9717.  
  9718.       Open Mode Bits
  9719.    5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
  9720.    O W * * * * * * I O O O * A A A
  9721.  
  9722.    I  Inheritance:   0 = Spawned processes inherit the pipe handle
  9723.                      1 = Spawned processes don't inherit the pipe
  9724.    W  Write-through: 0 = Write-behind to remote pipes is allowed
  9725.                      1 = Write-behind to remote pipes is not allowed
  9726.    AAA Access mode:  000 = Inbound pipe (client to server)
  9727.                      001 = Outbound pipe (server to client)
  9728.                      010 = Full-duplex pipe (server to/from client)
  9729.                      Other values invalid
  9730.  
  9731.  Pmode: Pipe-specific mode parameters
  9732.  
  9733.    5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
  9734.    B * * * T T R R |... I count..|
  9735.  
  9736.    B Blocking:  0 = Reads/writes block if no data available
  9737.                 1 = Reads/writes return immediately if no data
  9738.  
  9739.       Reads normally block until at least partial data can be returned.
  9740.       Writes by default block until all bytes requested have been written.
  9741.       Nonblocking mode (B=1) changes this behavior as follows:
  9742.  
  9743.    1) Reads will return immediately with BytesRead = 0 if no data is
  9744.       available.
  9745.  
  9746.    2) Writes will return immediately with BytesWritten  = 0 if the data
  9747.       transfer cannot be started. Otherwise, the entire data area will be
  9748.       transferred.
  9749.  
  9750.       TT Type of a pipe:     00 = Pipe is a byte stream pipe
  9751.                              01 = Pipe is a message stream pipe
  9752.  
  9753.          All writes to message stream pipes record the length of the write
  9754.          along with the written data.
  9755.  
  9756.       RR Read mode           00 = Read pipe as a byte stream pipe
  9757.                              01 = Read pipe as a message stream
  9758.  
  9759.          Message pipes can be read as byte or message streams, depending
  9760.          on the setting of RR. Byte pipes can only be read as pipe streams
  9761.  
  9762.       Icount: 8-bit count to control pipe instancing. When making the
  9763.          first instances of a named pipe, Icount specifies how many
  9764.          instances can be created: 1 means that this can be the only
  9765.          instance (pipe is unique) and -1 means the number of
  9766.          instances is unlimited; 0 is a reserved value. Subsequent
  9767.          attempts to make a pipe will fail if the maximum number of
  9768.          allowed instances already exists. The Icount parameter is
  9769.          ignored when making other than the first instance of a pipe.
  9770.          When multiple instances are allowed multiple clients can
  9771.          simultaneously DosOpen to the same pipe name and get handles
  9772.          to distinct pipe instances.
  9773.  
  9774.    Size1: Hint to system, number of bytes to allocate for outgoing buffer.
  9775.  
  9776.    Size2: Hint to system, number of bytes to allocate for incoming buffer.
  9777.  
  9778.    Timeout: Default value for timeout parameter to PipeWait. This value
  9779.          may be set only at the creation time of the first instance of the
  9780.          pipe name. If at that time the value is zero, a systemwide default
  9781.          value (50 ms) will be chosen.
  9782.  
  9783.  ████████████████████████████████████████████████████████████████████████████
  9784.  
  9785.  A Complete Guide to Writing Your First OS/2 Program
  9786.  
  9787.  Charles Petzold
  9788.  
  9789.  For the program developer, OS/2 opens up a whole new world of capabilities
  9790.  and challenges, and along with them, new concepts to learn. We'll explore
  9791.  some of these new concepts by walking through the creation of a typical OS/2
  9792.  application that demonstrates inter-process communication (IPC) and
  9793.  multitasking.
  9794.  
  9795.  FINDDEMO and FINDER work in conjunction to locate and list all files on a
  9796.  disk that fit a particular file specification, for instance, *.EXE. You can
  9797.  enter up to nine different file specifications at any one time, and all nine
  9798.  run simultaneously. Figure 1 shows FINDDEMO in action.
  9799.  
  9800.  FINDDEMO.EXE is the program that you execute from the OS/2 command line.
  9801.  FINDER.EXE must be in the current subdirectory or a subdirectory in your
  9802.  PATH. FINDDEMO manages the screen display and keyboard input and loads as
  9803.  many as nine instances of FINDER.EXE as child processes. FINDER.EXE does the
  9804.  actual file searching and funnels data back to FINDDEMO via a queue.
  9805.  Separate threads within FINDDEMO read the queue and monitor termination of
  9806.  the FINDER instances.
  9807.  
  9808.  In other words, there's a real party going on here. I'll have more to say
  9809.  about the workings of this program after we look at what's involved in
  9810.  coding, compiling, and linking it. If you've already done some Windows
  9811.  programming, don't be surprised to see some familiar things along the
  9812.  way──you're several steps ahead of everybody else.
  9813.  
  9814.  
  9815.  Protected Mode
  9816.  
  9817.  Your first OS/2 programs will probably be existing MS-DOS programs that you
  9818.  will port to OS/2. Most programs written in straight C with standard library
  9819.  functions can simply be recompiled by using the new .LIB files.
  9820.  
  9821.  However, programs with assembly language routines that make software
  9822.  interrupt calls, programs that use the Microsoft C int86 or intdos library
  9823.  functions, and programs that access memory outside the program or directly
  9824.  manipulate I/O ports will have to be modified somewhat.
  9825.  
  9826.  OS/2 supports nearly 200 function calls that you'll use for communicating
  9827.  with the operating system and the PC's hardware. Obviously, these new
  9828.  function calls encompass virtually everything you can do now under MS-DOS
  9829.  3.x using Int 21H. However, they also include a much faster and more
  9830.  versatile set of character-mode video I/O calls than the calls that have
  9831.  previously been available from the IBM PC BIOS services. You will no longer
  9832.  have to directly access video display memory in order to get good character-
  9833.  mode performance. OS/2 also includes a set of mouse function calls and many
  9834.  new calls that are related to tasking and inter-process communication.
  9835.  
  9836.  
  9837.  DOS Function Calls
  9838.  
  9839.  In C programs, OS/2 function calls look like ordinary function or subroutine
  9840.  calls. The names of all the functions are capitalized, and they begin with
  9841.  the letters DOS, KBD (keyboard), VIO (video), or MOU (mouse).
  9842.  
  9843.  Virtually all OS/2 function calls return a zero if the function is
  9844.  successful and an error code otherwise. If the function must return
  9845.  information back to your program, one or more of the arguments will be a far
  9846.  pointer to your program's data segment. OS/2 uses this pointer to store the
  9847.  result of the function. Some OS/2 function calls also require far pointers
  9848.  to structures for returning more extensive information.
  9849.  
  9850.  For instance, if you want to obtain the current cursor position, the OS/2
  9851.  function that you must use is VIOGETCURPOS. The first argument is a far
  9852.  pointer to a variable where OS/2 stores the cursor row position, the second
  9853.  argument is a far pointer to a variable to store the column, and the third
  9854.  parameter is a "video I/O handle" that currently must be set to zero.
  9855.  
  9856.  Within a C program you would define two variables for the row and column
  9857.  cursor positions like this:
  9858.  
  9859.    unsigned row, col ;
  9860.  
  9861.  and then call VIOGETCURPOS by using pointers to these variables:
  9862.  
  9863.    VIOGETCURPOS(&row, &col, 0) ;
  9864.  
  9865.  The C source code files for FINDDEMO, shown in Figure 2, and FINDER, shown
  9866.  in Figure 3 begin with the statement
  9867.  
  9868.    #include <doscall.h>
  9869.  
  9870.  This DOSCALL.H header file contains just two lines:
  9871.  
  9872.    #include       <doscalls.h>
  9873.    #include       <subcalls.h>
  9874.  
  9875.  In the DOSCALLS.H and SUBCALLS.H header files are the declarations for all
  9876.  OS/2 functions. The DOS functions are declared in DOSCALLS.H, and the KBD,
  9877.  VIO, and MOU functions are declared in SUBCALLS.H. For instance, the
  9878.  VIOGETCURPOS function is declared in SUBCALLS.H as
  9879.  
  9880.    extern unsigned
  9881.    far pascal
  9882.    VIOGETCURPOS(unsigned far *, unsigned far *, unsigned) ;
  9883.  
  9884.  All OS/2 functions are declared far, which directs the compiler to generate
  9885.  a far inter-segment call, and pascal. The pascal keyword indicates a Pascal
  9886.  calling sequence──the arguments are pushed onto the stack from left to
  9887.  right, and the called function then adjusts the stack before returning
  9888.  control to the caller.
  9889.  
  9890.  Normally, C compilers generate code that pushes arguments on the stack from
  9891.  right to left, and the caller fixes the stack. This calling sequence permits
  9892.  C to support functions with a variable number of arguments. Moreover, you
  9893.  can usually call a C function with an incorrect number of parameters, and
  9894.  while the function may not work correctly, in many cases the program will
  9895.  not crash.
  9896.  
  9897.  A function that uses the Pascal calling sequence, however, expects to
  9898.  receive a fixed number of arguments. Therefore, if you get the compiler
  9899.  warning message "too few actual parameters" for an OS/2 function call, you
  9900.  should fix the code before you try to run the program.
  9901.  
  9902.  Note that the function declaration shows the first two parameters to be far
  9903.  pointers to unsigned integers, but the VIOGETCURPOS call used simply &row
  9904.  and &col. The C compiler generates far addresses without casting based on
  9905.  the function declaration.
  9906.  
  9907.  The DOSCALLS.H and SUBCALLS.H header files also contain structure
  9908.  declarations used in some of the other OS/2 function calls. For instance,
  9909.  FINDER.EXE uses the FileFindBuf structure in conjunction with the
  9910.  DOSFINDFIRST and DOSFINDNEXT function calls. FINDDEMO.C uses the ResultCodes
  9911.  structure for returning values from the DOSEXECPGM and DOSCWAIT functions,
  9912.  and the KeyData structure for returning keyboard input from KBDCHARIN.
  9913.  Here's what the KeyData structure declaration looks like:
  9914.  
  9915.    struct KeyData
  9916.      {unsigned char char_code ;
  9917.       unsigned char scan_code ;
  9918.       unsigned char status ;
  9919.       unsigned char nls_shift ;
  9920.       unsigned shift_state ;
  9921.       unsigned long time} ;
  9922.  
  9923.  That's a little more information than you currently get from Int 16H, isn't
  9924.  it?
  9925.  
  9926.  
  9927.  Assembly Language
  9928.  
  9929.  If you are programming in assembly language, OS/2 function calls are made by
  9930.  pushing arguments on the stack and calling external far functions. Near the
  9931.  top of your source code you specify that the OS/2 function is in a different
  9932.  segment:
  9933.  
  9934.    extrn
  9935.    VIOGETCURPOS:far
  9936.  
  9937.  The two variables to receive the row and column positions would go in your
  9938.  data segment:
  9939.  
  9940.    row  dw   ?
  9941.    col  dw   ?
  9942.  
  9943.  When you call VIOGETCURPOS, you push the arguments on the stack and make the
  9944.  call:
  9945.  
  9946.    push ds
  9947.    push offset row
  9948.    push ds
  9949.    push offset col
  9950.    push 0
  9951.    call VIOGETCURPOS
  9952.  
  9953.  When you return from the VIOGETCURPOS call, the AX register contains the
  9954.  return value of the function call, and the stack pointer is the value prior
  9955.  to pushing the arguments on the stack.
  9956.  
  9957.  This code sample pushes immediate values on the stack. Because these
  9958.  instructions are supported by the 80286 but not by the 8086, you have to
  9959.  include the assembler directive
  9960.  
  9961.    .286c
  9962.  
  9963.  in your source code file. Or you can simply transfer the values to registers
  9964.  before pushing them on the stack.
  9965.  
  9966.  
  9967.  Linking
  9968.  
  9969.  The new LINK generates a "new-format executable." The extension is still
  9970.  .EXE, but the format of the file is not the same as that used in MS-DOS
  9971.  versions through 3.2. However, it is the same as the new-format executable
  9972.  currently in use for Windows applications.
  9973.  
  9974.  Among other things, the new-format executable contains an "import table"
  9975.  that OS/2 uses to match the far calls in your program code to the
  9976.  appropriate OS/2 function calls. The actual entry address of the function
  9977.  call routine is inserted into the code when the program is loaded into
  9978.  memory. (This is dynamic linking, which has appeared previously in Windows
  9979.  programming.)
  9980.  
  9981.  LINK requires that two libraries be explicitly listed in the command line.
  9982.  These are SLIBC5.LIB, which contains normal C library functions that make
  9983.  OS/2 functions calls, and DOSCALLS.LIB.
  9984.  
  9985.  The DOSCALLS.LIB library is an "import library." It merely provides LINK
  9986.  with the module names and ordinal numbers associated with the OS/2 function
  9987.  calls. LINK uses this information to set up the import table contained in
  9988.  the new-format executable. DOSCALLS.LIB performs the same function in OS/2
  9989.  programs that the SLIBW.LIB library performs in Windows applications.
  9990.  
  9991.  
  9992.  Module Definition File
  9993.  
  9994.  The new LINK also requires that a module definition file be specified as the
  9995.  fifth argument on the LINK command line. The module definition file (yet
  9996.  another concept that Windows programmers are already familiar with) provides
  9997.  information about your program's segments, stack size, and local heap
  9998.  size. The two module definition files that describe FINDDEMO.DEF and
  9999.  FINDER.DEF are shown in Figures 4 and 5, respectively.
  10000.  
  10001.  The NAME line indicates the name of the module, which is usually the same as
  10002.  that of the program. The DESCRIPTION is generally a copyright notice
  10003.  imbedded in the .EXE file. The keyword PROTMODE indicates that the program
  10004.  runs only in protected mode.
  10005.  
  10006.  The CODE and DATA lines specify characteristics of the code and data
  10007.  segments in your program. The options that are shown here are normal for
  10008.  protected mode.
  10009.  
  10010.  Both the code and data segments are MOVABLE. This is no sweat in protected
  10011.  mode because a segment can be physically moved but still retain the same
  10012.  selector address, previously known as the segment address. Specifying the
  10013.  CODE segment as PURE means that the same code segment loaded in memory can
  10014.  be used for multiple instances of the program. The automatic data segment of
  10015.  a program cannot be PURE because it contains the stack and most likely
  10016.  contains read/write data as well. However, data segments with read-only data
  10017.  without relocation information can also be flagged as PURE and can be shared
  10018.  among multiple instances of the program.
  10019.  
  10020.  For large applications, you might divide the program into many different
  10021.  segments and specify characteristics of each in the module definition file.
  10022.  These characteristics would include the keywords PRELOAD, which means the
  10023.  segment is loaded into memory when the program is run, and LOADONCALL, which
  10024.  means the segment is loaded into memory only when a routine within it is
  10025.  needed. Based on a least-recently-used algorithm, OS/2 can free up memory
  10026.  that is occupied by code segments and later reload them when needed from the
  10027.  .EXE file. This facility is essentially a built-in, hassle-free overlay
  10028.  manager.
  10029.  
  10030.  The automatic data segment of your program contains static variables, the
  10031.  stack, and a local heap organized as in Figure 6.
  10032.  
  10033.  The stack is fixed to the size specified in the STACKSIZE line of the module
  10034.  definition file. The HEAPSIZE value is a minimum local heap size, which is
  10035.  the size that OS/2 sets aside when your program is first loaded into memory.
  10036.  If you use C memory allocation functions such as malloc to allocate more
  10037.  memory from your local heap than is specified by HEAPSIZE, OS/2 can expand
  10038.  the local heap──by physically moving the data segment in memory if
  10039.  necessary.
  10040.  
  10041.  
  10042.  MAKEing the Program
  10043.  
  10044.  A make-file for the FINDDEMO application is shown in Figure 7. You create
  10045.  the FINDDEMO.EXE and FINDER.EXE executables by running
  10046.  
  10047.    MAKE FINDDEMO
  10048.  
  10049.  The compile step uses several compiler switches:
  10050.  
  10051.    cl -c -G2 -Zp
  10052.    finddemo.c
  10053.  
  10054.  The -c switch specifies that the program is to be compiled but not linked;
  10055.  this is necessary because the link step requires nondefault arguments. The
  10056.  -G2 switch compiles code by using 80286 instructions. This is optional, but
  10057.  it results in a slightly smaller .EXE size and faster execution.
  10058.  
  10059.  The -Zp switch indicates that structures are "packed." Normally the C
  10060.  compiler aligns each element of a structure on an even address. However,
  10061.  some of the structures declared in DOSCALLS.H and SUBCALLS.H, such as the
  10062.  KeyData structure shown earlier, contain consecutive char variables.
  10063.  
  10064.  
  10065.  New Executable
  10066.  
  10067.  The new-format executable created by LINK is really a superset of the old-
  10068.  format executable. It begins with an old-format header. This makes it appear
  10069.  to old DOS versions (and the OS/2 compatibility box) as a normal .EXE
  10070.  program. However, when you link the FINDDEMO and FINDER programs, the old-
  10071.  format header is constructed so that the program appears to require more
  10072.  memory than is available. So, if you attempt to run FINDDEMO.EXE under real
  10073.  mode (the OS/2 compatibility box or MS-DOS 2.x or 3.x), you get the message
  10074.  "Program too big to fit in memory."
  10075.  
  10076.  You have some alternatives to this message. The first alternative involves
  10077.  the STUB option in the module definition file, which Windows programmers
  10078.  already know about:
  10079.  
  10080.    STUB 'oldprog.EXE'
  10081.  
  10082.  The file oldprog.EXE is an old-format executable that runs under MS-DOS 2.x
  10083.  or 3.x. The STUB option directs LINK to insert this old-format executable in
  10084.  the top of the new-format executable. Now the .EXE file appears to MS-DOS
  10085.  2.x or 3.x as a regular program. When MS-DOS 2.x or 3.x loads the program,
  10086.  this old program is executed instead.
  10087.  
  10088.  Windows programmers generally include the line
  10089.  
  10090.    STUB 'WINSTUB.EXE'
  10091.  
  10092.  in their module definition files. The standard WINSTUB.EXE program simply
  10093.  displays the message "This program requires Microsoft Windows." You might
  10094.  want to create a similar program that would display a message such as "This
  10095.  program must be run in OS/2 protected mode."
  10096.  
  10097.  The second alternative is to write a real-mode version and include that in
  10098.  the STUB line if your program is short. If you're writing a program using
  10099.  straight C without any explicit OS/2 function calls, you could simply
  10100.  compile and link it under a different name by using the old real-mode
  10101.  libraries. This process creates one .EXE file that actually contains two
  10102.  versions of the same program: the protected-mode version runs under
  10103.  protected mode, and the real-mode version runs under real mode.
  10104.  
  10105.  For longer programs, or programs that use explicit OS/2 function calls, you
  10106.  should consider the third alternative: going family style.
  10107.  
  10108.  
  10109.  Family Style
  10110.  
  10111.  The OS/2 developer's kit includes a library file called API.LIB that
  10112.  translates many OS/2 function calls into equivalent MS-DOS Int 21H function
  10113.  calls. Video I/O calls are translated into equivalent BIOS calls and direct
  10114.  screen accesses. When you run
  10115.  
  10116.    BIND program
  10117.  
  10118.  after linking, parts of this API.LIB library are tacked onto the end of the
  10119.  .EXE file, and the old-format header is modified to run a loader program
  10120.  that is also inserted into the .EXE file. When you run the resulting .EXE
  10121.  file under a real-mode DOS version, the loader patches addresses into your
  10122.  program that call the routines within this library rather than OS/2
  10123.  functions. If you run the program under OS/2 protected mode, the loader and
  10124.  library routines are simply discarded.
  10125.  
  10126.  Thus, you have one executable file and one program that runs under
  10127.  protected-mode and real-mode MS-DOS. This is called the "family API" model.
  10128.  
  10129.  Not all OS/2 functions can be converted into old MS-DOS Int 21H calls. For
  10130.  instance, the FINDDEMO and FINDER programs shown here use several OS/2
  10131.  facilities that have no old MS-DOS equivalents. Some options of other OS/2
  10132.  functions are not fully supported. However, you can determine within your
  10133.  program whether it is running in real mode or protected mode and have
  10134.  separate logic that is appropriate for each.
  10135.  
  10136.  
  10137.  The Workings
  10138.  
  10139.  Now that we know how to create FINDDEMO.EXE and FINDER.EXE, let's look at
  10140.  how they work. FINDER is a child process run by FINDDEMO. FINDER does all
  10141.  the work while FINDDEMO sits back and waits for the messages from FINDER
  10142.  that report what it has found.
  10143.  
  10144.  FINDDEMO starts off by creating a queue named \QUEUES\FINDDEMO. A queue is a
  10145.  linked list maintained by OS/2 that you can use to transfer data from one
  10146.  program to another. One program creates the queue; another program opens the
  10147.  same queue. The program that creates the queue──in this case, FINDDEMO──has
  10148.  read and write privileges. The program that then opens the existing
  10149.  queue──in this case, FINDER──has write privileges. Queues may be first-in-
  10150.  first-out (FIFO), last-in-first-out (LIFO), or based on priority; the
  10151.  FINDDEMO queue is FIFO.
  10152.  
  10153.  FINDDEMO also creates two additional threads of execution. These appear in
  10154.  the program as functions called dispthread and waitthread. These two threads
  10155.  run simultaneously with the main thread, sometimes called the "parent"
  10156.  thread. I'll explain what they do a little later.
  10157.  
  10158.  FINDDEMO then prompts for a number and a file specification from the user.
  10159.  For each file specification you enter, FINDDEMO executes FINDER by using the
  10160.  OS/2 function call DOSEXECPGM. FINDER is executed in an asynchronous
  10161.  mode──the DOSEXECPGM call returns immediately, and FINDDEMO can prompt for
  10162.  the next file specification. As many as nine instances of FINDER can be
  10163.  executed. Although these multiple instances use different data segments,
  10164.  they share the same code segment in memory.
  10165.  
  10166.  When FINDDEMO executes FINDER it passes to it the file specification, the
  10167.  name of the queue, and an index number from 1 to 9 that indicates the
  10168.  display line on which the file specification appears.
  10169.  
  10170.  Now let's look at FINDER. FINDER opens the queue and searches for files
  10171.  meeting the file specification over the entire disk by using a recursive
  10172.  function called find. You'll note that FINDER changes the subdirectory
  10173.  during this search. Under OS/2, each process maintains its own current
  10174.  subdirectory. So each instance of FINDER can change the subdirectory and
  10175.  not affect other instances. Nice, huh?
  10176.  
  10177.  When FINDER finds a file that fits the file specification, it allocates a
  10178.  small shared segment of memory using DOSALLOCSEG and copies the pathname
  10179.  into it. The DOSGIVESEG function obtains a new selector (segment address)
  10180.  appropriate for its parent FINDDEMO. (FINDER obtained the process ID of its
  10181.  parent when it opened the queue.) FINDER can then write this segment to the
  10182.  queue.
  10183.  
  10184.  Now back to FINDDEMO. During initialization, FINDDEMO created a second
  10185.  execution thread, the subroutine called dispthread. This thread sits in an
  10186.  infinite loop that starts off with a call to DOSREADQUEUE. When DOSREADQUEUE
  10187.  returns control to the program, it reads the contents of the shared memory
  10188.  segment created by FINDER and writes it to the display.
  10189.  
  10190.  The other thread created during FINDDEMO initialization is waitthread. This
  10191.  is also an infinite loop that waits, using DOSCWAIT, for instances of FINDER
  10192.  to terminate. Because DOSCWAIT returns with an error if no child processes
  10193.  are running, FINDDEMO first sets nine semaphores and clears one only when
  10194.  executing FINDER. The waitthread thread is suspended, through the use of the
  10195.  function DOSMUXSEMWAIT, until one of these semaphores is cleared.
  10196.  
  10197.  Another set of semaphores, which are in the array runsem, are normally
  10198.  cleared. They are set when an instance of FINDER is executed and cleared
  10199.  when the instance terminates. These semaphores are used when you terminate
  10200.  FINDDEMO by using the Esc key. FINDDEMO uses DOSKILLPROCESS to terminate any
  10201.  instances of FINDER that are still running and then waits for all of the
  10202.  runsem semaphores to be cleared.
  10203.  
  10204.  
  10205.  The New Generation
  10206.  
  10207.  I claimed earlier that FINDDEMO's use of inter-process communication and
  10208.  multitasking represented a "typical" OS/2 application. Obviously, you can
  10209.  take advantage of the larger protected mode addressing space and more
  10210.  versatile OS/2 API without getting into the fancy stuff. Whether you do get
  10211.  fancy or not, I'm sure you'll find some interesting applications for these
  10212.  new OS/2 capabilities.
  10213.  
  10214.  
  10215.  Figure 2:  FINDDEMO.C
  10216.  
  10217.  /* finddemo.c -- program to demonstrate IPC and multitasking */
  10218.  
  10219.  #include <doscall.h>
  10220.  
  10221.  #define WORD  unsigned int
  10222.  #define DWORD unsigned long
  10223.  
  10224.  #define NUMPROC     9                   /* number of process */
  10225.  #define CHILDPROG   "FINDER.EXE"
  10226.  #define QUEUENAME   "\\QUEUES\\FINDDEMO"
  10227.  #define THREADSTACK 1024
  10228.  
  10229.  void far dispthread (void) ;
  10230.  void far waitthread (void) ;
  10231.  
  10232.  WORD   queuehandle, count [NUMPROC] ;
  10233.  DWORD  runsem [NUMPROC], waitsem [NUMPROC] ;
  10234.  struct ResultCodes rc [NUMPROC] ;
  10235.  struct {
  10236.       WORD count ;
  10237.       struct {
  10238.            WORD  reserved ;
  10239.            DWORD far *sem ;
  10240.            } index [NUMPROC] ;
  10241.       } semtab ;
  10242.  
  10243.  main ()
  10244.       {
  10245.       static char prompt[] = "Line number or Esc to end -->  \b" ;
  10246.       struct KeyData keydata ;
  10247.       WORD   index, len, i, dispID, waitID, sigaction ;
  10248.       DWORD  sigaddr ;
  10249.       char   dirmask [15], dispstack [THREADSTACK],
  10250.              waitstack [THREADSTACK] ;
  10251.  
  10252.       /*
  10253.           Initialize: Set up "semtab" structure for DOSMUXSEMWAIT.
  10254.           ----------  Disable Ctrl-Break and Ctrl-C exits.
  10255.                       Create queue for IPC with FINDER.EXE.
  10256.                       Create threads for messages from FINDER
  10257.                           and waiting for FINDER terminations.
  10258.                       Display text.
  10259.       */
  10260.  
  10261.       semtab.count = NUMPROC ;
  10262.       for (index = 0 ; index < NUMPROC ; index++) {
  10263.            DOSSEMSET ((DWORD) &waitsem[index]) ;
  10264.            semtab.index[index].sem = &waitsem[index] ;
  10265.            }
  10266.       DOSSETSIGHANDLER (0L, &sigaddr, &sigaction, 1, 1) ;
  10267.       DOSSETSIGHANDLER (0L, &sigaddr, &sigaction, 1, 4) ;
  10268.  
  10269.       if (DOSCREATEQUEUE (&queuehandle, 0, QUEUENAME)) {
  10270.            puts ("FINDDEMO: Cannot create new queue") ;
  10271.            DOSEXIT (1, 1) ;
  10272.            }
  10273.       if (DOSCREATETHREAD (dispthread, &dispID, dispstack + THREADSTACK) ||
  10274.           DOSCREATETHREAD (waitthread, &waitID, waitstack + THREADSTACK)) {
  10275.            puts ("FINDDEMO: Cannot create threads") ;
  10276.            DOSEXIT (1, 1) ;
  10277.            }
  10278.       displayheadings () ;
  10279.  
  10280.                 /*
  10281.                      Main Loop: Display prompt and read keyboard.
  10282.                      ---------  Execute FINDER.EXE.
  10283.                 */
  10284.       do   {
  10285.            VIOSETCURPOS (18, 0, 0) ;
  10286.            VIOWRTTTY (prompt, sizeof prompt - 1, 0) ;
  10287.            KBDCHARIN (&keydata, 0, 0) ;
  10288.  
  10289.            index = keydata.char_code - '1' ;
  10290.  
  10291.            if (index <= NUMPROC && rc[index].TermCode_PID == 0) {
  10292.                 VIOWRTTTY (&keydata.char_code, 1, 0) ;
  10293.                 VIOWRTNCHAR (" ", 77, 7 + index, 3, 0) ;
  10294.                 do   {
  10295.                      VIOSETCURPOS (7 + index, 3, 0) ;
  10296.                      len = 13 ;
  10297.                      KBDSTRINGIN (dirmask, &len, 0, 0) ;
  10298.                      }
  10299.                 while (len == 0) ;
  10300.  
  10301.                 dirmask [len] = '\0' ;
  10302.                 executeprogram (index, dirmask) ;
  10303.                 }
  10304.            }
  10305.       while (keydata.char_code != 27) ;
  10306.  
  10307.                 /*
  10308.                      Clean-up: Kill all existing FINDER.EXE processes.
  10309.                      --------  Wait for processes to terminate.
  10310.                                Close the queue and exit.
  10311.                 */
  10312.  
  10313.       for (index = 0 ; index < NUMPROC ; index++)
  10314.            if (rc[index].TermCode_PID)
  10315.                 DOSKILLPROCESS (0, rc[index].TermCode_PID) ;
  10316.  
  10317.       for (index = 0 ; index < NUMPROC ; index++)
  10318.            DOSSEMWAIT ((DWORD) &runsem [index], -1L) ;
  10319.  
  10320.       DOSCLOSEQUEUE (queuehandle) ;
  10321.       DOSEXIT (1, 0) ;
  10322.       }
  10323.  
  10324.  displayheadings ()
  10325.       {
  10326.       static char heading  [] = "286DOS File Finder Demo Program",
  10327.                   colheads [] = "Dir Mask     Status  Files",
  10328.                   colunder [] = "--------     ------  -----" ;
  10329.       char        buffer  [5] ;
  10330.       WORD        row, col, i, len ;
  10331.  
  10332.       VIOGETCURPOS (&row, &col, 0) ;              /* get current attr */
  10333.       VIOWRTTTY (" ", 1, 0) ;
  10334.       len = 2 ;
  10335.       VIOREADCELLSTR (buffer, &len, row, col, 0) ;
  10336.       VIOSCROLLUP (0, 0, -1, -1, -1, buffer, 0) ;     /* clear screen */
  10337.  
  10338.       len = sizeof heading - 1 ;
  10339.       col = (80 - len) / 2 ;
  10340.       VIOWRTCHARSTR (heading, len, 1, col, 0) ;            /* heading */
  10341.       VIOWRTNCHAR   ("\xC6",   1, 2, col - 1,   0) ;     /* underline */
  10342.       VIOWRTNCHAR   ("\xCD", len, 2, col,       0) ;
  10343.       VIOWRTNCHAR   ("\xB5",   1, 2, col + len, 0) ;
  10344.       VIOWRTCHARSTR (colheads, sizeof colheads - 1, 5, 3, 0) ;
  10345.       VIOWRTCHARSTR (colunder, sizeof colunder - 1, 6, 3, 0) ;
  10346.  
  10347.       for (i = 0 ; i < NUMPROC ; i++) {                    /* numbers */
  10348.            sprintf (buffer, "%d.", i + 1) ;
  10349.            VIOWRTCHARSTR (buffer, 2, 7 + i, 0, 0) ;
  10350.            }
  10351.       }
  10352.  
  10353.  executeprogram (index, dirmask)
  10354.       WORD index ;
  10355.       char *dirmask ;
  10356.       {
  10357.       char objbuf [32] ;
  10358.       char args [128] ;
  10359.  
  10360.       strcat (strcpy (args, CHILDPROG), " ") ;        /* construct args */
  10361.       strcat (strcat (args, dirmask), " ") ;
  10362.       strcat (strcat (args, QUEUENAME), " ") ;
  10363.       itoa (index, args + strlen (args), 10) ;
  10364.  
  10365.       count [index] = 0 ;                             /* initialize count */
  10366.  
  10367.       if (DOSEXECPGM (objbuf, 32, 2, args, 0, &rc[index], CHILDPROG))
  10368.            {
  10369.            puts ("FINDDEMO: Can't run FINDER.EXE") ;
  10370.            DOSEXIT (1, 1) ;
  10371.            }
  10372.       VIOWRTCHARSTR ("Running", 7, index + 7, 16, 0) ;/* now executing */
  10373.       DOSSEMSET   ((DWORD) &runsem [index]) ;
  10374.       DOSSEMCLEAR ((DWORD) &waitsem[index]) ;
  10375.       }
  10376.  
  10377.  void far dispthread ()        /* thread to read messages from FINDER */
  10378.       {                        /*   and display filenames.            */
  10379.       DWORD request ;
  10380.       WORD  len, index, i ;
  10381.       char  far *farptr ;
  10382.       char  priority, pathname [80], buffer [64] ;
  10383.  
  10384.       while (1) {
  10385.            DOSREADQUEUE (queuehandle, &request, &len,
  10386.                           &(DWORD)farptr, 0, 0, &priority, 0L) ;
  10387.            i = 0 ;
  10388.            while (pathname [i++] = *farptr++) ;
  10389.            index = (WORD) (request >> 16) ;
  10390.            count [index] += len > 0 ;
  10391.            sprintf (buffer, "%5d   %-48.48s", count [index], pathname) ;
  10392.            VIOWRTCHARSTR (buffer, 56, 7 + index, 24, 0) ;
  10393.            DOSFREESEG ((WORD) ((DWORD) farptr >> 16)) ;
  10394.            }
  10395.       }
  10396.  
  10397.  void far waitthread ()     /* thread to wait for FINDER terminations */
  10398.       {
  10399.       WORD   index, PID ;
  10400.       struct ResultCodes rescode ;
  10401.  
  10402.       while (1) {
  10403.            DOSMUXSEMWAIT (&index, (WORD far *) &semtab, -1L) ;
  10404.            DOSCWAIT (0, 0, &rescode, &PID, 0) ;
  10405.  
  10406.            for (index = 0 ; index < NUMPROC ; index++)  /* find index */
  10407.                 if (PID == rc[index].TermCode_PID)
  10408.                      break ;
  10409.  
  10410.            VIOWRTCHARSTR (rescode.TermCode_PID ? "Halted " : "Done   ",
  10411.                                7, index + 7, 16, 0) ;
  10412.            rc[index].TermCode_PID = 0 ;
  10413.            DOSSEMCLEAR ((DWORD) &runsem [index]) ;
  10414.            DOSSEMSET   ((DWORD) &waitsem[index]) ;
  10415.            }
  10416.       }
  10417.  
  10418.  
  10419.  Figure 3:  FINDER.C
  10420.  
  10421.  /* finder.c -- child process of finddemo */
  10422.  
  10423.  #include <doscall.h>
  10424.  
  10425.  unsigned index ;
  10426.  unsigned queuehandle ;
  10427.  unsigned parentPID ;
  10428.  
  10429.  main (argc, argv)
  10430.       int  argc ;
  10431.       char *argv [] ;
  10432.       {
  10433.       if (argc < 4) {
  10434.            puts ("Run FINDDEMO rather than FINDER") ;
  10435.            DOSEXIT (1, 1) ;
  10436.            }
  10437.       if (DOSOPENQUEUE (&parentPID, &queuehandle, argv [2])) {
  10438.            puts ("FINDER: Cannot open queue") ;
  10439.            DOSEXIT (1, 1) ;
  10440.            }
  10441.       index = atoi (argv [3]) ;
  10442.  
  10443.       writequeue ("") ;
  10444.       chdir ("\\") ;
  10445.       find (argv [1]) ;
  10446.       writequeue ("") ;
  10447.  
  10448.       DOSCLOSEQUEUE (queuehandle) ;
  10449.       DOSEXIT (1, 0) ;
  10450.       }
  10451.  
  10452.  find (searchstr)
  10453.       char     *searchstr ;
  10454.       {
  10455.       struct   FileFindBuf ffb ;
  10456.       char     cwd [81], pathname [100] ;
  10457.       unsigned handle = 0xFFFF, num = 1 ;
  10458.  
  10459.       if (cwd [strlen (getcwd (cwd, 80)) - 1] != '\\')
  10460.            strcat (cwd, "\\") ;
  10461.  
  10462.       DOSFINDFIRST (searchstr, &handle, 7, &ffb, sizeof ffb, &num, 0L) ;
  10463.       while (num) {
  10464.            writequeue (strcat (strcpy (pathname, cwd), ffb.file_name)) ;
  10465.            DOSFINDNEXT (handle, &ffb, sizeof ffb, &num) ;
  10466.            }
  10467.       DOSFINDCLOSE (handle) ;
  10468.  
  10469.       handle = 0xFFFF ;
  10470.       num  = 1 ;
  10471.       DOSFINDFIRST ("*.*", &handle, 0x17, &ffb, sizeof ffb, &num, 0L) ;
  10472.       while (num) {
  10473.            if (ffb.attributes & 0x10 && ffb.file_name [0] != '.') {
  10474.                 chdir (ffb.file_name) ;
  10475.                 find (searchstr) ;
  10476.                 chdir ("..") ;
  10477.                 }
  10478.            DOSFINDNEXT (handle, &ffb, sizeof ffb, &num) ;
  10479.            }
  10480.       DOSFINDCLOSE (handle) ;
  10481.       }
  10482.  
  10483.  writequeue (str)
  10484.       char *str ;
  10485.       {
  10486.       unsigned selector, parentselector ;
  10487.       char far *farptr ;
  10488.       int      len = strlen (str) ;
  10489.  
  10490.       DOSALLOCSEG (len + 1, &selector, 1) ;
  10491.       farptr = (char far *) (((unsigned long) selector) << 16) ;
  10492.  
  10493.       while (*farptr++ = *str++) ;
  10494.  
  10495.       DOSGIVESEG (selector, parentPID, &parentselector) ;
  10496.       DOSFREESEG (selector) ;
  10497.       farptr = (char far *) (((unsigned long) parentselector) << 16) ;
  10498.  
  10499.       DOSWRITEQUEUE (queuehandle, index, len, farptr, 0) ;
  10500.       }
  10501.  
  10502.  
  10503.  Figure 4:  FINDDEMO.DEF
  10504.  
  10505.  NAME            FINDDEMO
  10506.  DESCRIPTION     'File Finder Demonstration Program'
  10507.  PROTMODE
  10508.  DATA            MOVABLE
  10509.  CODE            MOVABLE PURE
  10510.  HEAPSIZE        2048
  10511.  STACKSIZE       4096
  10512.  
  10513.  
  10514.  Figure 5:  FINDER.DEF
  10515.  
  10516.  NAME            FINDER
  10517.  DESCRIPTION     'File Finder Module for FINDDEMO'
  10518.  PROTMODE
  10519.  DATA            MOVABLE
  10520.  CODE            MOVABLE PURE
  10521.  HEAPSIZE        2048
  10522.  STACKSIZE       8192
  10523.  
  10524.  
  10525.  Figure 6:  Automatic data segment containing static variables, the stack,
  10526.             and a local heap.
  10527.  
  10528.                 ┌───────────────────────────┐▄
  10529.                 │                         ╔═══════════════╗
  10530.                 │        Local Heap       ║  High Memory  ║
  10531.                 │                         ╚═══════════════╝
  10532.                 └───────────────────────────┘█
  10533.                   ████████████████████████████
  10534.  
  10535.                 ┌───────────────────────────┐
  10536.                 │                           │█
  10537.                 │           Stack           │█
  10538.                 │                           │█
  10539.                 └───────────────────────────┘█
  10540.                   ████████████████████████████
  10541.  
  10542.                 ┌───────────────────────────┐▄
  10543.                 │                         ╔═══════════════╗
  10544.                 │     Static Variables    ║   Low Memory  ║
  10545.                 │                         ╚═══════════════╝
  10546.                 └───────────────────────────┘█
  10547.                   ████████████████████████████
  10548.  
  10549.  
  10550.  Figure 7:  Make-file for FINDDEMO.EXE and FINDER.EXE
  10551.  
  10552.  finddemo.obj  :  finddemo.
  10553.      cl -c -G2 -Zp finddemo.c
  10554.  
  10555.  finddemo.exe  :  finddemo.obj finddemo.def
  10556.      link finddemo, /align:16, /map, doscalls slibc5, finddemo.def
  10557.  
  10558.  finder.obj  :  findef.c
  10559.      cl -c -G2 -Zp finder.c
  10560.  
  10561.  finder.exe  :  finder.obj finder.def
  10562.      link finder, /align:16, /map, doscalls slibc5, finder.def
  10563.  
  10564.  ████████████████████████████████████████████████████████████████████████████
  10565.  
  10566.  An Interview with Gordon Letwin: OS/2: Turning Off the Car to Change Gears
  10567.  
  10568.  ───────────────────────────────────────────────────────────────────────────
  10569.  Also see the related article:
  10570.    Gordon Letwin: The Challenge of the 286 Speaks His Language
  10571.  ───────────────────────────────────────────────────────────────────────────
  10572.  
  10573.  Lori Valigra
  10574.  
  10575.  The development of Microsoft's multitasking operating system has taken many
  10576.  twists and turns since Microsoft began working on it three years ago. The
  10577.  many stages it has gone through so far have resulted from working with the
  10578.  schizophrenic Intel 80286 chip, which can run in either of two incompatible
  10579.  modes: protected (286) mode, and unprotected (8086 or "real") mode. The main
  10580.  problem has been to get an operating system which runs both protected- and
  10581.  real-mode programs. Microsoft solved the problem with mode switching──a
  10582.  feature which essentially works by mimicking the act of turning off the
  10583.  computer and restarting it, similar to turning off a car in order to switch
  10584.  gears.
  10585.  
  10586.  The new operating system began as an ambitious project to produce an
  10587.  operating system for Intel 80286-based computers running a mixture of office
  10588.  automation applications. This new operating system was to be capable of
  10589.  multitasking and compatible with the protected 286 mode. But as the project
  10590.  got too unwieldly, Microsoft split it up in early 1985, announcing a version
  10591.  called DOS 4. This version incorporates multitasking but does not include
  10592.  protected mode features. The DOS 5 project, as it was called, is intended to
  10593.  support the protected mode and add other features.
  10594.  
  10595.  OS/2, as the project has finally been named, was easily the most difficult
  10596.  and challenging program written at Microsoft to date, with more than 350,000
  10597.  lines of code. While Microsoft generally feels that the most efficient
  10598.  projects are those staffed by one person, the complexity and time
  10599.  constraints of the OS/2 project required that the company assign more than
  10600.  35 programmers to it. Managing the project──dividing the programmers into
  10601.  teams, dividing the project among them, and coordinating the various teams'
  10602.  modifications to the program's code──became an enormous challenge. Adding to
  10603.  the challenge was the joint development of the product with IBM, a company
  10604.  whose culture and methodology are quite different from Microsoft's.
  10605.  
  10606.  Microsoft Systems Journal talked with the project's chief architect, Gordon
  10607.  Letwin, to learn more about the planning, development, and cooperative
  10608.  effort that went into the development of OS/2.
  10609.  
  10610.  MSJ: What was the goal of the OS/2 project?
  10611.  
  10612.  Letwin: We tried to create the underpinnings for the ideal office automation
  10613.  operating system. In our opinion, that operating system would allow
  10614.  applications direct access to high-bandwidth peripherals, offer device-
  10615.  independent graphics drivers, provide multitasking without performance
  10616.  degradation (compared with a single-tasking system), run programs in both
  10617.  the protected 286 and real 8086 modes of the 286 processor, and provide a
  10618.  protected environment to assure system stability. I think we've done a
  10619.  pretty good job.
  10620.  
  10621.  MSJ: Microsoft has been developing multitasking operating systems for three
  10622.  years now, beginning with DOS 4. What was the purpose of that operating
  10623.  system, and where does it stand now that OS/2 is out?
  10624.  
  10625.  Letwin: DOS 4 was the first product to result from Microsoft's multitasking
  10626.  DOS effort. We began it even before IBM introduced the PC AT. It was an
  10627.  ambitious product that was originally to include a protected mode with mode
  10628.  switching abilities so it could run on the 8086 or the 286.
  10629.  
  10630.  A general-purpose multitasking system needs to run in both modes: the
  10631.  unprotected 8086 mode so that we can run existing DOS applications, and the
  10632.  protected 286 mode so that we can multitask arbitrary, unrelated
  10633.  applications. But the architecture of the 286 caused some delays. Although
  10634.  we knew the project would be difficult, it was only after we'd gotten deeply
  10635.  into it that we realized just how difficult it would be.
  10636.  
  10637.  As a result, DOS 4 became too complicated for our schedules. Because of the
  10638.  pressure of customer demand as well as that of previous commitments, we
  10639.  broke the project into two parts. DOS 4 runs only in real mode and provides
  10640.  multitasking only for specialized applications. DOS 5, which has now been
  10641.  released as OS/2, includes the protected mode and other features. DOS 4 was
  10642.  delivered in the last half of 1986 and is being sold in special application
  10643.  environments, primarily in Europe. It is a specialized product that can
  10644.  share the market with OS/2, because it runs on 8086 hardware, while OS/2
  10645.  requires a 286. The move from DOS 4 to OS/2 was a gradual evolutionary
  10646.  process. OS/2 is by far the most complex and sophisticated operating system
  10647.  project we've worked on. To offer multitasking in a protected environment,
  10648.  we developed it jointly with IBM to get around the constraints of the 286.
  10649.  
  10650.  MSJ: How does OS/2 compare with earlier multitasking operating systems like
  10651.  Concurrent CP/M?
  10652.  
  10653.  Letwin: Unlike OS/2, Concurrent CP/M has a major problem. It serializes
  10654.  access to the file system. When multitasking you need to make the operating
  10655.  system features such as the file system accessible to multiple programs at a
  10656.  time. But Concurrent CP/M didn't do this. It only allowed one program to
  10657.  call the file system at a time. So in a real life situation with lots of
  10658.  I/O, Concurrent CP/M was too slow.
  10659.  
  10660.  MSJ: What are some of the features of OS/2 that you consider particularly
  10661.  neat?
  10662.  
  10663.  Letwin: One is the seamless system service interface, which means that
  10664.  system services don't need to be in the kernel. Some can be provided by
  10665.  subroutine libraries and others by system processes. Many services can be
  10666.  upgraded piecemeal, in the field, without changing the whole operating
  10667.  system. This is accomplished by a technique called "dynamic linking," which
  10668.  allows external procedure calls to be linked up at execution time, rather
  10669.  than link time. This same mechanism is used as the standard system interface
  10670.  between applications, utility packages, etc.
  10671.  
  10672.  Another exceptional feature is direct hardware access, which allows an
  10673.  application to write directly to the hardware. For example, it will do
  10674.  special graphics on a screen.
  10675.  
  10676.  MSJ: How did you get around the limitations of the 286 chip?
  10677.  
  10678.  Letwin: The 286 is designed to run in only one mode, either real mode or
  10679.  protected mode. Since we knew we needed to support programs written using
  10680.  protected mode, a group of us, including Bill Gates and some project
  10681.  engineers, brainstormed to try to figure out how to run 8086 mode
  10682.  applications from within protected 286 mode. One option was "real mode
  10683.  emulation," where we could emulate the operation of 8086 real mode by doing
  10684.  a lot of special things to the protected mode segment tables. IBM was
  10685.  examining this technique independently. We thought emulation would be too
  10686.  slow, since the 286 would run at one-third the speed of an 8086. IBM's
  10687.  research later showed this to be the case. Soon thereafter, Intel began
  10688.  shipping a chip with a bug in it that would stop the technique anyway, so
  10689.  we tried another approach: mode switching.
  10690.  
  10691.  The 286 chip switches from real mode to protected mode very easily, but it
  10692.  was not designed to switch back from protected mode to real mode at all. You
  10693.  could only switch back to real mode by causing a full system reset. The idea
  10694.  of doing a full system reset while running a multitasking system was very
  10695.  radical. IBM was aware of the mode switching technique itself and had the
  10696.  capability for it in the AT BIOS for use during power-up diagnostics and for
  10697.  special block copies from high memory. However, this feature was never
  10698.  intended for use during full system operation. What we were thinking of was
  10699.  similar to switching gears in a car by turning the motor off. The idea was
  10700.  to master-clear the processor and restart it. The question was, would this
  10701.  interfere with DMA transfers that might be happening at the same time, lose
  10702.  pending interrupts, or fail in some other way? The IBM ROM code was designed
  10703.  to "switch gears by turning off the car" with the car standing still. We
  10704.  wanted to switch gears with the car traveling at highway speeds, and without
  10705.  causing a jolt. Working together with IBM, we set up an experiment to see if
  10706.  it would work, and it did. However, it took us months to convince everyone
  10707.  involved that the technique was feasible. The first time we presented the
  10708.  idea to IBM Boca, they gave us a funny look and suggested that perhaps we'd
  10709.  had a bit too much sun.
  10710.  
  10711.  There is one difficulty with mode switching, however. Because the designers
  10712.  thought that it would be an infrequent operation, done primarily during the
  10713.  power-on diagnostics, they implemented it in a fashion that saved some
  10714.  components but produced a very slow mode switch: typically 1,000
  10715.  microseconds, round trip. During this 1 millisecond interval the system
  10716.  cannot respond to interrupts. Any interrupts that arrive then are processed
  10717.  after the mode switch. This delay would normally be a problem only for high-
  10718.  speed communications at 96K bps or faster while you are simultaneously
  10719.  interacting with a real-mode program. You may experience data overruns while
  10720.  the real-mode screen group is on the display. Mode switching will affect any
  10721.  program that requires a very high-speed interrupt service rate faster than
  10722.  500 Hz. If the communications protocols on your system can handle missing
  10723.  characters by asking for a retransmit, and most can, then communications
  10724.  will be okay.
  10725.  
  10726.  MSJ: Why is protection so important in OS/2?
  10727.  
  10728.  Letwin: We knew we needed a multitasking system to accomplish our goals, and
  10729.  we knew we needed to have it protected. If you run a multiprocessor system,
  10730.  software can accidentally read or change other programs or data if the
  10731.  memory is unprotected. You can't stop it and you can't tell it happened. The
  10732.  system can crash or you could corrupt data. Even in an unprotected DOS 3
  10733.  system you can have problems with running programs simultaneously. SideKick,
  10734.  for example, loads into memory and terminates itself. It keeps memory in
  10735.  use, but the system doesn't know what is in that memory location. If you
  10736.  load several such programs at the same time and they conflict on how they
  10737.  use memory or interrupt vectors, the software will not work correctly, or
  10738.  the system will crash. So in an unprotected environment the system is always
  10739.  vulnerable to flaky software or conflicts between software products from
  10740.  different vendors.
  10741.  
  10742.  Of even greater importance than this is the fact that we needed to use
  10743.  protected mode to allow programs to access more than 640K bytes of memory.
  10744.  The 640K limit in earlier versions of DOS is a result of the addressing
  10745.  limitations of real mode. Protected mode programs are released from this
  10746.  restriction, and can use many megabytes of memory. As memory costs continue
  10747.  to drop and memory consumption by applications rises, it has become
  10748.  unacceptable to be restricted to the 640K that real mode provides.
  10749.  
  10750.  MSJ: How did you plan such an ambitious project, both conceptually and in
  10751.  getting the needed resources?
  10752.  
  10753.  Letwin: The first step was to decide what we wanted the system to do. Then,
  10754.  when we discovered how many of those capabilities we could implement, and by
  10755.  when, we could revise our goals. As a result, the initial planning process
  10756.  was iterative. We needed to have multitasking and to execute existing DOS 3
  10757.  programs. While there were still only a few engineers on the project, we
  10758.  thought about techniques such as mode switching and when necessary,
  10759.  prototyped some code. As we got a better handle on what we could do, and how
  10760.  long it would take, we met again with Bill Gates and revised our goals. The
  10761.  tone of the first phase was as ambitious as possible, and we listed all the
  10762.  things we thought we could do. The second phase involved determining which
  10763.  of those features were feasible in the required time frame. As soon as the
  10764.  general scope of the project was determined, the engineers met in groups of
  10765.  two and three to lay out further details of the architecture of the system.
  10766.  
  10767.  After the initial meetings came the joint design work. Engineering then
  10768.  finalized the goals. Because of the scope of the project, this design phase
  10769.  took a few months.
  10770.  
  10771.  Next was the sign-up phase, a tradition in the computer industry, where we
  10772.  explained the excitement, the importance, and the tight schedule of the
  10773.  project, then let everyone "sign up" for a long stretch of hard, focused
  10774.  work. You can't force someone to excel; you've got to provide the
  10775.  opportunity and the encouragement and then let them excel.
  10776.  
  10777.  Then the actual work began. We decided on the key systems and the key
  10778.  characteristics of the product. Next came the design sessions for each of
  10779.  the key systems. About a half dozen people were involved, typically three
  10780.  from Microsoft and three from IBM.
  10781.  
  10782.  During the coding months, my job was to understand fully all of the parts of
  10783.  the system and to make sure that everthing worked toward achieving our
  10784.  overall system goals. In a project this large, with this many people working
  10785.  on it, it's easy for design elements to diverge. One project member might
  10786.  think that speed is more important than code size, and write his code that
  10787.  way. Another might think the opposite and write his code the other way. The
  10788.  result would be that neither goal is achieved. I participated in the design
  10789.  of each component to insure that the goals and design philosophies of the
  10790.  system's architecture were consistent throughout the code. On most projects,
  10791.  this role can be fulfilled on a "part time" basis, leaving time for the
  10792.  chief designer to also get to write some code. Unfortunately, on the OS/2
  10793.  project this position required a full time effort, leaving me little
  10794.  opportunity to write any real code.
  10795.  
  10796.  MSJ: How did you divide up tasks on the project?
  10797.  
  10798.  Letwin: The most efficient programming is done by one person. It's a classic
  10799.  saying in the industry that "if one person can do it in one year, two people
  10800.  can do it in two years." But in a project like OS/2, although one person
  10801.  might be able to do it in 8 man years, you can't afford to wait 8 years, so
  10802.  you have to make it a multi-person project and have it take 30 man years. In
  10803.  order to reduce the inefficiency introduced by having multiple people on a
  10804.  project, it's important to break the work up properly, and to have each
  10805.  piece of work be a one man project. This project had about 35 people on it.
  10806.  
  10807.  The programmers worked on individual parts of the code, but they were
  10808.  organized into teams with a maximum of eight people each to facilitate
  10809.  coordinating their work. There was a test team, two teams of developers each
  10810.  headed by a senior engineer, a build team with four people, a test team with
  10811.  five people, and myself as the chief architect. This project was so
  10812.  complicated, especially since development work was simultaneously taking
  10813.  place at IBM in Boca Raton, Florida, that we needed a build team just to
  10814.  coordinate the sources, build the executable binaries, and otherwise bring
  10815.  organization to the physical aspects of software development. The test team,
  10816.  as its name implies, was responsible for the development of test cases, bug
  10817.  tracking, and performance measurement.
  10818.  
  10819.  MSJ: What was the most difficult part of managing the project?
  10820.  
  10821.  Letwin: This project's very large staff added to the complexity of
  10822.  integrating changes made at the IBM locations and timing the development
  10823.  between the two sites. We had to be especially careful that any changes made
  10824.  didn't affect other parts of the project. In the source code control system,
  10825.  for example, we split the source into about 200 modules, with modification
  10826.  tracking on each. We had a rule that you couldn't add a change to a module
  10827.  until you'd proven that the change wouldn't break anything. That way we
  10828.  retained a totally stable version. Without this rule, each engineer could
  10829.  have spent days looking for problems that were, in fact, introduced by
  10830.  another engineer.
  10831.  
  10832.  MSJ: IBM and Microsoft, to put it mildly, have very different corporate
  10833.  cultures and philosophies. How did you mesh the two environments during the
  10834.  development of OS/2?
  10835.  
  10836.  Letwin: First, whenever we arrived in Boca Raton, we rushed into the
  10837.  bathroom and changed into suits.
  10838.  
  10839.  But seriously, there were some times when differences of opinions were
  10840.  frustrating. Normally each company builds its own products and calls all the
  10841.  shots. But in a joint development, compromises were necessary. IBM engineers
  10842.  were slowed down by a set of rules which are intended to bring stability to
  10843.  large IBM projects. This naturally caused some conflicts. IBM-ers
  10844.  undoubtedly felt that Microsoft engineers were "wild and crazy guys."
  10845.  
  10846.  MSJ: What parts of OS/2 would you do differently if you had the choice?
  10847.  
  10848.  Letwin: A year ago it became clear we weren't going to meet our schedules,
  10849.  and we had to go through a simplification procedure. For example, we took
  10850.  out named pipes, a form of inter-process communication to send information
  10851.  to another program (now part of the OS/2 LAN Manager). We'll put that in
  10852.  another release of the product. Installable file systems were another
  10853.  victim. As a result, the hard disk file system still doesn't perform as well
  10854.  as we'd all like. These features will be added later. Also, we now know the
  10855.  parts of the system which turned out to be bigger or slower than they
  10856.  should, or which in some other way were a bit disappointing. Naturally we'd
  10857.  give them additional attention. At the end of any project, there are always
  10858.  a few things you'd like to improve. And we will, in the next release.
  10859.  
  10860.  MSJ: How does it feel to give up your product when it's ready to go to
  10861.  market?
  10862.  
  10863.  Letwin: At first, I felt relief when we got the product through testing and
  10864.  it went out the door. The last 20 percent is hard work. You just get tired
  10865.  of the product. The exciting stuff takes place in the earlier stages of the
  10866.  project. The last days are dealing with problem after problem──fixing bugs,
  10867.  making it smaller, making it faster, etc. You've worked on it so much there
  10868.  dosen't seem to be much change over the last few months. You're tired and
  10869.  all you do is deal with problems. In my opinion, it's the ability to keep
  10870.  working hard during the last part of a long project that separates the great
  10871.  people from the average. In any case, when you finally move on to something
  10872.  else, you feel a mixture of relief that it's completed, excitement about
  10873.  your new project, and anxiety about how well your efforts will be received.
  10874.  
  10875.  MSJ: What's your philosophy of programming?
  10876.  
  10877.  Letwin: Software is special at our level. Computer programming is 20 percent
  10878.  science, 60 percent engineering, and 20 percent art. The "art" element is
  10879.  the hardest to achieve, and it's the hardest to explain. When engineers talk
  10880.  about the "elegance" of their work, people usually think that such elegance
  10881.  is non-functional: the engineers are just indulging themselves. This might
  10882.  be the case in mechanical or civil engineering since an elegant bridge
  10883.  carrys no more load than an inelegant one. But it isn't so in advanced
  10884.  software development because mechanical engineering deals with physical
  10885.  objects and software engineering deals with information. Physical objects
  10886.  have properties independent of human perception, but information doesn't. In
  10887.  other words, although a bridge is equally functional whether or not it's
  10888.  elegant, information is much more useful when it's organized in a clear,
  10889.  flexible and useable form──a form a programmer would call "elegant." Bridge
  10890.  design is organized to meet just two goals──buildability and final function.
  10891.  Software is designed to meet those goals plus one more. It has to have
  10892.  minimal intellectual complexity and maximum flexibility so that it can be
  10893.  understood by the programmers who will maintain and upgrade it in the
  10894.  future.
  10895.  
  10896.  
  10897.  ───────────────────────────────────────────────────────────────────────────
  10898.  GORDON LETWIN: THE CHALLENGE OF THE 286 SPEAKS HIS LANGUAGE
  10899.  ───────────────────────────────────────────────────────────────────────────
  10900.  
  10901.  What attracted 34-year-old Gordon Letwin to computers in the late 1960s has
  10902.  also kept him in the forefront of programming today: an insatiable drive to
  10903.  overcome technical obstacles.
  10904.  
  10905.  "I'd read about computers in the late 1960s, that they were supposed to be
  10906.  super complicated. Computers held an abstract mystique. And I wanted to
  10907.  understand how that stuff worked," he says. That was a tough goal at a time
  10908.  when the cheapest computers cost $100,000 and were locked behind glass.
  10909.  Letwin taught himself FORTRAN from a manual, without access to a computer to
  10910.  try it on. While in high school he took some courses at a small private
  10911.  technical college to get free time on the school's computer.
  10912.  
  10913.  Letwin studied physics at Purdue, but found he was more interested in
  10914.  getting computer time than in studying physics. Whenever he could, he'd
  10915.  spend his day breaking through the computer's protective environments
  10916.  instead of studying physics. "Saying that there's something that I can't
  10917.  understand, or can't access because I'm not initiated into some mystery is
  10918.  like waving a flag in front of a bull," he says. He finally solved this
  10919.  conflict by changing his major to computer science.
  10920.  
  10921.  A large part of the enjoyment in computing for Letwin is getting immediate
  10922.  results. He likens computers to pinball machines. "You get positive and
  10923.  negative reinforcement right away." And, being "innately lazy," he likes the
  10924.  idea of computers doing work for him.
  10925.  
  10926.  Upon getting his Bachelor's and Master's degrees in computer science from
  10927.  Purdue, Letwin went to work for Wintek, a company run by one of his
  10928.  professors. He ended up bidding on a contract to supply Heathkit with a
  10929.  BASIC interpreter, assembler, and editor.
  10930.  
  10931.  He later moved to Michigan and went to work at Heath but found he didn't
  10932.  agree with its management's direction. "I wasn't happy with the direction of
  10933.  the company, which was run by `suits' who didn't know computers well but
  10934.  insisted on making the technical decisions anyway." When his concerns with
  10935.  Heath's approach at that time weren't improving, he signed on with
  10936.  Microsoft, then in Albuquerque, New Mexico. "I came here because of Bill
  10937.  Gates," says Letwin, who met Microsoft's chairman during sales meetings at
  10938.  Heath.
  10939.  
  10940.  Letwin became the first new employee when the firm relocated to Seattle
  10941.  about eight years ago. He started working on a BASIC compiler, then a Pascal
  10942.  compiler.
  10943.  
  10944.  When he became tired of being a "compiler compiler," he took advantage of
  10945.  his operating system experience to switch to the newly formed operating
  10946.  systems group, starting work on the IBM PC. "We thought the 8086 chip was
  10947.  the first processor powerful enough for general purpose desktop use. We had
  10948.  a vision of an office of the future. We knew that to realize this vision,
  10949.  we'd need a powerful operating system, much more sophisticated then those
  10950.  which were available at that time. Since the only way to insure its
  10951.  existence was to write it ourselves, we got into the operating system
  10952.  business." The widespread acceptance of MS-DOS makes that statement
  10953.  Microsoft's platform for its development of the single-user office
  10954.  automation operating system
  10955.  
  10956.  ████████████████████████████████████████████████████████████████████████████
  10957.  
  10958.  A Simple Windows Application for Custom Color Mixing
  10959.  
  10960.  Charles Petzold
  10961.  
  10962.  COLORSCR ("Color Scroll"), is a program for Microsoft Windows that displays
  10963.  three scroll bars in the left half of the client area, which are labeled
  10964.  "Red," "Green," and "Blue," as shown in Figure 1. As you scroll the scroll
  10965.  bars, the right half of the client area will change to the composite color
  10966.  that is produced by the mix of the three primary colors. The numeric values
  10967.  of the three primary colors are displayed under the three scroll bars.
  10968.  
  10969.  You can scroll the scroll bars by using either the mouse or the keyboard.
  10970.  COLORSCR works best when Windows is installed to use a color display, such
  10971.  as an Enhanced Graphics Adapter connected to a color monitor, but you can
  10972.  also run the program on a monochrome display.
  10973.  
  10974.  You can use COLORSCR as a development tool in experimenting with color and
  10975.  choosing attractive colors for your own Windows programs. But COLORSCR is
  10976.  most interesting for what it doesn't do.
  10977.  
  10978.  For instance, if you've done some Windows programming, you'll probably
  10979.  assume that the Red, Green, and Blue labels on top of the scroll bars and
  10980.  the color values at the bottom are displayed to the client area by using the
  10981.  TextOut function.
  10982.  
  10983.  However, COLORSCR does not use TextOut.
  10984.  
  10985.  Also, you'll probably assume that the colored rectangle at the right half of
  10986.  the client area is drawn by using the Rectangle or FillRect function.
  10987.  
  10988.  No, COLORSCR doesn't use these functions either, or any of the other GDI
  10989.  drawing functions.
  10990.  
  10991.  When COLORSCR is made into an icon, the entire surface of the icon is
  10992.  painted with the selected color. You might think that COLORSCR traps WM_SIZE
  10993.  messages to check if it's being iconed and then paints the client area
  10994.  differently if it is.
  10995.  
  10996.  Wrong again. COLORSCR does not know when it's an icon and doesn't have
  10997.  separate icon logic.
  10998.  
  10999.  In fact, COLORSCR doesn't process WM_PAINT messages and doesn't directly
  11000.  display anything to its client area. At some point the COLORSCR program does
  11001.  obtain a handle to a display context, but only for the purpose of
  11002.  determining the height of a character in the default system font.
  11003.  
  11004.  In short, COLORSCR is one of the laziest Windows programs ever. How can it
  11005.  afford to do so little? Because COLORSCR puts its children to work.
  11006.  
  11007.  
  11008.  Child Windows
  11009.  
  11010.  Child windows are separate windows that can be overlaid on the client area
  11011.  of a tiled or pop-up window. They usually have their own window functions
  11012.  separate from the parent window function. In their simplest form, child
  11013.  windows can simply divide the client area of a window into smaller
  11014.  rectangular windows, each with its own window function.
  11015.  
  11016.  For instance, the Windows MS-DOS Executive uses three child windows to
  11017.  divide its client area into sections for the drive icons, the current
  11018.  subdirectory path, and the file list. Three different window functions
  11019.  process messages from the three child windows. This simplifies painting and
  11020.  mouse processing in the MS-DOS Executive because each child window has its
  11021.  own client area coordinates.
  11022.  
  11023.  One type of child window is called a child window control, which usually
  11024.  sends messages back to its parent window (the tiled or pop-up window on
  11025.  which the child control appears) based on mouse or keyboard input to the
  11026.  control.
  11027.  
  11028.  Windows contains several predefined child window controls, which take the
  11029.  form of buttons, check boxes, edit boxes, list boxes, text strings, and
  11030.  scroll bars. Child window controls are most often used in dialog boxes; in
  11031.  such instances, the placement and size of the child window controls are
  11032.  defined in a dialog box template contained in the program's resource script.
  11033.  When you create a dialog box template, you can either calculate the position
  11034.  and sizes manually or you can use the DIALOG program included with the
  11035.  Windows Software Development Kit 1.03 (see "Latest Dialog Editor Speeds
  11036.  Windows Applications Development," MSJ, Vol. 1 No. 1).
  11037.  
  11038.  However, you can also use predefined child window controls on the surface of
  11039.  a tiled or pop-up window's client area. You create each child window with a
  11040.  CreateWindow call and adjust the position and size of the child windows with
  11041.  calls to MoveWindow.
  11042.  
  11043.  COLORSCR uses ten predefined child window controls. The color that appears
  11044.  on the right half of the window is actually the background color of the
  11045.  tiled window, as shown in Figure 2.
  11046.  
  11047.  On the left half of the client area there is a "static" child window called
  11048.  SS_WHITERECT (white rectangle) that blocks out half the client area (see
  11049.  Figure 3). The three scroll bars are child window controls with the style
  11050.  SBS_VERT (see Figure 4) placed on top of the SS_WHITERECT child. Six more
  11051.  static child windows of style SS_CENTER (centered text) provide the labels
  11052.  and the color values (see Figure 5).
  11053.  
  11054.  
  11055.  Raising the Children
  11056.  
  11057.  The COLORSCR.C source code, the COLORSCR.DEF module definition file, and the
  11058.  COLORSCR make-file are shown in Figures 6, 7, and 8, respectively. If you
  11059.  have the Microsoft C Compiler 4.0 and the Windows Software Development Kit
  11060.  installed on your hard disk, you will be able to create COLORSCR.EXE by
  11061.  executing:
  11062.  
  11063.    MAKE COLORSCR
  11064.  
  11065.  COLORSCR creates its normal tiled window and the ten child windows within
  11066.  the WinMain function through the use of the Windows CreateWindow call.
  11067.  
  11068.  The first parameter to the CreateWindow call indicates the window class of
  11069.  the window. The tiled window uses the window class called "ColorScr," which
  11070.  is defined and registered during WinMain initialization; this is perfectly
  11071.  normal. The child windows use classes called "static" (for the white
  11072.  rectangle and the text) and "scrollbar," which are the predefined child
  11073.  window classes. The window functions for these child windows are contained
  11074.  within Windows.
  11075.  
  11076.  For a tiled window, the second parameter to the CreateWindow call is the
  11077.  text that appears in the window's caption bar. For the six child windows
  11078.  that display text, this parameter is the text displayed in the child window.
  11079.  For the rectangle and scroll bars, the text is ignored.
  11080.  
  11081.  The window style parameter for all ten child windows includes the WS_CHILD
  11082.  indentifier. The window style for the three scroll bars includes the
  11083.  SBS_VERT identifier, indicating a vertical scroll bar. For the static child
  11084.  windows, the SS_CENTER identifier indicates centered text, and the
  11085.  SS_WHITERECT identifier indicates a white rectangle.
  11086.  
  11087.  The fourth through seventh parameters to the CreateWindow call normally
  11088.  indicate a position and size of the child window. The position is relative
  11089.  to the upper left-hand corner of the client area of the parent window. These
  11090.  values are all set to 0 initially because the placement and sizing depend on
  11091.  the size of the client area, which is not yet known.
  11092.  
  11093.  For child windows, the eighth parameter is the window handle of the parent,
  11094.  which is simply the hWnd value returned from the first CreateWindow call.
  11095.  The following parameter is a child ID that uniquely identifies the child
  11096.  window. We'll use this child ID later on.
  11097.  
  11098.  COLORSCR's window function (WndProc) resizes all ten child windows when it
  11099.  receives a WM_SIZE message. The formulas are based upon xClient, the width
  11100.  of the client area, yClient, the height of the client area, and yChar, the
  11101.  height of a character.
  11102.  
  11103.  Whenever you resize the COLORSCR window, the sizes of the child windows
  11104.  change proportionally. This is particularly interesting for the scroll bars.
  11105.  Depending on the dimensions of COLORSCR's window, the scroll bars can be
  11106.  long and thin or short and stubby (see Figure 9). They may look a little
  11107.  peculiar, but they work just fine.
  11108.  
  11109.  
  11110.  Scroll Bar Messages
  11111.  
  11112.  Child window controls that take the form of buttons, edit boxes, list boxes,
  11113.  and scroll bars pass messages back to the window function of the parent
  11114.  window. Buttons, edit boxes, and list boxes will post WM_COMMAND messages to
  11115.  the parent window. Scroll bar controls, however, will use the messages
  11116.  WM_VSCROLL (to scroll vertically) and WM_HSCROLL (for scrolling
  11117.  horizontally) to indicate that the scroll bar has been clicked with the
  11118.  mouse or the scroll bar thumb is being dragged.
  11119.  
  11120.  The first step in using the scroll bars is to set a range for the scroll
  11121.  bar, which is done in WinMain. Since the primary color values range from 0
  11122.  to 255, the range is set accordingly:
  11123.  
  11124.    SetScrollRange (hChScrol[n],SB_CTL,0, 255, FALSE) ;
  11125.  
  11126.  When the WndProc window function receives a WM_VSCROLL message, the high
  11127.  word of the lParam parameter is the child window ID number, which is the
  11128.  number specified in the ninth parameter to the CreateWindow call. For the
  11129.  three scroll bars, we have conveniently set that ID to a 0, 1, and 2. That
  11130.  tells WndProc which scroll bar is generating the message:
  11131.  
  11132.    n = GetWindowWord(HIWORD(lParam),GWW_ID) ;
  11133.  
  11134.  Because the handles to the child windows had been saved in arrays when the
  11135.  windows were created, WndProc can set the new value of the appropriate
  11136.  scroll bar by using the SetScrollPos call:
  11137.  
  11138.    SetScrollPos(hChScrol[n],SB_CTL,color[n],TRUE) ;
  11139.  
  11140.  and it can change the text of the child window at the bottom of the scroll
  11141.  bar:
  11142.  
  11143.    SetWindowText(hChValue[n],itoa(color[n],szbuffer,10)) ;
  11144.  
  11145.  
  11146.  Keyboard Interface
  11147.  
  11148.  Scroll bar controls can also process keystrokes, but only if they have the
  11149.  input focus. (Because only one window can receive keyboard input at any
  11150.  time, the window that receives the keyboard input is referred to as the
  11151.  window with the input focus.) The keyboard cursor keys are translated into
  11152.  the scroll bar messages:
  11153.  
  11154.                           Scroll Bar
  11155.                           Message
  11156.    Cursor Key             wParam Value
  11157.  
  11158.    Home                   SB_TOP
  11159.    End                    SB_BOTTOM
  11160.    PgUp                   SB_PAGEUP
  11161.    PgDown                 SB_PAGEDOWN
  11162.    Left-arrow             SB_LINEUP
  11163.    Up-arrow               SB_LINEUP
  11164.    Right-arrow            SB_LINEDOWN
  11165.    Down-arrow             SB_LINEDOWN
  11166.  
  11167.  In fact, the SB_TOP and SB_BOTTOM scroll messages are never generated when
  11168.  using the mouse on the scroll bar, only when using the keyboard.
  11169.  
  11170.  
  11171.  The Input Focus
  11172.  
  11173.  If you want a scroll bar control to obtain the input focus when the scroll
  11174.  bar is clicked with the mouse, you must include the WS_TABSTOP identifier in
  11175.  the window class parameter of the CreateWindow call. When a scroll bar has
  11176.  the input focus, a blinking gray block is displayed on the scroll bar
  11177.  thumb.
  11178.  
  11179.  However, to provide a full keyboard interface to the scroll bars, some more
  11180.  work is necessary. First, the WndProc window function must specifically
  11181.  give a scroll bar the input focus. It does this by processing the WM_FOCUS
  11182.  message, which the parent tiled window receives when the parent window
  11183.  obtains the input focus. WndProc simply sets the input focus to one of the
  11184.  scroll bars:
  11185.  
  11186.    SetFocus(hChScrol[nFocus]);
  11187.  
  11188.  But you also need some way to get from one scroll bar to another by using
  11189.  the keyboard, preferably with the Tab key. This is more difficult, because
  11190.  once a scroll bar has the input focus it processes all keystrokes. The
  11191.  scroll bar only cares about the cursor keys and ignores the Tab key.
  11192.  
  11193.  
  11194.  Window Subclassing
  11195.  
  11196.  Adding a facility to COLORSCR in order to jump from one scroll bar to
  11197.  another using the Tab key requires window subclassing.
  11198.  
  11199.  The window function for the scroll bar controls is somewhere inside Windows,
  11200.  but the address of this window function can be obtained by a call to
  11201.  GetWindowLong using the GWL_WNDPROC identifier as a parameter. Moreover, you
  11202.  can set a new window function for the scroll bars by calling SetWindowLong.
  11203.  
  11204.  This facility allows you to hook into the existing scroll bar window
  11205.  function, process some messages within your own program, and pass all the
  11206.  other messages to the old window function.
  11207.  
  11208.  The window function that does preliminary scroll bar message processing in
  11209.  COLORSCR is called ScrollProc and is found toward the end of the COLORSCR.C
  11210.  listing. Since ScrollProc is a function within COLORSCR that is called by
  11211.  Microsoft Windows, it must be defined as FAR PASCAL and it must be listed in
  11212.  the EXPORTS in the COLORSCR.DEF module definition file.
  11213.  
  11214.  First, to ensure that ScrollProc accesses the proper data segment, you must
  11215.  obtain a far address for the function with MakeProcInstance:
  11216.  
  11217.    lpfnScrollProc = MakeProcInstance((FARPROC)ScrollProc,hInstance);
  11218.  
  11219.  For each of the three scroll bars, COLORSCR uses GetWindowLong to obtain and
  11220.  save the address of the existing scroll bar window function:
  11221.  
  11222.    lpfnOldScr[n] =(FARPROC)GetWindowLong(hChScrol[n],GWL_WNDPROC) ;
  11223.  
  11224.  Next, it sets the new scroll bar window function:
  11225.  
  11226.    SetWindowLong(hChScrol[n],GWL_WNDPROC,(LONG)lpfnScrolProc) ;
  11227.  
  11228.  Now, the function ScrollProc gets first dibs on all messages that Windows
  11229.  sends to the scroll bar window function for the three scroll bars in
  11230.  COLORSCR (but not, of course, scroll bars in other programs). The ScrollProc
  11231.  window function simply changes the input focus to the next, or previous,
  11232.  scroll bar when it receives a Tab or Shift-Tab keystroke. It calls the old
  11233.  scroll bar window function with CallWindowProc.
  11234.  
  11235.  
  11236.  Use of Color
  11237.  
  11238.  The Windows functions that require (or return) a color value use an unsigned
  11239.  long (32-bit) integer, where the lowest three bytes specify red, green, and
  11240.  blue values ranging from 0 through 255 (see Figure 10).
  11241.  
  11242.  This results in a potential 224 (about 16 million) possible colors.
  11243.  
  11244.  This unsigned long is often referred to in the Windows documentation as
  11245.  rgbColor. The WINDOWS.H header file provides several macros for working with
  11246.  rgbColor values. The RGB macro in WINDOWS.H takes three arguments
  11247.  representing red, green, and blue values and then sticks the bytes together
  11248.  in order to form an unsigned long:
  11249.  
  11250.    #define RGB(r,g,b)(((DWORD)(b << 8 | g) = << 8)| r)
  11251.  
  11252.  Thus, the value
  11253.  
  11254.    RGB (255, 0, 255)
  11255.  
  11256.  is really 0x00FF00FF, an rgbColor value for magenta. When all three
  11257.  arguments are set to 0, the rgbColor is black; all three arguments set to
  11258.  255 yields white.
  11259.  
  11260.  C mavens will justifiably recoil upon seeing this RGB macro. The b, g, and r
  11261.  identifiers within the definition should be surrounded by parentheses to
  11262.  avoid errors when one of the parameters is an expression involving operators
  11263.  of higher precedence than << and |. You should watch out when you use
  11264.  expressions for the b, g, and r values.
  11265.  
  11266.  The GetRValue, GetGValue, and GetBValue macros extract the unsigned
  11267.  character primary color values from an unsigned long rgbColor value. These
  11268.  macros are not used in COLORSCR, but they sometimes come in handy when you
  11269.  are using Windows functions that will return rgbColor values to your
  11270.  program.
  11271.  
  11272.  The Windows display driver for the Enhanced Graphics Adapter attached to a
  11273.  color display uses only three of the four color planes of the EGA board, so
  11274.  only eight pure colors are possible. (In COLORSCR, the eight pure colors
  11275.  result when all scroll bars are either at the top or bottom positions.)
  11276.  
  11277.  Windows can display additional colors on the EGA by dithering, the creation
  11278.  of a pixel pattern that combines pixels of different pure colors. You'll
  11279.  find from experimenting on an EGA that the dithering pattern remains the
  11280.  same for every increment of four units for the red, green, and blue values.
  11281.  So, for an EGA, you have 218 or 262,144 dithered colors.
  11282.  
  11283.  On a monochrome display──such as a CGA, an EGA connected to a monochrome
  11284.  display, or a Hercules board──Windows uses 64 different patterns of white
  11285.  and black. The density of white is approximately equal to
  11286.  
  11287.    red + green + blue
  11288.    ──────────────────
  11289.         255 * 3
  11290.  
  11291.  where the red, green, and blue values range from 0 to 255.
  11292.  
  11293.  Watch out for this──if you're developing using a color display, you might
  11294.  see nothing wrong with putting a pure red object against a pure blue back-
  11295.  ground. However, on a monochrome display the pure red and pure blue
  11296.  "colors" appear to be the same.
  11297.  
  11298.  
  11299.  Background Color
  11300.  
  11301.  When COLORSCR is first executed, it sets up a window class structure, called
  11302.  wndclass, that defines certain characteristics of its window. One member of
  11303.  this structure is a handle to a brush that Windows uses to color the
  11304.  background of the client area. This starts off as a solid black brush:
  11305.  
  11306.    wndclass.hbrBackground=CreateSolidBrush(0L);
  11307.  
  11308.  CreateSolidBrush requires an rgbValue and returns a handle to a brush.
  11309.  Although the hbrBackground member of the structure refers to a background,
  11310.  in more technical terms it is the brush that Windows uses to erase the
  11311.  window's client area in preparation for repainting by the program's window
  11312.  function.
  11313.  
  11314.  When you change the settings of COLORSCR's scroll bars, the program must
  11315.  create a new brush and put the new brush handle in the window class
  11316.  structure. Just as we were able to get and set the scroll bar window
  11317.  function by using GetWindowLong and SetWindowLong, we can get and set the
  11318.  handle to this brush by using the calls GetClassWord and SetClassWord.
  11319.  
  11320.  First, it's necessary to delete the existing brush:
  11321.  
  11322.    DeleteObject(GetClassWord(hWnd,GCW_HBRBACKGROUND)) ;
  11323.  
  11324.  Then the new brush can be created and the handle inserted in the window
  11325.  class structure:
  11326.  
  11327.    SetClassWord (hWnd,GCW_HBRBACKGROUND,
  11328.       CreateSolidBrush(RGB(color[0],color[1],color[2]))) ;
  11329.  
  11330.  Now, the next time Windows recolors the background of the window, Windows
  11331.  will use this new brush. To force Windows to erase the background, we
  11332.  invalidate the entire client area:
  11333.  
  11334.    InvalidateRect(hWnd, NULL, TRUE);
  11335.  
  11336.  The TRUE (nonzero) value as the third parameter indicates that we want the
  11337.  background erased before repainting.
  11338.  
  11339.  InvalidateRect causes Windows to put a WM_PAINT message in the message queue
  11340.  of the window function. Because WM_PAINT messages are low priority, this
  11341.  message will not be processed immediately if you are still moving the scroll
  11342.  bar with the mouse or the cursor keys. Alternatively, if you want the window
  11343.  to be updated immediately after the color is changed, you could add the
  11344.  statement
  11345.  
  11346.    UpdateWindow(hWnd) ;
  11347.  
  11348.  after the InvalidateRect call. However, this slows down keyboard and mouse
  11349.  processing.
  11350.  
  11351.  COLORSCR's WndProc function doesn't process the WM_PAINT message and just
  11352.  passes it on to DefWindowProc. Windows's default processing of WM_PAINT
  11353.  messages simply involves calling BeginPaint and EndPaint to validate the
  11354.  window. Because we specified in the InvalidateRect call that the background
  11355.  should be erased first, the BeginPaint call causes Windows to generate the
  11356.  message WM_ERASEBKGND (erase background). WndProc ignores this message also.
  11357.  Windows processes it by erasing the background of the client area with the
  11358.  brush specified in the window class.
  11359.  
  11360.  Normally Windows would erase the entire client area using the window class
  11361.  brush. This would cover the 10 child windows. Windows would then have to
  11362.  send WM_PAINT messages to all 10 child windows so that they could repaint
  11363.  themselves, which would be very annoying. However, you can avoid this
  11364.  problem by using the WS_CLIPCHILDREN style value when you are first creating
  11365.  the parent window with the CreateWindow call. The WS_CLIPCHILDREN style
  11366.  prevents the parent window from painting over its children. If you then take
  11367.  the WS_CLIPCHILDREN style out of CreateWindow, you'll see a big difference
  11368.  in how COLORSCR works.
  11369.  
  11370.  Like all GDI objects, the brushes created by a program that uses
  11371.  CreateSolidBrush are not automatically deleted by Windows when the program
  11372.  terminates. So far, we've been good about deleting each brush before
  11373.  creating a new one, but when the program is about to terminate, there is
  11374.  still one final brush in the window class that we should discard. Thus,
  11375.  during the processing of the WM_DESTROY message, DeleteObject is called once
  11376.  more:
  11377.  
  11378.    DeleteObject(GetClassWord(hWnd,GCW_HBRBACKGROUND));
  11379.  
  11380.  
  11381.  Multiple Instances
  11382.  
  11383.  Normally, most Windows programs will reuse the same window class when you
  11384.  load multiple instances of the program. The window class is registered only
  11385.  if the previous instance is NULL:
  11386.  
  11387.    if (!hPrevInstance){wndclass.style = CS_HREDRAW | CS_VREDRAW;
  11388.  
  11389.        .
  11390.        .
  11391.        .
  11392.  
  11393.    }
  11394.  
  11395.  But COLORSCR must take a different approach because the background color is
  11396.  specified in the window class. If all instances of COLORSCR used the same
  11397.  window class, then each instance would alter and use the same background
  11398.  color.
  11399.  
  11400.  This problem is easy to solve: each instance simply registers its own window
  11401.  class. COLORSCR doesn't check if it's the first instance or the twentieth.
  11402.  Contrary to Windows documentation, a window class registered with the same
  11403.  name as a previously registered window class does not replace the earlier
  11404.  window class. A program simply has the option of using its own window class
  11405.  or a previously registered window class. COLORSCR takes the former
  11406.  approach.
  11407.  
  11408.  
  11409.  COLORSCR as an Icon
  11410.  
  11411.  One final mystery remains. When you make COLORSCR into an icon, the entire
  11412.  surface of the icon appears as the selected color rather than just the right
  11413.  half. Yet COLORSCR doesn't seem to have any separate icon logic.
  11414.  
  11415.  Notice that COLORSCR specifies a NULL icon in the window class:
  11416.  
  11417.    wndclass.hIcon=NULL;
  11418.  
  11419.  This indicates that COLORSCR is responsible for painting its icon. But why
  11420.  does the entire icon appear as the selected color?
  11421.  
  11422.  This is the simplest part of COLORSCR──Windows hides child windows when a
  11423.  program is iconed. The colored background is then completely uncovered. So,
  11424.  when the parent is sleeping, the children are neither seen nor heard.
  11425.  
  11426.  
  11427.  Figure 6:  COLORSCR.C
  11428.  
  11429.  /* COLORSCR.C -- Color Scroll (using child window controls) */
  11430.  
  11431.  #include <windows.h>
  11432.  #include <stdlib.h>
  11433.  
  11434.  long FAR PASCAL WndProc    (HWND, unsigned, WORD, LONG) ;
  11435.  long FAR PASCAL ScrollProc (HWND, unsigned, WORD, LONG) ;
  11436.  
  11437.  FARPROC lpfnOldScr[3] ;
  11438.  HWND    hChScrol[3], hChLabel[3], hChValue[3], hChRect ;
  11439.  short   color[3], nFocus = 0;
  11440.  
  11441.  int PASCAL WinMain (hInstance, hPrevInstance, lpszCmdLine, nCmdShow)
  11442.       HANDLE   hInstance, hPrevInstance;
  11443.       LPSTR    lpszCmdLine;
  11444.       int      nCmdShow;
  11445.       {
  11446.       MSG      msg;
  11447.       HWND     hWnd ;
  11448.       WNDCLASS wndclass ;
  11449.       FARPROC  lpfnScrollProc ;
  11450.       short    n ;
  11451.       static   char *szColorLabel[] = { "Red", "Green", "Blue" } ;
  11452.       static   char szAppName[] = "ColorScr" ;-
  11453.  
  11454.       wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
  11455.       wndclass.lpfnWndProc   = WndProc ;
  11456.       wndclass.cbClsExtra    = 0 ;
  11457.       wndclass.cbWndExtra    = 0 ;
  11458.       wndclass.hInstance     = hInstance ;
  11459.       wndclass.hIcon         = NULL ;
  11460.       wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
  11461.       wndclass.hbrBackground = CreateSolidBrush (0L) ;
  11462.       wndclass.lpszMenuName  = NULL ;
  11463.       wndclass.lpszClassName = szAppName ;
  11464.  
  11465.       if (!RegisterClass (&wndclass)) return FALSE;
  11466.  
  11467.       lpfnScrollProc = MakeProcInstance ((FARPROC) ScrollProc, hInstance) ;
  11468.  
  11469.       hWnd = CreateWindow (szAppName, " Color Scroll ",
  11470.                      WS_TILEDWINDOW | WS_CLIPCHILDREN,
  11471.                      0, 0, 0, 0, NULL, NULL, hInstance, NULL) ;
  11472.  
  11473.       hChRect = CreateWindow ("static", NULL,
  11474.                      WS_CHILD | WS_VISIBLE | SS_WHITERECT,
  11475.                      0, 0, 0, 0, hWnd, 9, hInstance, NULL) ;
  11476.  
  11477.       for (n = 0 ; n < 3 ; n++)
  11478.            {
  11479.            hChScrol[n] = CreateWindow ("scrollbar", NULL,
  11480.                          WS_CHILD | WS_VISIBLE | WS_TABSTOP | SBS_VERT,
  11481.                          0, 0, 0, 0, hWnd, n, hInstance, NULL) ;
  11482.  
  11483.            hChLabel[n] = CreateWindow ("static", szColorLabel[n],
  11484.                          WS_CHILD | WS_VISIBLE | SS_CENTER,
  11485.                          0, 0, 0, 0, hWnd, n + 3, hInstance, NULL) ;
  11486.  
  11487.            hChValue[n] = CreateWindow ("static", "0",
  11488.                          WS_CHILD | WS_VISIBLE | SS_CENTER,
  11489.                          0, 0, 0, 0, hWnd, n + 6, hInstance, NULL) ;
  11490.  
  11491.            lpfnOldScr[n] = (FARPROC) GetWindowLong (hChScrol[n],
  11492.                          GWL_WNDPROC) ;
  11493.            SetWindowLong (hChScrol[n], GWL_WNDPROC,(LONG) lpfnScrollProc) ;
  11494.  
  11495.            SetScrollRange (hChScrol[n], SB_CTL, 0, 255, FALSE) ;
  11496.            SetScrollPos   (hChScrol[n], SB_CTL, 0, FALSE) ;
  11497.            }
  11498.  
  11499.       ShowWindow (hWnd, nCmdShow) ;
  11500.       UpdateWindow (hWnd);
  11501.  
  11502.       while (GetMessage (&msg, NULL, 0, 0))
  11503.            {
  11504.            TranslateMessage (&msg) ;
  11505.            DispatchMessage  (&msg) ;
  11506.            }
  11507.       return msg.wParam ;
  11508.       }
  11509.  
  11510.  long FAR PASCAL WndProc (hWnd, iMessage, wParam, lParam)
  11511.       HWND       hWnd;
  11512.       unsigned   iMessage;
  11513.       WORD       wParam;
  11514.       LONG       lParam;
  11515.       {
  11516.       HDC        hDC ;
  11517.       TEXTMETRIC tm ;
  11518.       char       szbuffer[10] ;
  11519.       short      n, xClient, yClient, yChar ;
  11520.  
  11521.       switch (iMessage)
  11522.            {
  11523.            case WM_SIZE :
  11524.                 xClient = LOWORD (lParam) ;
  11525.                 yClient = HIWORD (lParam) ;
  11526.  
  11527.                 hDC = GetDC (hWnd) ;
  11528.                 GetTextMetrics (hDC, &tm) ;
  11529.                 yChar = tm.tmHeight ;
  11530.                 ReleaseDC (hWnd, hDC) ;
  11531.  
  11532.                 MoveWindow (hChRect, 0, 0, xClient / 2, yClient, TRUE);
  11533.  
  11534.                 for (n = 0 ; n < 3 ; n++)
  11535.                      {
  11536.                      MoveWindow (hChScrol[n],
  11537.                           (2 * n + 1) * xClient / 14, 2 * yChar,
  11538.                           xClient / 14, yClient - 4 * yChar, TRUE) ;
  11539.  
  11540.                      MoveWindow (hChLabel[n],
  11541.                           (4 * n + 1) * xClient / 28, yChar / 2,
  11542.                           xClient / 7, yChar, TRUE) ;
  11543.  
  11544.                      MoveWindow (hChValue[n],
  11545.                           (4 * n + 1) * xClient / 28,
  11546.                           yClient - 3 * yChar / 2,
  11547.                           xClient / 7, yChar, TRUE) ;
  11548.                      }
  11549.                 SetFocus (hWnd) ;
  11550.                 break ;
  11551.  
  11552.            case WM_SETFOCUS:
  11553.                 SetFocus (hChScrol[nFocus]) ;
  11554.                 break ;
  11555.  
  11556.            case WM_VSCROLL :
  11557.                 n = GetWindowWord (HIWORD (lParam), GWW_ID) ;
  11558.  
  11559.                 switch (wParam)
  11560.                      {
  11561.                      case SB_PAGEDOWN :
  11562.                           color[n] += 15 ;         /* fall through */
  11563.                      case SB_LINEDOWN :
  11564.                           color[n] = min (255, color[n] + 1) ;
  11565.                           break ;
  11566.                      case SB_PAGEUP :
  11567.                           color[n] ╤= 15 ;         /* fall through */
  11568.                      case SB_LINEUP :
  11569.                           color[n] = max (0, color[n] - 1) ;
  11570.                           break ;
  11571.                      case SB_TOP:
  11572.                           color[n] = 0 ;
  11573.                           break ;
  11574.                      case SB_BOTTOM :
  11575.                           color[n] = 255 ;
  11576.                           break ;
  11577.                      case SB_THUMBPOSITION :
  11578.                      case SB_THUMBTRACK :
  11579.                           color[n] = LOWORD (lParam) ;
  11580.                           break ;
  11581.                      default :
  11582.                           break ;
  11583.                      }
  11584.                 SetScrollPos  (hChScrol[n], SB_CTL, color[n], TRUE) ;
  11585.                 SetWindowText (hChValue[n], itoa (color[n], szbuffer, 10)) ;
  11586.  
  11587.                 DeleteObject (GetClassWord (hWnd, GCW_HBRBACKGROUND)) ;
  11588.                 SetClassWord (hWnd, GCW_HBRBACKGROUND, CreateSolidBrush
  11589.                              (RGB (color[0], color[1], color[2]))) ;
  11590.  
  11591.                 InvalidateRect (hWnd, NULL, TRUE) ;
  11592.                 break ;
  11593.  
  11594.            case WM_DESTROY:
  11595.                 DeleteObject (GetClassWord (hWnd, GCW_HBRBACKGROUND)) ;
  11596.                 PostQuitMessage (0) ;
  11597.                 break ;
  11598.  
  11599.            default :
  11600.                 return DefWindowProc (hWnd, iMessage, wParam, lParam) ;
  11601.            }
  11602.       return 0L ;
  11603.       }
  11604.  
  11605.  long FAR PASCAL ScrollProc (hWnd, iMessage, wParam, lParam)
  11606.       HWND      hWnd ;
  11607.       unsigned  iMessage ;
  11608.       WORD      wParam ;
  11609.       LONG      lParam ;
  11610.       {
  11611.       short     n = GetWindowWord (hWnd, GWW_ID) ;
  11612.  
  11613.       switch (iMessage)
  11614.            {
  11615.            case WM_KEYDOWN:
  11616.                 if (wParam == VK_TAB)
  11617.                      SetFocus (hChScrol[(n +
  11618.                           (GetKeyState (VK_SHIFT) < 0 ? 2 : 1)) % 3]) ;
  11619.                 break ;
  11620.  
  11621.            case WM_SETFOCUS:
  11622.                 nFocus = n ;
  11623.                 break ;
  11624.            }
  11625.       return CallWindowProc (lpfnOldScr[n], hWnd, iMessage, wParam,
  11626.                             lParam) ;
  11627.       }
  11628.  
  11629.  
  11630.  Figure 7:  COLORSCR.DEF
  11631.  
  11632.  NAME            ColorScr
  11633.  STUB            'WINSTUB.EXE'
  11634.  CODE            MOVABLE
  11635.  DATA            MOVABLE MULTIPLE
  11636.  HEAPSIZE        1024
  11637.  STACKSIZA       4096
  11638.  EXPORTS         WndProc
  11639.                  ScrollProc
  11640.  
  11641.  
  11642.  Figure 8:  Make-file for COLORSCR.EXE
  11643.  
  11644.  colorscr.obj  :  colorscr.c
  11645.      cl -c -d -Gsw -Os -W2 -Zpd colorscr.c
  11646.  
  11647.  colorscr.exe  :  colorscr.obj colorscr.def
  11648.      link4 colorscr, /align:16, /map /line, slibw, colorscr
  11649.      mapsym colorscr
  11650.  
  11651.  
  11652.  Figure 10:  An unsigned long (32-bit) integer where the lowest three bytes
  11653.              specify red, green, and blue values.
  11654.  
  11655.  ╔═╤═╤═╤═╤═╤═╤═╤══╤══╤═╤═╤═╤═╤═╤═╤══╤══╤═╤═╤═╤═╤═╤═╤══╤══╤═╤═╤═╤═╤═╤═╤══╗
  11656.  ║0│ │ │ │ │ │ │7 │ 8│ │ │ │ │ │ │15│16│ │ │ │ │ │ │23│24│ │ │ │ │ │ │31║
  11657.  ╚═╧═╧═╧═╧═╧═╧═╧══╧══╧═╧═╧═╧═╧═╧═╧══╧══╧═╧═╧═╧═╧═╧═╧══╧══╧═╧═╧═╧═╧═╧═╧══╝
  11658.                                                               
  11659.                                                             
  11660.                                                           
  11661.                                                             
  11662.       ╔═════╗           ╔═════╗           ╔═════╗           ╔═════╗
  11663.       ║  0  ║           ║BLUE ║           ║GREEN║           ║ RED ║
  11664.       ╚═════╝           ╚═════╝           ╚═════╝           ╚═════╝
  11665.  
  11666.  ████████████████████████████████████████████████████████████████████████████
  11667.  
  11668.  Ask Dr. Bob
  11669.  
  11670.  The Driverless Printer
  11671.  
  11672.  Dear Dr. Bob,
  11673.  Could you tell me if it is possible in a Windows application to send text
  11674.  and control characters to a printer for which there is no driver?──Wondering
  11675.  
  11676.  Dear Wondering,
  11677.  If you're using a commercial Windows app, you're in trouble; you can't print
  11678.  without a driver. If you're writing your own app, you can't set up a DC
  11679.  (display context) and use standard GDI functions like Rectangle(), BitBlt(),
  11680.  and TextOut(). But you can use DOS file I/O to open, write, and close the
  11681.  DOS PRN: device. If you do this, it's up to you to understand the language
  11682.  your printer talks──its escape codes and special control characters. This,
  11683.  of course, ties your program to that specific printer and loses the device
  11684.  independence normally provided by Windows.
  11685.  
  11686.  Math Library Functions
  11687.  
  11688.  Dear Dr. Bob,
  11689.  How can I use Microsoft C math library functions in my assembler
  11690.  program?──Math Maven
  11691.  
  11692.  Dear Maven,
  11693.  Microsoft C, Version 4.0, has several floating-point options: in-line 8087
  11694.  code, calls to an 8087 emulator, or calls to an alternative library. Before
  11695.  we tell you how to go about this task, we'll make a few disclaimers: C
  11696.  floating-point functions are meant for use by the compiler and handle
  11697.  arguments differently from standard functions, they're undocumented, and the
  11698.  fact that they work with Version 4.0 is no guarantee that they'll work with
  11699.  future versions.
  11700.  
  11701.  To figure out how the compiler uses floating point, we used the /Fc compiler
  11702.  switch to examine the compiler's assembly language output and /FPa or /FPc
  11703.  so that the compiler would produce calls to the floating-point functions
  11704.  instead of 8087 code.
  11705.  
  11706.  First, you have to initialize the library. If possible, let the regular C
  11707.  startup code handle this. If this won't work for you, look at the __CINIT
  11708.  function found in CRT0DAT.ASM from the C startup code disk. You should be
  11709.  able to pull the floating-point initialization code from this file.
  11710.  
  11711.  To find the names of the floating-point functions and the arguments that
  11712.  each function requires, write small C programs that demonstrate the
  11713.  floating-point functions you need and compile them with the /Fc switch.
  11714.  Study the assembler code and model yours after it. There are library
  11715.  functions for putting arguments onto a stack, doing math functions, and
  11716.  popping them off. Figure 1 is a sample C program and the assembler code it
  11717.  generates.
  11718.  
  11719.  You can explore other areas of compiler code this way, too. Another one you
  11720.  might want to look at is long int arithmetic.
  11721.  
  11722.  You don't have to worry about formatting your numbers because MASM's DD,
  11723.  DQ, and DT directives use the IEEE formats for the real number constants
  11724.  that the library expects.
  11725.  
  11726.  The floating-point functions documented in the C library manual, such as
  11727.  sin() and exp(), use standard C calling conventions, but before you use
  11728.  them, look at how the compiler converts all operands to double precision.
  11729.  
  11730.  Using COM2 Without COM1
  11731.  
  11732.  Dear Dr. Bob,
  11733.  I just recently ran into a problem with QuickBASIC. I opened COM2 for I/O
  11734.  and kept getting a bad filename error. The problem turned out to be that I
  11735.  did not have anything addressed as COM1 in my system. Everything worked fine
  11736.  after I installed a modem or serial card addressed as COM1.
  11737.  
  11738.  All that Microsoft product support would say is that you should not use
  11739.  COM2 if you do not have a COM1 already installed. I have designed IBM-
  11740.  compatible serial interfaces, so I know how the hardware works. To me they
  11741.  are two completely separate entities with two unique I/O addresses. Why am I
  11742.  having this problem, and why doesn't Microsoft consider it a problem? They
  11743.  told me they would not try to fix it.──Constrained
  11744.  
  11745.  Dear Constrained,
  11746.  We're on your side. The hardware for COM2 can work without COM1. It seems
  11747.  that QuickBASIC looks in the BIOS data area at RS232_BASE. This area stores
  11748.  the addresses of up to four serial communications ports. When you start your
  11749.  system, the BIOS initialization code checks for serial ports, puts in
  11750.  addresses of any it finds, and sets the rest of the locations to zero. When
  11751.  both COM1 and COM2 are present, the address of COM1 (3F8h), is in the first
  11752.  word of RS232_BASE and the address of COM2 (2F8h) is in the second word. But
  11753.  when COM2 is the only port installed, its address, 2F8h, is in the first
  11754.  word, and the second word is zero. When QuickBASIC is loaded, it looks at
  11755.  RS232_BASE to see what serial ports are present, but it only recognizes COM2
  11756.  when its address is in the second word.
  11757.  
  11758.  Here's a way to work around this. The program shown in Figure 2 will patch
  11759.  the second word of RS232_BASE with the address of COM2. You must run it
  11760.  before you run your QuickBASIC application that uses COM2. If you put this
  11761.  code at the beginning of your program, it will be executed too late to do
  11762.  any good because QuickBASIC checks for serial ports before it starts running
  11763.  your first BASIC statement.
  11764.  
  11765.  C Queries and Suggestions
  11766.  
  11767.  Dear Dr. Bob,
  11768.  I'd like to offer some suggestions and ask some questions regarding Version
  11769.  4.0 of Microsoft C. In CodeView, in order to trace through a C source
  11770.  program, one must link with the object module. This is fine, but it would
  11771.  also be nice to have CodeView trace through the C source programs that have
  11772.  modules within a library. Is this possible?
  11773.  
  11774.  A related question: does the EXEPACK linker option remove the extra
  11775.  debugging information from specified libraries? If so, this would at least
  11776.  eliminate the need to compile a file twice──once with the CodeView options
  11777.  and once without. This way I would compile everything with the CodeView
  11778.  options before creating the libraries. Then if I needed to debug a
  11779.  particular program from the library, I could link in the appropriate object
  11780.  file.
  11781.  
  11782.  While using the watch option in CodeView, I tried to enter the following
  11783.  expression:
  11784.  
  11785.    strptr[ (idx==0)
  11786.      ? 0
  11787.      : idx-1 ],s
  11788.  
  11789.  where strptr was declared as char *strptr[] and idx was an int. CodeView did
  11790.  not allow this and returned a message that ']' was missing. Am I missing
  11791.  something?
  11792.  
  11793.  Also, I often find myself setting several debugging options in CodeView
  11794.  (tracepoints, watchpoints, breakpoints, etc.), finding a bug, exiting
  11795.  CodeView, editing the source, compiling, linking, and then starting CodeView
  11796.  and having to reset the options I had before. It would be nice to have a
  11797.  save/reset option for CodeView that will save and later reset the
  11798.  tracepoints, watchpoints, and breakpoints. Here's what I suggest. I usually
  11799.  enter CodeView with the same options. There could be an environment variable
  11800.  (let's say CV) that would look something like "set CV= -w -f" so that these
  11801.  options will now be the default for entering CodeView.
  11802.  
  11803.  One more question, about malloc, free, and _memavl. If I have a program
  11804.  like the one shown in Figure 3, the last _memavl () does not reflect the
  11805.  amount of space now available from the prior free () call. This was done by
  11806.  using the small compiler model with the two options /Zi and /Od.
  11807.  
  11808.  Finally, I would like to share some tips about the MAKE facility. I now set
  11809.  up MAKE files that look like Figure 4.
  11810.  
  11811.  There might be standard include files in subdirectories \include,\gf\s,
  11812.  \gf\m, and gf\l, and standard libraries in subdirectories \lib, \gf\s,
  11813.  \gf\m, and \gf\l.──C Buff
  11814.  
  11815.  Dear C Buff,
  11816.  Yes, you are missing something: the CodeView manual, Section 4.4, which says
  11817.  CodeView supports a subset of C operators. The conditional operator ?: is
  11818.  not in the subset. That's the reason for the error.
  11819.  
  11820.  One thing we found interesting about the complex operations CodeView does
  11821.  support is that it reevaluates the entire expression each time it displays
  11822.  the watch window. This means that if you're watching an array such as
  11823.  strptr[idx], CodeView may not only watch a fixed location; when idx changes,
  11824.  you'll be watching a different element of the array. This is not what we
  11825.  expected, but we like what CodeView does.
  11826.  
  11827.  Your idea for the CodeView save/reset options is a good one. We'll pass it
  11828.  on. We, too, get tired of turning case sense off each time.
  11829.  
  11830.  Your malloc(), free(), and _memavl() question is interesting. By marking the
  11831.  block as no longer in use, free() releases the memory. But as you
  11832.  discovered, this free space does not show up immediately in _memavl().
  11833.  According to the C manual, _memavl() returns "approximately" how much memory
  11834.  is available. Because it doesn't add up the free blocks until they're
  11835.  needed, what it in fact returns is the largest size you can be sure you can
  11836.  allocate. You may, however, be able to allocate a much larger block, because
  11837.  if malloc() gets a request for a block larger than _memavl(), it goes down
  11838.  the list of free blocks joining adjacent ones, trying to make a large enough
  11839.  block. There is no way to force garbage collection analogous to the BASIC
  11840.  FRE($) function.
  11841.  
  11842.  Figure 5 is a simple function, _maxavl(), that returns the size of the
  11843.  largest possible block. We suggest using it instead of _memavl().
  11844.  
  11845.  
  11846.  Figure 1:  A sample C program and the assembler code it generates.
  11847.  
  11848.  float a,b,c,d,e;
  11849.  main()
  11850.   {a = (b+c)*(d+e) ; }
  11851.  
  11852.  lea   bx,WORD PTR _b       ; put address of b into the bx register
  11853.  call   __flds              ; push b onto fp stack
  11854.  lea   bx,WORD PTR _c
  11855.  call   __fadds             ; add c to top of stack
  11856.  lea   bx,WORD PTR _d
  11857.  call   __flds              ; push d onto fp stack
  11858.  lea   bx,WORD PTR _e
  11859.  call   __fadds             ; add e to top of stack
  11860.  call   __fmul              ; multiply top two elements of stack
  11861.                             ; product replaces previous two
  11862.  values on stack
  11863.  lea   bx,WORD PTR _a
  11864.  call   __fstsp             ; pop stack putting result in variable a
  11865.  
  11866.  
  11867.  Figure 2:  This program demonstrates how to patch the second word of
  11868.             RS232_BASE with COM2. You must run it before you run your
  11869.             QuickBASIC application that uses COM2.
  11870.  
  11871.  def seg _ &h0040
  11872.  if (peek(0) + 256*peek (1))= &h02f8 then _
  11873.  poke 2,&hf8: poke 3,&h02:_
  11874.  print "COM2 patch made"else_
  11875.  print "COM2 patch NOT made"
  11876.  
  11877.  
  11878.  Figure 3:  The last _memavl () does not reflect the amount of space now
  11879.             available from the prior free () call. This was done by using the
  11880.             small compiler model with the two options /Zi and /Od.
  11881.  
  11882.  printf ("starting available memory = %u\n",_memavl () );
  11883.  if (NULL == (ptr=malloc ((unsigned) 5000)) )
  11884.      printf("Out of memory.");
  11885.  printf("Available memory after malloc() = %u\n",_memavl () );
  11886.  free(ptr);
  11887.  printf("Available memory after free() = %u\n",_memavl () );
  11888.  
  11889.  
  11890.  Figure 4:  MAKE file template
  11891.  
  11892.  # MAKE file template -  ###############################
  11893.  # Assume the application program is called DOIT and the source
  11894.  # directory is \CUSTOMER\SOURCE. The target directory for all
  11895.  # obj and exe files is \CUSTOMER\S (which can easily be changed
  11896.  # to \CUSTOMER\M or CUSTOMER\L by setting M=specific model
  11897.  # type. The switches are:
  11898.  #
  11899.  #         M= model type S, M, or L
  11900.  #
  11901.  #          O= Compiler options (set for CodeView)
  11902.  #
  11903.  #          L= LINT_ARGS switch (currently set on)
  11904.  #            To set this off, use L=
  11905.  #
  11906.  #          I= Switch to set environment variables
  11907.  #
  11908.  #            To set this off, use I=null
  11909.  #
  11910.  #          S= Switch to force a compile
  11911.  #
  11912.  #            To force a compile, use S=null
  11913.  #
  11914.  ######################################################
  11915.  M=S
  11916.  O=/Zi/Od
  11917.  L=/DLINT_ARGS
  11918.  D=\CUSTOMER\$(M)
  11919.  I=null
  11920.  S=\include\stdio.h
  11921.  
  11922.  COMP=\bin\msc $*,$@ /A$(M) $(L) $(0);
  11923.  LINK=\bin\link $(D)\doit.obj,$(D)\doit.exe,$(D)\doit.map,/M/E
  11924.  LINKCV=$(LINK)/CO
  11925.  
  11926.  # Make sure the include and library environ vars are correctly set
  11927.  $(I):  \bin\inclib.bat
  11928.         \bin\inclib $(M)
  11929.  
  11930.  $(D)\doit.obj:       doit.c $(S)
  11931.      $(COMP)
  11932.  
  11933.  $(D)\doitcv.exe:      $(D)\doit.obj
  11934.      $(LINKCV)
  11935.  
  11936.  $(D)\doit.exe:        $(D)\doitcv.exe
  11937.      $(LINK)
  11938.      dir $(D)\doit*.*
  11939.  # End of MAKE file#####################################
  11940.  
  11941.  The \BIN\INCLIB.BAT file looks something like this:
  11942.  
  11943.  if "" == "%1" goto :default
  11944.  set INCLUDE=\include;\gf\%1
  11945.  set LIB=\gf\%1;\lib
  11946.  goto :end
  11947.  :default
  11948.  set INCLUDE=\include;\gf\s
  11949.  set LIB=\gf\s;\lib
  11950.  goto :end
  11951.  :end
  11952.  
  11953.  
  11954.  Figure 5:  A simple function, _maxavl (), that returns the size of the
  11955.             largest block that malloc () can allocate.
  11956.  
  11957.  /*
  11958.  *  _maxavl() - Return the size of the largest block that
  11959.  *              malloc() can allocate
  11960.  */
  11961.  int _maxavl() {
  11962.    register unsigned size;
  11963.    register unsigned incr;
  11964.    char   *p;
  11965.  
  11966.  /*
  11967.  * Do binary search for largest block malloc() can return.
  11968.  * Start search with a size of 32K. Keep going until the
  11969.  * increment value is reduced to 0, with size homing in on
  11970.  * its largest possible value.
  11971.  */
  11972.   for (size=incr=0x8000; incr; ) {
  11973.     incr >>= 1;   /* cut increment in half */
  11974.     if ( (p=malloc(size))==NULL )
  11975.       size -= incr; /* malloc() failed, so decrease size */
  11976.     else {
  11977.       free (p); /* malloc() succeeded, so free block */
  11978.       size += incr; /* and increase size */
  11979.        }
  11980.     }
  11981.  /* If last malloc() failed, return size-1 instead of size: */
  11982.    return( (==NULL) ? size-1 : size );
  11983.     }
  11984.  
  11985.  
  11986.  ════════════════════════════════════════════════════════════════════════════
  11987.  
  11988.  
  11989.  Vol. 2 No. 3 Table of Contents
  11990.  
  11991.  
  11992.  PLEXUS Introduces Windows-based Tools for Building Image Databases
  11993.  
  11994.  The Plexus Extended Data Processing (XDP) system integrates a UNIX-based
  11995.  file server with Microsoft Windows-based workstation software. XDP provides
  11996.  a high-level DBMS environment for creating sophisticated, graphics-oriented
  11997.  database applications.
  11998.  
  11999.  
  12000.  Porting MS-DOS Assembly Language Programs to the OS/2 Environment
  12001.  
  12002.  OS/2's protected mode precludes programs and routines written in assembly
  12003.  language from taking certain liberties allowed under real mode. This article
  12004.  offers a guide that can minimize the conversion effort necessary to insure
  12005.  programs will work under both MS-DOS and MS OS/2.
  12006.  
  12007.  
  12008.  Microsoft Windows 2.0: Enhancements Offer Developers More Control
  12009.  
  12010.  Windows 2.0 gives developers over 60 new functions, a new user interface
  12011.  with overlapping windows, and a substantially improved development
  12012.  environment. Our author uses some of the new facilities to create TILER,
  12013.  which supplies one feature missing in Windows 2.0──tiled windows.
  12014.  
  12015.  
  12016.  Keeping Up With The Real World: Speedy Serial I/O Processing
  12017.  
  12018.  Serial I/O processing under MS-DOS benefits dramatically when the BIOS
  12019.  serial communications routines available through INT 14H are replaced.
  12020.  Strategies for handling the necessary interrupts,  XON/XOFF processing,
  12021.  and reliable communications up to 38,400 baud are suggested.
  12022.  
  12023.  
  12024.  BLOWUP: A Windows Utility for Viewing and Manipulating Bitmaps
  12025.  
  12026.  Using bitmaps in the Windows environment is not trivial. The programmer
  12027.  must understand memory display contexts, how bitmaps are scaled, and how
  12028.  to transfer bitmaps via the clipboard. BLOWUP, a useful Windows tool, can
  12029.  capture and manipulate any part of the windows display.
  12030.  
  12031.  
  12032.  Increase the Performance of Your Programs  with a Math Coprocessor
  12033.  
  12034.  The performance of calculations, especially those that make extensive
  12035.  use of floating-point operations, increases significantly when the CPU is
  12036.  supplemented with an 8087/80287/80387 coprocessor. Learn how your programs
  12037.  can take advantage of these coprocessors.
  12038.  
  12039.  
  12040.  TIFF: An Emerging Standard for Exchanging Digitized Graphics Images
  12041.  
  12042.  The growing popularity of desktop publishing has created a need for the
  12043.  different vendors' software to be able to exchange digitized graphics
  12044.  images. The Tag Image File Format (TIFF) is gaining momentum as the
  12045.  possible industry standard for scanner-created digital images.
  12046.  
  12047.  
  12048.  Ask Dr. Bob
  12049.  
  12050.  
  12051.  EDITOR'S NOTE
  12052.  
  12053.  The shipment of the Microsoft OS/2 Software Development Toolkit allows
  12054.  developers to begin programming for the new operating system. In this
  12055.  issue, and in those to come, MSJ focuses on specific OS/2 programming
  12056.  topics. Article 2, "Porting MS-DOS(R) Assembly Language Programs to the
  12057.  Microsoft OS/2 Environment", offers a step by step guide to converting
  12058.  assembler code so that it will work under OS/2.
  12059.  
  12060.  The importance of the Windows environment continues to grow. More and
  12061.  more companies, such as PLEXUS (spotlighted in article 1) rely on the
  12062.  Windows environment for their workstation software environment. Our article
  12063.  on the about-to-be-released Windows 2.0 highlights why the new and enhanced
  12064.  features are of particular interest to developers. And, Charles Petzold
  12065.  continues his Windows programming series with Blowup, another small but
  12066.  interesting Windows tool.
  12067.  
  12068.  One of the dangers a publication encounters when working with prerelease
  12069.  software is the inevitable changes products go through before they
  12070.  are released. We do our best to ensure the sample code works when the
  12071.  magazine is printed. You can find the most current versions of all our
  12072.  source code, as well as full code listings that we don't have room to
  12073.  print, on the DIAL system. We will soon be posting the complete code
  12074.  listings on other popular bulletin boards. Check in upcoming issues for
  12075.  more information.
  12076.  
  12077.  Some readers of our last issue commented that our discussion of the OS/2
  12078.  DOS Environment implied that bypassing MS-DOS, writing directly to the
  12079.  hardware, and playing games with segment reisters should be punishable
  12080.  by death. Not at all! We recognize that MS-DOS has certain limitations.
  12081.  The article merely meant that OS/2 eliminates a good deal of those
  12082.  limitations and that it will no longer be necessary to bypass the
  12083.  operating system. If you have any comments, suggestions, or critiques,
  12084.  please write us and let us know.──Ed.
  12085.  
  12086.  
  12087.  Masthead
  12088.  
  12089.  JONATHAN D. LAZARUS
  12090.  Editor and Publisher
  12091.  
  12092.  EDITORIAL
  12093.  
  12094.  TONY RIZZO
  12095.  Technical Editor
  12096.  
  12097.  CHRISTINA G. DYAR
  12098.  Associate Editor
  12099.  
  12100.  JOANNE STEINHART
  12101.  Production Editor
  12102.  
  12103.  GERALD CARNEY
  12104.  Staff Editor
  12105.  
  12106.  DIANA E. PERKEL
  12107.  Editorial Assistant
  12108.  
  12109.  ART
  12110.  
  12111.  MICHAEL LONGACRE
  12112.  Art Director
  12113.  
  12114.  VALERIE MYERS
  12115.  Associate Art Director
  12116.  
  12117.  CIRCULATION
  12118.  
  12119.  WILLIAM B. GRANBERG
  12120.  Circulation Manager
  12121.  
  12122.  L. PERRIN TOMICH
  12123.  Assistant to the Publisher
  12124.  
  12125.  BETSY KAUFER
  12126.  Administrative Assistant
  12127.  
  12128.  Copyright(C) 1987 Microsoft Corporation. All rights reserved; reproduction
  12129.  in part or in whole without permission is prohibited.
  12130.  
  12131.  Microsoft Systems Journal is a publication of Microsoft Corporation, 16011
  12132.  NE 36th Way, Box 97017, Redmond, WA 98073-9717. Officers: William H.
  12133.  Gates, III, Chairman of the Board and Chief Executive Officer; Jon Shirley,
  12134.  President and Chief Operating Officer; Francis J. Gaudette, Treasurer;
  12135.  William Neukom, Secretary.
  12136.  
  12137.  Microsoft Corporation assumes no liability for any damages resulting from
  12138.  the use of the information contained herein.
  12139.  
  12140.  Microsoft, the Microsoft logo, CodeView, MS, MS-DOS, and XENIX are
  12141.  registered trademarks of Microsoft Corporation. IBM is a registered
  12142.  trademard of International Busness Machines Corporation. PageMaker is a
  12143.  registered trademark of Aldus Corporation. dBase is a registered trademark
  12144.  of Ashton-Tate. UNIX is a registered trademark of AT&T. Lotus and 1-2-3
  12145.  are registered trademarks of Lotus Development Corporation. Intel is a
  12146.  registered trademark of Intel Corporation. Macintosh is a trademark of
  12147.  of Apple Computer, Inc. Paintbrush is a registered trademark of ZSoft
  12148.  Corporation. Motorola is a registered trademark of Motorola, Inc.
  12149.  
  12150.  ████████████████████████████████████████████████████████████████████████████
  12151.  
  12152.  Plexus Introduces Windows-based Tools for Building Image Databases
  12153.  
  12154.  Kevin Strehlo
  12155.  
  12156.  When personal computers first began to make inroads into the business
  12157.  environment, database managers such as dBASE(R) were fine for what was then
  12158.  a text-only world. They gave people who weren't crack programmers the
  12159.  ability to build custom applications with reasonable, if limited,
  12160.  interfaces for the management and manipulation of textual data. But
  12161.  Microsoft(R) Windows, scanners, and laser printers have taken us from that
  12162.  text-only world into one in which we can generate and manipulate images
  12163.  intermingled with text. OCR devices even allow us to translate images of
  12164.  documents into computer graphics and text. Where are the visual database
  12165.  managers with which we can easily build custom applications in order to
  12166.  manage huge volumes of this image data?
  12167.  
  12168.  Plexus(R) thinks it has the answer: its Extended Development Environment
  12169.  (XDE), which couples Windows with a fourth-generation database language.
  12170.  Still in its beta-test stage, XDE is designed to run on Plexus's Extended
  12171.  Data Processing (XDP) System, an elaborate configuration of LAN hardware
  12172.  and software for handling an Informix-based graphics database. XDE serves
  12173.  as a tool for creating custom applications that manage graphics data with
  12174.  a sophisticated visual interface.
  12175.  
  12176.  The real beauty of XDE is its simplicity for the programmer. Writing a
  12177.  picture perfect (or rather, a perfect picture) database with it requires
  12178.  no knowledge of the event-driven mysteries of Windows, the intricacies of
  12179.  C, or the problem of finding a bitmap in an optical disk stack. It lets
  12180.  the programmer create a Windows database application without worry about
  12181.  such things as WinMain functions or WINDOWS.H files.
  12182.  
  12183.  
  12184.  Hardware and Software
  12185.  
  12186.  When it comes to system requirements, XDE and standard database managers
  12187.  part ways, however. XDE will run only on a complete XDP system, which
  12188.  means a substantial investment in hardware and software. Complete systems
  12189.  range from $75,000 to more than $1 million.
  12190.  
  12191.  At the center of XDP is a 68020-based Plexus supermicro that acts as the
  12192.  database server for an Ethernet(TM) LAN. Hooked up to the LAN are a number
  12193.  of AT-compatible workstations running Windows on high-resolution, 1,664-
  12194.  by 1,200-pixel displays. The cabinets of the system can provide as much as
  12195.  6.7Gb of magnetic disk storage and 8Gb of optical disk storage. If more
  12196.  is needed, a jukebox of optical laser disk drives can be attached to
  12197.  handle as much as 280Gb with an average access time of under 10 seconds.
  12198.  If you think that's an incredible amount of disk capacity, you're right,
  12199.  but let's face it──at 300Kb or more per compressed image, a database that
  12200.  replaces a roomful of microfiche or shelves full of technical manuals is
  12201.  going to require an incredible amount of storage.
  12202.  
  12203.  To record all of that visual information, you can attach microfiche and
  12204.  paper scanners to the workstations. To translate captured document images
  12205.  into ASCII text suitable for manipulation, a powerful OCR device handles a
  12206.  variety of fonts at a minimum speed of 60 characters per second with
  12207.  greater than 99 percent accuracy.
  12208.  
  12209.  For handling large images with dispatch, each AT workstation contains a
  12210.  board with enough memory to hold and manipulate an uncompressed 400-dpi
  12211.  image and a graphics coprocessor tailored for pixel manipulation. Having
  12212.  an entire 1.5Mb bitmap available in memory speeds up image manipulation.
  12213.  The coprocessor board also minimizes transmission times among the database
  12214.  server, the workstations, and the OCR subsystem by quickly compressing
  12215.  each image before transferring it across the Ethernet.
  12216.  
  12217.  The software at the heart of XDP is as much a mixed bag as the database
  12218.  that it supports. On the AT-compatible workstations, the system runs MS-
  12219.  DOS(R) 3.1 because of its networking hooks. The Plexus DataServer runs an
  12220.  enhanced version of the Informix relational database under UNIX(R) System V.
  12221.  Meanwhile, communication between server and workstations is handled
  12222.  according to TCP/IP protocols on an Excelan implementation of Ethernet.
  12223.  The Informix database running on the server has been extended with data
  12224.  types for handling images. A similarly extended version of the SQL
  12225.  standard for database queries (which is dubbed XESQL) allows mixed-mode
  12226.  (text and graphics) transactions. The custom applications created by XDE
  12227.  and running on the AT workstations generate the XESQL queries.
  12228.  
  12229.  
  12230.  How XDE Works
  12231.  
  12232.  The Plexus XDE consists of two components: an application called UI-Build
  12233.  (UI stands for user interface) and an extended version of the Informix 4GL
  12234.  language. UI-Build does for the overall user interface what the Microsoft
  12235.  Windows Dialog Editor does for dialog boxes (see "Latest Dialog Editor
  12236.  Speeds Windows Application Development," MSJ, Vol. 1 No. 1). It allows a
  12237.  sophisticated end-user or relatively inexperienced programmer to define
  12238.  the Windows front-end of the application interactively. After defining the
  12239.  elements of the Windows interface, the application developer uses the 4GL
  12240.  language to write the code behind the drop-down menus, buttons, image
  12241.  fields, and text fill-in boxes.
  12242.  
  12243.  The 4GL language is not strictly declarative. It is a high-level
  12244.  programming language with standard procedural capabilities, including
  12245.  IF/THEN/ELSE, CASE, FUNCTION, and VARIABLE.
  12246.  
  12247.  Upon compilation, XDE links that simple 4GL code to the appropriate
  12248.  elements of the Windows interface. XDE handles all of the tasks associated
  12249.  with building a full-blown Windows application, which are by no means
  12250.  trivial.
  12251.  
  12252.  "We try to cover up all the difficulties that Windows can bring in and
  12253.  present to the user," says Maurizio Gianola, software tools manager for
  12254.  Plexus. The visual and event-driven nature of Windows throws the unwary
  12255.  programmer up against new and strange concepts, such as client area and
  12256.  messages, he points out. "You have to know so many things to write a
  12257.  Windows application. What is the window handler? What are all those
  12258.  messages coming in? What do you do with those messages? Which ones go to
  12259.  the menu handler? Which ones go to the system?"
  12260.  
  12261.  Despite its high-level nature, the 4GL language offers rich functionality,
  12262.  including a number of graphics-oriented extensions to SQL, such as the
  12263.  capacity to adjust scanner parameters and a "magnifying glass" function
  12264.  that automatically rescales the on-screen presentation of an image from
  12265.  100 dpi to 300 dpi. And to accommodate needs that the language's
  12266.  developers didn't foresee or thought too specialized to implement,
  12267.  specialized routines can be written in C and dropped into the 4GL
  12268.  program.
  12269.  
  12270.  
  12271.  Windows and Houses
  12272.  
  12273.  To illustrate the general form and function of XDE, Gianola whipped up a
  12274.  sample real estate application: the user specifies a range of house prices
  12275.  and asks to see some listings. The application in turn queries a database
  12276.  on the Plexus server and responds by allowing the user to view matching
  12277.  listings one at a time. The address, the number of bedrooms, and,
  12278.  optionally, a scanned image of each house are displayed (see Figure 1).
  12279.  
  12280.  When you invoke UI-Build to begin creating a new application, all you see
  12281.  above the Windows icon area are the two free-floating menu bars. Figure 2
  12282.  shows the UI-Build environment a step into the process of creating the
  12283.  sample real estate application. Gianola has already named the title bar
  12284.  "Real Estate Database." Had he chosen to do so, he could have specified in
  12285.  the main UI-Build menu that the system box appear in the title bar, that
  12286.  the window be sizable and movable, and so on. Wishing to keep the
  12287.  application simple, Gianola merely chooses Menu_Bar from the Palette, adds
  12288.  the Options menu, and names the only command in that menu Show Picture.
  12289.  
  12290.  In Figure 3, Gianola has filled in the UI-Build dialog box that comes up
  12291.  when Menu is chosen from the Palette; Figure 4 shows the result after he
  12292.  has added two menu choices.
  12293.  
  12294.  Gianola next designs the forms that will pop up in the course of running
  12295.  the application. When users ask to see listings, they must use the form
  12296.  shown in Figure 5 to specify the minimum and maximum prices they wish to
  12297.  consider. The form is a Group_Box; it is labeled Price Range on screen and
  12298.  priceRange internally. Within this box Gianola has defined an Entry area,
  12299.  which he can stretch to the desired size by means of handles. He assigns
  12300.  Min as an ID for whatever text a user will input; this lets him reference
  12301.  it easily from within his 4GL program.
  12302.  
  12303.  Figure 6 shows the form that will appear when the user selects Show
  12304.  Listing from the Commands menu. Gianola has labeled the rectangle
  12305.  Available Houses, and because it is currently selected, it can be resized
  12306.  by stretching its handles. Note that he has defined two buttons in the
  12307.  lower right corner──Next and Done. To define a large area in which the
  12308.  scanned images of the houses can be displayed, he chose Image from the
  12309.  Palette. The figure shows Gianola in the process of defining fields in
  12310.  which to display the number of rooms, price, and address of the current
  12311.  house listing.
  12312.  
  12313.  Had he desired, Gianola could have included other means of interacting
  12314.  with the user. For example, he could have let the user specify the desired
  12315.  number of rooms by clicking on a toggle to enable a table of numbers drawn
  12316.  from the database and then vertically scrolling to his or her choice──much
  12317.  as point sizes are selected in the Type Specifications dialog box of
  12318.  PageMaker(R).
  12319.  
  12320.  
  12321.  Writing the Code
  12322.  
  12323.  After establishing the user interface with UI-Build, the programmer then
  12324.  uses a text editor to write the 4GL code that goes with it. Figure 7 shows
  12325.  the code that takes care of the preliminaries. The database is named
  12326.  "houses," and the predefined table in that database called "house" is used
  12327.  to define the records that will appear in windows on the AT's screen.
  12328.  Three variables are declared, one of which will serve as a flag.
  12329.  
  12330.  The first lines of the MAIN section name the help file and initialize the
  12331.  showpicture flag. Next, the program diplays the menu defined in Figures 2,
  12332.  3, and 4 and names mainMenuHdl as the routine that will deal with user
  12333.  selections of items in that menu. The first instance of the modeless form
  12334.  availHouses is brought up as well.
  12335.  
  12336.  Figure 8 shows the menu-handling routine for the main menu. When the user
  12337.  selects Show Picture from the Options menu, that choice (the internal
  12338.  IDfor it is showHousePicture) is checked by calling the XDE standard Check
  12339.  function and the showpicture flag is changed if necessary. If the user
  12340.  chooses Show Listing from Commands, the form defined in Figure 5 pops up.
  12341.  The routine creates a cursor to allow user input and selects all records
  12342.  in the database that fall within the range specified by the user. It then
  12343.  calls the function showData, which we will discuss later. Finally, since
  12344.  the user is now looking at listings, the Show Listing choice in the menu
  12345.  is disable, and the End Listings choice changes from gray to black to
  12346.  indicate it is enabled.
  12347.  
  12348.  Figure 9 shows the form handling routine availHouseHdl, which uses the 4GL
  12349.  construct called Before Input to initialize the blank form. A message
  12350.  explaining how to view the picture appears along with the Windows note
  12351.  icon and an OK box for the user to check. If the user clicks in the Next
  12352.  button, the showData function is called, while a click in Print calls the
  12353.  printHouseInfo function. The line ON photo initiates a rather nifty
  12354.  feature of XDE: if the user clicks on the empty field in which houses are
  12355.  displayed in an effort to bring up an image, a message tells him or her to
  12356.  make the appropriate menu selection. Finally, when the user is done, the
  12357.  application asks if the current listing should be printed before it
  12358.  disappears.
  12359.  
  12360.  The routine in Figure 10 performs a simple check to ensure that the user
  12361.  has filled in minimum and maximum values that make sense. If this is not
  12362.  the case, a warning message will appear.
  12363.  
  12364.  Finally, the two functions called in the earlier routines appear in
  12365.  Figure 11. The first displays the proper image and values within their
  12366.  respective fields in the current instance (availH) of the form
  12367.  availHouses. The second prints the listing at the user's request.
  12368.  
  12369.  
  12370.  Unique Approach
  12371.  
  12372.  Several unique aspects of the Plexus XDE should appeal to anyone who wants
  12373.  to build Windows applications quickly. For one thing, the system
  12374.  automatically sets up everything required by Microsoft Windows. When the
  12375.  application is compiled, it produces not only a resource file that encodes
  12376.  everything done with UI-Build, but also a WINDOW.H file that includes the
  12377.  predefined XDE functions. For example, the functions available for
  12378.  creating rich menus include gray and ungray──that is, disable and enable
  12379.  menu choices──and check and uncheck.
  12380.  
  12381.  Gianola seems especially proud of another unique feature of XDE. "Usually,
  12382.  every time you change something in the user interface, you have to
  12383.  recompile a Windows application, but we tried to avoid that." Essentially,
  12384.  Plexus bound the user interface to the program at link time rather than
  12385.  during compilation. "In other words, when you go back and touch up your
  12386.  user interface after you've gotten the application running," Gianola says,
  12387.  "you will need to link but not to recompile." This feature is actually
  12388.  almost a necessity in XDE since compilation involves two levels of
  12389.  translation──first from 4GL to C, and then from C to object code.
  12390.  Recompilation would thus be a more cumbersome task than usual.
  12391.  
  12392.  
  12393.  Applications
  12394.  
  12395.  The Plexus XDP is already in use by some firms even though XDE is not yet
  12396.  available. Such was their need for a visual approach to managing large
  12397.  volumes of image data that these companies (with the help of Plexus) wrote
  12398.  ordinary Windows programs in C to run the system. The applications created
  12399.  thus far are fairly complex and demonstrate that there will often be a
  12400.  need to enhance an application roughed out with XDE by embedding at least
  12401.  a modicum of C code. For example, one customer is using a five-workstation
  12402.  system for producing CD ROM discs to replace microfiche. One feature of
  12403.  this system enables the user to exclude parts of a page from being
  12404.  recorded by the scanner. Such refinements are beyond the capabilities of
  12405.  XDE, although Plexus plans to enhance the environment in future releases.
  12406.  
  12407.  Another Plexus customer, the regional phone company US West, uses an XDP
  12408.  system to manage the placement of ads in its yellow pages publishing
  12409.  operations. When a customer calls, ad sales representatives can quickly
  12410.  find the order through a text-oriented database search that's facilitated
  12411.  by an easy-to-use Windows interface. They can view an image of the
  12412.  advertisement on-screen.
  12413.  
  12414.  Desktop publishing ventures involving a large number of technical diagrams
  12415.  or other images would seem to be another natural application for the
  12416.  system. Since an application built with XDE resides in the Windows
  12417.  environment, retrieved images could be transferred directly into a Windows
  12418.  desktop publishing program──and rich text could be moved just as easily.
  12419.  
  12420.  Altogether, XDP seems a big improvement over comma-delimited ASCII export
  12421.  files and pasted photocopies.
  12422.  
  12423.  
  12424.  Figure 7:  DATABASE houses
  12425.  
  12426.  DEFINE
  12427.  {The record prospect is defined like the database table house }
  12428.  prospect RECORD LIKE house.*
  12429.  Min, Max INTEGER
  12430.  showpicture SMALLINT
  12431.  
  12432.  MAIN
  12433.      HELP FILE "houses.hlp"                    { F1 is request for help }
  12434.      LET showpicture = FALSE
  12435.      SHOW MENU mainMenu USING mainMenuHdl
  12436.      OPEN FORM availHouses USING availHousesHdl AS availH  { modeless form }
  12437.  END MAIN
  12438.  
  12439.  
  12440.  Figure 8:  mainMenuHdl
  12441.  
  12442.  MENUHANDLER mainMenuHdl
  12443.      ON SHOW MENU
  12444.          DISABLE IN MENU endListing
  12445.      MENU
  12446.          ON showHousePicture
  12447.              IF showhouse = FALSE THEN
  12448.                  CHECK IN MENU showHousePicture
  12449.                  LET showhouse = TRUE
  12450.              ELSE
  12451.                  UNCHECK IN MENU showHousePicture
  12452.                  LET showhouse = FALSE
  12453.              END IF
  12454.          ON showListing
  12455.              OPEN FORM priceRange USING priceRangeHdl { modal form }
  12456.              DECLARE listingCursor CURSOR FOR
  12457.                  SELECT * INTO prospect.* FROM house
  12458.                      WHERE house.price <= Max AND
  12459.                            house.price > Min
  12460.              OPEN listingCursor
  12461.              CALL showData()
  12462.              DISABLE IN MENU showListing
  12463.              ENABLE IN MENU endListing
  12464.          ON endListing
  12465.              ENABLE IN MENU showListing
  12466.              DISABLE IN MENU endListing
  12467.              CLOSE listingCursor
  12468.  END MENUHANDLER
  12469.  
  12470.  
  12471.  Figure 9:  availHouseHdl
  12472.  
  12473.  FORMHANDLER availHouseHdl()
  12474.      DEFINE
  12475.          print SMALLINT
  12476.  
  12477.      ON OPEN FORM
  12478.          DISPLAY   0, "", NULL, 0 TO FIELD price, address, photo, numOfRooms
  12479.      INPUT
  12480.          ON Done
  12481.              CLOSE FORM
  12482.          ON Next
  12483.              CALL showData()
  12484.          ON PrintAllInfo
  12485.              CALL printHouseInfo()
  12486.          ON photo
  12487.              MESSAGE
  12488.                  "To see the picture of the house check the menu selection"
  12489.                  NOTE_ICON OK_BUTTON
  12490.      END INPUT
  12491.      ON CLOSE FORM
  12492.          MESSAGE "Do you want to print this listing?"
  12493.                  NOTE_ICON YES_NO_BUTTONS RETURNING print
  12494.          IF print = YES THEN
  12495.              CALL printHouseInfo(prospect.code)
  12496.          END IF
  12497.  END FORMHANDLER
  12498.  
  12499.  
  12500.  Figure 10:  priceRangeHdl
  12501.  
  12502.  FORMHANDLER priceRangeHdl()
  12503.      INPUT BY NAME Min, Max
  12504.          ON idOk
  12505.              IF Min <= Max THEN
  12506.                  CLOSE FORM
  12507.              ELSE
  12508.                  MESSAGE "Incorrect price range!!" WARNING_ICON OK_BUTTON
  12509.              END IF
  12510.      END INPUT
  12511.  END FORMHANDLER
  12512.  
  12513.  
  12514.  Figure 11:  Other Functions
  12515.  
  12516.  FUNCTION showData()
  12517.      FETCH listingCursor
  12518.      DISPLAY BY NAME prospect.* TO FORM availH
  12519.      IF showpicture = TRUE THEN
  12520.          DISPLAY prospect.photo TO FORM availH FIELD photo
  12521.      END IF
  12522.  END FUNCTION
  12523.  
  12524.  FUNCTION printHouseInfo()
  12525.      PRINT prospect.price, prospect.address, prospect numOfRooms
  12526.      IF showpicture = TRUE THEN
  12527.          PRINT prospect.photo
  12528.      END IF
  12529.  END FUNCTION
  12530.  
  12531.  ████████████████████████████████████████████████████████████████████████████
  12532.  
  12533.  Porting MS-DOS Assembly Language Programs to the OS/2 Environment
  12534.  
  12535.  Ray Duncan
  12536.  
  12537.  OS/2 the protected-mode operating system for the 80286 microprocessor,
  12538.  offers the applications programmer a rich variety of system services and
  12539.  is comparable in many ways to the powerful real-time operating systems
  12540.  used on minicomputers and superminicomputers. It supports preemptive
  12541.  multitasking, multiple screen groups, a broad spectrum of inter-process
  12542.  communication facilities, and virtual memory management. It includes high-
  12543.  performance device drivers for the video display, keyboard, mouse, and
  12544.  serial port. It even boasts a true print spooler. In short, OS/2 provides
  12545.  a sturdy foundation for an entirely new generation of business
  12546.  applications with vastly expanded features and capabilities.
  12547.  
  12548.  Yet at first programmers will be more concerned with porting their
  12549.  existing MS-DOS tools and applications to OS/2's protected-mode
  12550.  environment than with attempting to exploit the capabilities of the new
  12551.  system. Those who write code in high-level languages, such as Microsoft(R)
  12552.  C, can simply relink and run the programs when the appropriate protected-
  12553.  mode libraries are available. But those of us who still labor in the
  12554.  vineyards of bit-banging──writing primarily in assembly language or at
  12555.  least making extensive use of assembly language subroutines to obtain
  12556.  optimum performance──face a much more extensive revision process.
  12557.  
  12558.  Here I will outline a useful strategy for porting MS-DOS(R) assembly
  12559.  language programs to OS/2. Derived from my own experience in working with
  12560.  the alpha- and beta-test versions of OS/2, this conversion process should
  12561.  not be construed as an official recommendation from Microsoft, although I
  12562.  find that it works well. The procedure consists of five steps: segmentation,
  12563.  rationalization, encapsulation, conversion, and optimization. The first
  12564.  three should be performed and tested in the MS-DOS environment. It is only
  12565.  the last two that require OS/2 and the associated programming tools.
  12566.  
  12567.  
  12568.  Segmentation
  12569.  
  12570.  The Intel(R) 80286 microprocessor can function in either of two distinct
  12571.  modes: real mode or protected virtual address mode. In real mode, the
  12572.  80286 has a 1Mb address space and behaves like an 8086 or 8088 processor,
  12573.  except that some machine instructions are more efficiently implemented and
  12574.  a few new instructions are available. MS-DOS runs on the 80286 in real mode.
  12575.  
  12576.  In protected mode, the personality of the 80286 is considerably different.
  12577.  Hardware features come into play letting an operating system manage 1Gb of
  12578.  virtual address space, isolate the memory used by one process from that
  12579.  used by another, perform fast context switching between processes and
  12580.  interrupt handlers, intercept certain privileged machine instructions, and
  12581.  detect the execution of invalid opcodes. OS/2, of course, runs in the
  12582.  80286's preferred state: protected mode.
  12583.  
  12584.  Most of the 80286's protected-mode capabilities revolve around a change in
  12585.  the way memory is addressed. In real mode, the value in a segment register
  12586.  directly corresponds to a physical memory address. In protected mode, this
  12587.  correspondence is indirect: a segment register holds a selector, which is
  12588.  an index into a table of descriptors. A descriptor defines the physical
  12589.  address and length of a memory segment, its characteristics (executable,
  12590.  read-only data, or read/write data) and access rights, and whether the
  12591.  segment is currently resident in RAM or has been swapped to disk. Each
  12592.  time a program loads a segment register or accesses memory, the 80286
  12593.  hardware checks the relevant descriptor table entry and the program's
  12594.  privilege level, generating a hardware interrupt (called a protection
  12595.  fault) if the selector or memory operation is not valid. Needless to say,
  12596.  manipulation of the descriptor table itself is reserved for the operating
  12597.  system's memory manager.
  12598.  
  12599.  This scheme of memory addressing in protected mode has two immediate
  12600.  consequences for applications programs. First, they can no longer perform
  12601.  arithmetic on the contents of segment registers (since selectors are only
  12602.  index numbers and have no direct relationship to physical memory
  12603.  addresses) or use segment registers for storing temporary values. A
  12604.  program must not load a segment register with anything but a legitimate
  12605.  selector that it received from the OS/2 loader or as a result of an OS/2
  12606.  memory allocation function call. Second, machine code ("text") and data
  12607.  must be strictly segregated from each other by placing them in separate
  12608.  segments with distinct selectors, since an executable selector is not
  12609.  writable, and vice versa.
  12610.  
  12611.  Accordingly, the first step in converting a program for OS/2 is to impose
  12612.  upon it a segmented structure compatible with the protected-mode
  12613.  environment. The program must have at least one code and one data segment,
  12614.  and it should use the special name DGROUP to declare a group containing
  12615.  the "near data" segment, stack, and local heap (if there is one). Figure 1
  12616.  shows this skeletal structure. It's best to follow the segment naming and
  12617.  ordering conventions used by the Microsoft compilers (see Figures 2 and
  12618.  3). At this stage, you should also remove or rewrite any code that
  12619.  directly manipulates segment values.
  12620.  
  12621.  Now reassemble and link your program and make sure it still works as
  12622.  expected under MS-DOS. Changing or adding segmentation often uncovers
  12623.  hidden addressing assumptions in the code, so it is best to track these
  12624.  problems down before making any other substantive changes to the program.
  12625.  
  12626.  
  12627.  Rationalization
  12628.  
  12629.  Once you've successfully segmented your program so that it can be linked
  12630.  and executed as an .EXE file under MS-DOS, the next step is to rationalize
  12631.  your code. This means converting your program into a completely well-
  12632.  behaved MS-DOS application, ruthlessly eliminating any elements that
  12633.  manipulate the peripheral device controllers directly, alter interrupt
  12634.  priorities or edit the system interrupt vector table, depend on CPU speed
  12635.  or characteristics (such as timing loops), or are incompatible with MS-
  12636.  DOS's memory management or handle-based file and record management.
  12637.  
  12638.  When an MS-DOS application directly accesses the hardware, it is usually
  12639.  in connection with display routines and keyboard handling. Caught between
  12640.  the user's demand for snappy screen interaction and the regrettably poor
  12641.  performance of the MS-DOS and IBM(R) PC ROM BIOS video drivers, most
  12642.  programmers have felt it necessary to go around the operating system and
  12643.  give the applications program complete control of the video adapter. This
  12644.  allows the screen to be updated at the maximum speed possible under MS-DOS
  12645.  but poses obvious difficulties in a multitasking, protected memory
  12646.  environment such as OS/2. For porting purposes, all routines in your
  12647.  program that write text to the display, control character attributes, or
  12648.  affect cursor shape or position should be converted into Write Handle
  12649.  (Interrupt 21H Function 40H) calls with ANSI escape sequences or ROM BIOS
  12650.  Interrupt 10H calls. Similarly, convert all keyboard operations to Read
  12651.  Handle (Interrupt 21H Function 3FH) or ROM BIOS Interrupt 16H calls.
  12652.  
  12653.  After you've expunged all hardware dependence from your program, your next
  12654.  priority is to make sure it uses system memory properly. Whereas MS-DOS
  12655.  typically hands an application all of the unoccupied memory in the system,
  12656.  this is not true of OS/2. A process is initially allocated only enough
  12657.  memory to hold its code segment and stack and to meet its declared data
  12658.  storage needs. You can make the MS-DOS loader behave in the same way that
  12659.  the OS/2 loader does by linking your application with the /CPARMAXALLOC
  12660.  switch. Alternatively, your program can execute a Set Memory Block
  12661.  (Interrupt 21H Function 4AH) call early in its initialization routine to
  12662.  release any extra memory it may have been allotted by the loader.
  12663.  
  12664.  When your program has begun its main sequence of operation, it should
  12665.  dynamically obtain and release any additional memory it may require for
  12666.  buffers and tables with the MS-DOS Interrupt 21H Functions 48H (allocate
  12667.  memory block) and 49H (free memory block). The size of any single
  12668.  allocated block should not exceed 65,536 bytes, even though MS-DOS allows
  12669.  the allocation of larger blocks; this will ensure compatibility with
  12670.  protected mode.
  12671.  
  12672.  Finally, you must turn your attention to file handling. MS-DOS takes a
  12673.  rather schizoid approach to mass storage by supporting two completely
  12674.  different sets of file and record management calls. The first set relies
  12675.  on a data structure known as a file control block (FCB), while the second
  12676.  and far more powerful group uses null-terminated (ASCIIZ) filename strings
  12677.  and 16-bit file tokens called handles. The FCB calls became obsolete with
  12678.  the advent of the hierarchical directory structure in MS-DOS 2.0, but MS-
  12679.  DOS still supports them in the name of upward compatibility, and
  12680.  unfortunately there are still programs around that use them. If your
  12681.  program is one of these, you'll have to replace every FCB file or record
  12682.  function call with its handle-based equivalent, because OS/2's protected
  12683.  mode contains no support at all for FCBs.
  12684.  
  12685.  
  12686.  Encapsulation
  12687.  
  12688.  Now that you have a well-behaved, segmented MS-DOS application in hand,
  12689.  the most painful part of the porting process is behind you. To prepare
  12690.  your program for the actual conversion to protected-mode operation, you
  12691.  should next isolate all parts of it that are specific to the host
  12692.  operating system and encapsulate them inside individual subroutines. The
  12693.  objective here is to localize the program's knowledge of the environment
  12694.  into small procedures that you can subsequently modify without affecting
  12695.  the remainder of the program.
  12696.  
  12697.  As an example of a program component that needs encapsulation, consider a
  12698.  typical call by an MS-DOS application to write a string to the standard
  12699.  output device (ordinarily the video display). MS-DOS services are invoked
  12700.  with a software interrupt, which occupies less space than a subroutine
  12701.  call, so it is common practice to code MS-DOS calls in-line, as shown in
  12702.  Figure 4. To facilitate conversion to OS/2, you should replace every
  12703.  instance of such a write to a file or device with a call to a small
  12704.  subroutine that hides the mechanics of the actual operating system
  12705.  function call, as illustrated in Figure 5.
  12706.  
  12707.  Another candidate for encapsulation, which does not necessarily involve an
  12708.  operating system function call, is the application's code to gain access
  12709.  to command line parameters, environment block variables, and the name of
  12710.  the file it was loaded from. MS-DOS divides this information between the
  12711.  program segment prefix and the environment block. Nearly every assembly
  12712.  language programmer has evolved his or her own techniques for obtaining
  12713.  these parameters when they're needed. My own solution is to write three
  12714.  subroutines that return the same information as C's argc, argv, and
  12715.  getenv: the number of command line parameters, a pointer to a specific
  12716.  parameter, and the string associated with a specific environment block
  12717.  variable, respectively.
  12718.  
  12719.  When you've finished encapsulating the system services, subject your
  12720.  program to thorough testing once more under MS-DOS. Since you are still
  12721.  working in a familiar milieu and have access to your favorite debugging
  12722.  tools, this is the best time to catch any subtle errors you may have
  12723.  introduced during the three steps discussed thus far. You'll have more
  12724.  trouble trying to uncover them once you move to the OS/2 environment.
  12725.  
  12726.  
  12727.  Conversion
  12728.  
  12729.  Now each system-dependent procedure you created during the encapsulation
  12730.  stage must be rewritten so that your program can execute under OS/2. In
  12731.  contrast to MS-DOS functions, which are actuated through software
  12732.  interrupts and use registers for parameter passing, OS/2's Application
  12733.  Program Interface functions are requested through a "far call" to a named
  12734.  entry point. Parameters are passed on the stack, along with the addresses
  12735.  of variables or structures that lie within the calling program's data
  12736.  segment and will receive any results returned by the function. The status
  12737.  of an operation is returned in register AX (0 if the function succeeded,
  12738.  an error code otherwise). All other registers are preserved.
  12739.  
  12740.  Figures 6, 7, and 8 list OS/2 services that are equivalent to selected
  12741.  MS-DOS and ROM BIOS Interrupt 21H, Interrupt 10H, and Interrupt 16H calls.
  12742.  These tables don't include MS-DOS functions related to file control blocks
  12743.  or program segment prefixes because OS/2 does not support either of these
  12744.  structures. MS-DOS TSR functions are also omitted. Since OS/2 is a true
  12745.  multitasking system, there is no need for a process to terminate in order
  12746.  to stay resident while another process is running.
  12747.  
  12748.  As you examine each encapsulation subroutine, refer to the entry for the
  12749.  appropriate function in the MS(R) OS/2 Programmer's Reference manual. All
  12750.  you have to do is write code that takes the parameters passed into the
  12751.  procedure and pushes them onto the stack in the correct order, calls the
  12752.  appropriate OS/2 function, tests the returned status, and loads any
  12753.  returned values into the appropriate registers.
  12754.  
  12755.  While working your way through the program, you must remember to declare
  12756.  each OS/2 function used as a "far external." If the function requires any
  12757.  additional storage space in the application's data segment, you must also
  12758.  create this. For some OS/2 calls (such as DOSOPEN), it will also be
  12759.  necessary to push other parameters that had no counterpart under MS-DOS.
  12760.  These extra parameters can usually be given reasonable values that will
  12761.  make their existence temporarily invisible to the remainder of the
  12762.  application.
  12763.  
  12764.  Figure 9 illustrates the final form of our sample procedure after its
  12765.  conversion for OS/2. Note especially the addition of the EXTRN statement
  12766.  and the wlen variable, as well as the simulation of the MS-DOS function
  12767.  status. This code may not be elegant, but it is the result of only a
  12768.  minimum of changes in the source file.
  12769.  
  12770.  Once you've converted all the encapsulation subroutines and added the
  12771.  necessary variables and external references, you may delete the STACK
  12772.  segment declaration from the .ASM source file and change the ASSUME
  12773.  statement for the SS register to DGROUP. Next, assemble the source file
  12774.  into a relocatable .OBJ module file with the Microsoft Macro Assembler in
  12775.  the usual manner. In order to transform the .OBJ file into a protected-
  12776.  mode .EXE file, however, you will need to supply the linker with two
  12777.  additional files: DOSCALLS.LIB and a module definition (.DEF) file for
  12778.  your application.
  12779.  
  12780.  DOSCALLS.LIB is a special type of object module library, which contains
  12781.  stub records, called dynamic link reference records, for each OS/2
  12782.  function call. Each time the linker finds a stub record that matches an
  12783.  EXTRN declaration within the program, it adds the name of the external
  12784.  routine to a table in the .EXE file header. When the program is ultimately
  12785.  loaded for execution, the table is examined, the necessary external
  12786.  procedures are brought into memory from dynamic link (.DLL) libraries (if
  12787.  they are not already resident), and the far calls within the program are
  12788.  fixed up appropriately.
  12789.  
  12790.  The .DEF file is just an ASCII text file that can be created with any line
  12791.  or screen editor. It contains directives that control various
  12792.  characteristics of a protected-mode executable, such as the module name,
  12793.  whether the file is an application or a dynamic link library, the
  12794.  attributes of each segment within the program, and the size of the stack
  12795.  (and for C programs, the size of the local heap). Figure 10 provides a
  12796.  very simple sample .DEF file.
  12797.  
  12798.  You will be pleasantly surprised to find that testing and debugging a
  12799.  newly converted protected-mode application is a relatively straightforward
  12800.  endeavor. MASM has been upgraded so that it can include line numbers in
  12801.  object modules, and the Microsoft OS/2 Software Development Kit includes a
  12802.  protected-mode version of CodeView(R), Microsoft's excellent symbolic
  12803.  debugger. In addition, the very nature of protected mode means that
  12804.  although your programs are just as prone to crashing as before, it's
  12805.  fairly difficult to crash the entire system──you won't need that big red
  12806.  switch nearly as often as you did with MS-DOS.
  12807.  
  12808.  
  12809.  Optimization
  12810.  
  12811.  Now that your program is running properly in protected mode, you will be
  12812.  anxious to smooth out some of the alterations you made for purposes of
  12813.  conversion and introduce various optimizations. There are three obvious
  12814.  categories of optimization to consider: taking advantage of the additional
  12815.  functionality of the OS/2 calls already being used, exploiting 80286-
  12816.  specific machine instructions where appropriate, and revamping the
  12817.  application in order to cash in on OS/2's multitasking, timer, virtual
  12818.  memory, and inter-process communication facilities.
  12819.  
  12820.  To make optimum use of the OS/2 function calls, you can quickly modify and
  12821.  enhance the subroutines that encapsulate them. For example, the OS/2
  12822.  DOSOPEN function allows the programmer to determine separately what action
  12823.  will be taken if the named file already exists (open, fail, or truncate to
  12824.  zero length) or does not exist (create or fail). Similarly, the OS/2 video
  12825.  driver offers performance and a variety of services far superior to the
  12826.  screen support under MS-DOS.
  12827.  
  12828.  In the case of OS/2 functions that are called only once or twice by the
  12829.  application, it may be more efficient to throw away the encapsulating
  12830.  subroutine and place the function call in-line, thus eliminating the need
  12831.  to load parameters into registers outside the subroutine and push them on
  12832.  the stack inside.
  12833.  
  12834.  If you do not intend to use the Family Application Program Interface so
  12835.  that your program will also run on 8086/88-based machines, you can add the
  12836.  directive .286c to the beginning of the source file and take advantage of
  12837.  80286-specific machine instructions. The most useful of these are the
  12838.  instructions that let you shift or rotate by an immediate count other than
  12839.  one, a three-operand multiply where one of the operands is an immediate
  12840.  (literal) value, and a push immediate value instruction. The last of these
  12841.  is particularly handy for setting up OS/2 function calls. For example, in
  12842.  Figure 9, the sequence
  12843.  
  12844.    mov  ax,offset DGROUP:wlen
  12845.    push ax
  12846.  
  12847.  could be replaced by the single instruction
  12848.  
  12849.    push offset DGROUP:wlen
  12850.  
  12851.  If you wish to restructure your application to take full advantage of
  12852.  OS/2's programmable timers, multitasking, virtual memory, inter-process
  12853.  communication, dynamic linking, and device monitors you'll have to make a
  12854.  close examination of both your application and the OS/2 Application
  12855.  Programming Interface. Such study will often pay off in sizable benefits
  12856.  in performance, maintainability, and code sharing.
  12857.  
  12858.  For instance, in many cases, different elements of an application deal
  12859.  with I/O devices of vastly different speeds, such as the keyboard, disk,
  12860.  and video display. You can both simplify the application and increase its
  12861.  performance by separating these elements into threads (subprocesses) that
  12862.  execute asynchronously, communicating through shared data structures and
  12863.  synchronizing when necessary via semaphores.
  12864.  
  12865.  As another example, when several applications are closely related and
  12866.  contain many identical or highly similar procedures, it makes sense to
  12867.  extract those procedures from the individual applications and place them
  12868.  in a private dynamic link library. This reduces the size of each
  12869.  application's .EXE file, since the dynamic link library routines are
  12870.  brought into memory and bound to the application when it's loaded. It
  12871.  allows more efficient use of memory, since the code segments in the
  12872.  dynamic library can be shared among all of the related applications when
  12873.  they are running concurrently. Best of all, using private dynamic link
  12874.  libraries vastly simplifies code maintenance, since you can debug or
  12875.  improve the routines in the libraries at any time without relinking the
  12876.  calling applications, which will automatically benefit from the new code
  12877.  the next time they are executed.
  12878.  
  12879.  
  12880.  Figure 1:  Skeleton of a properly segmented MS-DOS assembly language
  12881.             application being prepared for conversion to OS/2.
  12882.  
  12883.          name    myprog
  12884.          page    55,132
  12885.          title   MYPROG - segmentation skeleton
  12886.                                      ∙   ; miscellaneous equates
  12887.                                      ∙   ; structures, and other
  12888.                                      ∙   ; declarations go here
  12889.  DGROUP  group   _DATA                   ; 'automatic data group'
  12890.  
  12891.  _TEXT   segment byte public 'CODE'      ; all executable code
  12892.                                          ; goes in this segment
  12893.  
  12894.          assume  cs:_TEXT,ds:DGROUP,ss:STACK
  12895.  
  12896.  main    proc    far                     ; the routine that initially
  12897.                                      ∙   ; receives control can be
  12898.                                      ∙   ; called anything ...
  12899.                                      ∙
  12900.          mov     ax,4c00h                ; main routine terminates,
  12901.          int     21h                     ; passing return code=0
  12902.  
  12903.  main    endp
  12904.                                      ∙   ; other routines needed by
  12905.                                      ∙   ; the program go here
  12906.                                      ∙
  12907.  _TEXT   ends
  12908.  
  12909.  _DATA   segment word public 'DATA'      ; all read/write or static
  12910.                                          ; data items in this segment
  12911.                                      ∙
  12912.                                      ∙
  12913.                                      ∙
  12914.  _DATA   ends
  12915.  
  12916.  STACK   segment para stack 'STACK'
  12917.  
  12918.          db      256 dup (?)
  12919.  
  12920.  STACK   ends
  12921.  
  12922.          end     main                     ; declares end of module
  12923.                                           ; and initial entry point
  12924.  
  12925.  
  12926.  Figure 2:  These memory models are commonly used in assembly language and
  12927.             C programs. Microsoft FORTRAN programs uniformly use the large
  12928.             model. Microsoft C also supports a huge model, which allows
  12929.             creation of data structures larger than 64Kb.
  12930.  
  12931.  Model            Code Segments           Data Segments
  12932.  
  12933.  Small            One                     One
  12934.  Medium           Multiple                One
  12935.  Compact          One                     Multiple
  12936.  Large            Multiple                Multiple
  12937.  
  12938.  
  12939.  Figure 3:  Microsoft assembler programs use these naming conventions for the
  12940.             standard memory models. Microsoft C programs use a superset of
  12941.             these segments and classes.
  12942.  
  12943. ╓┌──────────┌─────────────┌───────┌─────────┌──────────┌─────────────────────╖
  12944.  Memory     Segment        Align    Combine    Class
  12945.  Model      Name           Type     Classes    Name        Group
  12946.  
  12947.  Small      _TEXT          byte     public     CODE
  12948.             _DATA          word     public     DATA        DGROUP
  12949.             STACK          para     stack      STACK       DGROUP
  12950.  
  12951.  Medium     module_TEXT    byte     public     CODE
  12952.               ∙
  12953.               ∙
  12954.             _DATA          word     public     DATA        DGROUP
  12955.             STACK          para     stack      STACK       DGROUP
  12956.  
  12957.  Compact    _TEXT          byte     public     CODE
  12958.             data           para     private    FAR_DATA
  12959.  Memory     Segment        Align    Combine    Class
  12960.  Model      Name           Type     Classes    Name        Group
  12961.            data           para     private    FAR_DATA
  12962.               ∙
  12963.               ∙
  12964.             _DATA          word     public     DATA        DGROUP
  12965.             STACK          para     stack      STACK       DGROUP
  12966.  
  12967.  Large      module_TEXT    byte     public     CODE
  12968.               ∙
  12969.               ∙
  12970.             data           para     private    FAR_DATA
  12971.               ∙
  12972.               ∙
  12973.             _DATA          word     public     DATA        DGROUP
  12974.             STACK          para     stack      STACK       DGROUP
  12975.  
  12976.  
  12977.  
  12978.  Figure 4:  This typical in-line code sequence for an MS-DOS service call
  12979.             writes a string to the standard output device. Since the
  12980.             standard output might be redirected to a file without the
  12981.             program's knowledge, it must also check that all of the
  12982.             requested characters were actually written. If the returned
  12983.             length is less than the requested length, this usually indicates
  12984.             that the standard output has been redirected to a disk file and
  12985.             the disk is full.
  12986.  
  12987.  stdin     equ  0              ; handle for standard input
  12988.  stdout    equ  1              ; handle for standard output
  12989.  stderr    equ  2              ; handle for standard error
  12990.                                      ∙
  12991.                                      ∙
  12992.                                      ∙
  12993.  msg       db   'This is a sample message'
  12994.  msg_len   equ  $-msg
  12995.                                      ∙
  12996.                                      ∙
  12997.                                      ∙
  12998.            mov  dx,seg msg     ; DS:DX = address of message
  12999.            mov  ds,dx
  13000.            mov  dx,offset DGROUP:msg
  13001.            mov  cx,msg_len     ; CX = length of message
  13002.            mov  bx,stdout      ; BX = file or device handle
  13003.            mov  ah,40h         ; AH = function 40h, write
  13004.            int  21h            ; transfer to MS-DOS
  13005.            jc   error          ; on return, CY=true if error
  13006.            cmp  ax,msg_len     ; were all characters written?
  13007.            jne  dev_full       ; no, output device is full
  13008.                                      ∙
  13009.                                      ∙
  13010.                                      ∙
  13011.  
  13012.  
  13013.  Figure 5:  The code in Figure 4 has here been encapsulated. The portion of
  13014.             the code that is operating system-dependent has been isolated
  13015.             inside a subroutine that is called from other points within the
  13016.             application.
  13017.  
  13018.  stdin     equ  0              ; handle for standard input
  13019.  stdout    equ  1              ; handle for standard output
  13020.  stderr    equ  2              ; handle for standard error
  13021.                                      ∙
  13022.                                      ∙
  13023.                                      ∙
  13024.  msg       db   'This is a sample message'
  13025.  msg_len   equ  $-msg
  13026.                                      ∙
  13027.                                      ∙
  13028.                                      ∙
  13029.            mov  dx,seg msg     ; DS:DX = address of message
  13030.            mov  ds,dx
  13031.            mov  dx,offset DGROUP:msg
  13032.            mov  cx,msg_len     ; CX = length of message
  13033.            mov  bx,stdout      ; BX = file or device handle
  13034.            call write          ; perform the write
  13035.            jc   error          ; on return, CY=true if error
  13036.            cmp  ax,msg_len     ; were all characters written?
  13037.            jne  dev_full       ; no, output device is full
  13038.                                      ∙
  13039.                                      ∙
  13040.                                      ∙
  13041.  write     proc near           ; Write to file or device
  13042.                                ; Call with:
  13043.                                ; BX = handle
  13044.                                ; CX = length of data
  13045.                                ; DS:DX = address of data
  13046.                                ; Returns:
  13047.                                ; If successful, Carry clear
  13048.                                ;   and AX = bytes written
  13049.                                ; If error, Carry set
  13050.                                ;   and AX = error code
  13051.  
  13052.            mov  ah,40h         ; function 40h = write
  13053.            int  21h            ; transfer to MS-DOS
  13054.            ret                 ; return status in CY and AX
  13055.  
  13056.  write     endp
  13057.                                      ∙
  13058.                                      ∙
  13059.                                      ∙
  13060.  
  13061.  
  13062.  Figure 6:  Selected MS-DOS function calls and their OS/2 counterparts. Note
  13063.             that OS/2 functions are generally much more powerful and flexible
  13064.             than their MS-DOS ancestors.
  13065.  
  13066. ╓┌───────────┌────────────────────────────────────┌──────────────────────────╖
  13067.  MS-DOS
  13068.  Interrupt
  13069.  21H
  13070.  Function    Description                          OS/2 Function
  13071.  
  13072.  00H         Terminate process                    DosExit
  13073.  01H         Keyboard input with exho             KbdCharIn
  13074.  02H         Output character to screen           VioWrtTTY
  13075.  03H         Auxiliary input                      DosRead
  13076.  04H         Auxiliary output                     DosWrite
  13077.  05H         Printer output                       DosWrite
  13078.  06H         Direct console I/O                   KbdCharIn,
  13079.              VioWrtTTY
  13080.  07H         Unfiltered input without echo        KbdCharIn
  13081.  08H         Keyboard input without echo          KbdCharIn
  13082.  09H         Output string to screen              VioWrtTTY
  13083.  0AH         Buffered keyboard input              KbdStringIn
  13084.  0BH         Get keyboard Status                  KbdPeek
  13085.  MS-DOS
  13086.  Interrupt
  13087.  21H
  13088.  Function    Description                          OS/2 Function
  13089. 0BH         Get keyboard Status                  KbdPeek
  13090.  0CH         Reset buffer and input               KbdFlushBuffer,
  13091.              KbdCharIn
  13092.  0DH         Disk reset                           DosBufReset
  13093.  0EH         Select default disk drive            DosSelectDisk
  13094.  19H         Get default disk drive               DosQCurDisk
  13095.  1BH         Get information for default drive    DosQFSInfo
  13096.  1CH         Get information for selected drive   DosQFSInfo
  13097.  2AH         Get system date                      DosGetDateTime
  13098.  2BH         Set system date                      DosSetDateTime
  13099.  2CH         Get system time                      DosGetDateTime
  13100.  2DH         Set system time                      DosSetDateTime
  13101.  2EH         Set Verify Switch                    DosSetVerify
  13102.  30H         Get MS-DOS version                   DosGetVersion
  13103.  36H         Get free disk space                  DosQFSInfo
  13104.  38H         Get or select country                DosGetCtryInfo
  13105.  39H         Create directory                     DosMkdir
  13106.  MS-DOS
  13107.  Interrupt
  13108.  21H
  13109.  Function    Description                          OS/2 Function
  13110. 39H         Create directory                     DosMkdir
  13111.  3AH         Delete directory                     DosRmdir
  13112.  3BH         Select directory                     DosChdir
  13113.  3CH         Create or truncate file              DosOpen
  13114.  3DH         Open file                            DosOpen
  13115.  3EH         Close file                           DosClose
  13116.  3FH         Read file or device                  DosRead
  13117.  40H         Write file or device                 DosWrite
  13118.  41H         Delete file                          DosDelete
  13119.  42H         Move file pointer                    DosChgFilePtr
  13120.  43H         Get or set file attributes           DosQFileMode
  13121.  44H         Device driver control (IOCTL)        DocDevIOCtl
  13122.  45H         Duplicate handle                     DosDupHandle
  13123.  46H         Force duplicate of handle            DosDupHandle
  13124.  47H         Get current subdirectory             DosQCurDir
  13125.  48H         Allocate memory block                DosAllocSeg
  13126.  49H         Release memory block                 DosFreeSeg
  13127.  MS-DOS
  13128.  Interrupt
  13129.  21H
  13130.  Function    Description                          OS/2 Function
  13131. 49H         Release memory block                 DosFreeSeg
  13132.  4AH         Resize memory block                  DosReAllocSeg
  13133.  4BH         Load and execute chils process       DosExecPgm
  13134.  4CH         Terminate process with return code   DosExit
  13135.  4DH         Get return code of child process     DosCWait
  13136.  4EH         Search for first match               DosFindFirst
  13137.  4FH         Search for next match                DosFindNext
  13138.  54H         Get verify flag                      DosQVerify
  13139.  56H         Rename file                          DosMove
  13140.  57H         Get or set file date and time        DosQFileInfo,
  13141.                                                   DosSetFileInfo
  13142.  59H         Get extended error information       DosErrClass
  13143.  5BH         Create unique file                   DosOpen
  13144.  5CH         Lock or unlock file region           DosFileLock
  13145.  
  13146.  
  13147.  Figure 7:  OS/2 video services include these equivalents to ROM BIOS
  13148.             Interrupt 10H video display driver functions used by MS-DOS
  13149.             applications.
  13150.  
  13151.  ROM BIOS
  13152.  Interrupt
  13153.  10H
  13154.  Function     Description                         OS/2
  13155.  
  13156.  00H          Select display mode                 VioSetMode
  13157.  01H          Set cursor type                     VioSetCurType
  13158.  02H          Set Cursor position                 VioSetCurPos
  13159.  03H          Get cursor position                 VioGetCurPos
  13160.  06H          Initialize or scroll window up      VioScrollUp
  13161.  07H          Initialize or scroll window down    VioScrollDn
  13162.  08H          Read character and attribute        VioReadCellStr
  13163.  09H          Write character and attribute       VioWrtNCell
  13164.  0AH          Write character                     VioWrtNChar
  13165.  0EH          Write character in teletype mode    VioWrtTTY
  13166.  0FH          Get current display mode            VioGetMode
  13167.  13H          Write string in teletype mode       VioWrtTTY
  13168.  
  13169.  
  13170.  Figure 8:  OS/2 keyboard services include these equivalents to ROM BIOS
  13171.             Interrupt 16H keyboard driver functions used by MS-DOS
  13172.             applications.
  13173.  
  13174.  ROM BIOS
  13175.  Interrupt
  13176.  16H
  13177.  Function     Description                OS/2 Function
  13178.  
  13179.  00H          Read keyboard character    KbdCharIn
  13180.  01H          Get keyboard status        KbdPeek
  13181.  02H          Get keyboard flags         KbdGetStatus
  13182.  
  13183.  
  13184.  Figure 9:  The code in Figure 5 after conversion. The equivalent OS/2
  13185.             function call has replaced the MS-DOS function call. Since
  13186.             dependence on the operating system has been confined to the
  13187.             subroutine by the previous encapsulation step, the surrounding
  13188.             program's requests for write operations should run unchanged.
  13189.             Note that the OS/2 function had to be declared as an external
  13190.             name with the "far" attribute and that a variable named when
  13191.             was added to the data segment of the application to receive the
  13192.             actual bytes written.
  13193.  
  13194.  stdin     equ  0              ; handle for standard input
  13195.  stdout    equ  1              ; handle for standard output
  13196.  stderr    equ  2              ; handle for standard error
  13197.  
  13198.            extrn DOSWRITE:far
  13199.                                      ∙
  13200.                                      ∙
  13201.                                      ∙
  13202.  msg       db   'This is a sample message'
  13203.  msg_len   equ  $-msg
  13204.  wlen      dw   ?
  13205.                                      ∙
  13206.                                      ∙
  13207.                                      ∙
  13208.            mov  dx,seg msg     ; DS:DX = address of message
  13209.            mov  ds,dx
  13210.            mov  dx,offset DGROUP:msg
  13211.            mov  cx,msg_len     ; CX = length of message
  13212.            mov  bx,stdout      ; BX = file or device handle
  13213.            call write          ; perform the write
  13214.            jc   error          ; on return, CY=true if error
  13215.            cmp  ax,msg_len     ; were all characters written?
  13216.            jne  dev_full       ; no, output device is full
  13217.                                      ∙
  13218.                                      ∙
  13219.                                      ∙
  13220.  write     proc near           ; Write to file or device
  13221.                                ; Call with:
  13222.                                ; BX = handle
  13223.                                ; CX = length of data
  13224.                                ; DS:DX = address of data
  13225.                                ; Returns:
  13226.                                ; If successful, Carry clear
  13227.                                ;   and AX = bytes written
  13228.                                ; If error, Carry set
  13229.                                ;   and AX = error code
  13230.  
  13231.            push bx             ; handle
  13232.            push ds             ; long address of data
  13233.            push dx
  13234.            push cx             ; length of data
  13235.            push ds             ; address of variable to
  13236.            mov  ax,offset DGROUP:wlen    ; receive length written
  13237.            push ax
  13238.            call DOSWRITE       ; transfer to OS/2
  13239.            or   ax,ax          ; did write succeed?
  13240.            jnz  writerr        ; jump, error was returned
  13241.            mov  ax,wlen        ; no error, OR cleared CY
  13242.            ret                 ; and AX := bytes written
  13243.  
  13244.  writerr:  stc                 ; write error, return CY set
  13245.            ret                 ;   and AX = error number
  13246.  
  13247.  write     endp
  13248.                                      ∙
  13249.                                      ∙
  13250.                                      ∙
  13251.  
  13252.  
  13253.  Figure 10:  This simple module definition (.DEF) file defines MYPROG.EXE
  13254.              as a protected-mode application rather than a dynamic link
  13255.              library and declares that the code segment is movable and
  13256.              discardable (a fresh copy can be loaded when needed, so
  13257.              swapping is not necessary), the data segment is movable and
  13258.              swappable but not discardable (it contains read/write data
  13259.              items), and the stack size is 4,096 bytes.
  13260.  
  13261.  NAME MYPROG
  13262.  PROTMODE
  13263.  DATA MOVEABLE
  13264.  CODE MOVEABLE PURE
  13265.  STACKSIZE 4096
  13266.  
  13267.  ████████████████████████████████████████████████████████████████████████████
  13268.  
  13269.  Microsoft Windows 2.0: Major Enhancements Offer More Control
  13270.  
  13271.  ───────────────────────────────────────────────────────────────────────────
  13272.  Also see the related article:
  13273.    The Beta Version of Windows 2.0 Update
  13274.  ───────────────────────────────────────────────────────────────────────────
  13275.  
  13276.  Michael Geary
  13277.  
  13278.  Windows Version 2.0 is Microsoft(R)'s first major revision of its windowed
  13279.  operating environment. This updated version features the new overlapping-
  13280.  window user interface that will be used in the OS/2 presentation manager.
  13281.  Unlike the presentation manager, however, Windows 2.0 uses the same
  13282.  programming interface as Windows 1.x, albeit with a number of new
  13283.  functions and messages, and retains, for the most part, upward
  13284.  compatibility with existing Windows applications. Most .EXE files will run
  13285.  with no alterations. Yet source code will require some minor revisions to
  13286.  iron out several incompatibilities and bring it in line with changes made
  13287.  to the application style.
  13288.  
  13289.  Once you convert to Windows 2.0, you'll be rewarded with a fine assortment
  13290.  of new function calls and messages that make Windows a much more flexible,
  13291.  capable product for the applications developer.
  13292.  
  13293.  
  13294.  User Interface
  13295.  
  13296.  The most obvious change in Windows 2.0 is, of course, the new user
  13297.  interface. (For a closer look at the features of this interface, see "OS/2
  13298.  Windows Presentation Manager: Microsoft Windows on the Future," MSJ Vol. 2,
  13299.  No. 2.) For the most part, this won't affect your programs. If an
  13300.  application ran in a tiled window, it will now simply use an overlapping
  13301.  window instead──and most likely will not even notice the difference.
  13302.  However, you should make several changes in the menu structure and
  13303.  keyboard accelerators so that your application will conform to the new
  13304.  user interface guidelines.
  13305.  
  13306.  Applications should no longer add menu items to the System menu. At the
  13307.  bottom of the File menu, add a separator line and the menu item Exit,
  13308.  which will allow the user to exit the application. Then move the
  13309.  "About..." item from the System menu to the end of the File menu,
  13310.  appending the name of your application here. Help menu items should now be
  13311.  flush right in the menu bar, and there is a new HELP dialog option to
  13312.  allow this. The Edit menu items remain unchanged, but the keyboard
  13313.  accelerators are different and are also shown in a different manner.
  13314.  Therefore, you should change your resource file and any special code you
  13315.  may have to handle the editing keys. Figure 1 gives the editing key
  13316.  assignments.
  13317.  
  13318.  You can now specify which letter of each menu item can be used in
  13319.  combination with the Alt key as a shortcut for making a menu selection.
  13320.  Simply insert an ampersand (&) in the item name preceding the shortcut
  13321.  letter, and Windows will underline it. For examples of this, see Figure 2,
  13322.  which shows a portion of an .RC file in the style recommended for the File
  13323.  and Edit menus.
  13324.  
  13325.  You can also add keyboard mnemonics to your dialog boxes. As with menu
  13326.  items, insert an ampersand at the appropriate point in the text of the
  13327.  dialog item. For push buttons, radio buttons, and check boxes, you can put
  13328.  a shortcut letter right in the name. You can't do this with an edit
  13329.  control, of course, but you can instead put a shortcut letter in an
  13330.  adjacent static text control. When the user presses the shortcut key, it's
  13331.  interpreted as a tab to the next tab stop after the static control.
  13332.  
  13333.  
  13334.  MDI
  13335.  
  13336.  A major new style recommendation for both Microsoft Windows 2.0 and the
  13337.  OS/2 presentation manager is the Multiple Document Interface (MDI). This
  13338.  is a method for using child windows in an application that works on
  13339.  multiple documents or multiple views of one document. Instead of creating
  13340.  a top-level overlapping window for each document or view, the application
  13341.  creates a single main window called the application workspace, which
  13342.  contains a child window for each document or view. These child windows can
  13343.  overlap and can be moved, sized, minimized, and maximized, just like top-
  13344.  level windows. The difference, of course, is that all the child windows
  13345.  are clipped at the edge of the workspace window.
  13346.  
  13347.  The active child window has its title bar highlighted along with the
  13348.  parent window's title bar. Each child window has a System menu, but none
  13349.  can have its own menu bar. The workspace window contains the menu for the
  13350.  entire application. If different menus are needed for the different child
  13351.  windows, the application should change or add items in the workspace
  13352.  window's menu as appropriate.
  13353.  
  13354.  Any child window can be maximized, that is, enlarged to fill up the
  13355.  workspace window, hiding all the other child windows. The user can still
  13356.  flip through the different child windows, but each one in turn becomes
  13357.  maximized. The workspace window has a pop-up Window menu, which lists all
  13358.  the child windows so that any one can be easily selected, whether it's
  13359.  visible or not.
  13360.  
  13361.  For example, a sequencer program for controlling musical instruments via
  13362.  the Musical Instrument Digital Interface (MIDI) might have these child
  13363.  windows in its application workspace:
  13364.  
  13365.    ■  conventional music (staff) notation
  13366.    ■  piano-roll notation
  13367.    ■  a piano keyboard
  13368.    ■  synthesizer voice editing
  13369.    ■  miscellaneous windows for tempo control, score formatting, key
  13370.       signature, MIDI options, and so on.
  13371.  
  13372.  If these were all independent top-level windows, it would be a real chore
  13373.  for the user to switch from the sequencer program to, say, Windows Write.
  13374.  There would be a good half-dozen windows cluttering up the screen. With
  13375.  the Multiple Document Interface, these windows would all be contained in a
  13376.  single application workspace window, and the user could get them all out
  13377.  of the way by simply minimizing the application window (that is, by changing
  13378.  it to an icon).
  13379.  
  13380.  Thus, MDI helps organize applications as independent entities, cleanly
  13381.  separating the windows of different programs. This will become more
  13382.  important as users really start to make use of the multitasking abilities
  13383.  of Windows and OS/2.
  13384.  
  13385.  Although Windows provides several new features that let developers create
  13386.  possible MDI applications, such applications require a fair amount of code
  13387.  to support MDI. The Windows 2.0 Application Style Guide details what's
  13388.  needed in an MDI application.
  13389.  
  13390.  
  13391.  Scheduling and Input
  13392.  
  13393.  Windows 2.0 has nearly 60 new functions, and several of the old functions
  13394.  have been extended with new options. Many of the changes were made to open
  13395.  up the Windows architecture, giving the application designer more control.
  13396.  Many things that just couldn't be done in earlier versions of Windows are
  13397.  now simple function calls.
  13398.  
  13399.  One change is relatively invisible to applications but quite welcome to
  13400.  the developer: pulling down a menu no longer stops scheduling, so
  13401.  applications continue to run when a menu is pulled down. But what of those
  13402.  cases in which an application really needs to turn off scheduling? For
  13403.  example, a forms-design program may want to let the user drag a selection
  13404.  rectangle across the entire screen when creating a pop-up window. The
  13405.  program can't yield to other applications, because it is temporarily
  13406.  writing into their screen space.
  13407.  
  13408.  In Windows 1.x, there was no way to turn off scheduling and still be able
  13409.  to determine when the mouse button was released. You couldn't use
  13410.  PeekMessage or GetMessage, because they would yield to other applications.
  13411.  Nor would GetKeyState work, because this gives you the key state at the
  13412.  time of the last message, not the real-time key state.
  13413.  
  13414.  Now, both of these methods are available. A new option in the PeekMessage
  13415.  function lets you choose not to yield to other applications. Complementing
  13416.  this is a new GetAsyncKeyState function, which resembles GetKeyState but
  13417.  gives you the real-time key state that is not synchronized with the
  13418.  message queue.
  13419.  
  13420.  A related function for keyboard control is GetKeyboardState, which gives
  13421.  you the current state of all 256 virtual keys in a buffer you provide. Its
  13422.  inverse, SetKeyboardState, lets you set the current state of the 256
  13423.  virtual keys.
  13424.  
  13425.  Another useful function is GetInputState, which returns a Boolean value
  13426.  indicating whether any mouse, keyboard, or timer events are waiting in the
  13427.  system queue. And if the old limit of eight messages in your application's
  13428.  queue isn't enough, you can call SetMessageQueue to change your queue
  13429.  size.
  13430.  
  13431.  Rounding out the input functions are a couple of handy Get functions.
  13432.  GetCapture tells you which window (if any) has captured the mouse with
  13433.  SetCapture. GetCaretPos is the complement of SetCaretPos, returning the
  13434.  current position of the blinking caret. (Actually, GetCaretPos has always
  13435.  been available in Windows──it just wasn't documented until now.)
  13436.  
  13437.  
  13438.  Creating Windows
  13439.  
  13440.  The CreateWindow function has some new style bits. Since a window can now
  13441.  have Minimize and Maximize icons in its title bar, there are style bits
  13442.  for these. Also, some of the other style flags have acquired new names. If
  13443.  you feel silly using WS_TILEDWINDOW when your window is no longer tiled,
  13444.  you can use the more accurate but functionally identical
  13445.  WS_OVERLAPPEDWINDOW.
  13446.  
  13447.  More important, you can now specify the initial location and size of a
  13448.  top-level window. You're no longer at the mercy of the tiling algorithm.
  13449.  If you want to create two windows side by side, you can do it. You can
  13450.  still let Windows assign a default size and location, though. You just use
  13451.  the special value CW_USEDEFAULT (which happens to be 0x8000) for the
  13452.  position and size parameters to CreateWindow.
  13453.  
  13454.  Now we come to a slight anomaly. A Windows application certainly isn't
  13455.  passing 0x8000 for its position and size. More likely, it's passing zeros
  13456.  for all four values, thinking it's creating a tiled window. Windows
  13457.  assigns default values in this case, just as it used to. But, if you try
  13458.  passing zeros to CreateWindow in your Windows 2.0 application, you'll get
  13459.  a very tiny window instead. What's going on here? There certainly aren't
  13460.  two different CreateWindow functions.
  13461.  
  13462.  The key here is that the Resource Compiler sets a version number flag in
  13463.  your .EXE file, and Windows uses that to decide how to interpret some
  13464.  function parameters, such as the position and size in CreateWindow. If it
  13465.  encounters a Windows 1.x application, it ignores the position and size as
  13466.  before. But if it sees the 2.0 flag, it pays attention to those parameters.
  13467.  
  13468.  
  13469.  Window Management
  13470.  
  13471.  The window management capability of Microsoft Windows 2.0 really made me
  13472.  happy, since Windows 1.x is frustratingly limiting in this area at times.
  13473.  When using Windows 1.x, I knew it was maintaining a linked list of my
  13474.  windows, but I couldn't manipulate the list at all. For one of my own
  13475.  programs, this was a definite problem. I really needed a way to change the
  13476.  order of windows in the list and step through the list backwards and
  13477.  forwards as the dialog manager does. Previous versions of Windows just
  13478.  don't allow that. The only functions that come close are BringWindowToTop
  13479.  and EnumWindows.
  13480.  
  13481.  Windows 2.0 gives you all kinds of direct access to the window list. Let's
  13482.  start with GetWindow and GetNextWindow. These functions take a window
  13483.  handle and return your choice of next sibling, previous sibling, first
  13484.  sibling, last sibling, parent, or first child. And you can navigate in the
  13485.  same way that the dialog manager does. Next, we have SetWindowPos, an all-
  13486.  purpose window rearranger. It duplicates some of the functions of
  13487.  MoveWindow and ShowWindow but also has the capability to change the
  13488.  ordering of the window list. If you have Windows A and B, for example, you
  13489.  can place window A after (underneath) window B and even resize and move
  13490.  the window at the same time.
  13491.  
  13492.  If that's not enough, there's SetParent. This lets you change the parent
  13493.  of a child window──you can actually move a child window from one parent to
  13494.  another.
  13495.  
  13496.  Next, we have ShowOwnedPopups, which lets you show or hide all the pop-up
  13497.  windows associated with a window. Similarly, with ShowScrollBar you can
  13498.  show or hide a scroll bar (or both scroll bars) without having to fiddle
  13499.  with the scroll bar range.
  13500.  
  13501.  To round out window management, there are a few more functions.
  13502.  GetTopWindow returns the topmost child of a parent window, GetWindowTask
  13503.  returns the task handle for a window, and EnumTaskWindows resembles
  13504.  EnumWindows but enumerates only windows belonging to a specified task
  13505.  handle.
  13506.  
  13507.  
  13508.  Window Drawing
  13509.  
  13510.  Windows 2.0 contains several new GDI functions. Most important of these
  13511.  are EnumMetaFile and PlayMetaFileRecord, which together are the equivalent
  13512.  of PlayMetaFile. EnumMetaFile, however, calls a function you provide
  13513.  instead of just playing the file. It passes each metafile record to your
  13514.  function, where you can do whatever you want with it. If you just want to
  13515.  play it, call PlayMetaFileRecord. The new Microsoft Windows Software
  13516.  Development Kit documentation gives all the details of the metafile
  13517.  format, another case in which Microsoft has really opened up the Windows
  13518.  architecture.
  13519.  
  13520.  The TextOut function now lets you specify how the text will be aligned
  13521.  with the x-y starting point. Previously, that point could correspond only
  13522.  to the top left corner of the text, but now you have the choice of top
  13523.  left, top right, left center, top center, bottom center, or font baseline.
  13524.  You can also specify whether TextOut updates GDI's current position. These
  13525.  options form the text alignment flag and are part of the new SetTextAlign
  13526.  function. (The calling sequence for TextOut itself isn't changed.) There's
  13527.  also a GetTextAlign function to retrieve the current text alignment flag.
  13528.  
  13529.  DrawText has a couple of new options, too. There's a DT_CALCRECT option,
  13530.  which updates the formatting rectangle to show how much space DrawText
  13531.  actually used. Also, DrawText now interprets the ampersand as a flag to
  13532.  underline the next character; if you want to display an ampersand, you
  13533.  need to use two of them (&&). You can defeat this action with the
  13534.  DT_NOPREFIX option.
  13535.  
  13536.  For the font mapper, there's a new SetMapperFlags function. This lets you
  13537.  alter the font mapper's algorithm, determining how much priority it places
  13538.  on matching the aspect ratios of the font and the output device.
  13539.  
  13540.  There's one new graphics output function, Chord, which is the same as Arc
  13541.  except it draws the line segment as well as the ellipse segment, filling
  13542.  in the enclosed area with the current brush. For bitmaps, there's
  13543.  CreateDiscardableBitmap, which duplicates CreateCompatibleBitmap but makes
  13544.  the bitmap discardable.
  13545.  
  13546.  A new scrolling function, ScrollDC, is similar to ScrollWindow but allows
  13547.  more control over the scrolling. Instead of letting the uncovered area
  13548.  accumulate in the window's update region, it passes the uncovered region
  13549.  and its bounding rectangle back to the caller.
  13550.  
  13551.  Finally, there are two new region functions. ExcludeUpdateRgn is related
  13552.  to ExcludeClipRect, but it removes a window's update region from a
  13553.  clipping region. This could be useful when you're doing some drawing in
  13554.  advance of a WM_PAINT message and want to draw only in the region that
  13555.  will not be taken care of by the WM_PAINT. SetRectRgn is just like
  13556.  CreateRectRgn, but instead of allocating a new region, it takes an
  13557.  existing region and sets it to the specified rectangle.
  13558.  
  13559.  
  13560.  Dialog Boxes
  13561.  
  13562.  Have you ever wished you could create a dialog box on the fly at run time
  13563.  instead of having to use a dialog template from the resource file?
  13564.  Previously you had to give explicit CreateWindow calls for all the child
  13565.  windows in the dialog box. There was no way you could just set up a dialog
  13566.  template data structure and let it run.
  13567.  
  13568.  Now two new functions let you do just that. CreateDialogIndirect and
  13569.  DialogBoxIndirect correspond to CreateDialog and DialogBox, but instead of
  13570.  taking a resource from your .EXE file, they take a pointer to a dialog
  13571.  template in memory. (Actually, DialogBoxIndirect takes a global handle,
  13572.  and CreateDialogIndirect takes a far pointer.) You are no longer forced to
  13573.  choose between using a canned dialog box or using dozens of CreateWindow
  13574.  calls.
  13575.  
  13576.  Two other functions, GetNextDlgGroupItem and GetNextDlgTabItem, let you
  13577.  scan items the way the dialog manager does in response to the arrow and
  13578.  tab keys. In fact, I'd wager that these functions were there all
  13579.  along──because the dialog manager needed them──but were never made publicly
  13580.  available. This seems to be yet another example of the architecture being
  13581.  opened up to let you get at things previously hidden.
  13582.  
  13583.  
  13584.  Control Windows
  13585.  
  13586.  There aren't too many changes in the various types of control windows, but
  13587.  radio buttons have been given additional functionality. You can now create
  13588.  an automatic radio button. This is just like any other radio button,
  13589.  except that when clicked it automatically checks itself and unchecks all
  13590.  the other radio buttons in its group. There's always been a
  13591.  CheckRadioButton function, of course, but this was for dialog boxes only.
  13592.  If you wanted to add a group of radio buttons to some other kind of
  13593.  window, you had to write your own code to check and uncheck them. Now the
  13594.  buttons are smart enough to take care of themselves.
  13595.  
  13596.  An enhancement of both radio buttons and check boxes gives you the option
  13597.  of having label text appear on the left rather than the right side of the
  13598.  graphic.
  13599.  
  13600.  
  13601.  Menus
  13602.  
  13603.  Menus used to be write only, at least in part. For example, given a menu
  13604.  position, you could get the corresponding text string with GetMenuString,
  13605.  but there was no way to find out what the menu ID was. This made it
  13606.  difficult to write helper utilities that would send menu commands to other
  13607.  applications, a process that required a menu ID.
  13608.  
  13609.  Three new functions have overcome this limitation. GetMenuItemID takes a
  13610.  menu position and menu handle and returns the menu item ID. GetMenuItemCount
  13611.  tells you how many items are in either a top-level or a pop-up menu.
  13612.  GetMenuState allows you to determine what the menu flags (MF_ENABLED and so
  13613.  on) are for a menu item.
  13614.  
  13615.  The new LoadMenuIndirect function replicates LoadMenu, but instead of
  13616.  loading the menu resource from your .EXE file, it takes a pointer to a
  13617.  menu resource in memory. This makes it easy to build a menu on the fly
  13618.  without having to go through a whole series of CreateMenu and ChangeMenu
  13619.  calls. Like the new indirect dialog box calls, this function provides a
  13620.  nice middle ground between a static menu from the .EXE file and all the
  13621.  explicit code it would otherwise take to build a menu.
  13622.  
  13623.  
  13624.  Window Hooks
  13625.  
  13626.  Window hooks are now almost fully documented and functional. They are no
  13627.  longer among the most mysterious aspects of Microsoft Windows. The
  13628.  SetWindowsHook function can install any of the hook functions listed in
  13629.  Figure 3.
  13630.  
  13631.  Two new functions help support hook functions. DefHookProc lets a hook
  13632.  function chain to the previous hook function, so that there can be more
  13633.  than one hook of the same type. UnhookWindowsHook removes a hook function
  13634.  from a hook chain──something that before could only be done by exiting from
  13635.  Windows.
  13636.  
  13637.  They haven't taken all the mystery out of Windows, though. Along with the
  13638.  documented hooks, WINDOWS.H shows three new ones that are undocumented:
  13639.  WH_CBT, WH_SYSMSGFILTER, and WH_WINDOWMGR.
  13640.  
  13641.  
  13642.  Memory Management
  13643.  
  13644.  Windows now comes equipped with a kind of long-term equivalent of
  13645.  GlobalLock, called GlobalWire. When you allocate global memory and declare
  13646.  it FIXED, Windows allocates the space at the low end of memory to avoid
  13647.  fragmentation problems. If you allocate MOVEABLE memory, Windows doesn't
  13648.  worry about where it's located; then if you issue a GlobalLock, Windows
  13649.  just locks this memory in its current location for the sake of speed. This
  13650.  is fine for a short-term lock, but if you lock down memory for a long
  13651.  time, the memory space becomes fragmented, causing all sorts of memory
  13652.  management problems. GlobalWire prevents fragmentation by allocating a
  13653.  MOVEABLE memory block to low memory, as if it had been declared FIXED in
  13654.  the first place. GlobalUnwire unlocks memory that had been moved and
  13655.  locked with GlobalWire.
  13656.  
  13657.  SetSwapAreaSize lets you increase the amount of global memory dedicated to
  13658.  code rather than data. Normally, Windows allows nearly all global memory
  13659.  to be allocated for data with GlobalAlloc, reserving just enough space for
  13660.  the largest single code segment from any application or library. Although
  13661.  flexible, this arrangement will cause Windows to slow down severely if
  13662.  memory gets extremely tight, because it has to keep swapping code segments
  13663.  in. With SetSwapAreaSize, you can increase the amount of memory dedicated
  13664.  for code. This reduces the amount of global data you can allocate but
  13665.  prevents Windows from doing a great deal of low-memory thrashing.
  13666.  
  13667.  ValidateFreeSpaces is a new debugging function that helps you catch wild
  13668.  pointers. In the debugging version of Windows 2.0, the kernel fills all
  13669.  free memory with 0x0C. ValidateFreeSpaces scans all the free memory, and
  13670.  if it finds any byte that's not equal to 0x00C, it returns that address.
  13671.  If you run into a memory-clobbering problem, frequent calls to this
  13672.  function could help track it down.
  13673.  
  13674.  
  13675.  Miscellany
  13676.  
  13677.  Windows now has global atoms. These are like local atoms, but they are
  13678.  allocated from the global heap instead of your local heap and are shared
  13679.  among all applications. Four new functions serve as the global equivalents
  13680.  of the local atom calls: GlobalAddAtom, GlobalDeleteAtom, GlobalFindAtom,
  13681.  and GlobalGetAtomName.
  13682.  
  13683.  A few more miscellaneous new functions: EqualRect simply compares two
  13684.  rectangles and returns a Boolean value indicating whether they're equal.
  13685.  GetTickCount returns the number of milliseconds since the system was
  13686.  started. EnableHardwareInput enables or disables mouse and keyboard input.
  13687.  Figure 4 lists a number of new or enhanced messages.
  13688.  
  13689.  
  13690.  Upward Compatibility
  13691.  
  13692.  Most existing Windows applications will run under Microsoft Windows 2.0,
  13693.  but a change made to CreateWindow will affect nearly every
  13694.  application──fortunately, this should be the only real incompatibility for
  13695.  most programs. The position and size parameters of CreateWindow now have
  13696.  different meanings. The parameters (0,0,0,0) will still work, but the
  13697.  window will start out as being very tiny. You must either put in the
  13698.  CW_USEDEFAULT parameters or give an explicit position and size. If you do
  13699.  the latter, don't assume a particular screen size──use GetSystemMetrics.
  13700.  (0,0,0,0) on a Windows 1.x application gives the same result as
  13701.  CW_USEDEFAULT.
  13702.  
  13703.  WM_NCxxxxxx messages can cause problems. Most programs pass the nonclient
  13704.  messages through to DefWindowProc. If you process any of these messages to
  13705.  do any kind of special nonclient area handling, expect some trouble. You
  13706.  may also have problems if you have created a pop-up window that the user
  13707.  can iconize or zoom, since you probably had to fiddle with the WM_NCxxxxxx
  13708.  messages to get this to work. Since your application will be in an
  13709.  overlapping window anyway, you can eliminate the problem by just removing
  13710.  code (the easiest way to fix a bug).
  13711.  
  13712.  Any code that depended on the order of the System menu items or their
  13713.  exact functions will probably fail, since these have been changed. It's no
  13714.  problem if you just added an "About. . ." box to the System menu, although
  13715.  you will want to move it to the File menu in order to stay consistent with
  13716.  other applications.
  13717.  
  13718.  The last parameter to PeekMessage used to be called bRemoveMsg and was
  13719.  treated like most Booleans──zero meant false and nonzero meant true. If
  13720.  your code uses a value other than 0 or 1 for this parameter, you could run
  13721.  into trouble, because the parameter has been extended. It's now called
  13722.  wRemoveMsg, and the 0 and 1 values work as they always did, but any other
  13723.  values will have different meanings. For example, a value of 2 would be
  13724.  interpreted as PM_NOYIELD | PM_NOREMOVE instead of PM_REMOVE (TRUE).
  13725.  You're not likely to run into this problem, but if you do, it could be
  13726.  hard to track down. It's worth checking all your PeekMessage calls. (Do
  13727.  this at the same time you convert your CreateWindow calls.)
  13728.  
  13729.  Since DrawText now attaches a special meaning to the ampersand, this
  13730.  function won't do what you want if you have one in the string. You must
  13731.  either change it to a double ampersand or add the DT_NOPREFIX option to
  13732.  the call.
  13733.  
  13734.  A few of the things that Windows 1.x let you get away with aren't
  13735.  tolerated by Windows 2.0. Strictly speaking, they were errors all
  13736.  along──they just weren't caught before. I've run into a couple of these so
  13737.  far, but there are probably more.
  13738.  
  13739.  The one that gave me a little trouble converting to Windows 2.0 was
  13740.  GetParent(NULL). It's not legal to pass a NULL window handle to GetParent,
  13741.  but earlier versions of Windows accepted it and returned a NULL. Windows
  13742.  2.0 (at least in the debugging version) will reject it.
  13743.  
  13744.  The other problem occurred in Microsoft's CUBE demo program. This program
  13745.  loads a binary resource into memory with LoadResource and then locks it
  13746.  with GlobalLock. That's not kosher at all, but somehow it worked before.
  13747.  The correct procedure is to use LockResource instead of GlobalLock.
  13748.  
  13749.  
  13750.  New Documentation
  13751.  
  13752.  The new programmer's documentation is one of the nicest features of
  13753.  Windows 2.0. The manuals have been extensively revised, with excellent
  13754.  tutorial discussions of such areas as window creation, painting, and
  13755.  scrolling. Unlike the old Windows manuals we've all struggled with, the
  13756.  new programmer's manual actually explains things. Newcomers to Windows
  13757.  programming will now have a much easier time learning the ropes.
  13758.  
  13759.  Besides containing new explanatory and overview material, the manual has
  13760.  been reorganized to list all the functions and messages in alphabetical
  13761.  order. No more scratching your head trying to figure out whether SetRect
  13762.  is a GDI or User function.
  13763.  
  13764.  
  13765.  TILER
  13766.  
  13767.  Despite all the enhancements made to Windows 2.0, I really miss one
  13768.  feature of the old version: tiled windows. As nice as the new overlapping
  13769.  windows are, tiled windows can be very handy too. So, which will it be,
  13770.  friends──New Windows or Windows Classic?
  13771.  
  13772.  Since we're talking about software, not soft drinks, why not have both? To
  13773.  this end, I wrote a program called TILER, which does just what its name
  13774.  implies──it moves and resizes your windows into a tiled arrangement. TILER
  13775.  runs as an icon and gives you two menu items to choose from: Tile Columns
  13776.  and Tile Rows (see Figure 5). When you select either of these menu
  13777.  options, TILER searches for windows that it can rearrange and then tiles
  13778.  them. According to which option you choose, TILER will first try to place
  13779.  your windows either in columns (side by side) or in rows (one above the
  13780.  other). Figures 6 and 7 show the different ways TILER will rearrange
  13781.  overlapping windows.
  13782.  
  13783.  This demonstrates the greater versatility of Windows 2.0. Windows 1.x
  13784.  allowed only a single tiling arrangement, with columns taking precedence.
  13785.  There was no way you could arrange your windows in rows (see Figure 8). A
  13786.  program such as TILER, which resizes the windows of other applications,
  13787.  could not even be written under the old Windows, which strictly prohibited
  13788.  an application from controlling the size of any tiled window, even its
  13789.  own. Now an application can resize its own windows or those of other
  13790.  applications, and no particular window arrangement is mandatory.
  13791.  
  13792.  A good tiling algorithm can be fairly complex. Because of space
  13793.  limitations, the version of TILER given in Figures 9, 10, 11, and 12
  13794.  uses a very simplified algorithm that merely divides the screen into equally
  13795.  sized rows or columns. In fact, TILER will handle no more than four
  13796.  windows, simply leaving any additional windows where they were, hidden
  13797.  underneath the tiled windows. Even with this limitation, TILER is fairly
  13798.  useful. But you can download a more sophisticated version of TILER, which
  13799.  allows more-flexible window placement, from Microsoft's DIAL service.
  13800.  
  13801.  
  13802.  Initializing TILER
  13803.  
  13804.  TILER's WinMain resembles the main programs of most Windows applications.
  13805.  Yet, while most Windows applications allow several copies of themselves to
  13806.  be run concurrently, this wouldn't be useful for TILER, so it just beeps
  13807.  and exits if you try to run more than one instance of it.
  13808.  
  13809.  TILER calls its Initialize function to create and display its icon. Then
  13810.  it settles into a GetMessage/DispatchMessage loop, which pulls messages
  13811.  from the application queue and routes them to TILER's window function,
  13812.  TilerWndProc. As in most Windows applications, the real work consists of
  13813.  processing these messages.
  13814.  
  13815.  You may notice there's no TranslateMessage call in TILER's message loop.
  13816.  This function is needed only when your application wants to use WM_CHAR
  13817.  messages to get ASCII keyboard input. Since TILER does not require
  13818.  WM_CHAR, it doesn't have to call TranslateMessage. Similarly, there's no
  13819.  TranslateAccelerator call, since TILER doesn't use any keyboard
  13820.  accelerators.
  13821.  
  13822.  TILER's Initialize function registers its window class and creates its
  13823.  window using the RegisterClass and CreateWindow functions found in nearly
  13824.  every Windows application. It also adds Tile Columns and Tile Rows to
  13825.  TILER's System menu. Although it's generally not a good idea for
  13826.  applications to add items to their System menus, this is quite appropriate
  13827.  for a program like TILER, which has no other menu. (Indeed, since TILER
  13828.  runs as an icon, there would be no place to display another menu if it had
  13829.  one.)
  13830.  
  13831.  Initialize performs a little trick to determine how much of the screen the
  13832.  tiled windows should take up. We don't want to use the full screen, since
  13833.  we have to leave room at the bottom for icons, just as in the previous
  13834.  version of Windows. So TILER first creates a normal (noniconic) but
  13835.  invisible window and tells Windows to assign a default window size and
  13836.  position. The bottom edge of this default window happens to be just where
  13837.  we want the bottom of the tiled windows to be, so TILER saves that value
  13838.  as the bottom coordinate of TileRect.
  13839.  
  13840.  Finally, Initialize makes the window visible with the ShowWindow function
  13841.  call. The SW_SHOWMINIMIZED parameter to ShowWindow converts the window
  13842.  into an icon. (This parameter is the same as SHOW_ICONWINDOW in Windows
  13843.  1.x.)
  13844.  
  13845.  
  13846.  Take a Message
  13847.  
  13848.  TILER's window function, TilerWndProc, handles only three messages
  13849.  specially. It passes all others along to DefWindowProc, which performs
  13850.  Windows' default message processing. This lets TilerWndProc concentrate on
  13851.  just the things it needs to do.
  13852.  
  13853.  The most important message for TILER is WM_SYSCOMMAND, since this is sent
  13854.  when the user selects an item from the System menu, where TILER's two menu
  13855.  items are located. TilerWndProc checks to see whether the message
  13856.  originates from a TILER menu item. If so, it processes the message by
  13857.  calling the TileWindows function. All other WM_SYSCOMMAND messages are
  13858.  passed on to DefWindowProc so Windows can handle the other System menu
  13859.  commands.
  13860.  
  13861.  Another message useful to TILER is WM_QUERYOPEN. Windows sends this
  13862.  message whenever the user tries to open the icon, that is, change it back
  13863.  into an open window. Since TILER runs only as an icon, it replies to this
  13864.  message by returning a 0, so Windows won't open it.
  13865.  
  13866.  Finally, the WM_DESTROY message tells TILER that the user has selected the
  13867.  Close item from the System menu. TilerWndProc then calls PostQuitMessage
  13868.  to tell the main application loop to terminate the next time through.
  13869.  
  13870.  
  13871.  Tiling Algorithm
  13872.  
  13873.  TILER really gets down to business with the TileWindows function. First,
  13874.  TileWindows calls CalcWindowRects, which loops through the active windows,
  13875.  calculating new sizes and positions for them. CalcWindowRects is the
  13876.  actual tiling algorithm. If there's just one window, it gets the entire
  13877.  tiling area, of course. Two windows split the area in half, either
  13878.  horizontally or vertically, depending on which menu item was selected. If
  13879.  there are three windows, the first one gets half the tiling area, and the
  13880.  second and third split the remaining half. Four windows get the four
  13881.  corners of the tiling area. With more than four windows, TILER just tiles
  13882.  the first four and leaves the remaining windows hidden underneath them.
  13883.  
  13884.  In the more sophisticated version of TILER available on DIAL,
  13885.  CalcWindowRects can handle more than four windows and is sensitive to how
  13886.  the user had already sized them. For example, if the user makes the
  13887.  topmost window larger, the enhanced TILER will shrink the other windows to
  13888.  fit instead of arbitrarily splitting the screen in half (see Figure 13).
  13889.  
  13890.  To keep things simple, TILER attempts to tile only top-level overlapping
  13891.  windows that are resizable and are not minimized (iconic) or maximized
  13892.  (zoomed). The IsTileable function determines whether a window fits these
  13893.  criteria, and CalcWindowRects ignores the window if IsTileable returns the
  13894.  value FALSE.
  13895.  
  13896.  CalcWindowRects is much simpler than it would have been in Windows 1.x,
  13897.  because it uses the slick new GetWindow function. Anyone who has suffered
  13898.  through having to use the clumsy EnumWindows will appreciate GetWindow,
  13899.  which lets you write an ordinary C loop to scan through the window list
  13900.  instead of forcing you to use a "call back" function. Figure 14 makes it
  13901.  clear just how much easier it is to use GetWindow instead of EnumWindows.
  13902.  
  13903.  Once the window rectangles are calculated, the TileWindows function moves
  13904.  the windows into place with the new SetWindowPos function. SetWindowPos
  13905.  also sends the WM_MOVE and WM_SIZE messages to the windows to notify them
  13906.  that they have been moved. The old MoveWindow function would have done the
  13907.  job here, but SetWindowPos does two things at once: besides setting the
  13908.  window positions, it reorders the window list, so the Alt-Tab and Alt-Esc
  13909.  key combinations will scan through the tiled windows in a natural order.
  13910.  
  13911.  TileWindows performs one other function here. It checks each window to see
  13912.  whether the CS_HREDRAW or CS_VREDRAW bits are set. If they are, it
  13913.  invalidates the window, causing it to be repainted. This is necessary
  13914.  because some Windows applications──notably CLOCK──do not properly respond to
  13915.  the WM_SIZE message but depend on those redraw flags to generate a
  13916.  WM_PAINT message.
  13917.  
  13918.  Programs like CLOCK that change their displays to fit the window size
  13919.  should really respond to the WM_SIZE message and recalculate their
  13920.  displays at that point. But CLOCK doesn't. If you feel like experimenting,
  13921.  remove the InvalidateRect call from TileWindows, and watch what happens
  13922.  when CLOCK is one of the windows you're tiling. You'll get some strange-
  13923.  looking results.
  13924.  
  13925.  
  13926.  Building TILER
  13927.  
  13928.  To compile and link TILER, you will need the Microsoft C Compiler, Version
  13929.  4.0 or 5.0, and the Windows Software Development Kit, Version 2.0. Put the
  13930.  TILER.C source code, TILER.RC resource file, TILER.DEF module definition
  13931.  file, and TILER make-file (see Figures 9, 10, 11, and 12) in a directory,
  13932.  and make up a TILER.ICO icon file with the Icon Editor. Then issue the
  13933.  command MAKE TILER to build TILER.EXE.
  13934.  
  13935.  
  13936.  Figure 1:  Editing Key Assignments
  13937.  
  13938.  Function      Assignment
  13939.  
  13940.  Undo          Alt+BkSp
  13941.  Cut           Shift+Del
  13942.  Copy          Ctrl+Ins
  13943.  Paste         Shift+Ins
  13944.  Clear         Del
  13945.  
  13946.  
  13947.  Figure 2:  This .RC file establishes the File and Edit menus and their
  13948.             accelerators.
  13949.  
  13950.  MyApp MENU
  13951.  BEGIN
  13952.   POPUP "&File"
  13953.      BEGIN
  13954.          MENUITEM "&New"                     , CMD_NEW
  13955.          MENUITEM "&Open..."                 , CMD_OPEN
  13956.          MENUITEM SEPARATOR
  13957.          MENUITEM "&Save"                    , CMD_SAVE
  13958.          MENUITEM "Save &As..."              , CMD_SAVEAS
  13959.          MENUITEM SEPARATOR
  13960.          MENUITEM "E&xit"                    , CMD_EXIT
  13961.          MENUITEM "A&bout My Application..." , CMD_ABOUT
  13962.      END
  13963.  
  13964.      POPUP "&Edit"
  13965.      BEGIN
  13966.          MENUITEM "&Undo\tAlt+BkSp"          , CMD_UNDO
  13967.          MENUITEM SEPARATOR
  13968.          MENUITEM "Cu&t\tShift+Del"          , CMD_CUT
  13969.          MENUITEM "&Copy\tCtrl+Ins"          , CMD_COPY
  13970.          MENUITEM "&Paste\tShift+Ins"        , CMD_PASTE
  13971.          MENUITEM "C&lear\tDel"              , CMD_CLEAR
  13972.      END
  13973.  
  13974.      MENUITEM "\a&Help"                      , CMD_HELP, HELP
  13975.  
  13976.  END
  13977.  
  13978.  MyApp ACCELERATORS
  13979.  BEGIN
  13980.      VK_DELETE,  CMD_CUT,        VIRTKEY
  13981.      VK_DELETE,  CMD_CLEAR,      VIRTKEY, SHIFT
  13982.      VK_INSERT,  CMD_COPY,       VIRTKEY, CONTROL
  13983.      VK_INSERT,  CMD_PASTE,      VIRTKEY, SHIFT
  13984.      VK_BACK,    CMD_UNDO,       VIRTKEY, ALT
  13985.  END
  13986.  
  13987.  
  13988.  Figure 3:  Hook Functions Installed by SetWindowsHook
  13989.  
  13990. ╓┌───────────────────┌───────────────────────────────────────────────────────╖
  13991.  Hook Function       Description
  13992.  
  13993.  WH_MSGFILTER        Message filter. Called when a dialog box, message box,
  13994.                      or menu has received a message but before it has
  13995.                      processed the message.
  13996.  
  13997.  WH_KEYBOARD         Keyboard filter. Called when the application calls
  13998.                      GetMessage or PeekMessage and there is any keyboard
  13999.                      input pending.
  14000.  
  14001.  WH_GETMESSAGE       GetMessage filter. Called from inside the GetMessage
  14002.                      function before it returns the message to the
  14003.                      application. (Debugging version of Windows only.)
  14004.  
  14005.  WH_CALLWNDPROC      Window function filter. Called whenever any message is
  14006.                      sent to a window function. (Debugging version of
  14007.                      Windows only.)
  14008.  
  14009.  Hook Function       Description
  14010. 
  14011.  WH_JOURNALRECORD    Journal recording. Called on every event message
  14012.                      (keyboard, mouse, or timer message) for any
  14013.                      application.
  14014.  
  14015.  WH_JOURNALPLAYBACK  Journal playback. Plays back messages recorded by
  14016.                      WH_JOURNALRECORD. With these two hook functions, you
  14017.                      could write a "ProMouseKey" program for Windows.
  14018.  
  14019.  
  14020.  Figure 4:  New Messages in Windows 2.0
  14021.  
  14022. ╓┌───────────────────┌───────────────────────────────────────────────────────╖
  14023.  New Messages        Description
  14024.  
  14025.  BM_SETSTYLE         Alters the style of a button. With this message, you
  14026.                      can change a button control from its current style to
  14027.                      any other style. The main purpose of the message is to
  14028.                      allow the new user interface trick where any push
  14029.                      button you tab to becomes the default push button.
  14030.  New Messages        Description
  14031.                     button you tab to becomes the default push button.
  14032.  
  14033.  BN_DOUBLECLICKED    Notifies the parent window when the user double-clicks
  14034.                      a button control.
  14035.  
  14036.  EM_LINEFROMCHAR     Returns the line number for a given character position
  14037.                      in an edit control.
  14038.  
  14039.  EM_SETWORDBREAK     Lets you specify a word-break function to replace the
  14040.                      default word-break function (which breaks on a blank).
  14041.  
  14042.  EN_UPDATE           Just like EN_CHANGE, except that it is sent before the
  14043.                      edit control displays the changed text instead of
  14044.                      after. The text has already been formatted, so you
  14045.                      can issue an EM_LINELENGTH or EM_GETLINECOUNT and
  14046.                      resize the edit control accordingly. This makes it
  14047.                      easy to create an edit control that expands and
  14048.                      shrinks to fit its text. With this message, I was
  14049.                      able to replace about 50 lines of very tricky code in
  14050.                      one of my programs with three lines of utterly simple
  14051.  New Messages        Description
  14052.                     one of my programs with three lines of utterly simple
  14053.                      code.
  14054.  
  14055.  WM_CHILDACTIVATE    Sent to a child window's parent when the SetWindowPos
  14056.                      function moves the child.
  14057.  
  14058.  WM_GETMINMAXINFO    By default, when you Maximize a window, it fills the
  14059.                      full screen. By replying to this message, you can
  14060.                      specify a location and size that your window will
  14061.                      take on when it's Maximized. You can also specify the
  14062.                      minimum and maximum tracking size (for when the user
  14063.                      sizes the window).
  14064.  
  14065.  WM_NCHITTEST        Returns new values to indicate when the mouse is in
  14066.                      one of the thick borders or in the Minimize or
  14067.                      Maximize icons.
  14068.  
  14069.  WM_SHOWWINDOW       Several new options to support the new Minimize,
  14070.                      Maximize, and Restore commands.
  14071.  
  14072.  New Messages        Description
  14073. 
  14074.  WM_SYSCOMMAND       New option to support the Restore command.
  14075.  
  14076.  
  14077.  Figure 9:  Make-File for TILER.EXE
  14078.  
  14079.  tiler.obj:  tiler.c
  14080.      msc -AS -Gcsw -Ox -u -W3 -Zdp $*;
  14081.  
  14082.  tiler.res:  tiler.rc  tiler.ico
  14083.      rc -r tiler.rc
  14084.  
  14085.  tiler.exe:  tiler.obj  tiler.res  tiler.def
  14086.      link4 tiler, tiler/align:16, tiler/map/line, slibw, tiler.def
  14087.      rc tiler.res
  14088.      mapsym tiler
  14089.  
  14090.  
  14091.  Figure 10:  DEF File for TILER.EXE
  14092.  
  14093.  TILER.DEF
  14094.  
  14095.  NAME         Tiler
  14096.  
  14097.  DESCRIPTION  'Windows Tiler by Michael Geary'
  14098.  
  14099.  STUB         'WINSTUB.EXE'
  14100.  
  14101.  CODE         MOVEABLE
  14102.  DATA         MOVEABLE MULTIPLE
  14103.  
  14104.  HEAPSIZE     1024
  14105.  
  14106.  STACKSIZE    4096
  14107.  
  14108.  EXPORTS
  14109.  
  14110.  
  14111.  Figure 11:  RC File for TILER.EXE
  14112.  
  14113.  TILER.RC
  14114.  
  14115.  /* Tiler.RC - resource file for TILER.EXE */
  14116.  
  14117.  #include <style.h>
  14118.  
  14119.  Tiler!  ICON    tiler.ico
  14120.  
  14121.  
  14122.  Figure 12:  Code for TILER.C
  14123.  
  14124.  TILER.C
  14125.  
  14126.  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *\
  14127.   *  Tiler.c                                                        *
  14128.   *  Windows 2.0 Tiling Utility                                     *
  14129.   *  Written by Michael Geary                                       *
  14130.  \* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  14131.  
  14132.  #include <windows.h>
  14133.  
  14134.  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  14135.  
  14136.  #define MAXINT      32767
  14137.  
  14138.  /* Menu command definitions */
  14139.  
  14140.  #define CMD_TILECOLS    1
  14141.  #define CMD_TILEROWS    2
  14142.  
  14143.  
  14144.  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  14145.  
  14146.  typedef struct {
  14147.      HWND    hWnd;
  14148.      RECT    rect;
  14149.  } WINDOW;
  14150.  
  14151.  WINDOW      Window[4];        /* Window info for each tiled window */
  14152.  int         nWindows;         /* How many windows we will tile */
  14153.  
  14154.  HANDLE      hInstance;        /* Our instance handle */
  14155.  int         hWndTiler;        /* hWnd of our icon */
  14156.  RECT        TileRect;         /* Overall tiling rectangle */
  14157.  
  14158.  char        szClass[] = "Tiler!";         /* Our window class name */
  14159.  char        szTitle[] = "Tiler";          /* Our window title */
  14160.  char        szTileCols[] = "&Tile Columns";
  14161.  char        szTileRows[] = "Tile &Rows";
  14162.  
  14163.  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  14164.  
  14165.  
  14166.  /*  Declare full templates for all our functions.  This gives us
  14167.   *  strong type checking on our functions.
  14168.   */
  14169.  
  14170.  void                CalcWindowRects( BOOL );
  14171.  BOOL                Initialize( void );
  14172.  BOOL                IsTileable( HWND );
  14173.  long    FAR PASCAL  TilerWndProc( HWND, unsigned, WORD, LONG );
  14174.  void                TileWindows( BOOL );
  14175.  int         PASCAL  WinMain( HANDLE, HANDLE, LPSTR, int );
  14176.  
  14177.  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  14178.  
  14179.  
  14180.  /*  Calculate window rectangles for the four topmost tileable windows
  14181.   *  and set up the Window array for them.  This is a simple-minded
  14182.   *  tiling algorithm that simply divides the tiling area into equal
  14183.   *  rows and columns.
  14184.   */
  14185.  
  14186.  void CalcWindowRects( bColumns )
  14187.      BOOL        bColumns;
  14188.  {
  14189.      HWND        hWnd;
  14190.      int         n;
  14191.  
  14192.      n = 0;
  14193.      for(
  14194.          hWnd = GetWindow( hWndTiler, GW_HWNDFIRST );
  14195.          hWnd;
  14196.          hWnd = GetWindow( hWnd, GW_HWNDNEXT )
  14197.      ) {
  14198.          if( ! IsTileable( hWnd ) )  continue;
  14199.  
  14200.          Window[n].hWnd = hWnd;
  14201.          CopyRect( &Window[n].rect, &TileRect );
  14202.  
  14203.          switch( n ) {
  14204.              case 0:
  14205.                  break;
  14206.              case 1:
  14207.                  if( bColumns ) {
  14208.                      Window[0].rect.right = TileRect.right / 2;
  14209.                      Window[1].rect.left = Window[0].rect.right - 1;
  14210.                  } else {
  14211.                      Window[0].rect.bottom = TileRect.bottom / 2;
  14212.                      Window[1].rect.top = Window[0].rect.bottom - 1;
  14213.                  }
  14214.                  break;
  14215.              case 2:
  14216.                  if( bColumns ) {
  14217.                      Window[2].rect.left = Window[1].rect.left;
  14218.                      Window[1].rect.bottom = TileRect.bottom / 2;
  14219.                      Window[2].rect.top = Window[1].rect.bottom - 1;
  14220.                  } else {
  14221.                      Window[2].rect.top = Window[1].rect.top;
  14222.                      Window[1].rect.right = TileRect.right / 2;
  14223.                      Window[2].rect.left = Window[1].rect.right - 1;
  14224.                  }
  14225.                  break;
  14226.              case 3:
  14227.                  if( bColumns ) {
  14228.                      Window[3].rect.right = Window[0].rect.right;
  14229.                      Window[0].rect.bottom = TileRect.bottom / 2;
  14230.                      Window[3].rect.top = Window[0].rect.bottom - 1;
  14231.                  } else {
  14232.                      Window[3].rect.bottom = Window[0].rect.bottom;
  14233.                      Window[0].rect.right = TileRect.right / 2;
  14234.                      Window[3].rect.left = Window[0].rect.right - 1;
  14235.                  }
  14236.                  break;
  14237.          }
  14238.          if( ++n == 4 )  break;
  14239.      }
  14240.      nWindows = n;
  14241.  }
  14242.  
  14243.  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  14244.  
  14245.  
  14246.  /*  Initialize TILER.  Assumes a single instance.
  14247.   *  Returns TRUE if initialization succeeded, FALSE if failed.
  14248.   */
  14249.  
  14250.  BOOL Initialize()
  14251.  {
  14252.      WNDCLASS    Class;        /* Class structure for RegisterClass */
  14253.      HMENU       hMenu;        /* Menu handle of system menu */
  14254.  
  14255.      /* Register our window class */
  14256.      Class.style          = 0;
  14257.      Class.cbClsExtra     = 0;
  14258.      Class.cbWndExtra     = 0;
  14259.      Class.lpfnWndProc    = TilerWndProc;
  14260.      Class.hInstance      = hInstance;
  14261.      Class.hIcon          = LoadIcon( hInstance, szClass );
  14262.      Class.hCursor        = LoadCursor( NULL, IDC_ARROW );
  14263.      Class.hbrBackground  = COLOR_WINDOW + 1;
  14264.      Class.lpszMenuName   = NULL;
  14265.      Class.lpszClassName  = szClass;
  14266.  
  14267.      if( ! RegisterClass( &Class ) )  return FALSE;
  14268.  
  14269.      /* Create our window but don't iconize it yet */
  14270.      hWndTiler = CreateWindow(
  14271.          szClass, szTitle,
  14272.          WS_OVERLAPPED | WS_SYSMENU,
  14273.          CW_USEDEFAULT, 0,
  14274.          CW_USEDEFAULT, 0,
  14275.          NULL, NULL, hInstance, NULL
  14276.      );
  14277.      if( ! hWndTiler )  return FALSE;
  14278.  
  14279.      /* Since we took the default size, the bottom of our window is
  14280.       * the base Y coordinate for tiling */
  14281.      GetWindowRect( hWndTiler, &TileRect );
  14282.      TileRect.top = TileRect.left = -1;
  14283.      TileRect.right = GetSystemMetrics( SM_CXSCREEN ) + 1;
  14284.  
  14285.      /* Add our menu items to the System (Control) menu */
  14286.      hMenu = GetSystemMenu( hWndTiler, FALSE);
  14287.      ChangeMenu( hMenu, 0, NULL, MAXINT, MF_APPEND | MF_SEPARATOR );
  14288.      ChangeMenu( hMenu, 0, szTileCols, CMD_TILECOLS,
  14289.                  MF_APPEND | MF_STRING );
  14290.      ChangeMenu( hMenu, 0, szTileRows, CMD_TILEROWS,
  14291.                  MF_APPEND | MF_STRING );
  14292.  
  14293.      /* Now display our window as an icon */
  14294.      ShowWindow( hWndTiler, SW_SHOWMINIMIZED );
  14295.  
  14296.      return TRUE;
  14297.  }
  14298.  
  14299.  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  14300.  
  14301.  
  14302.  /*  Tells whether a window can be tiled, returns TRUE if so.
  14303.   *  We will tile only top level, resizable windows that are not
  14304.   *  minimized and not maximized.
  14305.   */
  14306.  
  14307.  BOOL IsTileable( hWnd )
  14308.      HWND        hWnd;
  14309.  {
  14310.      DWORD       dwStyle;
  14311.  
  14312.      dwStyle = GetWindowLong( hWnd, GWL_STYLE );
  14313.      return(
  14314.          ! ( dwStyle & ( WS_POPUP | WS_MINIMIZE | WS_MAXIMIZE ) )  &&
  14315.            ( dwStyle & WS_SIZEBOX )  &&
  14316.            ( dwStyle & WS_VISIBLE )
  14317.      );
  14318.  }
  14319.  
  14320.  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  14321.  
  14322.  
  14323.  /*  Tiler's window function.
  14324.   */
  14325.  
  14326.  long FAR PASCAL TilerWndProc( hWnd, wMsg, wParam, lParam )
  14327.      HWND        hWnd;            /* Window handle */
  14328.      unsigned    wMsg;            /* Message number */
  14329.      WORD        wParam;          /* Word parameter for the message */
  14330.      LONG        lParam;          /* Long parameter for the message */
  14331.  {
  14332.      RECT        rect;            /* A rectangle */
  14333.  
  14334.      switch( wMsg ) {
  14335.  
  14336.      /* Destroy-window message - time to quit the application */
  14337.          case WM_DESTROY:
  14338.              PostQuitMessage( 0 );
  14339.              return 0L;
  14340.  
  14341.      /* Query open icon message - don't allow icon to be opened! */
  14342.          case WM_QUERYOPEN:
  14343.              return 0L;
  14344.  
  14345.      /* System menu command message - process the command if ours */
  14346.          case WM_SYSCOMMAND:
  14347.              switch( wParam ) {
  14348.                  case CMD_TILECOLS:
  14349.                      TileWindows( TRUE );
  14350.                      return 0L;
  14351.                  case CMD_TILEROWS:
  14352.                      TileWindows( FALSE );
  14353.                      return 0L;
  14354.                  default:
  14355.                      break;
  14356.              }
  14357.              break;
  14358.  
  14359.      /* For all other messages, we pass them on to DefWindowProc */
  14360.          default:
  14361.              break;
  14362.      }
  14363.      return DefWindowProc( hWnd, wMsg, wParam, lParam );
  14364.  }
  14365.  
  14366.  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  14367.  
  14368.  /*  This function actually tiles the windows. First, it calls
  14369.   *  CalcWindowRects to determine the window positions. Then, it
  14370.   *  loops through the windows and moves them into place.
  14371.   */
  14372.  
  14373.  void TileWindows( bColumns )
  14374.      BOOL        bColumns;     /* TRUE = tile columns; FALSE = rows */
  14375.  {
  14376.      int         n;
  14377.      HWND        hWnd = NULL;
  14378.  
  14379.      CalcWindowRects( bColumns );  /* Assign window rectangles */
  14380.  
  14381.      if( nWindows == 0 ) {
  14382.          MessageBox(
  14383.              hWndTiler,
  14384.              "There are no windows that can be tiled.",
  14385.              szTitle,
  14386.              MB_OK | MB_ICONEXCLAMATION
  14387.          );
  14388.          return;
  14389.      }
  14390.  
  14391.      /* Move, size, and reorder windows */
  14392.      for( n = 0;  n < nWindows;  ++n ) {
  14393.          SetWindowPos(
  14394.              Window[n].hWnd,
  14395.              hWnd,
  14396.              Window[n].rect.left,
  14397.              Window[n].rect.top,
  14398.              Window[n].rect.right  - Window[n].rect.left,
  14399.              Window[n].rect.bottom - Window[n].rect.top,
  14400.              SWP_NOACTIVATE
  14401.          );
  14402.          hWnd = Window[n].hWnd;
  14403.          if( GetClassWord( hWnd, GCW_STYLE ) &
  14404.                          ( CS_HREDRAW | CS_VREDRAW ) )
  14405.              /* Make sure it's redrawn */
  14406.              InvalidateRect( hWnd, NULL, TRUE );
  14407.      }
  14408.  }
  14409.  
  14410.  /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  14411.  
  14412.  /*  Application main program. */
  14413.  
  14414.  int PASCAL WinMain( hInst, hPrevInst, lpszCmdLine, nCmdShow )
  14415.  
  14416.      /* Our instance handle */
  14417.      HANDLE      hInst;
  14418.      /* Previous instance of this application */
  14419.      HANDLE      hPrevInst;
  14420.      /* Pointer to any command line params */
  14421.      LPSTR       lpszCmdLine;
  14422.      /* Parameter to use for first ShowWindow */
  14423.      int         nCmdShow;
  14424.  {
  14425.      MSG         msg;                /* Message structure */
  14426.  
  14427.      /* Allow only a single instance */
  14428.      if( hPrevInst ) {
  14429.          MessageBeep( 0 );
  14430.          return 0;
  14431.      }
  14432.  
  14433.      /* Save our instance handle in static variable */
  14434.      hInstance = hInst;
  14435.  
  14436.      /* Initialize application, quit if any errors */
  14437.      if( ! Initialize() )  return FALSE;
  14438.  
  14439.      /* Main message processing loop */
  14440.      while( GetMessage( &msg, NULL, 0, 0 ) ) {
  14441.          DispatchMessage( &msg );
  14442.      }
  14443.  
  14444.      return msg.wParam;
  14445.  }
  14446.  
  14447.  
  14448.  Figure 14:  EnumWindows vs. GetWindow
  14449.  
  14450.  /* UsingEnumWindows to loop through the windows */
  14451.  
  14452.  void LoopThroughWindows()
  14453.  {
  14454.      FARPROC lpEnumProc;
  14455.  
  14456.      lpEnumProc = MakeProcInstance( MyEnumProc, hInstance );
  14457.      EnumWindows( lpEnumProc );
  14458.      FreeProcInstance( MyEnumProc, 0L );
  14459.  }
  14460.  
  14461.  BOOL MyEnumProc( hWnd, lParam )
  14462.      HWND    hWnd;
  14463.      LONG    lParam;
  14464.  {
  14465.      /* do something with hWnd here */
  14466.  }
  14467.  
  14468.  And don't forget to EXPORT MyEnumProc, or Windows will crash!
  14469.  
  14470.  Now, with GetWindow:
  14471.  
  14472.  void LoopThroughWindows()
  14473.  {
  14474.      HWND    hWnd;
  14475.  
  14476.      for(
  14477.          hWnd = GetWindow( hWndMyWindow, GW_HWNDFIRST );
  14478.          hWnd;
  14479.          hWnd = GetWindow( hWnd, GW_HWNDNEXT ))
  14480.      {
  14481.          /* do something with hWnd here */
  14482.      }
  14483.  }
  14484.  
  14485.  
  14486.  ───────────────────────────────────────────────────────────────────────────
  14487.  The Beta Version of Windows 2.0: An Update
  14488.  ───────────────────────────────────────────────────────────────────────────
  14489.  
  14490.  Just at press time, a new release of Windows 2.0 has come along that forces
  14491.  me to (happily) eat a few of my words. One of the joys of working with
  14492.  alpha- and beta-test software is that things change right under your feet as
  14493.  you're writing about them.
  14494.  
  14495.  In this case, I don't mind, because Microsoft has put in the GlobalNotify
  14496.  function. I call it GlobalNifty, because it lets you implement swappable
  14497.  data for your application. You are able to mark global memory as
  14498.  discardable, but if you set the GMEM_GNOTIFY flag on it, Windows will call
  14499.  your notification function when it wants to discard a memory block. You can
  14500.  then write it to disk before it gets discarded, which is much cleaner than
  14501.  the kludges we had to use before.
  14502.  
  14503.  There's also a LocalShrink function, which compacts your application's local
  14504.  heap and shrinks down your data segment to match. Before, if your local heap
  14505.  grew past the initial allocation determined by the .DEF file, your data
  14506.  segment grew as needed, but it never shrank back again; LocalShrink allows
  14507.  you to do that.
  14508.  
  14509.  RegisterWindowDestroy is an interesting new function. It's easy to find out
  14510.  when one of your own windows is destroyed, since it receives a WM_DESTROY
  14511.  message. You may want to find out when another application's window gets
  14512.  destroyed, such as in an add-on program that works with an existing
  14513.  application and should exit when that application exits. You can do this
  14514.  with a RegisterWindowDestroy call, which sets──actually increments──a
  14515.  destroy-notification flag on a window. Then, when that window is destroyed,
  14516.  Windows sends a WM_DESTROYOTHERWINDOW to all top-level windows to let
  14517.  everyone know about the window being destroyed.
  14518.  
  14519.  For text output, the new ExtTextOut function is like a TextOut with two
  14520.  added parameters: a rectangle that can be used for background erasing, text
  14521.  clipping, or both, and an optional array of character horizontal spacing
  14522.  values. This array lets you set your own spacing, character by character.
  14523.  You can find out the default horizontal spacing for a font with the
  14524.  GetCharWidth function, which loads up an integer array of spacing values for
  14525.  a range of characters in a font.
  14526.  
  14527.  GetDCOrg gives you the offset from screen to client coordinates for a
  14528.  display context. GetUpdateRgn copies a window's update region into an actual
  14529.  hRgn that you can then manipulate. IsZoomed tells you if a window is zoomed.
  14530.  (Zoomed is still zoomed, even if they do call it Maximized.) Finally,
  14531.  GetNumTasks returns the number of tasks that are currently running.
  14532.  
  14533.  One of the undocumented SetWindowsHook options I mentioned is now
  14534.  documented: WH_SYSMSGFILTER. This is the same as WH_MSGFILTER, except that
  14535.  it gives you the DialogBox/MessageBox/menu messages for all windows, not
  14536.  just your own window. A warning to those who would use the hook functions:
  14537.  when Windows calls your hook function, your DS register is set up for your
  14538.  data segment, but you're running on the stack of whatever application
  14539.  triggered the hook. Therefore, you will run into the infamous DS != SS
  14540.  problem, which means that you will have to compile that part of your
  14541.  code with the -Aw switch and cannot call certain C library functions. The
  14542.  Windows documentation lists which C run-time functions will and won't work
  14543.  in the DS != SS environment. This affects all the Windows hooks except for
  14544.  WH_MSGFILTER and WH_KEYBOARD.
  14545.  
  14546.  Windows 2.0 directly supports LIM/EMS and EEMS memory. These bank-switched
  14547.  memory schemes provide one or more "windows" (not related to Windows'
  14548.  windows) within the 1Mb real-mode address space. Any area of memory on the
  14549.  bank-switched card can be mapped into these memory windows. Windows 2.0
  14550.  supports this by assigning each application its own bank(s) of memory. A
  14551.  single application doesn't get any more memory than it used to, but you can
  14552.  run more applications concurrently.
  14553.  
  14554.  The larger the banked memory window(s), the better. Windows will keep
  14555.  application code and data in banked memory if possible, or else will put as
  14556.  much code there as will fit. EEMS memory can do a much better job here than
  14557.  straight LIM/EMS memory, because of its larger bank windows.
  14558.  
  14559.  For the most part, the memory banking is transparent to applications, but
  14560.  it's important to note that the banking is done on an application-by-
  14561.  application basis, and that code and data belonging to other applications
  14562.  can be banked out. This means, for example, that you can't give another
  14563.  application a global handle to your default data segment, expecting the
  14564.  other app to do a GlobalLock on it. Nor can you attempt to get a window
  14565.  function address from another app and call it directly, bypassing
  14566.  SendMessage. That never was a good idea, but now it's a sure way to crash.
  14567.  
  14568.  The only methods for intertask communication that will work under the
  14569.  EMS/EEMS environment are the clipboard, a shared library's data segment,
  14570.  and the Dynamic Data Exchange (DDE) protocol. Memory allocated with the
  14571.  GMEM_DDESHARE global memory flag is treated specially, so any app can get to
  14572.  it. You can also force global memory to be allocated from the shared area
  14573.  with GMEM_LOWER. Avoid this if possible, since the shared memory area is a
  14574.  scarce resource.
  14575.  
  14576.  There is one new Windows function for EMS support──LimitEMSPages. This
  14577.  simply lets you set a limit on the amount of EMS memory that Windows will
  14578.  assign to your application. At the cost of some extra disk access (because
  14579.  all your code won't fit in memory), you can reduce the total memory used by
  14580.  your application. You can still explicitly allocate more memory with
  14581.  GlobalAlloc calls, regardless of the limit set with LimitEMSPages.
  14582.  
  14583.  ████████████████████████████████████████████████████████████████████████████
  14584.  
  14585.  Keeping Up With the Real World: Speedy Serial I/O Processing
  14586.  
  14587.  ───────────────────────────────────────────────────────────────────────────
  14588.  Also see the following related articles:
  14589.    MS-DOS(R) Comm Port Syntax Problems
  14590.    Status of the Transmit Shift Register Versus the Transmitter Holding
  14591.    XON/XOFF
  14592.    RS-232C: A Standard Problem
  14593.    Sample Code Fragments for TSRCOMM.ASM
  14594.    Combining Interrupts and Polling: An Adventure in Programming
  14595.  ───────────────────────────────────────────────────────────────────────────
  14596.  
  14597.  Ross M. Greenberg
  14598.  
  14599.  At first glance, writing a serial communications program for an MS-DOS(R)
  14600.  system looks fairly straightforward. Most high-level languages can interface
  14601.  easily with the BIOS, and the BIOS, by definition, knows how to interface
  14602.  with the hardware. After you've designed your program with pop-up windows,
  14603.  pull-down help screens, fancy sounds, and flashy colors, you'll probably
  14604.  build a stub to test your ideas. You may be quite surprised to learn that
  14605.  you're limited to under 1200 baud.
  14606.  
  14607.  Turning to the back of the nearest BIOS Technical Reference manual, you'll
  14608.  soon discover how few of the services that you consider basic are provided:
  14609.  there is no XON/XOFF processing, little hardware handshaking is performed,
  14610.  the baud rates that are allowed seem arbitrarily low, and, most important of
  14611.  all, there is no true asynchronous interrupt processing.
  14612.  
  14613.  Whether you are writing a modem communications program or just attempting to
  14614.  get proper XON/XOFF processing of your serial printer in your favorite word
  14615.  processing program, some enhancement of the system services available under
  14616.  MS-DOS in the handling of serial communications is necessary. This article
  14617.  examines the writing of a replacement handler for Interrupt 0x14, the serial
  14618.  communications interrupt.
  14619.  
  14620.  My design goals arose from real-life needs I encountered when writing a
  14621.  terminate-and-stay-resident (TSR) communications program. These goals
  14622.  included:
  14623.  
  14624.    ■  asynchronous interrupt-driven I/O of baud rates up to 38,400
  14625.    ■  the  ability to handle COM ports 1 through 4
  14626.    ■  XON/XOFF flow control, on both incoming and outgoing flow
  14627.    ■  proper error detection of parity, overrun, and other errors
  14628.    ■  transparency for existing software
  14629.    ■  extensibility as requirements changed
  14630.  
  14631.  
  14632.  Getting Started
  14633.  
  14634.  Due to the inherent limitations of the BIOS serial communications service
  14635.  routines, MS-DOS must be bypassed to address the hardware directly. The
  14636.  moment you interface directly with the hardware within your computer, you
  14637.  lose that most precious of commodities, transportability between machines.
  14638.  However, compatibility is less of an issue now than it was in the past, and
  14639.  these routines work on all IBM-compatible machines, as well as on many
  14640.  clones.
  14641.  
  14642.  Although the real-world applications for the BIOS routines soon outstrip
  14643.  their abilities, the programmers' interface to these routines should be
  14644.  preserved: they are more or less a standard, and without them existing
  14645.  programs would not work properly. They permit you to initialize the comm
  14646.  port, send or receive a byte to or from the comm port, and return the status
  14647.  of the comm port. These interfaces are through Interrupt 0x14. (For the rest
  14648.  of this article, I'll refer to software interrupts as "calls.") Maintaining
  14649.  compatibility with the old interface while expanding above and beyond it led
  14650.  to some interesting design decisions.
  14651.  
  14652.  
  14653.  Precursor
  14654.  
  14655.  Writing code to stay resident after termination can be tricky, and adding in
  14656.  asynchronous interrupt processing can make things even trickier. When your
  14657.  code is called, the only things you know for sure are the code segment (CS)
  14658.  and instruction pointer (IP) of the CPU. Registers, including the Data
  14659.  Segment (DS) register, are pointing to unknown values. They must all be
  14660.  restored to their original values when you return from the interrupt service
  14661.  routine, with the CS and IP, as well as the flags of the CPU, restored
  14662.  immediately upon execution of the final IRET instruction.
  14663.  
  14664.  There are a variety of programming practices to get around this
  14665.  inconvenience. One method is addressing all data off the code segment using
  14666.  what is called a segment override in the assembler code itself. This,
  14667.  however, makes the code a little bigger and more cumbersome and requires a
  14668.  couple of extra clock cycles for any instruction that uses segment
  14669.  overrides, causing the code to execute more slowly.
  14670.  
  14671.  The method used in this program differs from that approach: immediately upon
  14672.  the service of an interrupt, the data segment is saved on the stack, and the
  14673.  code segment itself is used for data addressing. It's faster in the long
  14674.  run, both in the design cycle and in the actual execution of the code.
  14675.  Although there are portions of the code that deal directly with the hardware
  14676.  attached to the bus, whenever possible I've used transportable features of
  14677.  MS-DOS, including the MS-DOS calls for intercepting and taking over various
  14678.  BIOS interrupts.
  14679.  
  14680.  
  14681.  Overall Structure
  14682.  
  14683.  The code is structured logically into three areas. The invocation code is
  14684.  run only once; it sets up the interrupt structure and initializes data
  14685.  areas. The second component handles the actual asynchronous interrupts
  14686.  themselves, properly handling the two different types of interrupts: receive
  14687.  and transmit interrupts. Error interrupts are not generated in this scheme.
  14688.  Also, if a hardware interrupt is generated and does not come from the serial
  14689.  device itself, the interrupt is handed off to the original interrupt service
  14690.  routine, permitting devices that use the same IRQ (Interrupt Request) to be
  14691.  processed properly.
  14692.  
  14693.  Finally, the third portion of the code is a replacement for the original
  14694.  BIOS Interrupt 0x14. It allows for a great deal of extensibility to be
  14695.  added, though at the price of having some additional programming overhead.
  14696.  
  14697.  
  14698.  Saving Parameters
  14699.  
  14700.  One of the requirements is the ability to remove this program from memory
  14701.  when desired. This implies that the original parameters of the hardware
  14702.  must be saved and then restored upon program termination. Therefore, a
  14703.  structure for each comm port has been designed to hold all relevant initial
  14704.  data. In addition, this structure contains pointers to the beginning and end
  14705.  of the transmit and receive interrupt buffers. All of this data must be
  14706.  initialized properly, with interrupts turned off, before the actual BIOS
  14707.  interrupt vector is taken over and made to point to our own interrupt
  14708.  service routines.
  14709.  
  14710.  Once all of the parameters are saved and interrupt services are enabled, the
  14711.  preamble code executes a DOS Terminate and Stay Resident call, reserving
  14712.  memory before exiting to COMMAND.COM.
  14713.  
  14714.  
  14715.  Replacing Services
  14716.  
  14717.  Interrupt 0x14 is the entry point into the BIOS service routines for serial
  14718.  communications. By setting register half AH equal to a given function
  14719.  number and register DX to the desired comm port (starting with COM1 equal to
  14720.  zero), comm software then generates a software interrupt to invoke the given
  14721.  service (see Figure 1). I've extended the BIOS services with a new call,
  14722.  AH=4, and used the BX register to allow for some of the special operations
  14723.  that the new comm driver permits (see Figure 2).
  14724.  
  14725.  Although the interface to the user program looks the same, there are
  14726.  substantial internal differences. When a call is made to initialize the comm
  14727.  port, character ready interrupts are enabled, buffer pointers are reset, and
  14728.  then the actual hardware is reset to the proper baud rates, parity, word
  14729.  length, etc. When a call is made to return the status of the comm port, the
  14730.  comm port status is read and processed to reflect the combined status of
  14731.  both the comm port and buffers. This status can be further monitored by
  14732.  earlier calls with AH=4. However, since multiple interrupts can be caused by
  14733.  errors for each "get status" call you make, you may lose some of the actual
  14734.  error information. The method that most software employs to deal with error
  14735.  status is preserved, however: when an error is apparent, all errors are set
  14736.  in the status word if possible.
  14737.  
  14738.  When reading a character from the comm port, whether the character you're
  14739.  receiving is directly from the communications port or from the interrupt
  14740.  buffer will be transparent. One particularly interesting aspect of the code
  14741.  is its ability to detect an error, store the apparent character, and return
  14742.  an error condition in the high bit of AH.
  14743.  
  14744.  The code is also able to set a variable to specify a looping constant. This
  14745.  permits you to wait in the "get a character" routine until a character
  14746.  appears in the receive buffer or until the specified number of loops have
  14747.  been used up. As a replacement to the original BIOS routines, this is far
  14748.  superior to the constant usually specified. Again, you use the AH=4
  14749.  extension to the serial communications command set to set this constant. If
  14750.  you want, you can set this looping constant to 1/18 of a second and use the
  14751.  hardware timer tick interrupt as a countdown timer for error status.
  14752.  
  14753.  When transmitting a character, there are two options (set with AH=4):
  14754.  transmit the characters with the use of interrupts and the transmit buffer
  14755.  or attempt to output each character one at a time. The interrupt-driven
  14756.  transmit method is much faster, of course, but it has certain disadvantages.
  14757.  
  14758.  
  14759.  Processing Interrupts
  14760.  
  14761.  The most important feature of the code is its ability to properly handle
  14762.  interrupts that occur very frequently──as often as two interrupts for each
  14763.  character transmitted or received. Some background information on how
  14764.  interrupts are generated and how they are handled by the hardware should
  14765.  help you to understand how this code works.
  14766.  
  14767.  The chip at the heart of the serial communications board in your MS-DOS
  14768.  machine is an 8250. The 8250 requires eight contiguous I/O ports, which are
  14769.  used to read and write serial data and to read and set various parameters
  14770.  (see Figure 3).
  14771.  
  14772.  One of these parameters determines under what conditions the 8250 should
  14773.  generate an interrupt signal on the bus. Depending on how the parameter is
  14774.  set, an interrupt can be generated when a new character has been received,
  14775.  when the transmit buffer is empty and ready for a new character, or when an
  14776.  error condition has been determined. Initially, the code is set to generate
  14777.  an interrupt only on error conditions or when a new character is ready to be
  14778.  placed in the buffer.
  14779.  
  14780.  However, when a character is to be transmitted and a check of the status of
  14781.  the 8250 shows it is busy, the possibility of queuing characters up in a
  14782.  transmit buffer, which transmits the characters asynchronously to any other
  14783.  process, becomes very attractive. This is made possible through one of the
  14784.  settings of the BIOS call with AH=4, which allows you to determine if a
  14785.  character should be queued into the transmit buffer. Otherwise, the code
  14786.  will be set to continue testing for a clear transmit buffer or a time-out
  14787.  and will not return until either condition has been satisfied.
  14788.  
  14789.  When an interrupt is received, it is routed through one of two different
  14790.  interrupt vectors, namely the interrupt handler of COM1 or COM2. They merely
  14791.  set the correct header and then jump to the actual interrupt handler. The
  14792.  interrupt handler first determines which kind of interrupt was generated and
  14793.  then jumps to the appropriate routine. There is a priority to each
  14794.  interrupt, with error conditions having the highest priority, followed first
  14795.  by data becoming apparent at the serial port, then by the transmit buffer
  14796.  being empty, and finally by a change in other portions of the modem status.
  14797.  Also, if this routine determines that the interrupt was not generated by the
  14798.  8250, it will revector the interrupt to the original interrupt service
  14799.  routine.
  14800.  
  14801.  If the interrupt is determined to be the result of a character being
  14802.  received, the next character is read from the physical hardware port and
  14803.  stored in a circular buffer. If the circular buffer is more than 80 percent
  14804.  full and XON/XOFF processing is turned on, then an XOFF character is
  14805.  sent──if one hasn't already been sent. Even at high baud rates, the
  14806.  remaining 20 percent of the buffer should be sufficient for the remote end
  14807.  to notice the XOFF and cease sending characters before the receive buffer
  14808.  runs out of room.
  14809.  
  14810.  If the remote end does not stop sending characters in response to the XOFF,
  14811.  or if XON/XOFF processing was not turned on, characters that were received
  14812.  while the buffer was full will be discarded, and a BELL character will be
  14813.  sent to the remote machine. If the flag is set to indicate that an XOFF
  14814.  character was sent due to a high level of data, then an XON is sent when the
  14815.  amount of space left in the buffer increases to some predetermined level
  14816.  (set as an equate, all XON/XOFF high- and low-water marks can be modifed
  14817.  easily). This should allow for processing of enough characters to prevent
  14818.  the remote end from timing out.
  14819.  
  14820.  Error conditions are generated by a fault in the incoming stream of
  14821.  characters, which is detected by the 8250, and include such errors as an
  14822.  overrun (the character in the transmit buffer was not read before the next
  14823.  character arrived), parity (even, off, mark, or space parity was set, and
  14824.  the parity of the incoming character did not match what was expected), or
  14825.  framing error (a stop bit was not detected by the hardware when one was
  14826.  expected──usually indicative of wrong baud-rate errors).
  14827.  
  14828.  For each character received, both the error flag and error status of the
  14829.  chip are read, if the appropriate flag is set with AH=4. If an error
  14830.  condition is determined to exist, a bit is set in the error array.
  14831.  Subsequent reads of the character will return the actual character read
  14832.  (which may not be accurate), along with an error condition status set in AH
  14833.  in the same way that the current BIOS calls will set an error condition. For
  14834.  space considerations, the actual cause of the error condition is not saved,
  14835.  and the return code is set to indicate that all errors were apparent──a
  14836.  brute-force method that saves as much as 4 bits for each byte in your
  14837.  receive buffer.
  14838.  
  14839.  Only one of these errors requires any special handling: that is the
  14840.  recognition of a Break having been generated from the remote end of a serial
  14841.  connection. By an appropriate setting of the call extensions with AH=4, this
  14842.  can either be considered an error condition or can safely be ignored.
  14843.  
  14844.  When a Transmit Buffer Empty interrupt is processed, a quick check is made
  14845.  to determine whether any outstanding characters to be transmitted are in the
  14846.  transmit buffer. If there are no outstanding transmit requests, transmit
  14847.  interrupts are turned off, the interrupt is reset, and control is returned
  14848.  to the interrupted program. If there are outstanding characters to be
  14849.  transmitted, the character at the head of the circular transmit buffer is
  14850.  sent out the appropriate port; pointers, counters, and interrupts are reset;
  14851.  and control is returned to the interrupted program by means of the IRET
  14852.  instruction. This approach generates an additional interrupt after the final
  14853.  character is transmitted, but this is an acceptable trade-off for the
  14854.  advantage that you get from transmit interrupt processing.
  14855.  
  14856.  
  14857.  Programming the 8259
  14858.  
  14859.  When the 8250 generates an interrupt, it is, in fact, only raising a signal
  14860.  on the interrupt line of the bus. Attached directly to these lines is the
  14861.  interrupt controller on the motherboard, the 8259. This chip is the
  14862.  interface between all interrupt generators and the CPU and is a rather
  14863.  intelligent and programmable device. The 8259 distinguishes which interrupt
  14864.  has been raised and then does some internal processing to determine what
  14865.  actions it should take.
  14866.  
  14867.  The concept of interrupts is a powerful one, and the 8259 expands on this
  14868.  power, allowing the programmer to rearrange certain parameters so as to
  14869.  practically rewire the hardware in his or her computer. Unfortunately, much
  14870.  of the power of the interrupt system is lost in IBM-compatible machines in
  14871.  which the ROM BIOS programs the 8259 and causes it to be reset in a less-
  14872.  than-polite way.
  14873.  
  14874.  One of the rules of the interrupt system is that interrupts usually have to
  14875.  be reset. Upon receipt of an interrupt on the bus, the basic architecture
  14876.  of the 8259 sets bits on an internal register to correspond to each
  14877.  interrupt. Each bit is usually prioritized so that the highest priority
  14878.  interrupt (IRQ 0) is bit 0, the next highest is bit 1 (IRQ 1), and so on.
  14879.  This priority can be modified as desired and is one of the most powerful
  14880.  features of the Interrupt Controller Chip.
  14881.  
  14882.  Once an interrupt has been received and entered into this register, the
  14883.  controller determines if the CPU is already handling an interrupt of equal
  14884.  or higher priority. If it is not, and if the CPU has allowed interrupts at
  14885.  all with an STI or Enable Interrupts instruction, a CPU interrupt is
  14886.  generated, and the actual interrupt vector (0-7) is placed on the data bus.
  14887.  Immediately upon receipt of an interrupt, the CPU turns interrupts off
  14888.  (which disables the 8259 from raising further interrupts), saves the flags
  14889.  and the current instruction address, and jumps to the appropriate vectored
  14890.  address as set in the interrupt table. Until the interrupt service routine
  14891.  (ISR) executes an STI or an IRET, which resets the flags from the stack,
  14892.  interrupts are effectively disabled. Generally, the flag word on the stack
  14893.  will have interrupts enabled, since they had to be enabled in order for the
  14894.  hardware interrupt to be processed in the first place.
  14895.  
  14896.  Even when the IRET or the STI instruction clears the way for the CPU to
  14897.  process interrupts, the 8259 will not generate any interrupts of lower or
  14898.  equal priority until it is told to do so by an End of Interrupt (EOI)
  14899.  instruction.
  14900.  
  14901.  The 8259 allows for a number of different End of Interrupt instructions to
  14902.  be sent to it. Among these instruction types is the ability to reset a
  14903.  specific interrupt or to automatically allow the 8259 to reset the interrupt
  14904.  as soon as it is acknowledged by the CPU. The easiest one to implement is a
  14905.  nonspecific End of Interrupt; this tells the 8259 to reset the highest
  14906.  priority interrupt, which is usually the one that caused the last interrupt
  14907.  to be generated.
  14908.  
  14909.  However, since the concept of "highest priority level" can be reset from
  14910.  inside an ISR, generating a nonspecific EOI would cause the highest
  14911.  outstanding interrupt to be reset, which may or may not have been the
  14912.  interrupt currently being processed. If an interrupt in the 8259 is reset,
  14913.  it is as if that interrupt had never existed, and an important hardware
  14914.  interrupt could be missed. So, using nonspecific EOIs effectively disables
  14915.  many useful features of the 8259.
  14916.  
  14917.  Unfortunately, each and every EOI instruction in the IBM(R) ROM BIOS (and in
  14918.  most of the IBM-compatible machines as well) is a nonspecific one, making it
  14919.  difficult to use some of the power of the 8259. COM2 will almost always have
  14920.  priority over COM1, and many interesting features that you may wish to take
  14921.  advantage of in the 8259 are off-limits, unless you play a few programming
  14922.  tricks.
  14923.  
  14924.  Interrupts are reenabled with the STI instruction as soon as possible after
  14925.  interrupt processing has begun. And, due to the problems outlined above,
  14926.  nonspecific EOIs are generated at the end of each interrupt. Why was the EOI
  14927.  not generated earlier in the code? Once interrupts are reenabled with the
  14928.  STI instruction, only interrupts of a higher priority than the interrupt
  14929.  currently being processed will be acted upon. Typically, these interrupts
  14930.  are acted upon and returned from rapidly, and while they're being acted on,
  14931.  further interrupts of a lower priority will only be queued while waiting for
  14932.  an EOI. This is safe, in keeping with limited stack space. However, if an
  14933.  EOI is generated within the interrupt routine, and another interrupt routine
  14934.  takes over for some undetermined time, it is possible to lose the character
  14935.  ready or transmit buffer empty interrupt entirely. By using the belt-and-
  14936.  suspenders approach of reenabling interrupts immediately upon the start of
  14937.  interrupt processing, disabling interrupts before generating the EOI, and
  14938.  then executing the IRET instruction, you won't have to worry about the
  14939.  system crashing and the snake eating its own tail.
  14940.  
  14941.  
  14942.  Next Version
  14943.  
  14944.  What might the next version of this program do that this version doesn't?
  14945.  Perhaps being able to turn on and off an internal Xmodem protocol would be
  14946.  helpful, enabling the protocol layer of file transfers to be transparent.
  14947.  Once we get started with that, there are several protocols we can start
  14948.  playing with: Ymodem and Zmodem, X.25, UUCP, and maybe even some of the
  14949.  synchronous protocols, which usually require an external clock. Now, if we
  14950.  add in device driver I/O and a little bit of modem-dialing ability, the
  14951.  possibility of having the disk drive of a remote machine be considered local
  14952.  isn't all that farfetched, is it?
  14953.  
  14954.  
  14955.  Figure 1:  The Original Interrupt 14 Calling Sequence
  14956.  
  14957.  Set AH  to one of these values, and DX to 0 for COM1 or 1 for
  14958.  COM2. The requested value is either sent or received in AL.
  14959.  
  14960.  AH = 0      Port Initialization
  14961.   AL = Parameters to Set, AH will return status as in AH = 3
  14962.  
  14963.       Bits 7   6   5  │    4  3    │       2        │   1   0
  14964.        Baud Rate      │ Parity     │ No. of Stopbits│ Word Length
  14965.        000 -  110     │ 00 -  None │ 0 - 1 Stopbit  │ 00 - 5 Bits
  14966.        001 -  150     │ 01 -  Odd  │ 1 - 2 Stopbits │ 01 - 6 Bits
  14967.        010 -  300     │ 10 -  None │                │ 10 - 7 Bits
  14968.        011 -  600     │ 11 -  Even │                │ 11 - 8 Bits
  14969.        100 - 1200     │            │                │
  14970.        101 - 2400     │            │                │
  14971.        110 - 4800     │            │                │
  14972.        111 - 9600     │            │                │
  14973.  
  14974.  AH = 1      Send Character
  14975.    AL = Character to Send, AH will return status as in AH = 3
  14976.         (Bit 7 set indicates unable to send character)
  14977.  
  14978.  AH = 2      Get a Character
  14979.   AL returns character from comm port within time-out period
  14980.          (AH returns status as in AH = 3)
  14981.  
  14982.  AH = 3      Return Status of Comm Port
  14983.   AH =
  14984.   Bit 7 = Time-Out (Couldn't Send or Receive Character)
  14985.   Bit 6 = Transmitter Shift Register Empty
  14986.   Bit 5 = Transmitter Holding Register Empty
  14987.   Bit 4 = Break Detected
  14988.   Bit 3 = Framing Error Detected
  14989.   Bit 2 = Parity Error Detected
  14990.   Bit 1 = Overrun Error Detected
  14991.   Bit 0 = Data Ready
  14992.  
  14993.   AL =
  14994.   Bit 7 = Data Carrier Detect
  14995.   Bit 6 = Ring Indicator
  14996.   Bit 5 = Data Set Ready
  14997.   Bit 4 = Clear to Send
  14998.   Bit 3 = Delta (Change in) DCD
  14999.   Bit 2 = Delta RI
  15000.   Bit 1 = Delta DSR
  15001.   Bit 0 = Delta CTS
  15002.  
  15003.  
  15004.  Figure 2:  Additions to the BIOS for Interrupt 14
  15005.  
  15006.  All of these new functions require that AH = 4. The desired new
  15007.  function is loaded into AL. The comm port to operate on is loaded
  15008.  into DX as in the original interrupt. Statuses are returned in
  15009.  AX, if possible.
  15010.  
  15011.  AL = 0        Return 0ff0 in AX to determine load status
  15012.   AL = 1       Initialize ports, desired mode in CX, clear buffers
  15013.   AL = 2       Initialize comm port with baud rate and other
  15014.                parameters in CL
  15015.  
  15016.       Bits 7   6   5   │    4  3    │        2        │    1  0
  15017.        Baud Rate       │ Parity     │ No. of Stopbits │ Word Length
  15018.        001 - 19.2K     │ 00 -  None │ 0 - 1 Stopbit   │ 00 - 5 Bits
  15019.        010 - 38.4K     │ 01 -  Odd  │ 1 - 2 Stopbits  │ 01 - 6 Bits
  15020.                        │ 10 -  None │                 │ 10 - 7 Bits
  15021.                        │ 11 -  Even │                 │ 11 - 8 Bits
  15022.  
  15023.   AL = 3 Set the time-out value to the number of 1/18ths
  15024.          of a second in CX
  15025.  
  15026.   AL = 4 Clear the Input Buffer (Reset its pointers)
  15027.  
  15028.   AL = 5 Return count in Input buffer in AX
  15029.  
  15030.   AL = 6 Clear the Transmit Buffer (Reset its pointers)
  15031.  
  15032.   AL = 7 Return the count in the Transmit Buffer in AX
  15033.  
  15034.   AL = 8 Uninstall the TSR driver, then release memory
  15035.  
  15036.  
  15037.  Figure 3:  Register Usage in the 8250 UART
  15038.  
  15039.  All register usage is based on the offset from the base port address.
  15040.  For COM1 this is 3F8, for COM2 it is 2F8.
  15041.  
  15042.  BPA + 0   Data
  15043.            When read from, the current character will be returned from the
  15044.            Receive Buffer Register.
  15045.            When written to, the byte is transferred first to the Transmitter
  15046.            Holding Register, then to theTransmitter Shift Register, where
  15047.            it is actually transmitted.
  15048.  
  15049.            If the Divisor Latch Access Bit (DLAB and bit 7 of the Line
  15050.            Control Register) is set, then this port is the Least Significant
  15051.            byte for the Baud Rate Divisor.
  15052.  
  15053.  BPA + 1   Interrupt Enable Register
  15054.            Bits 7──4    NA
  15055.            Bit 3        Enable Modem Status Interrupts
  15056.            Bit 2        Enable Receive Line Status Interrupts
  15057.            Bit 1        Enable Xmit Holding Register Empty Interrupts
  15058.            Bit 0        Enable Data Available Interrupts
  15059.  
  15060.            If the DLAB is set, then this port is the Most Significant
  15061.            byte for the Baud Rate Divisor.
  15062.  
  15063.  BPA + 2   Interrupt Identification Register
  15064.            Bits 7──3    NA
  15065.            Bits 2──0    (in descending priority)
  15066.            110          Receiver Line Status Interrupt
  15067.            100          Data Available Interrupt
  15068.            010          Transmitter Holding Buffer Empty Interrupt
  15069.            000          Modem Status Interrupt
  15070.            001          No Interrupt Pending (Not an Interrupt)
  15071.  
  15072.  BPA + 3   Line Control Register
  15073.            Bit 7        Divisor Latch Access Bit
  15074.            Bit 6        Set Break (1 turns break on, 0 off)
  15075.            Bit 5&4      Parity
  15076.            0 0          Odd Parity
  15077.            0 1          Even Parity
  15078.            1 0          Space (Zero) Parity
  15079.            1 1          Mark (Ones) Parity
  15080.            Bit 3        Parity Enable
  15081.            Bit 2        Number of Stopbits (0=1 Stopbit, 1=2)
  15082.            Bit 1&0      Word Length
  15083.            0 0          5 Bits
  15084.            0 1          6 Bits
  15085.            1 0          7 Bits
  15086.            1 1          8 Bits
  15087.  
  15088.  BPA + 4   Modem Control Register
  15089.            Bit 7──5     NA
  15090.            Bit 4        Loop Enable (Turns on Test Conditions)
  15091.            Bit 3        OUT2 (Hardware AND to Interrupts)
  15092.            Bit 2        External Connection, Not Used
  15093.            Bit 1        Request to Send
  15094.            Bit 0        Data Terminal Ready
  15095.  
  15096.  BPA + 5   Line Status Register
  15097.            Bit 7        NA
  15098.            Bit 6        Transmitter Shift Register Empty
  15099.            Bit 5        Transmitter Holding Register Empty
  15100.            Bit 4        Break Interrupt Received
  15101.            Bit 3        Framing Error
  15102.            Bit 2        Parity Error
  15103.            Bit 1        Overrun Error
  15104.            Bit 0        Data Ready
  15105.  
  15106.  BPA + 6   Modem Status Register
  15107.            Bit 7        Data Carrier Detect
  15108.            Bit 6        Ring Indicator
  15109.            Bit 5        Data Set Ready
  15110.            Bit 4        Clear to Send
  15111.            Bit 3        Delta (Change in) DCD
  15112.            Bit 2        Delta RI
  15113.            Bit 1        Delta DSR
  15114.            Bit 0        Delta CTS
  15115.  
  15116.  
  15117.  ───────────────────────────────────────────────────────────────────────────
  15118.  MS-DOS(R) Comm Port Syntax Problems
  15119.  ───────────────────────────────────────────────────────────────────────────
  15120.  
  15121.  There are certain problems when dealing with MS-DOS, which are caused by
  15122.  confusion in syntax. For example, the first communications port found by
  15123.  the POST code in the BIOS is loaded into a set memory location (40:0).
  15124.  What is actually stored is the base address of the communications
  15125.  port──each 8250 requires eight consecutive port addresses. Subsequent
  15126.  communications ports are loaded at 40:2, 40:3, and 40:4. (Yes, MS-DOS
  15127.  allows for more than two communications ports.)
  15128.  
  15129.  The problem arises when the first communications port found by the POST is
  15130.  really that of an 8250 set up as a physical COM2. Since this is the first
  15131.  communications port found, it will be stored at 40:0, which is the address
  15132.  reserved for COM1. However, COM2 generates an IRQ 3, whereas COM1
  15133.  generates an IRQ 4. Programs that rely on the data found at 40:0 to
  15134.  determine which comm ports exist and what their physical interrupt
  15135.  characteristics are may have a problem unless the data stored at that
  15136.  address is examined and interpreted, which thereby prohibits device
  15137.  independence.
  15138.  
  15139.  ───────────────────────────────────────────────────────────────────────────
  15140.  Status of the Transmit Shift Register Versus the Transmitter Holding
  15141.  ───────────────────────────────────────────────────────────────────────────
  15142.  
  15143.  The 8250 chip has some inherent problems that you should be aware of. One
  15144.  of them can cause a routine that you might write to appear to transmit
  15145.  characters improperly.
  15146.  
  15147.  When you direct the 8250 to output a character by writing to the
  15148.  Transmitter Holding Buffer (a write instruction out to the Base Port of
  15149.  the 8250), it immediately transfers that byte to the Transmitter Shift
  15150.  Register (an internal register within the 8250) and starts transmitting
  15151.  it a serial bit at a time.
  15152.  
  15153.  However, the moment the Transmitter Holding Buffer is empty, an interrupt
  15154.  will be generated to indicate that the transmitter buffer is empty. If you
  15155.  have no bytes left to transmit, you might think this is a safe time to
  15156.  reset the 8250 as required. There is a good chance, however, that the last
  15157.  character is still being transmitted. If you were to reset the 8250, you
  15158.  might cause the last character to be transmitted improperly, if it is
  15159.  transmitted at all.
  15160.  
  15161.  It's a good idea to check the status of both the Holding Buffer and the
  15162.  Shift Register before you consider a transmission to be over.
  15163.  Fortunately, the folks at National Semiconductor have made this very easy:
  15164.  when you check the status of the Transmitter Shift Register, you're
  15165.  actually checking the status of both registers. The secret of getting the
  15166.  highest output rate in the safest manner is for you to check only the
  15167.  status of the Transmitter Holding Register when you are attempting to
  15168.  output characters, but to check the status of the Transmitter Shift
  15169.  Register to determine when the last character has been sent.
  15170.  
  15171.  ───────────────────────────────────────────────────────────────────────────
  15172.  XON/XOFF
  15173.  ───────────────────────────────────────────────────────────────────────────
  15174.  
  15175.  The XON/XOFF protocol, which is often used during non-error-checking
  15176.  transmissions of ASCII files, permits the receiving station in a
  15177.  communications session to indicate to the transmitting station to stop
  15178.  sending characters until it is told to continue.
  15179.  
  15180.  Even though the code allows you to define a buffer as large as you want
  15181.  (which will receive characters as long as there is space in the buffer and
  15182.  hardware interrupts), you'll eventually run across the problem of receiving
  15183.  characters more rapidly than you can handle them. An example of this is when
  15184.  the buffer fills up and you now must write the captured data to disk. This
  15185.  takes a finite amount of time, during which interrupts may be turned off for
  15186.  more than one character receive interval. If you don't use some protocol,
  15187.  you'll lose characters.
  15188.  
  15189.  The XON/XOFF protocol defines two characters, a Ctrl-S for XOFF and a Ctrl-Q
  15190.  for XON, that tell the transmitting side to cease transmission by sending an
  15191.  XOFF and to restart transmission by sending an XON.
  15192.  
  15193.  Life is seldom as easy as it might seem, and this protocol is no exception.
  15194.  First, it has been very loosely implemented in many current programs to
  15195.  determine that the XON character may be any character. If you design a
  15196.  protocol that waits only for the XON character, you might wait a long, long
  15197.  time. (The terminal driver in your MS-DOS machine has been loosely
  15198.  implemented with an XOFF protocol. Try hitting a Ctrl-S during a long
  15199.  listing and then any other character-generating key as if it were an XON
  15200.  character.)
  15201.  
  15202.  Second, this protocol is rather timing-sensitive, especially with the
  15203.  increasing popularity of some of the packet-switching networks. This is
  15204.  because when the buffer is 80 percent full, an XOFF character is sent to the
  15205.  transmitter. It may take a few seconds for the character to be received and
  15206.  acted on by the transmitter and for the transmitter to cease sending
  15207.  characters. Until the transmitter does receive the XOFF, it is merrily
  15208.  dumping characters out at character rates that can be as high as almost
  15209.  1,000 cps. You might have to leave as much as 5 seconds of character space
  15210.  in your buffer to handle situations like this. A 5Kb high-water mark is
  15211.  probably too much in most situations, however, so the code lets you modify
  15212.  both the buffer size and the high-water mark as you require.
  15213.  
  15214.  You can only send one XOFF character, even if it looks as if the remote end
  15215.  is ignoring your first XOFF request. There is always the possibility that
  15216.  the packet-switch network has delayed your first XOFF and that subsequent
  15217.  XOFFs may actually be taken as XON characters.
  15218.  
  15219.  ───────────────────────────────────────────────────────────────────────────
  15220.  RS-232C: A Standard Problem
  15221.  ───────────────────────────────────────────────────────────────────────────
  15222.  
  15223.  RS-232C is the shorthand abbreviation for Recommended Standard 232,
  15224.  Revision C, from the engineering department of the Electronic Industries
  15225.  Association. Its full name is Interface between Data Terminal Equipment
  15226.  and Data Communication Equipment Employing Serial Binary Data Interchange.
  15227.  
  15228.  RS-232C defines the wiring at each end of a connection between two
  15229.  devices, such as a modem (the Data Communication Equipment, or DCE) and
  15230.  your computer (the Data Terminal Equipment, or DTE). It requires that the
  15231.  DCE have a female connection and the DTE a male connection. RS-232C
  15232.  defines the supposed purpose of 25 different wires, which pins they should
  15233.  terminate on and even what voltage levels and current drains and loads are
  15234.  allowed, but that's about it (see Figure A).
  15235.  
  15236.  So, if you have two computers that you want to hook up to one another,
  15237.  which one is the DTE and which is the DCE? That's not defined, but the
  15238.  standard seems to work only if there is one of each, since it defines the
  15239.  "direction" of the flow of information──whether a particular signal is
  15240.  generated from the DTE or the DCE. Therefore, you must define one or the
  15241.  other as DTE and fool the other side into thinking that it too is a DTE.
  15242.  
  15243.  You can do this with a null-modem cable; think of it as almost having two
  15244.  modems in between two machines. Of the 25 defined pins/wires, only 11 of
  15245.  them are used for asynchronous communications──the others are used for
  15246.  synchronous communications or reserved for future use (see Figure B).
  15247.  
  15248.  Usually, for each transmitted data or control function, there is a
  15249.  complementary receive data or control function. By properly wiring these
  15250.  two mutually exclusive sets of wires, you have designed a cable that can
  15251.  trick the other side into thinking it is a modem (see Figure C for a
  15252.  complete picture).
  15253.  
  15254.  Of course, when one of the serial cards mounted within your machine does
  15255.  not even follow what little defined standard there is, we've all got a
  15256.  problem. It shouldn't surprise you to find that many of the serial cards
  15257.  out there are following their own versions of RS-232C. Remember that the R
  15258.  stands for "Recommended."
  15259.  
  15260.  
  15261.  Figure A:  Pin Usage Table
  15262.  
  15263. ╓┌──────┌─────────┌──────────────────────────────────────────────────────────╖
  15264.  Pin    Circuit   Name
  15265.  
  15266.   1     AA        Protective Ground
  15267.   2     BA        Transmitted Data
  15268.   3     BB        Received Data
  15269.  Pin    Circuit   Name
  15270.  3     BB        Received Data
  15271.   4     CA        Request to Send
  15272.   5     CB        Clear to Send
  15273.   6     CC        Data Set Ready
  15274.   7     AB        Signal Ground or Common
  15275.   8     CF        Received Line Signal Detect (Data Carrier Detect)
  15276.   9     -         Reserved/Unassigned
  15277.  10     -         Reserved/Unassigned
  15278.  11     -         Reserved/Unassigned
  15279.  12     SCF       Secondary Received Line Signal Detect
  15280.  13     SCB       Secondary Clear to Send
  15281.  14     SBA       Secondary Transmitted Data
  15282.  15     DB        Transmission Signal Element Timing
  15283.  16     SBB       Secondary Received Data
  15284.  17     DD        Receiver Element Timing
  15285.  18     -         Reserved/Unassigned
  15286.  19     SCA       Secondary Request to Send
  15287.  20     CD        Data Terminal Ready (DTR)
  15288.  21     CG        Signal Quality Detector
  15289.  22     CE        Ring Indicator
  15290.  Pin    Circuit   Name
  15291. 22     CE        Ring Indicator
  15292.  23     CH/CI     Data Signal Rate Detector
  15293.  24     DA        Transmit Signal Element Timing
  15294.  25     -         Reserved/Unassigned
  15295.  
  15296.  
  15297.  Figure B:  Asynch Set of Pins
  15298.  
  15299.  Pin  Abbrev.  Name                 Direction  Function
  15300.  
  15301.   1   -        Protective Ground    None       Ground
  15302.   2   TD       Transmitted Data     to DCE     Outbound Data
  15303.   3   RD       Received Data        to DTE     Incoming Data
  15304.   4   RTS      Request to Send      to DCE     DTE wants to send data
  15305.   5   CTS      Clear to Send        to DTE     DCE okays send request
  15306.   6   DSR      Data Set Ready       to DTE     DCE is ready to communicate
  15307.   7   -        Signal Common        None       Common Ground
  15308.   8   DCD      Data Carrier Detect  to DTE     Carrier (Linkup) between
  15309.                                                DTE/DCE
  15310.  20   DTR      Data Terminal Ready  to DCE     Enable DCE (say DTE is ready)
  15311.  22   RI       Ring Indicator       to DTE     Phone is ringing
  15312.  23   DSRD     Data Sig. Rate       Undefined  Complicated, but used to
  15313.                Detect                          negotiate data rates between
  15314.                                                DCE and DTE
  15315.  
  15316.  
  15317.  Figure C:
  15318.  
  15319.                 │                                            │
  15320.               ┌─┴┐ Pin No.                          Pin No. ┌┴─┐
  15321.               │  └─────┐             ┌──┐             ┌─────┘  │
  15322.               │TD     2╞═══──────────┘┌─│──────────═══╡2     TD│
  15323.               │RD     3╞═══───────────┘ └──────────═══╡3     RD│
  15324.               │RTS    4╞═══──┬─────────┐        ┌──═══╡4    RTS│
  15325.               │CTS    5╞═══──┘      ┌──│────────┴──═══╡5    CTS│
  15326.               │DSP    6╞═══────┐    │  │        ┌──═══╡6    DSP│
  15327.               │COMMON 7╞═══─────────│───────────│──═══╡7 COMMON│
  15328.               │DCD    8╞═══────│────┘  └────────│──═══╡8    DCD│
  15329.               │DTR   20╞═══──┐ ├────────────────│──═══╡20   DTR│
  15330.               │RI    22╞═══──│─┘                ├──═══╡22    RI│
  15331.               │  ┌─────┘     └──────────────────┘     └─────┐  │
  15332.               └─┬┘                                          └┬─┘
  15333.                 │                                            │
  15334.  
  15335.  
  15336.  ───────────────────────────────────────────────────────────────────────────
  15337.  Sample Code Fragments for TSRCOMM.ASM
  15338.  ───────────────────────────────────────────────────────────────────────────
  15339.  
  15340.  ;;       TSRCOMM.ASM - Written by Ross M. Greenberg
  15341.  ;;       Sample Code Fragments Follow
  15342.                                      ∙
  15343.                                      ∙
  15344.                                      ∙
  15345.  P1_INLEN        equ     400h    ; Define sizes of input and output
  15346.  P2_INLEN        equ     400h    ; buffers. High-water and low-water
  15347.  P1_OUTLEN       equ     400h    ; marks are a direct reflection of
  15348.  P2_OUTLEN       equ     400h    ; these values
  15349.  
  15350.  ;; Be careful with these settings if yours have different lengths for
  15351.  ;; each of the COM_INBUF's: these only play off COM1_INBUF
  15352.  
  15353.  HIGH_MARK equ (P1_INLEN/10 * 8) ; send XOFF when buffer is 80% full
  15354.  LOW_MARK  equ (P1_INLEN/10 * 2) ; send XON when buffer is 20% full
  15355.  
  15356.  ;;      Definitions of all 8250 Registers and individual bit meanings
  15357.  
  15358.  DATA            equ     0h      ; DATA I/O is from the base
  15359.  
  15360.  IER             equ     1h      ; Interrupt Enable Register
  15361.   IER_RDA        equ     1h      ; Received Data Available int bit
  15362.   IER_THRE       equ     2h      ; Transmitter Hold Reg. Empty int bit
  15363.   IER_RLS        equ     4h      ; Receive Line Status int bit
  15364.   IER_MS         equ     8h      ; Modem Status int bit
  15365.  
  15366.  IIR             equ     2       ; Interrupt Identification Register
  15367.   IIR_RLS        equ     5h      ; *equal* to if Receiver Line Status int
  15368.   IIR_RDA        equ     4h      ; *equal* to if character ready
  15369.   IIR_THRE       equ     2h      ; *equal* to if TX Buffer empty
  15370.   IIR_PEND       equ     1h      ; set to zero if any interrupt pending
  15371.   IIR_MS         equ     0h      ; *equal* to if Modem Status int
  15372.  
  15373.  LCR             equ     3h      ; Line Control Register
  15374.   LCR_WLS0       equ     0h      ; Word Length Select Bit 0
  15375.   LCR_WLS1       equ     1h      ; Word Length Select Bit 1
  15376.   LCR_STOPBITS   equ     4h      ; number of stop bits
  15377.   LCR_PARITYEN   equ     8h      ; Enable Parity (see SPARITY & EPARITY)
  15378.   LCR_EPARITY    equ     10h     ; Even Parity Bit
  15379.   LCR_SPARITY    equ     20h     ; Stick Parity
  15380.   LCR_BREAK      equ     40h     ; set if break is desired
  15381.   LCR_DLAB       equ     80h     ; Divisor Latch Access Bit
  15382.  
  15383.  MCR             equ     4h      ; Modem Control Register
  15384.   MCR_DTR        equ     1h      ; Data Terminal Ready
  15385.   MCR_RTS        equ     2h      ; Request To Send
  15386.   MCR_OUT1       equ     4h      ; Output 1 (nobody uses this!)
  15387.   MCR_OUT2       equ     8h      ; Out 2 (Sneaky Int enable)
  15388.   MCR_LOOP       equ     10h     ; Loopback enable
  15389.  
  15390.  LSR             equ     5       ; Line Status Register
  15391.   LSR_DATA       equ     1h      ; Data Ready Bit
  15392.   LSR_OVERRUN    equ     2h      ; Overrun Error Bit
  15393.   LSR_PARITY     equ     4h      ; Parity Error Bit
  15394.   LSR_FRAMING    equ     8h      ; Framing Error Bit
  15395.   LSR_BREAK      equ     10h     ; Break Detect (sometimes an error!)
  15396.   LSR_THRE       equ     20h     ; Transmit Holding Register Empty
  15397.   LSR_TSRE       equ     40h     ; Transmit Shift Register Empty
  15398.  
  15399.  MSR             equ     6       ; Modem Status Register
  15400.   MSR_DEL_CTS    equ     1h      ; Delta Clear To Send
  15401.   MSR_DEL_DSR    equ     2h      ; Delta Data Set Ready
  15402.   MSR_EDGE_RI    equ     4h      ; Trailing Edge of Ring Indicator
  15403.   MSR_DEL_SIGD   equ     8h      ; Delta Receive Line Signal Detect
  15404.   MSR_CTS        equ     10h     ; Clear To Send
  15405.   MSR_DSR        equ     20h     ; Data Set Ready
  15406.   MSR_RI         equ     40h     ; Ring Indicator - during entire ring
  15407.   MSR_DCD        equ     80h     ; Data Carrier Detect - on-line
  15408.  CTRL_PORT       equ     20h     ; The 8259 lives here
  15409.  INT_EOI         equ     20h     ; The End of Interrupt reset value
  15410.  INT_MASK_PORT   equ     21h     ; The mask for the 8259 lives here
  15411.  COM1_MASK       equ     0efh    ;
  15412.  COM2_MASK       equ     0f7h    ;
  15413.  INTNO_COM1      equ     0ch     ; The physical interrupt number for
  15414.  INTNO_COM2      equ     0bh     ; Comm1 and for Comm2
  15415.  
  15416.  PORT1   equ     3f8h            ; Physical ports where Comm1 & Comm2
  15417.  PORT2   equ     2f8h            ; should be. See sidebar 1 for info.
  15418.                                      ∙
  15419.                                      ∙
  15420.                                      ∙
  15421.  ;;                      COMM PORT BLOCK   (CPB)
  15422.  ;;      Comm Port Block defines information unique for each comm port
  15423.  ;;      and includes information such as what the original interrupt
  15424.  ;;      vector pointed to, which parameters are set, etc.
  15425.  CPB     struc
  15426.  
  15427.  cpb_base        dw      ?       ; base port of comm port (2F8, 3F8)
  15428.  cpb_nint_off    dw      ?       ; new interrupt offset address
  15429.  cpb_pic_mask    db      ?       ; mask for enabling ints from 8259
  15430.  cpb_int_no      db      ?       ; what interrupt we are
  15431.  cpb_mode        dw      ?       ; whatever modes we have turned on
  15432.  cpb_timeout     dw      ?       ; time-out value off timer tick
  15433.  cpb_in_xoff     dw      0       ; true if we output an XOFF
  15434.  cpb_out_xoff    dw      0       ; true if an XOFF was sent to us
  15435.  cpb_inbase      dw      ?       ; start of input buffer
  15436.  cpb_inlen       dw      ?       ; length of input buffer allocated
  15437.  cpb_inhead      dw      ?       ; pointer to next input char location
  15438.  cpb_intail      dw      ?       ; pointer to last input char location
  15439.  cpb_incnt       dw      0       ; count of how many inp chars outstanding
  15440.  cpb_inerrors    dw      ?       ; pointer to the error bits
  15441.  cpb_outbase     dw      ?       ; start of output header
  15442.  cpb_outlen      dw      ?       ; total length of output buffer allocated
  15443.  cpb_outhead     dw      ?       ; pointer to next output char location
  15444.  cpb_outtail     dw      ?       ; pointer to last output char location
  15445.  cpb_outcnt      dw      0       ; count of how many outp chars outstanding
  15446.  cpb_outend      dw      ?       ; ptr to the end of the output buffer
  15447.  cpb_tx_stat     dw      0       ; set to no interrupts turned on
  15448.  cpb_oint_add    dw      ?       ; original int offset:segment order
  15449.                  dw      ?
  15450.  CPB     ends
  15451.                                      ∙
  15452.                                      ∙
  15453.                                      ∙
  15454.  ;;                      HANDSHAKING OPTIONS
  15455.  BREAK_IS_ERROR_OPTION   equ     01h     ; Set these bits (OR combination)
  15456.  DSR_INPUT_OPTION        equ     02h     ; to set desired options. Then
  15457.  DCD_INPUT_OPTION        equ     04h     ; set CX to this value, DX to the
  15458.  CTS_OUTPUT_OPTION       equ     08h     ; comm port desired, and generate
  15459.  XOFF_INPUT_OPTION       equ     10h     ; an INT0x14 with AH=4 and AL=1
  15460.  XOFF_OUTPUT_OPTION      equ     20h
  15461.  DTR_OPTION              equ     40h
  15462.  XON_IS_ANY_OPTION       equ     80h
  15463.  TX_INTS_ON_OPTION       equ     100h
  15464.  ;; Masm has a 256-byte static initialization limit.  NC is shorter than
  15465.  ;; NO_CHARS....
  15466.  NC              equ     0
  15467.  
  15468.  ;; WARNING!      Do not move the error array away from its approriate
  15469.  ;;               error array, or you'll probably crash at some point!
  15470.  
  15471.  com1_inbuf      db      P1_INLEN dup(0)          ; Allocate space for
  15472.  com1_errs       db      (P1_INLEN/8) + 1 dup(0)  ; both comm ports:
  15473.  com2_inbuf      db      P2_INLEN dup(0)          ; input, error and
  15474.  com2_errs       db      (P2_INLEN/8) + 1 dup(0)  ; output buffer
  15475.  com1_outbuf     db      P1_OUTLEN dup(0)
  15476.  com2_outbuf     db      P2_OUTLEN dup(0)
  15477.  
  15478.  ;; CPB1 and CPB2
  15479.  ;; Allocate space and initialize COMM PORT BLOCKS for com1 and com2
  15480.  cpb1 CPB <PORT1,offset com1_isr,COM1_MASK,INTNO_COM1,DEF_FLAGS,
  15481.          DEF_T_OUT,NO_XON,NO_XON,offset com1_inbuf,P1_INLEN,
  15482.          offset com1_inbuf,offset com1_inbuf,NC,offset com1_errs,
  15483.          offset com1_outbuf,P1_OUTLEN,offset com1_outbuf,
  15484.          offset com1_outbuf,NC>
  15485.  cpb2 CPB <PORT2,offset com2_isr,COM2_MASK,INTNO_COM2,DEF_FLAGS,
  15486.          DEF_T_OUT,NO_XON,NO_XON,offset com2_inbuf,P2_INLEN,
  15487.          offset com2_inbuf,offset com2_inbuf,NC,offset com2_errs,
  15488.          offset com2_outbuf,P2_OUTLEN,offset com2_outbuf,
  15489.          offset com2_outbuf,NC>
  15490.                                      ∙
  15491.                                      ∙
  15492.                                      ∙
  15493.  xmit_int        proc    near
  15494.          cmp     cpb_outcnt[si], 0        ; any work to do?
  15495.          jnz     xmit1                    ; yep!
  15496.          call    till_clear               ; wait for transmitter to clear
  15497.          call    tx_off                   ; turn xmit interrupts off
  15498.          ret                              ; and return
  15499.  xmit1:
  15500.          mov     bx, cpb_outtail[si]      ; get next character to xmit
  15501.          inc     bx                       ; now point right on it
  15502.          cmp     bx, cpb_outend[si]       ; cmp to the end
  15503.          jnz     xmit2                    ; if not past the end, jump
  15504.          mov     bx, cpb_outbase[si]      ; past end, reset to the head
  15505.  xmit2:
  15506.          cli                              ; don't get interrupted now
  15507.          dec     cpb_outcnt[si]           ; decr. count of chars to go
  15508.          mov     cpb_outtail[si], bx      ; save a pointer to next char
  15509.          mov     al, [bx]                 ; get the character in al
  15510.          cmp     cpb_outcnt[si], 0        ; any work left to do?
  15511.          jnz     out_it                   ; yep!
  15512.          call    till_clear               ; wait for transmitter to clear
  15513.          call    tx_off                   ; turn xmit interrupts off
  15514.  out_it:
  15515.          call    out_char                 ; output the character
  15516.          ret
  15517.  xmit_int        endp
  15518.                                      ∙
  15519.                                      ∙
  15520.                                      ∙
  15521.    interrupt_table label word               ;
  15522.          dw      offset ms_int            ; modem status int (ret only)
  15523.          dw      offset xmit_int          ; transmitter int
  15524.          dw      offset rda_int           ; character ready int
  15525.          dw      offset err_int           ; receiver line error int
  15526.                                      ∙
  15527.                                      ∙
  15528.                                      ∙
  15529.  com1_isr        proc far ; Entry point for comm1 interrupts
  15530.          push    ax
  15531.          lea     ax, cpb1
  15532.  jmp     short   common_isr
  15533.  com1_isr        endp
  15534.  
  15535.  com2_isr        proc far ; Entry point for comm2 interrupts
  15536.          push    ax
  15537.          lea     ax, cpb2
  15538.          jmp     short   common_isr
  15539.  com2_isr        endp
  15540.                                      ∙
  15541.                                      ∙
  15542.                                      ∙
  15543.  common_isr      proc    near             ; IRQ's come to here. If not
  15544.          push    bx                       ; ours jump to old int vector
  15545.          push    cx
  15546.          push    dx
  15547.          push    si
  15548.          push    di
  15549.          push    ds
  15550.          push    cs                       ; addressing off ds as cs
  15551.          pop     ds                       ; makes it easier to think
  15552.          mov     si, ax                   ; move in the cpb
  15553.          mov     di, cpb_base[si]         ; get the base port
  15554.  
  15555.          lea     dx, IIR[di]              ; and then the int ID Reg
  15556.          in      al, dx                   ; get the interrupt type
  15557.          test    al, IIR_PEND             ; is there a pending int?
  15558.          jz      is_mine                  ; interrupt on *this* chip!
  15559.  other_int:
  15560.          cli                              ; turn off ints since this
  15561.                                           ; is non-reentrant
  15562.          mov     ax, cpb_oint_add[di]     ; grab the old int out of
  15563.          mov     old_int, ax              ; the structure
  15564.          mov     ax, cpb_oint_add[di][2]
  15565.          mov     old_int[2], ax
  15566.          pop     ds                       ; pop everything back
  15567.          pop     di
  15568.          pop     si
  15569.          pop     dx
  15570.          pop     cx
  15571.          pop     bx
  15572.          pop     ax
  15573.          jmp     dword ptr cs:[old_int]   ; jump to whatever was there
  15574.  polling_loop:                            ; this is required to be sure
  15575.                                           ; we haven't lost any ints
  15576.          lea     dx, IIR[di]              ; load the int ID Reg
  15577.          in      al, dx                   ;
  15578.          test    al, IIR_PEND             ; is there a pending int?
  15579.          jnz     clear                    ; no. time to return
  15580.  
  15581.  is_mine:
  15582.          and     ax, 06h
  15583.          mov     bx, ax
  15584.          mov     bx, interrupt_table[bx]
  15585.          push    di                       ; save di for polling loop
  15586.          call    bx
  15587.          pop     di
  15588.          jmp     polling_loop             ; time to check for more work
  15589.  clear:                                   ; no further int processing
  15590.          pop     ds                       ; pop everything back
  15591.          pop     di
  15592.          pop     si
  15593.          pop     dx
  15594.          pop     cx
  15595.          pop     bx
  15596.          cli                              ; interrupts off, then reset
  15597.          mov     al, INT_EOI              ; interrupts on the 8259
  15598.          out     CTRL_PORT, al
  15599.  no_eoi:
  15600.          pop     ax
  15601.          iret                             ; iret will turn ints back on
  15602.  common_isr      endp
  15603.                                      ∙
  15604.                                      ∙
  15605.                                      ∙
  15606.  int14_functions label   word             ;
  15607.          dw      offset  init14           ; initialize the port
  15608.          dw      offset  send14           ; send the character in al
  15609.          dw      offset  get14            ; return next char in al,
  15610.                                           ; status in ah
  15611.          dw      offset  stat14           ; get serial status,return in ax
  15612.          dw      offset  newfuncs14       ; all of the new functions
  15613.  
  15614.  get_baud        proc    near             ; AX is the offset,
  15615.          shl     ax, 1                    ; divisor returned in AX
  15616.          push    bx                       ; make the table offset
  15617.          mov     bx, ax
  15618.          mov     ax, baudrate_table[bx]   ; and get the divisor
  15619.          pop     bx
  15620.          ret
  15621.  get_baud        endp
  15622.  
  15623.  baudrate_table  label   word
  15624.          dw      1047                     ; 110 baud
  15625.          dw      768                      ; 150 baud
  15626.          dw      384                      ; 300 baud
  15627.  
  15628.          dw      192                      ; 600 baud
  15629.          dw      96                       ; 1200 baud
  15630.          dw      48                       ; 2400 baud
  15631.          dw      24                       ; 4800 baud
  15632.          dw      12                       ; 9600 baud
  15633.          dw      6                        ; 19200 baud
  15634.          dw      3                        ; 38400 baud
  15635.                                      ∙
  15636.                                      ∙
  15637.                                      ∙
  15638.  funcs_table     label   word             ;
  15639.          dw      offset  new00            ; Each function corresponds to
  15640.          dw      offset  new01            ; AL value used for
  15641.          dw      offset  new02            ; subfunction
  15642.          dw      offset  new03            ;
  15643.          dw      offset  new04            ;
  15644.          dw      offset  new05            ;
  15645.          dw      offset  new06            ;
  15646.          dw      offset  new07            ;
  15647.          dw      offset  new08            ;
  15648.  
  15649.  newfuncs14      proc    near
  15650.          cmp     al, 08h                  ; out of bounds?
  15651.          jle     dispatch                 ; no
  15652.          mov     ax, 0ffffh               ; yes, error code
  15653.          ret
  15654.  dispatch:
  15655.          call    get_cpb                  ; get si to point to proper cpb
  15656.          mov     di, cpb_base[si]         ; point the ports!
  15657.          xor     bx, bx
  15658.          mov     bl, al
  15659.          shl     bx, 1
  15660.          mov     bx, funcs_table[bx]
  15661.          call    bx
  15662.          ret
  15663.  newfuncs14      endp
  15664.  
  15665.  new00   proc    near
  15666.          mov     ax, special_return_value
  15667.          ret
  15668.  new00   endp
  15669.  
  15670.  new01   proc    near
  15671.          mov     cpb_mode[si], cx         ; move the new mode in
  15672.          call    init_buffers             ; and reset the pointers
  15673.          ret
  15674.  new01   endp
  15675.  
  15676.  new02   proc    near
  15677.          lea     dx, LCR[di]              ; get the Latch
  15678.          in      al, dx
  15679.          or      al, LCR_DLAB             ; turn on the divisor
  15680.          out     dx, al                   ; in the chip
  15681.          push    cx
  15682.          mov     ax, cx
  15683.          and     ax, 00e0h                ; only the highest three bits
  15684.          mov     cl, 5
  15685.          shr     ax, cl
  15686.          add     ax, 7                    ; let offset start at 8 (19200)
  15687.          call    get_baud                 ; then get the correct divisor
  15688.                                           ; allows higher than 9600
  15689.          pop     cx
  15690.          lea     dx, DATA[di]             ; get the base address
  15691.          out     dx, ax                   ; output the whole word
  15692.          lea     dx, LCR[di]              ; get the Latch
  15693.          mov     al, cl                   ; get the other parameters and
  15694.          and     al, 01fh                 ; mask only parity, stop bits,
  15695.                                           ; word length
  15696.          out     dx, al                   ; set the params
  15697.          ret
  15698.  new02   endp
  15699.  
  15700.  new03   proc    near
  15701.          mov     cpb_timeout[si], cx
  15702.          ret
  15703.  new03   endp
  15704.  
  15705.  new04   proc    near
  15706.          cli
  15707.          mov     cpb_incnt[si], NO_CHARS
  15708.          mov     ax, cpb_inbase[si]
  15709.          mov     cpb_inhead[si], ax
  15710.          mov     cpb_intail[si], ax
  15711.          sti
  15712.          ret
  15713.  new04   endp
  15714.  
  15715.  new05   proc    near
  15716.          mov     ax, cpb_incnt[si]
  15717.          ret
  15718.  new05   endp
  15719.  
  15720.  new06   proc    near
  15721.          cli
  15722.          mov     cpb_outcnt[si], NO_CHARS
  15723.          mov     ax, cpb_outbase[si]
  15724.          mov     cpb_outhead[si], ax
  15725.          mov     cpb_outtail[si], ax
  15726.          sti
  15727.          ret
  15728.  new06   endp
  15729.  
  15730.  new07   proc    near
  15731.          mov     ax, cpb_outcnt[si]
  15732.          ret
  15733.  new07   endp
  15734.  
  15735.  new08   proc    near
  15736.          mov     si, offset cpb1          ; set up for port1
  15737.          cmp     cpb_oint_add[si], 0      ; com port installed?
  15738.          jz      new0801                  ; no
  15739.          call    unset_up                 ; and kill this comm port
  15740.  new0801:
  15741.          mov     si, offset cpb2          ; set up for port2
  15742.          cmp     cpb_oint_add[si], 0      ; com port installed?
  15743.          jz      new0802                  ; no
  15744.          call    unset_up                 ; and kill this comm port
  15745.  new0802:
  15746.          cli
  15747.          mov     dx, old_int14
  15748.          mov     al, 014h
  15749.          push    ds
  15750.          mov     ds, old_int14[2]
  15751.          DOSINT  25h                      ; reset the serial port int
  15752.          pop     ds
  15753.          mov     dx, orig_timer
  15754.          mov     al, TIMER_TICK_INT_NO
  15755.          push    ds
  15756.          mov     ds, orig_timer[2]
  15757.          DOSINT  25h                      ; reset the timer_tick int
  15758.          pop     ds
  15759.          push    cs
  15760.          pop     es                       ; free up our own memory
  15761.          DOSINT  49h                      ; the environment
  15762.          sti
  15763.          ret
  15764.  new08   endp
  15765.                                      ∙
  15766.                                      ∙
  15767.                                      ∙
  15768.  
  15769.  
  15770.  ───────────────────────────────────────────────────────────────────────────
  15771.  Combining Interrupts and Polling: An Adventure in Programming
  15772.  ───────────────────────────────────────────────────────────────────────────
  15773.  
  15774.  One of the things that made writing a program such as TSRCOMM so
  15775.  interesting was the amount of time spent thinking, "But, that should have
  15776.  worked!"
  15777.  
  15778.  When initially contemplated, TSRCOMM was going to be fully interrupt
  15779.  driven; no polling  was to be included at all. A good look at the
  15780.  hardware, confirmed by experimental programming, showed that it was
  15781.  possible for a "dual interrupt" system, one capable of handling transmit
  15782.  buffer empty and data interrupts, to actually lose an interrupt while
  15783.  processing another.
  15784.  
  15785.  Therefore, much to my chagrin, you'll find a polling loop right in the
  15786.  middle of the COMMON_ISR interrupt routine. The loop causes the code to
  15787.  continually cycle once an interrupt is generated until, by polling the
  15788.  hardware and reading the appropriate ports, it determines that there is no
  15789.  more work to be done during this cycle.
  15790.  
  15791.  Fortunately, once compiled, this ugliness is rarely noticed.
  15792.  
  15793.  ████████████████████████████████████████████████████████████████████████████
  15794.  
  15795.  BLOWUP: A Windows Utility for Viewing and Manipulating Bitmaps
  15796.  
  15797.  Charles Petzold
  15798.  
  15799.  When you first start programming for Microsoft(R) Windows, the concept of a
  15800.  bitmap seems rather easy to grasp. A bitmap is simply a collection of bits
  15801.  that correspond directly to the scan lines and pixels of a rectangular
  15802.  display image.
  15803.  
  15804.  However, working with bitmaps is not quite as easy as understanding them.
  15805.  Even the seemingly simple chore of displaying a bitmap on the client area
  15806.  of a window can be puzzling. If you search for a Windows function by the
  15807.  name of DrawBitmap, you do so in vain. The function does not exist, and by
  15808.  the time you learn that a "memory display context" is necessary for the
  15809.  job, you may be nostalgically recalling how easy and simple life was when
  15810.  you programmed strictly in character mode.
  15811.  
  15812.  BLOWUP is a program designed to dispel some of the mysteries of bitmaps
  15813.  and memory display contexts and provide a little fun as well. With BLOWUP
  15814.  you can use the mouse to transfer almost anything that appears on the
  15815.  screen to the Windows clipboard in a bitmap format. BLOWUP is also a
  15816.  "clipboard viewer" of bitmaps and will display any bitmap currently in the
  15817.  clipboard. As its name implies, BLOWUP will blow up or shrink down the
  15818.  bitmap to fit its own client area. Figure 1 shows BLOWUP's client area and
  15819.  the contents of the clipboard after BLOWUP has been used to capture part
  15820.  of the MS-DOS(R) Executive window.
  15821.  
  15822.  Once the bitmap is in the clipboard, you can transfer the image to any
  15823.  Windows program that can handle bitmaps. (Note that some programs, such as
  15824.  WRITE, PAINT, and CARDFILE, will convert a color bitmap to monochrome,
  15825.  while others, such as Aldus Corp.'s PageMaker(R), will not accept color
  15826.  bitmaps at all.)
  15827.  
  15828.  Although the image in the clipboard is the actual size of the image as it
  15829.  appeared on the screen, you can use BLOWUP a second time to transfer the
  15830.  expanded image in BLOWUP's client area to the clipboard. BLOWUP thus
  15831.  becomes a tool to manipulate bitmaps manually──you can crop them, turn them
  15832.  upside down or left to right, shrink them, and blow them up. The BLOWUP
  15833.  source code can also help you learn how to create bitmaps, display them on
  15834.  the screen, write a clipboard viewer, transfer bitmaps between your
  15835.  program and the clipboard, "capture" and track the mouse, and you can even
  15836.  draw outside the client area of your window.
  15837.  
  15838.  With an installed Microsoft(R) Windows Software Development Kit and
  15839.  Microsoft(R) C Compiler Version 4.0, you can create BLOWUP.EXE by
  15840.  executing:
  15841.  
  15842.    MAKE BLOWUP
  15843.  
  15844.  
  15845.  Capturing Images
  15846.  
  15847.  BLOWUP requires a mouse. The program is simple to use once you get the
  15848.  hang of it, but here are the precise instructions:
  15849.  
  15850.    1. The first step is to click the mouse in BLOWUP's client area. The
  15851.       cursor will then be changed to a crosshair.
  15852.  
  15853.    2. Move the mouse cursor to the upper left-hand corner of the screen area
  15854.       you want to capture. Press on the mouse button.
  15855.  
  15856.    3. Drag the mouse (with the button depressed) to the lower right-hand
  15857.       corner of the screen area you want to capture. As you move the mouse,
  15858.       BLOWUP briefly displays the blocked-out image in reverse video. Release
  15859.       the mouse button. The image will be transferred to the clipboard and
  15860.       then to BLOWUP's client area.
  15861.  
  15862.  If you block out the image starting at the lower left-hand corner, the
  15863.  image will then be turned upside down, both in the clipboard and in
  15864.  BLOWUP's client area. Starting at the upper right-hand corner will flip
  15865.  it around the vertical axis; starting from the lower right-hand corner
  15866.  will flip it both horizontally and vertically.
  15867.  
  15868.  
  15869.  Capturing the Mouse
  15870.  
  15871.  The first problem BLOWUP has to solve is how to track the mouse when it is
  15872.  outside of BLOWUP's client area. Normally a Windows program receives mouse
  15873.  messages only when the mouse cursor is positioned over the program's
  15874.  window. In order to get around this restriction, BLOWUP uses a technique
  15875.  called "capturing the mouse," which requires one Windows call:
  15876.  
  15877.    SetCapture (hWnd) ;
  15878.  
  15879.  After this call, all mouse movement messages and all mouse button messages
  15880.  will be directed to the window function whose handle is hWnd. The mouse
  15881.  capture is ended by a call to ReleaseCapture.
  15882.  
  15883.  The lParam parameter that accompanies mouse messages contains the current
  15884.  position of the mouse relative to the upper left-hand corner of the
  15885.  window's client area. The x coordinate is in the low word of lParam, and
  15886.  the y coordinate is in the high word. After you capture the mouse, one or
  15887.  both of these coordinates will be negative if the mouse is to the left of
  15888.  or above your window's client area.
  15889.  
  15890.  BLOWUP maintains two Boolean variables, bCapturing and bBlocking, which
  15891.  are used to keep track of what mode it is in while it is processing the
  15892.  WM_LBUTTONDOWN, WM_LBUTTONUP, and WM_MOUSEMOVE messages. During the first
  15893.  button-down message, BLOWUP sets the bCapturing flag, captures the mouse,
  15894.  and displays the crosshair mouse cursor. On the second button-down
  15895.  message, BLOWUP sets the bBlocking flag and saves the position of the
  15896.  mouse in org ("origin"), a structure of type POINT. That's the first
  15897.  corner of the rectangle you'll be blocking out.
  15898.  
  15899.  When BLOWUP receives a WM_MOUSEMOVE message while bBlocking is set, it
  15900.  retrieves the new mouse position in another POINT structure called len
  15901.  ("length") and subtracts from that the origin. The result is the size of
  15902.  the image measured from the origin. (The values can be negative.) BLOWUP
  15903.  also calls the routine InvertBlock twice, once to invert the colors of the
  15904.  blocked-out image, and the second time in order to change it back to
  15905.  normal.
  15906.  
  15907.  
  15908.  Painting
  15909.  
  15910.  The inversion of the blocked-out rectangle may shock some Windows
  15911.  programmers because it requires that BLOWUP paint outside its client area,
  15912.  which is normally impossible. When a Windows program prepares for
  15913.  painting, it obtains a handle to a display context using the GetDC or
  15914.  BeginPaint function. This display context allows a program to paint only
  15915.  within its client area.
  15916.  
  15917.  But BLOWUP's InvertBlock routines use the less common CreateDC function to
  15918.  obtain a display context. This function is normally used for obtaining a
  15919.  display context for a printer, but it can also obtain a display context
  15920.  for the entire screen. The first parameter is the string "DISPLAY", and
  15921.  the other three parameters are set to NULL. The origin of this display
  15922.  context is the upper left-hand corner of the display.
  15923.  
  15924.  Because the coordinates of the org structure are relative to the upper
  15925.  left-hand corner of BLOWUP's client area, the point must be converted to
  15926.  screen coordinates by using the ClientToScreen function first. BLOWUP
  15927.  then calls PatBlt ("pattern block transfer") with a raster operation code
  15928.  of DSTINVERT to invert the rectangular area of the display.
  15929.  
  15930.  Painting outside a program's window is generally not a polite thing to do,
  15931.  which is why BLOWUP restores the area right away with another call to
  15932.  InvertBlock. This is a good compromise──it gives you a visual indication of
  15933.  the area you're blocking out with BLOWUP, but it doesn't permanently
  15934.  affect the windows of other programs. And because the MS-DOS version of
  15935.  Windows is nonpreemptive, there is positively no chance of anything
  15936.  changing the screen between the two consecutive calls to InvertBlock.
  15937.  
  15938.  
  15939.  Creating the Bitmap
  15940.  
  15941.  When BLOWUP receives a WM_LBUTTONDOWN message while the bBlocking flag is
  15942.  set, it must create a bitmap containing the blocked-out image and transfer
  15943.  it to the clipboard. This job requires us to approach that strange animal
  15944.  called the "memory display context" and attempt to make friends with it.
  15945.  
  15946.  A display context is a data structure that describes a physical display
  15947.  device, such as a screen or a printer. When a program obtains a handle to
  15948.  a display context, the program is also getting permission to paint on the
  15949.  device.
  15950.  
  15951.  A memory display context is very similar to a normal display context
  15952.  except that the display "surface" is a block of memory. When you first
  15953.  obtain a handle to a memory display context through the CreateCompatibleDC
  15954.  function, this display surface is very small──exactly one pixel wide, one
  15955.  pixel high, and monochrome, which is not very useful. What you must do
  15956.  before actually working with a memory display context is select a bitmap
  15957.  into it by using SelectObject.
  15958.  
  15959.  When you do this, the bitmap becomes the display surface of the memory
  15960.  display context. The upper left-hand corner of the bitmap corresponds to
  15961.  the display context coordinate of (0,0). Any image previously in the
  15962.  bitmap becomes part of the memory display context's display surface. Any
  15963.  drawing you do on that memory display context is actually performed on the
  15964.  bitmap. When you delete the memory display context, you are then left with
  15965.  a bitmap containing everything that you painted on it.
  15966.  
  15967.  There are a variety of methods for creating bitmaps; the one BLOWUP uses
  15968.  is the CreateCompatibleBitmap call. This function creates a bitmap with
  15969.  the same number of color planes and color bits per pixel as the display
  15970.  context specified in the first parameter of the function──which in this
  15971.  case is the video display context. In BLOWUP, the height and width of the
  15972.  bitmap are the absolute values of the two sizes in the len structure. The
  15973.  bitmap itself is uninitialized, which means that it contains random data.
  15974.  
  15975.  Once BLOWUP selects the bitmap into the memory display context, all it
  15976.  needs to do is call StretchBlt to transfer the blocked-out area of the
  15977.  screen display context to the memory display context. The image winds up
  15978.  in the bitmap.
  15979.  
  15980.  Because the bitmap is the same height and width as the blocked-out display
  15981.  area, you may be wondering why I use StretchBlt rather than BitBlt for
  15982.  this job. BitBlt would work fine if you only blocked out the image
  15983.  starting at the upper left-hand corner; otherwise BLOWUP must flip the
  15984.  image, and BitBlt cannot flip images whereas StretchBlt can.
  15985.  
  15986.  
  15987.  Copying the Bitmap
  15988.  
  15989.  Getting the bitmap into the clipboard is the easy part of the job. BLOWUP
  15990.  simply calls OpenClipboard, EmptyClipboard, SetClipboardData, and
  15991.  CloseClipboard, and it's done.
  15992.  
  15993.  Normally a program would delete a bitmap after it has finished using it.
  15994.  However, when a bitmap is transferred to the clipboard, the bitmap becomes
  15995.  the responsibility of Windows itself. Windows will delete the bitmap the
  15996.  next time a program transfers something else to the clipboard.
  15997.  
  15998.  
  15999.  Becoming a Viewer
  16000.  
  16001.  BLOWUP is also a clipboard viewer, which means that it is notified
  16002.  whenever the contents of the clipboard change and will display the
  16003.  clipboard contents. Unlike the indiscriminate CLIPBRD.EXE program that
  16004.  comes packaged with Microsoft Windows, BLOWUP is very selective──it will
  16005.  only display bitmaps and will ignore the other clipboard formats.
  16006.  
  16007.  Becoming a clipboard viewer requires very little overhead. BLOWUP first
  16008.  makes a call to SetClipboardViewer while it is processing the WM_CREATE
  16009.  message. SetClipboardViewer returns the window handle of the previous
  16010.  clipboard viewer. BLOWUP then saves this as a static variable that is
  16011.  called hWndNext. Saving this window handle is very important──Windows
  16012.  maintains only one window handle as a "current clipboard viewer," and it
  16013.  relies on other programs to participate in the "clipboard viewer chain."
  16014.  
  16015.  Here's how it works: when the contents of the clipboard change, Windows
  16016.  sends the current clipboard viewer (the most recent program to register
  16017.  itself as a clipboard viewer) a WM_DRAWCLIPBOARD message. The program that
  16018.  receives this message is responsible for sending the message to the next
  16019.  clipboard viewer, which is the window whose handle was returned from the
  16020.  SetClipboardViewer call. Every clipboard viewer will at that point see the
  16021.  message WM_DRAWCLIPBOARD as it ripples down the clipboard viewer chain.
  16022.  
  16023.  When a program wants to get out of the clipboard viewer chain, it must
  16024.  call ChangeClipboardChain. (BLOWUP does this during the processing of the
  16025.  WM_DESTROY message right before it terminates.) Notice that the two
  16026.  parameters to this function are the program's own window handle and
  16027.  hWndNext. Windows will then respond by sending to the current clipboard
  16028.  viewer a WM_CHANGECBCHAIN message with wParam equal to the window handle
  16029.  of the program removing itself from the chain and the low word of lParam
  16030.  equal to the window handle of the next clipboard viewer──the same two
  16031.  values that are passed along to ChangeClipboardChain. If BLOWUP finds that
  16032.  the clipboard viewer that is removing itself from the chain is the next
  16033.  clipboard viewer after BLOWUP──in which case hWndNext will be equal to
  16034.  wParam──then it must change its own stored value of hWndNext in order to
  16035.  effectively skip over the departing program in future WM_DRAWCLIPBOARD
  16036.  calls.
  16037.  
  16038.  That's the extent of the overhead required for being a clipboard viewer.
  16039.  Of course, a clipboard viewer will also do a little something while
  16040.  processing the WM_DRAWCLIPBOARD message to display the new clipboard
  16041.  contents. After sending the WM_DRAWCLIPBOARD message down the clipboard
  16042.  viewer chain, BLOWUP simply invalidates its own client area. This is what
  16043.  causes Windows to generate a WM_PAINT message for BLOWUP to recreate its
  16044.  client area.
  16045.  
  16046.  
  16047.  Processing the Message
  16048.  
  16049.  When BLOWUP receives a WM_PAINT message, it must get the bitmap out of the
  16050.  clipboard and display it in its client area. BLOWUP opens the clipboard
  16051.  with a call to OpenClipboard and then uses GetClipboardData to get a
  16052.  handle to the bitmap currently stored in the clipboard. It's possible that
  16053.  the clipboard will not contain a bitmap, in which case the function will
  16054.  return NULL. If that's the case, BLOWUP closes the clipboard and leaves
  16055.  its client area unpainted.
  16056.  
  16057.  If BLOWUP is successful in getting a bitmap from the clipboard, it creates
  16058.  another memory display context. Earlier, when it was processing the
  16059.  WM_LBUTTONUP message, BLOWUP used SelectObject to select an uninitialized
  16060.  bitmap into a memory display context in order to transfer an image from
  16061.  the screen to the bitmap. Now while it is processing WM_PAINT, BLOWUP
  16062.  selects the clipboard's bitmap into the memory display context in order to
  16063.  transfer the bitmap image to its own client area.
  16064.  
  16065.  BLOWUP gets the dimensions of the bitmap by a call to GetObject. This
  16066.  function copies information about the bitmap into the bm variable, which
  16067.  is a structure of type BITMAP. At this point, the only things that we are
  16068.  interested in are the bm.bmWidth (the width) and bm.bmHeight (the height)
  16069.  fields of the bitmap.
  16070.  
  16071.  It is the StretchBlt call again that does the transfer, this time from
  16072.  the memory display context to the screen display context. The source width
  16073.  and height are the dimensions of the bitmap; the destination width and
  16074.  height are the dimensions of BLOWUP's client area. StretchBlt then blows
  16075.  up or shrinks the image appropriately. StretchBlt takes a little time to
  16076.  execute, particularly when working with large display surfaces. It is for
  16077.  this reason that BLOWUP sets the cursor to the IDC_WAIT cursor, also known
  16078.  as the hourglass, during the transfer.
  16079.  
  16080.  
  16081.  The Stretching Mode
  16082.  
  16083.  You'll notice in BLOWUP.C that immediately before the call to StretchBlt,
  16084.  there is a call to SetStretchBltMode.The "stretching mode" is an attribute
  16085.  of the display context and governs what Windows does when it uses
  16086.  StretchBlt to reduce the size of an image. You might think that Windows
  16087.  simply throws away rows and columns of pixels, but that's not what happens
  16088.  in the default case.
  16089.  
  16090.  By default, Windows uses a stretching mode, which is called BLACKONWHITE.
  16091.  When an image is reduced, Windows combines rows or columns of pixels by
  16092.  performing a logical AND operation between adjacent bits. A particular
  16093.  pixel ends up as white (a 1-bit) only if all the pixels being combined
  16094.  into that pixel are also white. This will preserve a black image on a
  16095.  white background.
  16096.  
  16097.  The opposite of this is the stretching mode which is called WHITEONBLACK.
  16098.  Windows here performs a logical OR operation between adjacent pixels.
  16099.  The result is a black pixel (a 0-bit) only if all the adjacent pixels are
  16100.  also black. This preserves white images on a black background.
  16101.  
  16102.  These two stretching modes can have some strange effects. For instance,
  16103.  suppose you had a display context that was colored gray, which for most
  16104.  display adapters is accomplished by alternating black and white pixels. If
  16105.  you used StretchBlt to reduce the size of the image in half, then the
  16106.  default stretching mode, known as BLACKONWHITE, would cause the result to
  16107.  be entirely black. The WHITEONBLACK stretching mode would make it white.
  16108.  
  16109.  The third option is COLORONCOLOR. This is the stretching mode that causes
  16110.  Windows to do what you might have thought it was doing anyway──throw away
  16111.  rows and columns of pixels. A gray image is copied as gray. This is
  16112.  probably the best approach when a program has no knowledge of the
  16113.  type of image it will be dealing with. (Alternatively, you could add a
  16114.  menu option in BLOWUP to allow changing the stretching mode yourself.)
  16115.  
  16116.  
  16117.  A Few Restrictions
  16118.  
  16119.  I've been careful to state that BLOWUP can capture almost anything you see
  16120.  on the screen. If a pull-down menu is displayed, clicking the mouse in
  16121.  BLOWUP's client area to start the capture will shift the input focus to
  16122.  BLOWUP and the menu will go away. If a system modal dialog box (a dialog
  16123.  box that does not allow the user to switch to another application) is
  16124.  displayed, then the dialog box must be exited before you can use BLOWUP.
  16125.  
  16126.  Microsoft Windows Version 1.04 can't create bitmaps larger than 64Kb. If,
  16127.  however, you attempt to capture an entire 8-color high-resolution 84Kb EGA
  16128.  screen to the clipboard, the call CreateCompatibleBitmap will fail, and
  16129.  BLOWUP will beep to indicate the problem.
  16130.  
  16131.  You'll be pleased to know that Windows Version 2.0 doesn't have this
  16132.  limitation, and Figure 5 proves it. Figure 5 may look like a typical
  16133.  Windows 2.0 desktop, but it's actually BLOWUP maximized to use the full
  16134.  screen after capturing an 84Kb screen image.
  16135.  
  16136.  
  16137.  Figure 2:  BLOWUP make-file
  16138.  
  16139.  blowup.obj : blowup.c
  16140.         cl -c -d -D LINT_ARGS -Gsw -Os -W2 -Zdp blowup.c
  16141.  
  16142.  blowup.exe : blowup.obj blowup.def
  16143.         link4 blowup, /align:16, /map, /line, slibw, blowup
  16144.         mapsym blowup
  16145.  
  16146.  
  16147.  Figure 3:  BLOWUP.C source code file
  16148.  
  16149.  /* BLOWUP.C -- Capture Screen Image to Clipboard by Charles Petzold */
  16150.  
  16151.  #include <windows.h>
  16152.  #include <stdlib.h>
  16153.  
  16154.  long FAR PASCAL WndProc (HWND, unsigned, WORD, LONG) ;
  16155.  
  16156.  int PASCAL WinMain (hInstance, hPrevInstance, lpszCmdLine, nCmdShow)
  16157.       HANDLE         hInstance, hPrevInstance ;
  16158.       LPSTR          lpszCmdLine ;
  16159.       int            nCmdShow ;
  16160.       {
  16161.       static char    szAppName [] = "Blowup" ;
  16162.       HWND           hWnd ;
  16163.       MSG            msg ;
  16164.       WNDCLASS       wndclass ;
  16165.  
  16166.       if (!hPrevInstance)
  16167.            {
  16168.            wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
  16169.            wndclass.lpfnWndProc   = WndProc ;
  16170.            wndclass.cbClsExtra    = 0 ;
  16171.            wndclass.cbWndExtra    = 0 ;
  16172.            wndclass.hInstance     = hInstance ;
  16173.            wndclass.hIcon         = NULL ;
  16174.            wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
  16175.            wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
  16176.            wndclass.lpszMenuName  = NULL ;
  16177.            wndclass.lpszClassName = szAppName ;
  16178.  
  16179.            if (!RegisterClass (&wndclass))
  16180.                 return FALSE ;
  16181.            }
  16182.  
  16183.       hWnd = CreateWindow (szAppName, szAppName,
  16184.                      WS_TILEDWINDOW,
  16185.                      0, 0, 0, 0,
  16186.                      NULL, NULL, hInstance, NULL) ;
  16187.  
  16188.       ShowWindow (hWnd, nCmdShow) ;
  16189.       UpdateWindow (hWnd) ;
  16190.  
  16191.       while (GetMessage (&msg, NULL, 0, 0))
  16192.            {
  16193.            TranslateMessage (&msg) ;
  16194.            DispatchMessage (&msg) ;
  16195.            }
  16196.       return msg.wParam ;
  16197.       }
  16198.  
  16199.  void InvertBlock (hWnd, org, len)
  16200.       HWND  hWnd ;
  16201.       POINT org, len ;
  16202.       {
  16203.       HDC   hDC ;
  16204.  
  16205.       hDC = CreateDC ("DISPLAY", NULL, NULL, NULL) ;
  16206.       ClientToScreen (hWnd, &org) ;
  16207.       PatBlt (hDC, org.x, org.y, len.x, len.y, DSTINVERT) ;
  16208.       DeleteDC (hDC) ;
  16209.       }
  16210.  
  16211.  long FAR PASCAL WndProc (hWnd, iMessage, wParam, lParam)
  16212.       HWND           hWnd ;
  16213.       unsigned       iMessage ;
  16214.       WORD           wParam ;
  16215.       LONG           lParam ;
  16216.       {
  16217.       static BOOL    bCapturing, bBlocking ;
  16218.       static HWND    hWndNext ;
  16219.       static POINT   org, len ;
  16220.       static short   xClient, yClient ;
  16221.       BITMAP         bm ;
  16222.       HDC            hDC, hMemDC ;
  16223.       HBITMAP        hBitmap ;
  16224.       PAINTSTRUCT    ps ;
  16225.  
  16226.       switch (iMessage)
  16227.            {
  16228.            case WM_CREATE:
  16229.                 hWndNext = SetClipboardViewer (hWnd) ;
  16230.                 break ;
  16231.  
  16232.            case WM_SIZE:
  16233.                 xClient = LOWORD (lParam) ;
  16234.                 yClient = HIWORD (lParam) ;
  16235.                 break ;
  16236.  
  16237.            case WM_LBUTTONDOWN:
  16238.                 if (!bCapturing)
  16239.                      {
  16240.                      bCapturing = TRUE ;
  16241.                      SetCapture (hWnd) ;
  16242.                      SetCursor (LoadCursor (NULL, IDC_CROSS)) ;
  16243.                      }
  16244.                 else if (!bBlocking)
  16245.                      {
  16246.                      bBlocking = TRUE ;
  16247.                      org = MAKEPOINT (lParam) ;
  16248.                      }
  16249.                 break ;
  16250.  
  16251.            case WM_MOUSEMOVE:
  16252.                 if (bCapturing)
  16253.                      SetCursor (LoadCursor (NULL, IDC_CROSS)) ;
  16254.  
  16255.                 if (bBlocking)
  16256.                      {
  16257.                      len = MAKEPOINT (lParam) ;
  16258.                      len.x -= org.x ;
  16259.                      len.y -= org.y ;
  16260.  
  16261.                      InvertBlock (hWnd, org, len) ;
  16262.                      InvertBlock (hWnd, org, len) ;
  16263.                      }
  16264.                 break ;
  16265.  
  16266.            case WM_LBUTTONUP:
  16267.                 if (!bBlocking)
  16268.                      break ;
  16269.  
  16270.                 bCapturing = bBlocking = FALSE ;
  16271.                 SetCursor (LoadCursor (NULL, IDC_ARROW)) ;
  16272.                 ReleaseCapture () ;
  16273.  
  16274.                 if (len.x == 0 || len.y == 0)
  16275.                      break ;
  16276.  
  16277.                 hDC = GetDC (hWnd) ;
  16278.                 hMemDC = CreateCompatibleDC (hDC) ;
  16279.                 hBitmap = CreateCompatibleBitmap (hDC,
  16280.                                 abs (len.x), abs (len.y)) ;
  16281.                 if (hBitmap)
  16282.                      {
  16283.                      SelectObject (hMemDC, hBitmap) ;
  16284.                      StretchBlt (hMemDC, 0, 0, abs (len.x), abs (len.y),
  16285.                           hDC, org.x, org.y, len.x, len.y, SRCCOPY) ;
  16286.  
  16287.                      OpenClipboard (hWnd) ;
  16288.                      EmptyClipboard () ;
  16289.                      SetClipboardData (CF_BITMAP, hBitmap) ;
  16290.                      CloseClipboard () ;
  16291.                      }
  16292.                 else
  16293.                      MessageBeep (0) ;
  16294.  
  16295.                 DeleteDC (hMemDC) ;
  16296.                 ReleaseDC (hWnd, hDC) ;
  16297.                 break ;
  16298.  
  16299.            case WM_PAINT:
  16300.                 hDC = BeginPaint (hWnd, &ps) ;
  16301.                 OpenClipboard (hWnd) ;
  16302.  
  16303.                 if (hBitmap = GetClipboardData (CF_BITMAP))
  16304.                      {
  16305.                      SetCursor (LoadCursor (NULL, IDC_WAIT)) ;
  16306.  
  16307.                      hMemDC = CreateCompatibleDC (hDC) ;
  16308.                      SelectObject (hMemDC, hBitmap) ;
  16309.                      GetObject (hBitmap, sizeof (BITMAP), (LPSTR) &bm) ;
  16310.  
  16311.                      SetStretchBltMode (hDC, COLORONCOLOR) ;
  16312.                      StretchBlt (hDC, 0, 0, xClient, yClient,
  16313.                                  hMemDC, 0, 0, bm.bmWidth, bm.bmHeight,
  16314.                                                    SRCCOPY) ;
  16315.  
  16316.                      SetCursor (LoadCursor (NULL, IDC_ARROW)) ;
  16317.                      DeleteDC (hMemDC) ;
  16318.                      }
  16319.                 CloseClipboard () ;
  16320.                 EndPaint (hWnd, &ps) ;
  16321.                 break ;
  16322.  
  16323.            case WM_DRAWCLIPBOARD :
  16324.                 if (hWndNext)
  16325.                      SendMessage (hWndNext, iMessage, wParam, lParam) ;
  16326.  
  16327.                 InvalidateRect (hWnd, NULL, TRUE) ;
  16328.                 break;
  16329.  
  16330.            case WM_CHANGECBCHAIN :
  16331.                 if (wParam == hWndNext)
  16332.                      hWndNext = LOWORD (lParam) ;
  16333.  
  16334.                 else if (hWndNext)
  16335.                      SendMessage (hWndNext, iMessage, wParam, lParam) ;
  16336.                 break ;
  16337.  
  16338.            case WM_DESTROY:
  16339.                 ChangeClipboardChain (hWnd, hWndNext) ;
  16340.                 PostQuitMessage (0) ;
  16341.                 break ;
  16342.  
  16343.            default:
  16344.                 return DefWindowProc (hWnd, iMessage, wParam, lParam) ;
  16345.            }
  16346.       return 0L ;
  16347.       }
  16348.  
  16349.  MAKE File for BLOWUP
  16350.  
  16351.  blowup.obj : blowup.c
  16352.       cl -c -d -D LINT_ARGS -Gsw -Os -W2 -Zdp blowup.c
  16353.  
  16354.  blowup.exe : blowup.obj blowup.def
  16355.       link4 blowup, /align:16, /map /line, slibw, blowup
  16356.       mapsym blowup
  16357.  
  16358.  
  16359.  Figure 4:  BLOWUP.DEF Module Definition File
  16360.  
  16361.  NAME           BLOWUP
  16362.  DESCRIPTION    'Capture Screen Image to Clipboard by Charles Petzold'
  16363.  STUB           'WINSTUB.EXE'
  16364.  CODE           MOVABLE
  16365.  DATA           MOVABLE MULTIPLE
  16366.  HEAPSIZE       1024
  16367.  STACKSIZE      4096
  16368.  EXPORTS        WndProc
  16369.  
  16370.  ████████████████████████████████████████████████████████████████████████████
  16371.  
  16372.  Increase the Performance of Your Programs with a Math Coprocessor
  16373.  
  16374.  Marion Hansen and Lori Sargent
  16375.  
  16376.  The microprocessor in your personal computer's CPU is powerful, but it
  16377.  wasn't designed to handle complex math operations rapidly. Whether it's an
  16378.  8086, 8088, 80286, or 80386, your microprocessor will perform floating-
  16379.  point and transcendental calculations far more quickly and with greater
  16380.  precision if a math coprocessor is linked to it. Coprocessors also have
  16381.  capabilities useful for business computing. A coprocessor can process
  16382.  binary coded decimal numbers up to 18 digits long without round-off
  16383.  errors, perform arithmetic on integers from 2 x 10-9 through 2 x 109, and
  16384.  carry out math functions on real numbers as small as 3.4 x 10-4932 or as
  16385.  large as 1.1 x 104932.
  16386.  
  16387.  When software written to use a coprocessor performs certain types of math,
  16388.  it engages the coprocessor rather than the microprocessor. The coprocessor
  16389.  performs the calculation and returns the answer to the microprocessor.
  16390.  This entire process takes a fraction of the time required by the
  16391.  microprocessor executing alone. To give you an idea of how fast a
  16392.  coprocessor is, Figure 1 compares spreadsheet recalculation times with and
  16393.  without an 8087 math coprocessor.
  16394.  
  16395.  Besides performing certain kinds of math faster, coprocessors also save
  16396.  programming time. Because trigonometric, logarithmic, and exponential
  16397.  functions are built into the coprocessor's hardware, the programmer
  16398.  doesn't have to write these routines. And with the routines in the chip
  16399.  instead of in the code, programs are smaller. Coprocessors generate
  16400.  instructions for many numeric operations such as number conversions,
  16401.  arithmetic operations, and transcendental functions (tangents, exponents,
  16402.  and logarithms).
  16403.  
  16404.  A coprocessor is the most cost-effective way to increase number-crunching
  16405.  power. For a fraction of the cost of an accelerator board, a coprocessor
  16406.  can dramatically speed up floating-point calculations. And it won't
  16407.  consume a precious expansion slot (or two), since it fits in a socket
  16408.  already on the motherboard.
  16409.  
  16410.  Math coprocessors come in three varieties: the 8087 (for 8086- and 8088-
  16411.  based computers), the 80287 (for 80286-based computers), and the 80387
  16412.  (for 80386-based machines). The 8087 and 80287 are both available in three
  16413.  different speeds. The speed you need depends on how fast your computer
  16414.  drives the coprocessor socket, not on the speed of your microprocessor.
  16415.  For example, some 10-MHz computers drive the socket at 8 MHz and thus need
  16416.  a coprocessor that runs at 8 MHz, not 10 MHz. If you aren't sure which
  16417.  speed is correct for your computer, contact the manufacturer.
  16418.  
  16419.  Hundreds of application programs have been written to take advantage of
  16420.  the coprocessor's speed and precision, including business, engineering,
  16421.  graphics, statistical, and econometric packages. Many compilers and
  16422.  assemblers can benefit from a coprocessor as well. Using a coprocessor
  16423.  with one of these programs couldn't be easier, because all interfacing
  16424.  between microprocessor and coprocessor is built in. The only difference
  16425.  you'll notice is the increased speed.
  16426.  
  16427.  
  16428.  Development Tools
  16429.  
  16430.  Most of today's compilers and assemblers can generate coprocessor code.
  16431.  This includes all recent versions of Microsoft C, Pascal, and FORTRAN
  16432.  compilers, as well as Borland's Turbo Pascal. No matter which of these
  16433.  languages you're writing in, incorporating complex math into programs is
  16434.  not difficult with a coprocessor.
  16435.  
  16436.  In a high-level language, using the coprocessor is quite painless.
  16437.  Coprocessor instructions such as sine, square root, hyperbolic tangent,
  16438.  and log are built into manufacturer-supplied library routines.
  16439.  
  16440.  Assembly language programmers using Microsoft's Macro Assembler Version
  16441.  1.25 or later have the option of writing code that explicitly references
  16442.  coprocessor instructions or implicitly does so by linking in a math
  16443.  library such as those supplied with the Microsoft C, Pascal, or FORTRAN
  16444.  compilers. In addition, a number of other software vendors market
  16445.  specialized math libraries that perform many math functions and can be
  16446.  linked to programs written in a variety of languages.
  16447.  
  16448.  Although programs can usually call math library routines with or without a
  16449.  coprocessor, programs running on systems with a coprocessor will execute
  16450.  significantly faster. Figure 2 illustrates how much faster an 8-MHz
  16451.  computer performs floating-point instructions on typical spreadsheet data
  16452.  when a coprocessor is installed.
  16453.  
  16454.  Most high-level languages link an emulation library into any program that
  16455.  contains floating-point instructions or data. Code to check for the
  16456.  presence of the coprocessor is generated at run time. If a coprocessor is
  16457.  detected, it is used. If a coprocessor is not present, the emulation
  16458.  library is used. This way, programs written to take advantage of a
  16459.  coprocessor can run on systems without one.
  16460.  
  16461.  Debugging code that contains coprocessor instructions is not much
  16462.  different from debugging code written for the microprocessor alone. A good
  16463.  debugger, such as the CodeView facility included in Microsoft's C Compiler
  16464.  Version 4.0, lets you examine and change all the coprocessor registers,
  16465.  including status and control registers. CodeView displays data register
  16466.  contents in both their 80-bit internal hexadecimal form and their decimal
  16467.  equivalents. This makes debugging floating-point instructions no more
  16468.  difficult than debugging microprocessor instructions.
  16469.  
  16470.  
  16471.  Synergy
  16472.  
  16473.  The coprocessor is an extension of the microprocessor. (In fact, Intel
  16474.  calls the coprocessor the numeric processor extension, or NPX.) They share
  16475.  the same buses and memory. The microprocessor's status lines and queue
  16476.  status lines are directly connected to the coprocessor, so the coprocessor
  16477.  is able to track the microprocessor's instruction queue. The coprocessor
  16478.  monitors and decodes instructions without any overhead. It reads each
  16479.  instruction into its queue but executes only its own instructions,
  16480.  treating each microprocessor instruction as a no-operation (NOP) command.
  16481.  In turn, the microprocessor treats each coprocessor instruction as a NOP
  16482.  and executes only its own instructions. The microprocessor controls
  16483.  program execution, and the coprocessor controls numeric operations.
  16484.  
  16485.  Instead of the 8-bit registers in the 8088, the 16-bit registers in the
  16486.  8086 and 80286, or the 32-bit registers in the 80386, the coprocessor has
  16487.  80-bit data registers, which allow it to hold more information. The
  16488.  coprocessor's registers were designed to hold specific types of data and
  16489.  are significantly different from the microprocessor's general-purpose
  16490.  registers. Nonetheless, the two chips can still share data through common
  16491.  memory.
  16492.  
  16493.  
  16494.  Data Types
  16495.  
  16496.  Coprocessor registers were designed to store 80-bit floating-point
  16497.  numbers. This format, which Intel calls temporary real, is compatible with
  16498.  the proposed IEEE 754 floating-point standard. A temporary real number is
  16499.  composed of a sign bit, a 15-bit exponent, and a 64-bit significand.
  16500.  Although the coprocessor stores all data as temporary real numbers, it can
  16501.  also read and write data in six other formats: packed decimal, long real,
  16502.  long integer, short real, short integer, and word integer (see Figure 3).
  16503.  Coprocessor load and store instructions automatically convert the other
  16504.  six data types to temporary real format and back again. Microsoft's Macro
  16505.  Assembler allows these formats to be declared with the directives DW (word
  16506.  integer), DD (short integer and short real), DQ (long integer and long
  16507.  real), and DT (packed binary coded decimal and temporary real).
  16508.  
  16509.  The coprocessor stores numbers in normalized format (scientific notation).
  16510.  A number is normalized by shifting the 1 that's furthest to the left up or
  16511.  down until it occupies bit 63. The coprocessor assumes the number in the
  16512.  significand is a real number between 1 and 2. The exponent field specifies
  16513.  how far the digits must be shifted to get the correct number back. Because
  16514.  the exponent is stored as an unsigned value, an offset (bias) is added to
  16515.  it so negative numbers can be represented. This lets the coprocessor
  16516.  compare the magnitude of two numbers without first performing arithmetic
  16517.  on the exponents, and execution time is thus shortened.
  16518.  
  16519.  
  16520.  Registers
  16521.  
  16522.  Coprocessor computations occur in eight data registers. The registers can
  16523.  be accessed as a LIFO (last-in-first-out) stack, with instructions
  16524.  operating on the top one or two stack elements. Or the registers can act
  16525.  as a fixed register set, with instructions operating on explicitly
  16526.  designated registers.
  16527.  
  16528.  Unlike those of the microprocessor, the coprocessor's data registers don't
  16529.  have unique names. They're treated as indexed entries in a stack, with the
  16530.  top of the stack designated as register ST(0) and the others designated
  16531.  ST(1) and so on. Values are loaded into the coprocessor by pushing them
  16532.  onto the stack, and some (but not all) are retrieved by popping them off.
  16533.  Many coprocessor instructions operate only on the top of the stack. Most
  16534.  other instructions default to operating on the stack's top. All register
  16535.  addresses are relative to the top of the stack.
  16536.  
  16537.  A 3-bit top-of-stack pointer in another type of register──the status word
  16538.  register──identifies the current top-of-stack register. A push decrements
  16539.  the value in this pointer by 1 and loads a value into the new top
  16540.  register. A pop increments the value in the pointer by 1 and removes the
  16541.  value from the register currently at the top. The stack is circular and
  16542.  can be overwritten if not managed properly.
  16543.  
  16544.  All the coprocessor's numeric opcodes (as opposed to control opcodes) use
  16545.  the top of the stack as at least one operand. Some instructions operate
  16546.  only on the top of the stack, while others operate on both the top and the
  16547.  second stack register. Some take their second operand from another stack
  16548.  register, and others can take their second operand from memory.
  16549.  
  16550.  Besides the eight data registers, the 8087 has five other registers
  16551.  accessible to the programmer, each 16 bits in size: status word, control
  16552.  word, tag word, operand pointer, and instruction pointer.
  16553.  
  16554.  The status word can be thought of as a flag register (see Figure 4). It
  16555.  contains a busy indicator, a top-of-stack pointer, condition codes, and
  16556.  exception indicators. To read the status word from Microsoft C, call the
  16557.  built-in _status87 function. To read the status word from the coprocessor
  16558.  in assembler, execute an FSTSW instruction to write the status word to
  16559.  memory where the microprocessor can examine it.
  16560.  
  16561.  The control word defines how the coprocessor should react to different
  16562.  exception conditions (see Figure 5). It also defines the precision, how
  16563.  the results will be rounded, and whether signed or unsigned infinity will
  16564.  be used. The control word register has three control fields and six
  16565.  exception masks. Masking the exception bit tells the coprocessor to handle
  16566.  all occurrences of this exception; leaving it unmasked means that the
  16567.  programmer will have to handle the exceptions.
  16568.  
  16569.  In assembly language, control words are sent to the coprocessor by writing
  16570.  them to a memory location and having the coprocessor execute an instruction
  16571.  that reads in the control word from memory. Programmers using a high-level
  16572.  language can check their library reference guide to see how this is
  16573.  implemented in the library they are using. For programmers who do not care
  16574.  to set these fields, Intel provides a set of default control conditions. The
  16575.  default settings are: exceptions masked, interrupts masked, 64-bit
  16576.  precision, rounding to the nearest number, and projective infinity.
  16577.  
  16578.  The tag word contains information about the contents of each data register
  16579.  (see Figure 6). This information is used by the coprocessor primarily to
  16580.  optimize performance. The coprocessor stores 2 bits for each data register,
  16581.  for a total of four possible tag values.
  16582.  
  16583.  The coprocessor uses the tag word to keep track of the contents of each of
  16584.  its data registers and to report invalid results. The coprocessor also
  16585.  uses the tag word to maintain stack integrity information. For example, if
  16586.  a register tagged as empty (tag value = 11) is popped from the stack, the
  16587.  coprocessor detects stack underflow. Similarly, the coprocessor uses the
  16588.  tag word to detect stack overflow when new data is stored in a register
  16589.  that wasn't previously empty. Stack underflow and overflow trigger an
  16590.  invalid operation exception. Programmers can mask or unmask this exception
  16591.  (the default is masked). If either stack underflow or overflow occur and
  16592.  the invalid operation exception is masked, the coprocessor adjusts the
  16593.  stack pointer and returns a standard result to indicate that the value is
  16594.  not meaningful.
  16595.  
  16596.  The operand pointer and instruction pointer registers provide information
  16597.  about the instruction and data that caused an exception and are used with
  16598.  user-written error handlers. Most programmers do not employ these
  16599.  registers, however, preferring to let the coprocessor handle exceptions.
  16600.  
  16601.  Unlike the status word and control word, the tag word, operand pointer,
  16602.  and instruction pointer cannot be accessed directly. These registers are
  16603.  accessed indirectly by writing to memory either the coprocessor's
  16604.  environment (using FSTENV) or the coprocessor's state (using FSAVE). The
  16605.  14-byte coprocessor environment consists of the status word, control word,
  16606.  tag word, instruction pointer, and operand pointer. The 94-byte
  16607.  coprocessor state includes everything in the environment plus the eight
  16608.  coprocessor data registers (see Figure 7). The format of the coprocessor
  16609.  state and environment depends on the coprocessor's operating mode.
  16610.  
  16611.  When an exception occurs while the coprocessor is in real mode, it
  16612.  supplies the 20-bit addresses of the offending instruction and its memory
  16613.  operand (if any), plus the 11 low-order bits of the opcode. In protected
  16614.  mode (with the 80287 and 80387 only), the coprocessor supplies the
  16615.  selectors and offsets of the offending instruction and its memory operand
  16616.  (if any). Although the 80287/80387 real-mode exception pointers have the
  16617.  same format as the 8087 exception pointers, the 80287/80387 instruction
  16618.  pointer indicates any prefixes preceding the opcode. In contrast, the 8087
  16619.  instruction pointer indicates the escape (ESC) instruction opcode.
  16620.  
  16621.  
  16622.  Exceptions
  16623.  
  16624.  The coprocessor recognizes six exception conditions: invalid operation,
  16625.  denormalized operand, division by zero, numeric overflow, numeric
  16626.  underflow, and inexact result. The coprocessor's exception masks give
  16627.  programmers the choice of trapping exceptions themselves or having the
  16628.  coprocessor return a fixed value. When an exception occurs during
  16629.  execution of a coprocessor instruction, the coprocessor sets the
  16630.  appropriate bit in its status register. The coprocessor then checks its
  16631.  control register to determine whether or not that type of exception is
  16632.  masked. If the exception is masked, then the coprocessor uses its on-chip
  16633.  logic to return a result. The exception indicator bits in the status
  16634.  register will hold their value until they are explicitly cleared with
  16635.  either a FINIT or FCLEX instruction. Consequently, with exceptions masked,
  16636.  programmers do not have to check the status register after every
  16637.  instruction. Checking the exception indicator bits periodically ensures
  16638.  accurate results.
  16639.  
  16640.  The other method of handling exceptions is to unmask one or more of the
  16641.  exception bits and clear the coprocessor's interrupt enable mask. Under
  16642.  these conditions, an exception will trigger an interrupt request. It is up
  16643.  to the programmer to write the interrupt handler that will respond to such
  16644.  requests. The coprocessor contains a lot of built-in support for writing
  16645.  such routines.
  16646.  
  16647.  
  16648.  Instructions
  16649.  
  16650.  Coprocessor instructions fall into six categories: data transfer, loading
  16651.  constants, transcendental calculations, comparison, arithmetic, and
  16652.  processor control. A coprocessor instruction can be written in assembler
  16653.  in either of two ways: as a microprocessor ESC instruction followed by a
  16654.  number (for example, ESC 0BH) or as a specific coprocessor mnemonic
  16655.  (FSTP). All versions of Microsoft assemblers later than 1.25 accept
  16656.  coprocessor mnemonics; ESC instructions are needed only for older
  16657.  assemblers that do not, and are thus rarely used. Programmers writing in
  16658.  high-level languages needn't worry about the format of coprocessor
  16659.  instructions──the compiler will take care of everything.
  16660.  
  16661.  A coprocessor mnemonic takes the form of a sequence of letters beginning
  16662.  with an F. Figures 8 and 9 give examples of these instructions
  16663.  incorporated into assembly language programs. Figure 10 is a sample of
  16664.  code created by Microsoft's C Compiler Version 4.0. While the coprocessor
  16665.  instructions are not apparent in the source code, you will see them if you
  16666.  compile the program with the /Fc option and look at the .COD file.
  16667.  
  16668.  If an instruction starts with 11011 the microprocessor recognizes it as a
  16669.  coprocessor instruction and responds by generating any necessary operand
  16670.  addresses, putting them on the address bus, and ignoring the coprocessor
  16671.  opcode. The microprocessor then continues fetching and executing
  16672.  instructions unless it is instructed to wait for the coprocessor to
  16673.  complete its task.
  16674.  
  16675.  Since the microprocessor and the coprocessor can work on separate tasks
  16676.  simultaneously, they can overwrite each other's data or miss instructions
  16677.  unless they're synchronized. All high-level languages automatically
  16678.  synchronize the activity of the two chips, while assembly language
  16679.  programmers must do so explicitly. In exchange for the extra programming
  16680.  effort, however, assembly language programmers get more flexibility
  16681.  (carefully managed concurrency) and faster performance.
  16682.  
  16683.  The 80286 and 80386 have instruction synchronization built in, but this is
  16684.  not true of the 8088 and 8086 or any of the coprocessors. Consequently,
  16685.  programmers must on occasion insert an FWAIT after a coprocessor store
  16686.  instruction.
  16687.  
  16688.  When you are using escape sequences, specify the FWAIT instruction when
  16689.  the microprocessor must wait for data from the coprocessor. All floating-
  16690.  point mnemonics have an FWAIT as their first byte, so it isn't necessary
  16691.  to code one explicitly. (A few coprocessor instructions assume an FN<op>
  16692.  form, which keeps the assembler from generating an FWAIT instruction.)
  16693.  
  16694.  In addition to synchronizing data, the 8086 and 8088 must also synchronize
  16695.  8087 instructions. Because the coprocessor gets its instructions by
  16696.  monitoring them as they go into the microprocessor prefetch queue, the
  16697.  8087 can miss an instruction if it is busy executing while the 8086/8088
  16698.  is fetching and executing.
  16699.  
  16700.  Any program that uses a coprocessor should also be able to run without
  16701.  one. Before the software tries to use the coprocessor, it should check to
  16702.  see if there is one. It can easily do this by attempting to initialize the
  16703.  coprocessor and then attempting to read the coprocessor's control word
  16704.  after the initialization. (If a coprocessor is present, the control word
  16705.  will be set to the default value specified by Intel. Many software
  16706.  libraries have this checking function built in. If a coprocessor is not
  16707.  found, the program should call an emulation library to handle coprocessor
  16708.  instructions or should gracefully exit. Figure 11 provides an example of
  16709.  this type of program.
  16710.  
  16711.  
  16712.  Real Vs. Protected
  16713.  
  16714.  The 8087 operates only in real mode, while the 80287 and 80387 can operate
  16715.  in either real or protected mode. All programs written to use the 8087 are
  16716.  compatible with the 80287 and 80387 in real mode. Executing the privileged
  16717.  SETPM instruction will place the 80287 or 80387 in protected mode. They
  16718.  can then be returned to real mode only by a hardware reset.
  16719.  
  16720.  The microprocessor's operating mode affects coprocessor code in two areas:
  16721.  exception handling and memory accesses. The memory image of the
  16722.  instruction pointer and data pointer following an FSTENV or FSAVE
  16723.  instruction depends on the coprocessor's operating mode (see Figures 8 and
  16724.  9). Any code that examines this information must consider the operating
  16725.  mode for accurate interpretation. In protected mode, Interrupt Vector 16
  16726.  is dedicated to the numeric exception handling routine. Coprocessor
  16727.  instructions that result in exception conditions will trigger an Interrupt
  16728.  16 if the exception is unmasked. Protected mode also has a built-in
  16729.  mechanism for handling coprocessor instructions when a coprocessor is not
  16730.  present (or if its absence is being emulated). Interrupt 7 is
  16731.  automatically triggered if a coprocessor ESC sequence is executed and the
  16732.  emulation bit (EM) of the microprocessor's machine status word is set.
  16733.  This built-in trapping can help programmers systematically include
  16734.  emulation code in their programs.
  16735.  
  16736.  MS(R) OS/2, Microsoft's new protected-mode version of MS-DOS(R), offers basic
  16737.  exception handling for coprocessors by supporting the exception handling
  16738.  capabilities of the 80287 and 80387. It doesn't supply a standard
  16739.  emulation library for coprocessors; this must be provided by the
  16740.  compiler.
  16741.  
  16742.  When in protected mode, the microprocessor checks all memory accesses
  16743.  (including coprocessor operands) for violations of protection rules.
  16744.  Coprocessor applications running in protected mode must comply with
  16745.  protected-mode memory management regulations. Any violations cause either
  16746.  dedicated Interrupt 13 (when the violation occurs on the first word of the
  16747.  numeric operand) or dedicated Interrupt 9 (when the violation occurs on
  16748.  subsequent words).
  16749.  
  16750.  If you want to port an 8087 program to a protected-mode system, consider
  16751.  reassembling the program on an 80286/80386 assembler. This removes the
  16752.  redundant FWAITs and usually gives you a more compact code image. In
  16753.  addition, make the following changes to the 8087 program:
  16754.  
  16755.    ■  Delete interrupt-controller-oriented instructions in numeric exception
  16756.       handlers.
  16757.  
  16758.    ■  Delete 8087 instructions FENI/FNENI (enable interrupts) and
  16759.       FDISI/FNDISI (disable interrupts). The 80287 and 80387 ignore these
  16760.       instructions, so none of the 80287/80387 internal states will be
  16761.       updated.
  16762.  
  16763.    ■  Be sure Interrupt Vector 16 points to the numeric exception handling
  16764.       routine.
  16765.  
  16766.    ■  Include a microprocessor exception handler for an Interrupt 7, which
  16767.       will occur during the execution of coprocessor instructions if the
  16768.       microprocessor's machine status word contains the settings TS=1 (task
  16769.       switched) or EM=1 (emulation).
  16770.  
  16771.    ■  Include a microprocessor exception handler for Interrupt 9 (which
  16772.       occurs when the second or later word of a floating-point operand falls
  16773.       outside a segment) and Interrupt 13 (caused by the starting address of
  16774.       a numeric operand falling outside a segment).
  16775.  
  16776.  
  16777.  Figure 1:  Recalculating Lotus 1-2-3(tm) spreadsheets on an IBM(r) PC can
  16778.             usually be done much more quickly with an 8087 coprocessor.
  16779.  
  16780.    Standard│░░
  16781.   Deviation│▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
  16782.            │
  16783.   Exponents│░░░
  16784.            │▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
  16785.            │
  16786.   Multiply/│░░░░░░░░░░░░░░░░░                 ┌────────────────────┐
  16787.      Divide│▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓               │ ░░ With an 8087    │
  16788.            │                                  │                    │
  16789.        Add/│░░░░░░░░░░░░░░░░░░░               │ ▓▓ Without an 8087 │
  16790.    Subtract│▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓               └────────────────────┘
  16791.            ├──┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
  16792.            0  10  20  30  40  50  60  70  80  90 100 110 120 130
  16793.  
  16794.  
  16795.  Figure 2:  Calculation times for floating-point instructions decrease
  16796.             dramatically when a coprocessor is added to an 8-MHz IBM PC.
  16797.  
  16798.  Instruction                Approximate Execution Time (in microseconds)
  16799.  
  16800.                                 With an 8087        Without an 8087
  16801.  
  16802.  Add/Subtract                       10.6                 1,000.0
  16803.  Multiply (short real nos.)         11.9                 1,000.0
  16804.  Multiply (temporary real nos.)     16.9                 1,312.5
  16805.  Divide                             24.4                 2,000.0
  16806.  Compare                             5.6                   812.5
  16807.  Load (long real nos.)               6.3                 1,062.5
  16808.  Store (long real nos.)             13.1                   750.0
  16809.  Square Root                        22.5                12,250.0
  16810.  Tangent                            56.3                 8,125.0
  16811.  Exponentiation                     62.5                10,687.5
  16812.  
  16813.  
  16814.  Figure 3:  The coprocessor can recognize seven numeric formats, which make
  16815.             use of up to 80 bits.
  16816.  
  16817.   79   78    64 63                                                0
  16818.  ┌────┬────────┬───────────────────────────────────────────────────┐
  16819.  │Sign│Biased  │                     Significand                   │Temporary
  16820.  │Bit │Exponent│                                                   │Real
  16821.  └────┴(3FFFH)─┴───────────────────────────────────────────────────┘
  16822.  
  16823.   79   78  72                                                     0
  16824.  ┌────┬────┬───────────────────────────────────────────────────────┐
  16825.  │Sign│Not │ d                Packed Decimal Digits            d   │Packed
  16826.  │Bit │Used│  17 │                              │  0 │BCD
  16827.  └────┴────┴─────┴────────────────────────────────────────────┴────┘
  16828.  
  16829.                63   62    52 51                                   0
  16830.               ┌────┬────────┬──────────────────────────────────────┐
  16831.               │Sign│Biased  │              Significand             │Long Real
  16832.               │Bit │Exponent│                                      │
  16833.               └────┴─(3FFH)─┴──────────────────────────────────────┘
  16834.  
  16835.                63                                                 0
  16836.               ┌────────────────────────────────────────────────────┐
  16837.               │                          2's                       │Long
  16838.               │                      Compliment                    │Integer
  16839.               └────────────────────────────────────────────────────┘
  16840.  
  16841.                                31   30     23 22                  0
  16842.                               ┌────┬─────────┬─────────────────────┐
  16843.                               │Sign│ Biased  │     Significand     │Short
  16844.                               │Bit │ Exponent│                     │Real
  16845.                               └────┴──(7FH)──┴─────────────────────┘
  16846.  
  16847.                                31                                 0
  16848.                               ┌────────────────────────────────────┐
  16849.                               │                  2's               │Short
  16850.                               │              Compliment            │Integer
  16851.                               └────────────────────────────────────┘
  16852.  
  16853.                                                    15             0
  16854.                                                   ┌────────────────┐
  16855.                                                   │                │Word
  16856.                                                   │                │Integer
  16857.                                                   └────────────────┘
  16858.  
  16859.  
  16860.  Figure 4:  The coprocessor's 16-bit status word register serves as a flag
  16861.             register.
  16862.  
  16863.  Reserved───────────────────────────┐
  16864.  Condition                          │ Exception Flags (1=Exception occurred)
  16865.    Codes──────┬─────────┬──┬──┐     │
  16866.  Stack-Top    │         │  │  │     │
  16867.    Pointer───────┬─┬──┐ │  │  │     │
  16868.  Busy───────┐ │  │ │  │ │  │  │     │
  16869.         15 ╔╧╤╧═╤╧╤╧═╤╧╤╧═╤╧═╤╧═╤══╤╧╤══╤══╤══╤══╤══╤══╗ 0
  16870.            ║B│C3│ │ST│ │C2│C1│CO│IR│X│PE│UE│OE│ZE│DE│IE║
  16871.            ║ │  │ │  │ │  │  │  │ES│ │  │  │  │  │  │  ║
  16872.            ╚═╧══╧═╧══╧═╧══╧══╧══╧═╤╧═╧═╤╧═╤╧═╤╧═╤╧═╤╧═╤╝
  16873.                                   │    │  │  │  │  │  └─Invalid Operation
  16874.                                   │    │  │  │  │  └────Denormalized Operand
  16875.                                   │    │  │  │  └───────Divide By Zero
  16876.                                   │    │  │  └──────────Numeric Overflow
  16877.                                   │    │  └─────────────Numeric Underflow
  16878.                                   │    └────────────────Precision
  16879.                                   │
  16880.                                   │                   ┌─{Interrupt Request
  16881.                                   │                   │   (8027)
  16882.                                   └───────────────────┤ {Error Summary Status
  16883.                                                       └─  (80287)
  16884.  
  16885.  
  16886.  Figure 5:  The control word register governs how the coprocessor reacts to
  16887.             exception conditions.
  16888.  
  16889.  Infinity Control───┐               Exception Masks (1=Exception masked)
  16890.  Rounding Control───────┐
  16891.                     │   │
  16892.  Precision Control────────────┐
  16893.              15     │   │     │                          0
  16894.              ╔═╤═╤═╤╧═╤═╪══╤══╪══╤═══╤═╤══╤══╤══╤══╤══╤══╗
  16895.              ║X│X│X│IC│R│C │ P│C │IEM│X│PM│UM│OM│ZM│DM│IM║
  16896.              ╚╤╧╤╧╤╧══╧═╧══╧══╧══╧╤═╤╧═╧═╤╧═╤╧═╤╧═╤╧═╤╧╤═╝
  16897.               ├─┴─┴───────────────┘ │    │  │  │  │  │ │
  16898.               │                     │    │  │  │  │  │ └─Invalid Operation
  16899.  Reserved─────┘                     │    │  │  │  │  └───Denormalized Operand
  16900.                                     │    │  │  │  └──────Divide By Zero
  16901.                                     │    │  │  └─────────Numeric Overflow
  16902.                                     │    │  └────────────Numeric Underflow
  16903.                                     │    └───────────────Precision
  16904.                                     │
  16905.                                     │                  ┌─{Interrupt Enable
  16906.                                     └──────────────────┤  (8087)
  16907.                                                        └─{Reserved (80287)
  16908.  
  16909.  
  16910.  Figure 6:  The tag word holds information about the contents of each data
  16911.             register.
  16912.  
  16913.  ┌───────────────────────────────────────────────────────────────────────┐
  16914.  │                                                                       │█
  16915.  │  15                                                                0  │█
  16916.  │  ╔════════╤═══════╤═══════╤═══════╤═══════╤═══════╤═══════╤════════╗  │█
  16917.  │  ║ Tag (7)│Tag (6)│Tag (5)│Tag (4)│Tag (3)│Tag (2)│Tag (1)│Tag (0) ║  │█
  16918.  │  ╚════════╧═══════╧═══════╧═══════╧═══════╧═══════╧═══════╧════════╝  │█
  16919.  │                                                                       │█
  16920.  │           Tag Values:   00 = valid (i.e., any finite nonzero number)  │█
  16921.  │                         01 = zero                                     │█
  16922.  │                         10 = invalid (i.e., NaN or infinity)          │█
  16923.  │                         11 = empty                                    │█
  16924.  │                                                                       │█
  16925.  └───────────────────────────────────────────────────────────────────────┘█
  16926.    ████████████████████████████████████████████████████████████████████████
  16927.  
  16928.  
  16929.  Figure 7:  The coprocessor environment consists of the contents of all
  16930.             registers but the data registers. The coprocessor state includes
  16931.             all registers.
  16932.  
  16933.  
  16934.                                   Real Mode
  16935.                           ┌─────────────────────────────────────┐
  16936.                           │             Control Word            │
  16937.                           ├─────────────────────────────────────┤
  16938.                           │             Status Word             │
  16939.                           ├─────────────────────────────────────┤
  16940.           Coprocessor     │              Tag Word               │
  16941.                           ├─────────────────────────────────────┤
  16942.              State        │     Instruction Address (15-0)      │
  16943.                           ├───────────────┬─────┬───────────────┤
  16944.                           │  Instruction  │  0  │  Instruction  │
  16945.                           │Address (19-16)│     │ Opcode (10-0) │
  16946.                           ├───────────────┴─────┴───────────────┤
  16947.                           │        Operand Address (15-0)       │
  16948.                           ├──────────────────┬──────────────────┤
  16949.                           │     Operand      │        0         │
  16950.                           │ Address (19-16)  │                  │
  16951.            ┌──────────────┴15──────────────11┴12───────────────0┤
  16952.            │                        ST (0)                      │
  16953.            ├────────────────────────────────────────────────────┤
  16954.            │                        ST (1)                      │
  16955.            ├────────────────────────────────────────────────────┤
  16956.            │                        ST (2)                      │
  16957.            ├────────────────────────────────────────────────────┤
  16958.            │                        ST (3)                      │
  16959.            ├────────────────────────────────────────────────────┤
  16960.            │                        ST (4)                      │
  16961.            ├────────────────────────────────────────────────────┤
  16962.            │                        ST (5)                      │
  16963.            ├────────────────────────────────────────────────────┤
  16964.            │                        ST (6)                      │
  16965.            ├────────────────────────────────────────────────────┤
  16966.            │                        ST (7)                      │
  16967.            └79─────────────────────────────────────────────────0┘
  16968.  
  16969.                                 Protected Mode
  16970.                           ┌─────────────────────────────────────┐
  16971.                           │             Control Word            │
  16972.                           ├─────────────────────────────────────┤
  16973.                           │             Status Word             │
  16974.                           ├─────────────────────────────────────┤
  16975.          Coprocessor      │              Tag Word               │
  16976.                           ├─────────────────────────────────────┤
  16977.          Environment      │          Instruction Offset         │
  16978.                           ├─────────────────────────────────────┤
  16979.                           │         Instruction Selector        │
  16980.                           ├─────────────────────────────────────┤
  16981.                           │            Operand Offset           │
  16982.                           ├─────────────────────────────────────┤
  16983.                           │           Operand Selector          │
  16984.            ┌──────────────┴15──────────────────────────────────0┤
  16985.            │                        ST (0)                      │
  16986.            ├────────────────────────────────────────────────────┤
  16987.            │                        ST (1)                      │
  16988.            ├────────────────────────────────────────────────────┤
  16989.            │                        ST (2)                      │
  16990.            ├────────────────────────────────────────────────────┤
  16991.            │                        ST (3)                      │
  16992.            ├────────────────────────────────────────────────────┤
  16993.            │                        ST (4)                      │
  16994.            ├────────────────────────────────────────────────────┤
  16995.            │                        ST (5)                      │
  16996.            ├────────────────────────────────────────────────────┤
  16997.            │                        ST (6)                      │
  16998.            ├────────────────────────────────────────────────────┤
  16999.            │                        ST (7)                      │
  17000.            └79─────────────────────────────────────────────────0┘
  17001.  
  17002.  
  17003.  Figure 8:  This assembly language program uses coprocessor instructions
  17004.             to calculate the circumference of a circle with a given radius.
  17005.             Each of these instructions begins with an F.
  17006.  
  17007.  title   circumference
  17008.  
  17009.  .287        ; Tell MASM there are coprocessor instructions
  17010.              ; in the program.
  17011.  
  17012.  data    segment
  17013.      radius              DD  2.468
  17014.      circumference       DD  ?
  17015.  data    ends
  17016.  
  17017.  code    segment
  17018.      assume cs:code, ds:data
  17019.  start:
  17020.      mov     ax, data        ; Initialize data segment
  17021.                              ; register
  17022.      mov     ds, ax
  17023.      finit                   ; Initialize coprocessor
  17024.      fldpi                   ; ST = pi
  17025.      fadd    st, st          ; ST = 2pi
  17026.      fld     radius          ; ST = radius
  17027.                              ; ST(1) = 2pi
  17028.      fmul    st, st(1)       ; ST = radius*2pi
  17029.      fstp    circumference   ; store result and pop
  17030.      fwait                   ; wait for store to complete
  17031.      mov     ah, 4ch         ; return to DOS
  17032.      int     21h
  17033.  code ends
  17034.      end start
  17035.  
  17036.  
  17037.  Figure 9:  This program uses coprocessor instructions to calculate the
  17038.             root of each element in an array of binary coded decimal
  17039.             integers. The results are stored in another binary coded
  17040.             decimal array and can easily be converted to ASCII strings for
  17041.             output.
  17042.  
  17043.  .287                ; Indicate to MASM program contains npx code.
  17044.  
  17045.  bcd_data    segment
  17046.      array_1 DT  1234567890, 82, 769823, 84165
  17047.              DT  246809, 1526374859, 199, 41290
  17048.              DT  98654210, 340126, 2400, 371849
  17049.      array_2 DT 12 DUP (?); storage for results
  17050.  bcd_data    ends
  17051.  
  17052.  code segment
  17053.      assume  cs:code, ds:bcd_data
  17054.  start:
  17055.      mov ax, bcd_data
  17056.      mov ds, ax
  17057.      finit                   ; initialize coprocessor
  17058.                              ; assume default control word
  17059.  
  17060.      mov cx, length array_2  ; initialize loop counter
  17061.      mov si, 0               ; initialize index
  17062.  
  17063.  process_array:
  17064.      fbld    array_1[si]     ; st(0) = array_1[index]
  17065.      fsqrt                   ; st(0) = sqrt (st(0))
  17066.      frndint                 ; round st(0) to integer
  17067.      fbstp   array_2[si]     ; store bcd result in
  17068.                              ; array_2[index] and
  17069.                              ; pop coprocessor stack
  17070.      add     si, 10          ; increment index to point
  17071.                              ;      to next DT array element
  17072.      loop    process_array   ; DO WHILE
  17073.                              ;      loop counter <= length array_2
  17074.  exit:
  17075.      fwait                   ; make sure last store completed
  17076.      mov ah, 4ch             ; exit to dos
  17077.      int 21h
  17078.  
  17079.  code    ends
  17080.      end start
  17081.  
  17082.  
  17083.  Figure 10:  Using Microsoft C library functions to calculate sine and
  17084.              cosine, this program in C draws a circle on the screen of a
  17085.              system equipped with a graphics adapter. The coprocessor
  17086.              instructions are apparent only after compilation.
  17087.  
  17088.  #include "stdio.h"
  17089.  #include "math.h"
  17090.  
  17091.  extern set_graph_mode();
  17092.  extern set_text_mode();
  17093.  extern plot_point();
  17094.  
  17095.  #define VERTICAL_CENTER 99.5
  17096.  #define HORIZONTAL_CENTER 319.5
  17097.  #define PI 3.1415927
  17098.  
  17099.  main()
  17100.      {
  17101.      char ch;
  17102.      float radians,radius,aspect_ratio;
  17103.  
  17104.      aspect_ratio=2.1; /* adjusts horizontal scaling to account */
  17105.                        /* for PC's "tall and skinny" pixels     */
  17106.      radius=90;
  17107.  
  17108.  
  17109.      set_graph_mode(); /* set screen to 640x200 graphics mode */
  17110.  
  17111.      /* step around the circle in 1/100th radian increments */
  17112.      for (radians=0; radians < 2*PI; radians=radians + 0.01)
  17113.          {
  17114.          long x,y;
  17115.  
  17116.          x=HORIZONTAL_CENTER+radius*aspect_ratio*cos(radians);
  17117.          y=VERTICAL_CENTER+radius*sin(radians);
  17118.  
  17119.  /* call routine to write a pixel on the screen */
  17120.          plot_point((int)x,(int)y);
  17121.          }
  17122.  
  17123.  /* wait for user to hit a key before erasing screen  */
  17124.      ch=getchar();
  17125.  
  17126.  /* restore user's screen to text mode */
  17127.      set_text_mode();
  17128.      }
  17129.  
  17130.  
  17131.  Figure 11:  This routine performs the same multiplication function with or
  17132.              without a coprocessor. First the program checks for the
  17133.              presence of a coprocessor. If it finds one, it executes the
  17134.              imul_32 procedure. If not, it jumps to the emulation
  17135.              procedure, emulate_imul_32.
  17136.  
  17137.  title        math_module
  17138.  
  17139.  .287                    ; Tell MASM that there are coprocessor
  17140.                          ; instructions here.
  17141.  public  init_math
  17142.  public  imul_32
  17143.  
  17144.      present     EQU     0
  17145.      missing     EQU     1
  17146.  
  17147.  code    segment     public      'code'
  17148.          assume  cs:code
  17149.  
  17150.      cp_flag         DB   1      ; local flag
  17151.      ctrl_word   DW  0           ; for storing '87 control word
  17152.  
  17153.  ;-------------------------------------------------------------;
  17154.  ;  init_math: Detects math coprocessor and sets global flag   ;
  17155.  ;      which is used to determine whether or not to use       ;
  17156.  ;      coprocessor instructions or emulation code.            ;
  17157.  ;                                                             ;
  17158.  ;  This procedure must be called before the coprocessor can   ;
  17159.  ;  be used by math routines in this module.                   ;
  17160.  ;-------------------------------------------------------------;
  17161.  
  17162.  init_math       PROC      FAR
  17163.  
  17164.      fninit                                ; initialize coprocessor
  17165.      fnstcw  cs:ctrl_word                  ; store '87 control word
  17166.      test    byte ptr cs:[ctrl_word+2], 03 ; if bits 8 and 9 are set
  17167.      je      yes_cp                        ; then coprocessor present
  17168.      mov     cs:cp_flag, missing           ; else no coprocessor
  17169.      jmp     init_math_exit
  17170.  
  17171.  yes_cp:
  17172.      mov    cs:cp_flag, present
  17173.  
  17174.  init_math_exit:
  17175.      ret
  17176.  init_math   ENDP
  17177.  
  17178.  ;-------------------------------------------------------------;
  17179.  ;  imul_32:  Performs signed multiplication on two 32 bit     ;
  17180.  ;            integers. (Note: can also be used to perform     ;
  17181.  ;            fixed point 32 bit decimal multiplication)       ;
  17182.  ;                                                             ;
  17183.  ;        Input:   Two 32 bit integers                         ;
  17184.  ;                 ds:si pointer to integer A                  ;
  17185.  ;                 ds:di pointer to integer B                  ;
  17186.  ;                                                             ;
  17187.  ;       Output:   64-bit result returned at [es:bx]           ;
  17188.  ;                                                             ;
  17189.  ;-------------------------------------------------------------;
  17190.  
  17191.  imul_32     PROC    FAR
  17192.  
  17193.      cmp     cs:cp_flag, missing      ; IF coprocessor missing
  17194.      je      emulate_imul_32          ; THEN emulate
  17195.                                       ; ELSE use coprocessor
  17196.      fild    dword ptr [si]           ; st(0)= A
  17197.      fimul   dword ptr [di]           ; st(0)=A*B
  17198.      fistp   qword ptr es:[bx]        ; store result and pop stack
  17199.      fwait                            ; wait for store to complete
  17200.      jmp     imul_32_exit             ; coprocessor done, exit
  17201.  
  17202.  emulate_imul_32:
  17203.  ;--------------------------------------------------------------------;
  17204.  ;                                                                    ;
  17205.  ;  The following code computes A x B where                           ;
  17206.  ;                                                                    ;
  17207.  ;         A is a 32 bit integer composed of                          ;
  17208.  ;              a low word (A0) and a high word (A1) and              ;
  17209.  ;         B is a 32 bit integer composed of                          ;
  17210.  ;              a low word (B0) and a high word (B1)                  ;
  17211.  ;                                                                    ;
  17212.  ;  The result is calculated by summing the partial products of       ;
  17213.  ;  individual 16 bit unsigned multiplies.  The final result is       ;
  17214.  ;  sign adjusted.                                                    ;
  17215.  ;                                                                    ;
  17216.  ;--------------------------------------------------------------------;
  17217.  ;
  17218.      push    ax                      ; save caller's state
  17219.      push    cx
  17220.      push    dx
  17221.      push    bp
  17222.  
  17223.  A0_x_B0:
  17224.      mov     ax, [si]                ; ax=A0
  17225.      mul     word ptr [di]           ; dx=A0B0H, ax=A0B0L
  17226.      mov     es:[bx], ax             ; store A0B0L - 4th column sum
  17227.      mov     cx, dx                  ; cx=A0B0H
  17228.  A1_x_B0:
  17229.      mov     ax, [si+2]              ; ax=A1
  17230.  
  17231.      mul     word ptr [di]           ; dx=A1B0H, ax=A1B0L
  17232.      push    bx                      ; running out of registers,
  17233.                                      ;    reuse bx
  17234.      mov     bx, ax                  ; bx=A1B0L
  17235.      mov     bp, dx                  ; bp=A1B0H
  17236.  
  17237.  
  17238.  A0_x_B1:
  17239.      mov     ax, [si]                ; ax=A0
  17240.      mul     word ptr [di+2]         ; dx=A0B1H, ax=A0B1L
  17241.      add     cx, bx                  ; cx=A0B0H+A1B1L
  17242.      adc     cx, ax                  ; cx=A0B0H+A1B1L+A0B1L+carry
  17243.      pop     bx                      ; need pointer back
  17244.      mov     es:[bx+2], cx           ; store 3rd column sum
  17245.      push    bx                      ; still short of register space
  17246.      xor     bx, bx
  17247.      adc     bx, 0                   ; save carry information
  17248.      mov     cx, dx                  ; cx=A0B1H
  17249.  A1_x_B1:
  17250.      mov     ax, [si+2]              ; ax=A1
  17251.      mul     word ptr [di+2]         ; dx=A1B1H, ax=A1B1L
  17252.      add     cx, bx                  ; cx=A0B1H+stored carry
  17253.      adc     cx, bp                  ; cx=A0B1H+A1B0H
  17254.      adc     cx, ax                  ; cx=A0B1H+A1B0H+A1B1L+carry
  17255.      pop     bx                      ; restore pointer
  17256.      mov     es:[bx+4], cx           ; store 2nd column sum
  17257.      adc     dx, 0                   ; dx=A1B1+carry
  17258.      mov     es:[bx+6], dx           ; store 1st column sum
  17259.  ; now adjust for negative numbers
  17260.  test_A:
  17261.      mov     ah, [si+2]              ; ah=high byte of A
  17262.      or      ah, ah                  ; IF A is negative
  17263.      js      subtract_B              ; THEN subtract B from
  17264.                                      ;        high DD of result
  17265.  test_B:
  17266.      mov     ah, [di+2]              ; ah=high byte of B
  17267.      or      ah, ah                  ; IF B is negative
  17268.      js      subtract_A              ; THEN subtract A from
  17269.                                      ;        high DD of result
  17270.      jmp     emulate_done
  17271.  subtract_B:
  17272.      mov     ax, [di]                ; ax=B0
  17273.      mov     cx, [di+2]              ; cx=B1
  17274.      sub     es:[bx+4],ax            ; adjust the two high words
  17275.      sbb     es:[bx+6],cx
  17276.      jmp     test_B
  17277.  subtract_A:
  17278.      mov     ax, [si]                ; ax=A0
  17279.      mov     cx, [si+2]              ; cx=B1
  17280.      sub     es:[bx+4], ax           ; adjust the two high words
  17281.      sbb     es:[bx+6], cx
  17282.  emulate_done:
  17283.      pop     bp                      ; restore caller's state
  17284.      pop     dx
  17285.      pop     cx
  17286.      pop     ax
  17287.  
  17288.  imul_32_exit:
  17289.      ret
  17290.  imul_32 ENDP
  17291.  
  17292.  code ends
  17293.      end
  17294.  
  17295.  ████████████████████████████████████████████████████████████████████████████
  17296.  
  17297.  TIFF: An Emerging Standard for Exchanging Digitized Graphic Images
  17298.  
  17299.  ───────────────────────────────────────────────────────────────────────────
  17300.  Also see the related article:
  17301.    The CCITT/3 Scheme in TIFF
  17302.  ───────────────────────────────────────────────────────────────────────────
  17303.  
  17304.  Nancy Andrews and Stan Fry
  17305.  
  17306.  In the early days of microcomputers, PC users considered themselves lucky to
  17307.  have a paint program with basic drawing tools. Users could create simple
  17308.  drawings for documents, print them, and paste them in by hand.
  17309.  Unfortunately, the end results looked amateurish and, for many purposes,
  17310.  were barely acceptable.
  17311.  
  17312.  The difficulty was what if users wanted something more complex than what the
  17313.  available tools and their drawing skills allowed? What if they wanted to use
  17314.  existing line art or photographs? Users really needed to have the ability to
  17315.  digitize the professionally drawn art they had on paper and then
  17316.  electronically paste it into their documents.
  17317.  
  17318.  The arrival of scanner hardware solved this problem. Most scanners could
  17319.  scan an existing piece of art or a photograph at an amazing 300 dots per
  17320.  inch (dpi). This sounded like the answer to a document producer's dreams,
  17321.  but the puzzle had one missing piece. Users still needed a standard file
  17322.  format so that they could use any scanner to digitize an image, edit it with
  17323.  the paint or graphics program of their choice, and than electronically paste
  17324.  the image into their documents. Furthermore, having the whole process work
  17325.  quickly would be very helpful.
  17326.  
  17327.  
  17328.  Enter TIFF
  17329.  
  17330.  Aldus Corp., creator of PageMaker for the Macintosh and PC, recognized the
  17331.  need for a standard way to exchange digital data, took the initiative, and,
  17332.  with the assistance of several vendors, developed Tag Image File Format
  17333.  (TIFF). TIFF provides features that support most input devices, supports a
  17334.  variety of data compression techniques, and has the flexibility to add new
  17335.  features easily in a controlled fashion.
  17336.  
  17337.  TIFF's features can support scanners, point programs, and cameras (see
  17338.  Figure 1). It works just as well with a simple Windows Paint document as it
  17339.  does with a complex medical imaging scanner. TIFF files contain tags that
  17340.  describe not only the height and width of an image, but also provide
  17341.  resolution information, information about gray scale or color, and the type
  17342.  of compression scheme being used.
  17343.  
  17344.  Currently TIFF supports a modified form of the run-length compression, CCITT
  17345.  Group 3. CCITT/3 provides reasonable compression on black-and-white images
  17346.  that are 200 or 300 dpi by identifying continuous runs of all black or all
  17347.  white pixels. It then replaces these with a unique code word that can be
  17348.  used to reconstruct the original continuous run of data. You get the
  17349.  compression because the code words are typically shorter than the run of
  17350.  data they are replacing. CCITT/3's compression on dithered gray scale or
  17351.  color images isn't a good because redundancy isn't found in the same way as
  17352.  it is in standard black-and-white images. For this reason, TIFF also
  17353.  supports a simple packed scheme in which data is packed into bytes as
  17354.  tightly as possible with no unused bits, except at the end of a row.
  17355.  Specific compression schemes for gray and colored images will be added;
  17356.  Microsoft, the current keeper of the TIFF specification, will welcome you
  17357.  suggestions.
  17358.  
  17359.  TIFF makes it possible to add new features without having to rewrite
  17360.  supporting software each time a new feature is added. For example, if one
  17361.  application wants to add a "date-of-creation" tag to an image, it can be
  17362.  included as a tag when the application is written. Other applications that
  17363.  read TIFF files don't need to be rewritten unless they intend to make use of
  17364.  this new date-of-creation tag. The TIFF specification claims that "very high
  17365.  priority has been given to structuring the data in such a way as to minimize
  17366.  the pain of future additions." When additional capabilities, such as
  17367.  scanning color images, are available, TIFF should be able to accommodate
  17368.  them as well.
  17369.  
  17370.  
  17371.  How TIFF Works
  17372.  
  17373.  Traditional painting file formats, such as PC Paintbrush, use a fixed format
  17374.  organization (see Figure 2). The organization and location of each value is
  17375.  known prior to reading the file. For example, the x resolution information
  17376.  in this example is always stored in bytes 2 and 3 of the record.
  17377.  
  17378.  TIFF is not positionally based. Instead it uses tagged information, a style
  17379.  typically used in database design. Tags define the attributes of the image
  17380.  and how the actual image is stored. Each image has a header followed by tags
  17381.  that describe information contained elsewhere in the file. Tags consist of
  17382.  the name of the tag information, the size and length of the information
  17383.  described in the tag, and a pointer to the actual information (see
  17384.  Figure 3). Tags can expand to contain as much information as needed about
  17385.  the image, so that you can add as many tags as you need. You'll only need a
  17386.  few tags for simple images; you may need many tags for complex images.
  17387.  
  17388.  The header and tags for an image are called the Image File Directory.
  17389.  Because of its structure, you can store multiple versions of the same image
  17390.  in one file. This could be useful if, for example, you wanted to store a
  17391.  full-resolution version of an image for editing and printing and a
  17392.  subsampled version for better display performance, as shown in Figure 4.
  17393.  Each version has its own IFD with a unique set of attributes and pointers to
  17394.  the actual data. TIFF permits you to store the image data in variable-length
  17395.  strips or scan lines, giving you random access to any part of the image. If
  17396.  you just need one section of a very large image and you've stored the image
  17397.  in strips, you can decompress and load only the strips you need. And when
  17398.  you modify an image, you need to restore only the modified strips, not the
  17399.  entire image.
  17400.  
  17401.  
  17402.  Advantages
  17403.  
  17404.  For software and hardware developers, the advantages of a standardized
  17405.  format for exchanging digital data are obvious. Rather than support a
  17406.  proprietary file format for each scanner vendor, desktop publishing
  17407.  application, and paint or draw program, developers need to support just one
  17408.  file format. Even with a complex file format like TIFF, this is better than
  17409.  supporting a constantly growing number of different formats.
  17410.  
  17411.  TIFF is considered to be a "rich" file format. Rather than reduce all images
  17412.  to the lowest common denominator, TIFF is designed to handle many different
  17413.  kinds of images, line art, gray scale, and color, and different resolutions
  17414.  as well.
  17415.  
  17416.  
  17417.  TIFF
  17418.  
  17419.  supports different compression schemes and is flexible enough to adapt the
  17420.  compression algorithm to fit the type of data. It will expand to handle more
  17421.  complex images and compression schemes as the technology advances. Adding
  17422.  more features does not require rewriting existing applications if those
  17423.  applications don't want to take advantage of the expanded capability.
  17424.  Besides handling complex images well, TIFF also works with such simple
  17425.  images as those produced by paint programs, because the application can use
  17426.  only the parts of TIFF it requires.
  17427.  
  17428.  Another advantage of TIFF is its machine independence. You can use it with
  17429.  Intel or Motorola processors with UNIX, MS-DOS, or Macintosh operating
  17430.  systems, which encouraged Mitch Murphy, project manager at OptiGraphics
  17431.  Corp., to convert to TIFF.
  17432.  
  17433.  OptiGraphics, which makes engineering workstations and large-document
  17434.  scanners, were looking for a file format to use for all raster images for
  17435.  both its PC and UNIX machines. Murphy said it chose TIFF so it could
  17436.  "gracefully upgrade and maintain compatibility as products mature." Products
  17437.  from Murphy's group, such as View! and Markup!, are Microsoft Windows
  17438.  applications that allow you to view and make minor changes to large
  17439.  engineering and architectural drawings. The scanner outputs the drawings in
  17440.  TIFF, and View! and Markup! read and display the TIFF output. Later, if a
  17441.  drawing needs more extensive work, there are products that automatically
  17442.  vectorize the TIFF file so it can be used with a CAD system.
  17443.  
  17444.  
  17445.  Limitations
  17446.  
  17447.  TIFF's richness, which is one of its major advantages, is also the source of
  17448.  its limitations. Although it's not terribly difficult for a scanner to write
  17449.  a TIFF image file, it is reasonably complicated to write a TIFF reader. Marc
  17450.  Cantor, who is president of MacroMind, faults Aldus and Microsoft for not
  17451.  including a decoding algorithm with the TIFF specification. As it stands
  17452.  now, each company has to write its own parser; Cantor's company, whose
  17453.  product Graphic Works reads a scanned TIFF document of any size, wrote its
  17454.  own parser and is selling it to others. To remedy the problem, Microsoft
  17455.  will be making TIFF tools available.
  17456.  
  17457.  Another limitation is that there is not a standard TIFF file, nor a common
  17458.  subset of features that everyone using the name TIFF must support. Different
  17459.  vendors can support different Tag fields of TIFF. OptiGraphics' Murphy says
  17460.  that "TIFF is so rich that dialects will emerge." To prevent this, he and
  17461.  others suggest using common tools, which are becoming available. Murphy has
  17462.  a library of routines to access TIFF files in an orderly way; this helps to
  17463.  keep track of all the levels of indirection.
  17464.  
  17465.  If you contact him at OptiGraphics at (619) 292-6060, he'll send them to you
  17466.  for free. However, he cautions that you "use them at your own risk." The
  17467.  development engineers at Hewlett-Packard have made a significant
  17468.  contribution to the TIFF spec and have written a lucid Guide to the Tagged
  17469.  Image File Format. Hewlett-Packard also has some source code for reading,
  17470.  writing, compressing, decompressing, and debugging TIFF files. For
  17471.  information, call HP's ISV marketing department at (303) 350-4000. Dest
  17472.  Corp., a scanner hardware manufacturer, also has a library of routines it
  17473.  will license to desktop publishing vendors free of charge. The phone number
  17474.  is (408) 946-7100. And Tim Davenport at Aldus will send you sample TIFF
  17475.  files; for information, call (206) 622-5500.
  17476.  
  17477.  
  17478.  The New Standard?
  17479.  
  17480.  TIFF is rapidly becoming the standard for handling bit-mapped images.
  17481.  Companies like HP, Aldus, and OptiGraphics have put a significant amount of
  17482.  research and development into TIFF. Most echo Murphy's sentiments: now that
  17483.  they've committed to TIFF, they want others to commit as well, and they're
  17484.  willing to help. David Rosenbloom of Cygnet Technologies, a high-end
  17485.  telecommunications firm, chose TIFF rather than a proprietary format for its
  17486.  FAX product because it had what it needed, a generic descriptive header with
  17487.  an unlimited number of fields and :a shot at becoming some sort of accepted
  17488.  standard." Other firms that have publicly announced support for TIFF are
  17489.  Dest, DataCopy, Microsoft, Microtek, Media Cybernetics, New Image
  17490.  Technology, and Software Publishing. Support for TIFF is bridging the
  17491.  traditional rift between the PC and Macintosh camps.
  17492.  
  17493.  Although Steve Carlson at Aldus is the originator of the TIFF specification,
  17494.  Microsoft is now the keeper of TIFF. With Microsoft, a neutral player,
  17495.  managing TIFF rather than a hardware or desktop publishing vendor, companies
  17496.  are more likely to see TIFF as a standard instead of an aid to competitors.
  17497.  Microsoft is currently working with those companies whose products scan and
  17498.  archive images. It also wants to work with such page description vendors as
  17499.  Adobe, DDL, and Interpress to ensure that the functions TIFF supports are
  17500.  mapped closely to future functions of page description languages. If, for
  17501.  example, compression schemes are compatible, print time can be significantly
  17502.  improved. Manny Vellon of the Microsoft Windows marketing group is the new
  17503.  TIFF administrator; you can write to him for a copy of the spec and also
  17504.  feel free to send him your suggestions.
  17505.  
  17506.  Currently, the desktop publishing market is most interested in TIFF, but the
  17507.  market is expanding to include OCR, FAX, 3-dimensional CAD, and
  17508.  sophisticated medical imaging. Someday, instead of going to your bookshelf
  17509.  and reaching for a volume in your published art collection, you may go to
  17510.  your CD ROM disc and get high-quality typeset images stored in TIFF format.
  17511.  Soon you may be able to receive an engineering drawing as a FAX on your PC,
  17512.  use an application to vectorize the drawing, touch it up with your CAD
  17513.  program or the specs that accompany the drawing in another FAX, run an
  17514.  application to OCR the specs, edit them with your word processor, and then
  17515.  send them back in FAX form. These hardware and software applications will be
  17516.  able to talk and work together when there is a true standard for exchanging
  17517.  digital image data; TIFF is well on its way to becoming that Standard.
  17518.  
  17519.  
  17520.  Figure 1:  TIFF's features can support scanners, paint programs, and cameras
  17521.             and treats simple paint programs just as it does complex medical
  17522.             documents.
  17523.                                                           ╔══════════════╗
  17524.                  ┌░░░▒▒┐                                  ║ ░░░░░░░░░░░  ║█
  17525.         Camera   └──░──┘            __________            ║ ░░░≡  ≡≡░░░░ ║█
  17526.  ┌───▀▀────────────▀▀▀─┐       ╓───/        /─╖           ║ ░░░≡≡≡≡≡░░░░ ║█
  17527.  │╔════╗ ▒░░░░╔════╗ │█    ╔═╝──┴────────┴──╚═╗         ║ ░░░░░░░░░░ ║█
  17528.  │║░░░▒║ ╔════╗ ║░▒▒▒║ │█    ║ ----  ║█      ╔═╝──────────────╚═╗
  17529.  │║░░░▒║ ║   ░║ ║░░▒▒║ │█    ╚══════════════════╝█      ║ ----  ║█
  17530.  │╚════╝ ╚════╝ ╚════╝ │█      ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀      ╚══════════════════╝█
  17531.  └─────────────────────┘█          Scanner         ┌────  ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
  17532.    ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀             │            │      Desktop Publishing
  17533.                       │             tiff           │
  17534.    ╔════════════╗    tiff             │          ascii
  17535.    ║ ░░░░░░░░  ║█    │         ┌──▄▄▄▄▄▄▄▄──\     │        ╔══════════╗
  17536.    ║ ░░░ █ ░░░░ ║█    └─────────│  ██████ █  │     │        ║ ▒▒▒▒▒▒▒▒▒║█
  17537.    ║ ░░░   ░░░░ ║█ ───tiff──────│┌──────────┐│─────┤        ║ ▒▒▒▒▒▒▒▒▒║█
  17538.    ║ ░░░░▌▐░░ ║█              ││   Disk   ││     │   ╔════╩──────────╩══╗
  17539.  ╔═╝────────────╚═╗             │└──────────┘│     │   ║ ----  ║█
  17540.  ║ ----  ║█            └────────────┘     │   ╚══════════════════╝█
  17541.  ╚════════════════╝█                               │     ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
  17542.    ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀                               │       Word Processsor
  17543.      Paint/Draw                                    └────
  17544.  
  17545.  
  17546.  Figure 2:  Fixed Format Organization
  17547.  
  17548.                 ╔════╗  ┌────────────────────────────┐
  17549.                 ║  0 ║  │         Revision #         │
  17550.                 ╟────╢  ├────────────────────────────┤
  17551.                 ║  2 ║  │       X Resolution         │
  17552.                 ╟────╢  ├────────────────────────────┤
  17553.                 ║  4 ║  │       Y Resolution         │
  17554.                 ╟────╢  ├────────────────────────────┤
  17555.                 ║  6 ║  │         H Pixels           │
  17556.                 ╟────╢  ├────────────────────────────┤
  17557.                 ║  8 ║  │         V Pixels           │
  17558.                 ╟────╢  ├────────────────────────────┤
  17559.                 ║ 10 ║  │        Colorplane          │
  17560.                 ╟────╢  ├────────────────────────────┤
  17561.                 ║    ║  │                            │
  17562.                 ║    ║  │                            │
  17563.                 ║    ║  │                            │
  17564.                 ║ 64 ║  ≈           Data             ≈
  17565.                 ║    ║  │                            │
  17566.                 ║    ║  │                            │
  17567.                 ║    ║  │                            │
  17568.                 ╚════╝  └────────────────────────────┘
  17569.  
  17570.  
  17571.  Figure 3:  TIFF uses tagged information, a style typically used in database
  17572.             design. Tags define the attributes of the image and how the actual
  17573.             image is stored. Each image has a header followed by tags that
  17574.             describe information conatined elsewhere in the file.
  17575.  
  17576.                  ┌─────────────────────┐
  17577.                  ≈    Tiff Header  ───≈──┐
  17578.                  ├─────────────────────┤  │  ┌──────────┐  ╔═══╗
  17579.            ╔═══╗ │       Tag 1 ───────┼──┘ │ Tag Type │█ ║ T ║
  17580.            ║ D ║ ├─────────────────────┤    ├──────────┤█ ║ A ║
  17581.            ║ I ║ │       Tag 2         │    │   Size   │█ ║ G ║
  17582.            ║ R ║ ├─────────────────────┤    ├──────────┤█ ║   ║
  17583.            ║ E ║ │       Tag 3         │    │  Length  │█ ║ E ║
  17584.            ║ C ║ ├─────────────────────┤     ├──────────┤█ ║ N ║
  17585.            ║ T ║ │       Tag 4     ───┼──┐  │ Data or  │█ ║ T ║
  17586.            ║ O ║ ├─────────────────────┤  │  │ Pointer  │█ ║ R ║
  17587.            ║ R ║ │       Tag 5         │  │  └──────────┘█ ║ Y ║
  17588.            ║ Y ║ ├─────────────────────┤  │    ███████████ ╚═══╝
  17589.            ╚═══╝ │       Tag 6     ───┼──│─┐
  17590.                  └──────────:──────────┘  │ │
  17591.                  ┌──────────:──────────┐  │ │
  17592.                  │  Long Tag Data ────┼──┘ │
  17593.                  └──────────:──────────┘    │
  17594.                  ┌──────────:──────────┐    │
  17595.                  │ Bitmap Image Data ─┼────┘
  17596.                  └─────────────────────┘
  17597.  
  17598.  
  17599.  Figure 4:  The header and tags for an image are called the Image File
  17600.             Directory (IFD). It is structured so that you can store multiple
  17601.             versions of the same image in one file.
  17602.  
  17603.                                              ┌────────────┐       Image Data
  17604.    ┌────────────────────┐                 ┌─│ Pointer to ├─┐    ┌──────────┐
  17605.    │    File Header     │                 │  │ Strip 1    ─────│ Strip 1  │
  17606.    ├────────────────────┤                 │  ├────────────┤ ├─┐  ├──────────┤
  17607.  ┌─Pointer to first ifd│                 │  ≈            ≈ │ │  │          │
  17608.  │ └────────────────────┘    ┌──────────┐ │  ├────────────┤ │ │  ≈          ≈
  17609.  │  Full Resolution ifd   ┌─│Pointer to│ │  │ Pointer to │ │ │  │          │
  17610.  │ ┌─────────┬──────────┐ │  │  Plane 1 ─┘  │ Strip n    ────┐ ├──────────┤
  17611.  └│  # of   │ Subfile  │ │  ├──────────┤    └┬───────────┘ │ │└│ Strip n  │
  17612.    │ entries │   Type   │ │  ≈          ≈     └┬────────────┘ │  ├──────────┤
  17613.    ├─────────┴──────────┤ │  ├──────────┤ ┌───│  Pointer to  │  │          │
  17614.    │        Tags        ─┘  │Pointer to│ │    │  Strip n     │  ≈          ≈
  17615.    ├────────────────────┤    │  Plane m ─┘    └──────────────┘  │          │
  17616.  ┌─Pointer to next ifd │    └──────────┘    ┌────────────┐      ├──────────┤
  17617.  │ └────────────────────┘                 ┌─│ Pointer to ├─┐  ┌│ Strip 1  │
  17618.  │     Subsample ifd         ┌──────────┐ │  │ Strip 1    ────┘ ├──────────┤
  17619.  │ ┌─────────┬──────────┐ ┌─│Pointer to│ │  ├────────────┤ ├─┐  │          │
  17620.  └│  # of   │ Subfile  │ │  │  Plane 1 ─┘  ≈            ≈ │ │  ≈          ≈
  17621.    │ entries │   Type   │ │  ├──────────┤    ├────────────┤ │ │  │          │
  17622.    ├─────────┴──────────┤ │  ≈          ≈    │  Pointer to│ │ │  ├──────────┤
  17623.    │        Tags        ─┘  ├──────────┤    │  Strip y   ───┼─│ Strip y  │
  17624.    ├────────────────────┤    │Pointer to│    └┬───────────┘ │ │  ├──────────┤
  17625.    │Pointer to next ifd │    │  Plane x ───┐ └┬────────────┘ │  │          │
  17626.    └────────────────────┘    └──────────┘   └─│  Pointer to  │  ≈          ≈
  17627.                                                │  Strip y     │  │          │
  17628.                                                └──────────────┘  │          │
  17629.  
  17630.  
  17631.  ───────────────────────────────────────────────────────────────────────────
  17632.  The CCITT/3 Scheme in TIFF
  17633.  ───────────────────────────────────────────────────────────────────────────
  17634.  
  17635.  The CCITT/3 scheme used in TIFF identifies continuous runs, which are called
  17636.  run-lengths, of either all white or all black pixels. Each of these run-
  17637.  lengths is then replaced with a code word that can later be used to
  17638.  reconstruct the continuous run of data.
  17639.  
  17640.  The compression scheme uses two tables. One table contains code words for
  17641.  different continuous runs of white pixels from 0 to 63 pixels in length.
  17642.  The other table contains code words for the same continuous runs of black
  17643.  pixels. The tables also contain values for some discrete run-lengths above
  17644.  63. If the white- or black-pixel run-lengths exceed 63, combinations of
  17645.  codewords can be used to compress these longer run-lengths.
  17646.  
  17647.  Each row of the bitmap is compressed independently from any other row. In
  17648.  addition, each row must start with a white pattern and must end on a byte
  17649.  boundary. Starting with a white pattern provides synchronization and allows
  17650.  the decompression algorithm know whether to search in the black or white
  17651.  run-length table. If the row starts with one or more black pixels, then a
  17652.  white run-length of 0 pixels must start the row of compressed data code
  17653.  words.
  17654.  
  17655.  End-of-line (EOL) markers, common to CCITT/3 used for FAX, are not used. If
  17656.  EOL markers are used, readers of TIFF files would not be able to interpret
  17657.  the information. Also, the TIFF PhotoMetric interpretation tag doesn't
  17658.  apply when using CCITT compression. The values for white and black are
  17659.  predefined. You may need to invert all binary data before encoding it in
  17660.  CCITT format.
  17661.  
  17662.  Any padding bits added to the uncompressed image row must not be compressed.
  17663.  You should only compress the length of data that was specified in the width
  17664.  tag field. For example, if a bitmap consisted of 10 pixels across, it would
  17665.  be stored uncompressed as 2 bytes so that each row would begin on a byte
  17666.  boundary. In this case, six padding bits would have been added. When
  17667.  compressing this information, only the first 10 bits should be considered as
  17668.  the data to be compressed, not the six padding bits.
  17669.  
  17670.  ████████████████████████████████████████████████████████████████████████████
  17671.  
  17672.  Ask Dr. Bob!
  17673.  
  17674.  Microsoft Pascal Inquiries
  17675.  
  17676.  Dear Dr. Bob,
  17677.  
  17678.  I received my first copy of MSJ last week, and from now on I will look
  17679.  forward to your column, since it gives me an inkling of what the rest of
  17680.  the world is doing. I have several questions about Microsoft(R) Pascal
  17681.  Version 3.31.
  17682.  
  17683.  In your March 1987 column (MSJ, Vol. 2 No.1), you refer to the C library
  17684.  function getenv(). Microsoft Pascal 3.31 supplies a part of the C library
  17685.  with the functions named SYSTEM and SPAWNLP, but no GETENV or any others.
  17686.  Is there a way to get and use them from Microsoft Pascal?
  17687.  
  17688.  In Microsoft Pascal, how can I make a single procedure definition file
  17689.  that can be included in all program modules, even the ones defining the
  17690.  procedures in the include file? Normally this gives some kind of
  17691.  "duplicate identifier" error.
  17692.  
  17693.  I use ALLMQQ extensively. Is there a problem with excessive use of ALLMQQ
  17694.  and FREMQQ, even if I am careful to free up what I don't use? When I
  17695.  allocate several large (25Kb) blocks and then free them up, sometimes I
  17696.  can't allocate them again. Granted, there are additional scores of ALLMQQs
  17697.  and FREMQQs going on at the same time.
  17698.  
  17699.  Where can I get information on resident programming techniques? I am
  17700.  totally ignorant, and the standard language manuals don't even hint at it.
  17701.  ──RHK
  17702.  
  17703.  If you have the Microsoft C Compiler, Version 3.0 or Version 4.0, you may
  17704.  use any of the large-memory model C libraries from a Microsoft Pascal 3.31
  17705.  program. Actually, you can mix large model C code with Microsoft Pascal or
  17706.  FORTRAN code, if you like. See the appendix on mixed-language programming
  17707.  in your Pascal User's Guide.
  17708.  
  17709.  With regard to GETENV, you are particularly lucky. It is present in the
  17710.  subset of the C library included with Pascal 3.31 in the file CEXEC.LIB.
  17711.  Figure 1 is a small program that uses GETENV to print out the PATH.
  17712.  
  17713.  The Microsoft Pascal metacommands for conditional compilation ($if, $then,
  17714.  $else, $end) provide a way to make a single module definition file. First,
  17715.  create the definition file with the procedure and function definitions
  17716.  followed by "extern;", grouping the definitions together by module. Next,
  17717.  put an "$if moduleX $else ... $end" conditional around each module's group
  17718.  of definitions, where X is the module number, as shown in Figure 2.
  17719.  
  17720.  Finally, include this definition file in all your program's modules
  17721.  following a list of constant definitions. Make all the constants 0, except
  17722.  for the one for the current module. In the file for module number 1, for
  17723.  instance, you would place the following:
  17724.  
  17725.    CONST
  17726.      module1 = 1;
  17727.      module2 = 0;
  17728.      module3 = 0;
  17729.      dummy   = 0;
  17730.    {$include:'define.inc'}
  17731.  
  17732.  The constant "dummy" is required because the compiler keeps one look-ahead
  17733.  symbol as described in section 18.3 of the Pascal 3.31 Reference Manual.
  17734.  
  17735.  Dr. Bob knows of no bugs in the ALLMQQ and FREMQQ memory management
  17736.  functions, but it is easy to make a sequence of calls to ALLMQQ and FREMQQ
  17737.  that will cause ALLMQQ to fail even when there seems to be lots of free
  17738.  space. This is because the memory heap can become fragmented, that is,
  17739.  broken into small pieces of alternating free and allocated blocks, so that
  17740.  there is no single chunk large enough for the ALLMQQ request. A more
  17741.  sophisticated memory management scheme would have a "garbage collector" to
  17742.  shuffle memory blocks around and put all the free space together to give
  17743.  ALLMQQ a chance to allocate a large block. However, adding a garbage
  17744.  collector opens its own can of worms and makes programming more
  17745.  complicated.
  17746.  
  17747.  There is no general-purpose method for working around this fragmentation
  17748.  problem. The best you can do is try to allocate large blocks early in your
  17749.  program, before the heap gets fragmented. Also, use ALLMQQ and FREMQQ less
  17750.  often by reusing a block that's already been allocated.
  17751.  
  17752.  Glad you asked about TSRs. The December 1986 issue of MSJ (Vol. 1 No. 2)
  17753.  has an article on TSRs, "Moving Toward an Industry Standard for Developing
  17754.  TSRs." Also, we've seen articles on writing TSRs in recent issues of Byte
  17755.  and Dr. Dobb's Journal.
  17756.  
  17757.  Environment Size in DOS
  17758.  
  17759.  Dear Dr. Bob,
  17760.  
  17761.  It appears that with MS-DOS Version 3.2 the only way to increase the
  17762.  environment size is with COMMAND /E:xxxx. I have tried putting this
  17763.  command into my AUTOEXEC.BAT with an additional /P switch to make it
  17764.  permanent. This results in an infinite regression of COMMAND invoking
  17765.  AUTOEXEC, which runs COMMAND and invokes AUTOEXEC , which ... you get the
  17766.  idea. How can I do this without getting thrown for a loop?──EG
  17767.  
  17768.  When started with the /P switch, COMMAND.COM looks for an AUTOEXEC.BAT in
  17769.  the root directory of the current disk. You can take advantage of this by
  17770.  making a small AUTOEXEC.BAT on your boot disk that copies the "real"
  17771.  AUTOEXEC.BAT to a RAMdisk and runs the secondary version of COMMAND from
  17772.  there. Figure 3 shows what such an AUTOEXEC might look like, where C: is
  17773.  the boot disk and D: is a RAMdisk.
  17774.  
  17775.  In the example, drive C: is the boot disk, drive D: is the RAMdisk, and
  17776.  AUTOEXEC.2ND contains the rest of the boot sequence normally found in
  17777.  AUTOEXEC.BAT.
  17778.  
  17779.  An Alternative Suggestion
  17780.  
  17781.  Dear Dr. Bob,
  17782.  
  17783.  Your delightfully convoluted response to Forgetful in the March 1987 issue
  17784.  of MSJ (Vol. 2 No. 1) prompts me to suggest this alternative. Instead of
  17785.  twice compiling an IFDEFed C file and piping it into both an .INC and an
  17786.  .H file for inclusion in both assembly and compilation processes, try the
  17787.  trick shown in Figure 4, which depends on a basic difference between C and
  17788.  MASM comments: C comments can cross line boundaries, while MASM comments
  17789.  go to the end of the line.
  17790.  
  17791.  This file can be directly included in both the assembly and the C source
  17792.  code. Perhaps there is an even more compact and bizarre method of achieving
  17793.  the same goal, but I can't think of it.──BN
  17794.  
  17795.  You have a cute idea there, but we find it deficient in two respects.
  17796.  First, there are still two distinct copies of each piece of information,
  17797.  and it would be all too easy to change one without changing the other.
  17798.  This defeats the purpose of the system, which is to make a single
  17799.  collection of definitions that will automatically serve for both assembler
  17800.  and C programming. Second, none of our C compilers will accept your code,
  17801.  as they all require the #define directives either to start in the first
  17802.  column or to be the first nonwhite space on a line.
  17803.  
  17804.  Comments and Corrections
  17805.  
  17806.  Dear Dr. Bob,
  17807.  
  17808.  In Dr. Bob's "Self-Modifying Code" in the March 1987 issue of MSJ (Vol.2
  17809.  No.1), the answer is incorrect. LINK4 does not accept the MULTIPLE option
  17810.  for the CODE statement. I have struggled with the problem of forcing Windows
  17811.  to use different code segments for multiple instances when I was writing
  17812.  about Windows memory management.
  17813.  
  17814.  To my mind, Windows should load different code segments for multiple
  17815.  instances when you specify that a CODE segment is IMPURE or NONSHARED in
  17816.  the module definition file. This is valid syntax, but Windows seems to
  17817.  ignore these options.
  17818.  
  17819.  The only way I found to force Windows to load multiple code segments for
  17820.  different instances is by making the .EXE name different from the NAME of
  17821.  the module in the .DEF file. I discovered this bug by accident, and it
  17822.  certainly doesn't make any sense to me.
  17823.  
  17824.  I should also note that even if you get Windows to load multiple code
  17825.  segments, it will still discard code segments if you specify only MOVEABLE
  17826.  without DISCARDABLE. This is implied by the text of your answer, but not
  17827.  by the example. You have to make a code segment FIXED to prevent Windows
  17828.  from discarding it.
  17829.  
  17830.  For the stock bitmap question, I think the answer should have included the
  17831.  fact that the stock bitmap has a width and height of only one pixel, so it
  17832.  is useless for drawing until you select another bitmap onto it.──CP
  17833.  
  17834.  MULTIPLE (or IMPURE or NONSHARED) code segments are not supported. All
  17835.  code segments are by definition shared. It is unfortunate that the
  17836.  documentation is not clearer on this point. The documentation is also not
  17837.  clear about saying that all CODE can either be FIXED or DISCARDABLE and
  17838.  that MOVEABLE implies DISCARDABLE for code segments. As for forcing
  17839.  Windows to load multiple code segments for different instances by making
  17840.  the .EXE name different from the NAME of the module in the .DEF file, I
  17841.  suspect that what was happening was not what you think it was. It
  17842.  definitely does not load multiple instances of the same code segment.
  17843.  
  17844.  Dear Dr. Bob,
  17845.  
  17846.  In MSJ, Vol. 2 No. 1, Dr. Bob tells a very good story about why
  17847.  Windows uses only three of the four planes on the EGA, but unfortunately
  17848.  it's totally wrong. As the programmer who first wrote the Windows EGA
  17849.  driver, I know what I'm talking about. The mouse cursor is stored in off-
  17850.  screen memory in planes 0-2. Windows also uses planes 0-2 for display.
  17851.  Plane 3 is not used at all by Windows. Plane 3 by itself wouldn't have
  17852.  been enough for the cursor──I wish it had been, as it would have made my
  17853.  job a lot easier──since the Windows cursor requires two planes: it supports
  17854.  white, black, transparent, and inverted pixels.
  17855.  
  17856.  Why is the fourth plane unused? When I did the driver originally, it was
  17857.  because I had to get the thing working in a matter of days and I didn't
  17858.  have time to fool with the fourth plane. Since the fourth plane is most
  17859.  logically used as some kind of intensity bit, and this is not independent
  17860.  of the R, G, and B bits (that is, you can't make R intensified without
  17861.  simultaneously making G and B intensified), this fourth bit doesn't really
  17862.  fit into the GDI virtual RGB model very well, so logical <-> physical
  17863.  mapping is hard to do.
  17864.  
  17865.  Since then, the code has been maintained by someone else, and support for
  17866.  the fourth plane was never added, probably because of the awkward logical
  17867.  <-> physical mapping mentioned above.──BC
  17868.  
  17869.  
  17870.  Figure 1:
  17871.  
  17872.  program EnvTest(input,output);
  17873.  
  17874.       type
  17875.            chrptr = ^array[0..1000] of char;
  17876.  
  17877.       var
  17878.             path : chrptr;
  17879.             i    : integer;
  17880.  
  17881.       function getenv : chrptr [c,varying]; extern;
  17882.  
  17883.       begin
  17884.            write('Dr Bob''s PATH = ');
  17885.            path := getenv(ads('PATH'*chr(0)));
  17886.            i := 0;
  17887.            while (path^[i] <> chr(0)) do
  17888.                 begin
  17889.                      write(path^[i]);
  17890.                      i := i+1;
  17891.                 end;
  17892.       end.
  17893.  
  17894.  
  17895.  Figure 2:
  17896.  
  17897.  {$if module1 $else}
  17898.    {definitions for module1:}
  17899.    procedure f1; extern;
  17900.  {$end}
  17901.  
  17902.  {$if module2 $else}
  17903.    {definitions for module2:}
  17904.    procedure f2(p2:integer); extern;
  17905.  {$end}
  17906.  
  17907.  
  17908.  Figure 3:
  17909.  
  17910.  COPY AUTOEXEC.2ND D:AUTOEXEC.BAT
  17911.  D:
  17912.  C:COMMAND /p
  17913.  
  17914.  
  17915.  Figure 4:
  17916.  
  17917.  ;/* ScreenHt=200 ; */ #define ScreenHt (200)    /* comment
  17918.  ScreenWidth=640  ; */ #define ScreenWidth (640) /* comment
  17919.  
  17920.  Point STRUC      ; */ struct Point {           /* comment
  17921.   x  DW           ; */    int x;                /* comment
  17922.   y  DW           ; */    int y;                /* comment
  17923.  Point ENDS       ; */ };
  17924.  
  17925.  
  17926.  ════════════════════════════════════════════════════════════════════════════
  17927.  
  17928.  
  17929.  Vol. 2 No. 4 Table of Contents
  17930.  
  17931.  
  17932.  Microsoft(R) Windows/386: Creating a Virtual Machine Environment
  17933.  
  17934.  Windows/386 exploits the 80386's virtual machine capabilities to offer an
  17935.  environment that allows the user to simultaneously run and switch between
  17936.  existing MS-DOS and Windows applications. The product fully implements
  17937.  preemptive multitasking and data exchange between applications.
  17938.  
  17939.  
  17940.  Programming in C the Fast and Easy Way with Microsoft(R) QuickC(TM)
  17941.  
  17942.  Making the process of C programming faster and easier has long been a goal
  17943.  for developers of Microsoft C programming products. The Microsoft QuickC
  17944.  compiler offers an integrated environment that provides programmers with
  17945.  high-speed compilation and an easy debugger.
  17946.  
  17947.  
  17948.  Character-Oriented Display Services Using OS/2's VIO Subsystem
  17949.  
  17950.  Microsoft OS/2 provides application programs with a wide variety of display
  17951.  functions. The OS/2 video capabilities detailed in this article are the
  17952.  VIO services──the subsystem that lets you get your applications up and
  17953.  running quickly without the Windows Presentation Manager.
  17954.  
  17955.  
  17956.  Dynamic Allocation Techniques for Memory Management in C Programs
  17957.  
  17958.  How well memory is managed can make a real difference in the performance
  17959.  of a program, particularly those programs that process unpredictable
  17960.  amounts of data. Dynamically allocating memory to hold array and linked
  17961.  list data provides your programs with extra flexibility and utility.
  17962.  
  17963.  
  17964.  CD ROM Technology Opens the Doors on a New Software Market
  17965.  
  17966.  Thanks in part to the success of the compact audio disc, CD ROM is viable
  17967.  and growing. Developers with applications requiring large amounts of data
  17968.  space, such as parts catalogs, can benefit from CD ROM's huge data
  17969.  capacity and low cost duplication.
  17970.  
  17971.  
  17972.  MS-DOS(R) CD ROM Extensions: A Standard PC Access Method
  17973.  
  17974.  Using the Microsoft MS-DOS CD ROM Extensions frees developers to concentrate
  17975.  solely on their applications, eliminating dependence on any particular drive
  17976.  technology. This article discusses CD ROM device drivers and the MSCDEX.EXE
  17977.  program that interfaces with MS-DOS.
  17978.  
  17979.  
  17980.  Microsoft(R) QuickBASIC: Everyone's First PC Language Gets Better
  17981.  
  17982.  You can create elegant applications in BASIC by combining the powerful
  17983.  Microsoft QuickBASIC 3.0 control structures and statements, the INT860
  17984.  interface to MS-DOS and BIOS services, and custom assembly-language
  17985.  routines of your own design. Here's how.
  17986.  
  17987.  
  17988.  Ask Dr. Bob
  17989.  
  17990.  
  17991.  EDITOR'S NOTE
  17992.  
  17993.  Microsoft(R) Windows/386 provides users of 80386-based computers with a
  17994.  new operating environment. Its principal benefit is the flexibility to
  17995.  simultaneously run nearly any combination of MS-DOS applications, each in
  17996.  its own virtual machine. While it does not offer developers a new
  17997.  programming interface, we think you will appreciate a behind-the-scenes look
  17998.  at the product's design.
  17999.  
  18000.  Meanwhile, many software developers are working hard to unravel the details
  18001.  and intricacies of MS(R) OS/2. The first OS/2 applications will take
  18002.  advantage of the rich set of character-based display functions that are part
  18003.  of the OS/2 API. In this issue, Ray Duncan explores the use of these
  18004.  functions.
  18005.  
  18006.  However, operating environments aren't all that's new. QuickC gives both
  18007.  the professional and beginning C programmer a new tool──the integrated
  18008.  working environment first seen in QuickBASIC. CodeView(R)-compatible
  18009.  debugging aids and superfast compilation speeds helped create TOUCH.C, a
  18010.  useful file date-stamp utility included here. We also have two articles
  18011.  about a new kid on the PC block──CD ROM, which finally seems ready for
  18012.  general acceptance.
  18013.  
  18014.  All our source code listings can now be found on DIAL, CompuServe, BIX, and
  18015.  two public access bulletin boards. On the East Coast, users can call (212)
  18016.  889-6438 to join the RamNet Bulletin board. On the West Coast, call (415)
  18017.  284-9151 for the ComOne bulletin board. In either case, look for the MSJ
  18018.  directory.
  18019.  
  18020.  A reminder──we read all of our mail, hardcopy and electronic (our MCI
  18021.  mailbox is MSJ). Write and let us hear from you. If you're interested in
  18022.  submitting a manuscript drop Tony Rizzo, our technical editor, a note.
  18023.  He would be more than happy to hear from you.──Ed.
  18024.  
  18025.  
  18026.  Masthead
  18027.  
  18028.  JONATHAN D. LAZARUS
  18029.  Editor and Publisher
  18030.  
  18031.  EDITORIAL
  18032.  
  18033.  TONY RIZZO
  18034.  Technical Editor
  18035.  
  18036.  CHRISTINA G.DYAR
  18037.  Associate Editor
  18038.  
  18039.  JOANNE STEINHART
  18040.  Production Editor
  18041.  
  18042.  GERALD CARNEY
  18043.  Staff Editor
  18044.  
  18045.  KIM HOROWITZ
  18046.  Editorial Assistant
  18047.  
  18048.  ART
  18049.  
  18050.  MICHAEL LONGACRE
  18051.  Art Director
  18052.  
  18053.  VALERIE MYERS
  18054.  Associate Art Director
  18055.  
  18056.  CIRCULATION
  18057.  
  18058.  WILLIAM B. GRANBERG
  18059.  Circulation Manager
  18060.  
  18061.  L. PERRIN TOMICH
  18062.  Assistant to the Publisher
  18063.  
  18064.  BETSY KAUFER
  18065.  Administrative Assistant
  18066.  
  18067.  Copyright(C) 1987 Microsoft Corporation. All rights reserved; reproduction
  18068.  in part or in whole without permission is prohibited.
  18069.  
  18070.  Microsoft Systems Journal is a publication of Microsoft Corporation, 16011
  18071.  NE 36th Way, Box 97017, Redmond, WA 98073-9717. Officers: William H.
  18072.  Gates, III, Chairman of the Board and Chief Executive Officer; Jon Shirley,
  18073.  President and Chief Operating Officer; Francis J. Gaudette, Treasurer;
  18074.  William Neukom, Secretary.
  18075.  
  18076.  Microsoft Corporation assumes no liability for any damages resulting from
  18077.  the use of the information contained herein.
  18078.  
  18079.  Microsoft, the Microsoft logo, CodeView, MS, MS-DOS, and XENIX are
  18080.  registered trademarks of Microsoft Corporation. QuickC and Bookshelf
  18081.  are trademarks of Microsoft Corporation. IBM and PC AT are registered
  18082.  trademarks of International Business Machines Corporation. PS/2 is a
  18083.  trademark of International Business Machines Corporation. dBASE II is a
  18084.  registered trademark of Ashton-Tate Corporation. AT&T and UNIX are
  18085.  registered trademarks of AT&T. Lotus and 1-2-3 are registered trademarks
  18086.  of Lotus Development Corporation. Intel is a registered trademark of Intel
  18087.  Corporation. COMPAQ is a registered trademark of COMPAQ Computer
  18088.  Corporation. DESKPRO 386 is a trademark of COMPAQ Computer Corporation. CP/M
  18089.  is a registered trademark of Digital Research, Inc. Hercules is a registered
  18090.  trademark of Hercules Computer Technology. WordStar is a registered
  18091.  trademark of MicroPro International Corporation. Zenith is a registered
  18092.  trademark of Zenith Radio Corporation.
  18093.  
  18094.  ████████████████████████████████████████████████████████████████████████████
  18095.  
  18096.  Microsoft Windows/386: Creating a Virtual Machine Environment
  18097.  
  18098.  ───────────────────────────────────────────────────────────────────────────
  18099.  Also see:
  18100.  A Comparison of Current and Future Windows Versions
  18101.  ───────────────────────────────────────────────────────────────────────────
  18102.  
  18103.  Ray Duncan
  18104.  
  18105.  The past year has seen the emergence of a new class of personal computers,
  18106.  based on the IBM(R) PC AT(R) architecture but incorporating an Intel 80386
  18107.  microprocessor with a 32-bit memory path for increased performance. The
  18108.  pioneer in this category was the COMPAQ(R) DESKPRO 386(TM), which has since
  18109.  been joined by the IBM PS/2(TM) Model 80, as well as a score of machines from
  18110.  other clone vendors. Until now, the primary benefit associated with these
  18111.  machines has been their formidable speed. However, 32-bit operating
  18112.  systems and programming tools for the 80386 are still in the development
  18113.  stage, while 32-bit applications software waits in the wings until the
  18114.  tools stabilize.
  18115.  
  18116.  Microsoft Corp.'s new product, Windows/386, unmasks the larger potential
  18117.  of 80386 machines while protecting the user's investment in MS-DOS(R)-
  18118.  compatible hardware and software. The key features of Windows/386 are:
  18119.  
  18120.    ■ a graphical user interface compatible with Windows 2.0 and the OS/2
  18121.      Presentation Manager
  18122.  
  18123.    ■ true preemptive multitasking of MS-DOS applications, each in a private
  18124.      640Kb memory space
  18125.  
  18126.    ■ applications that run under Windows/386 receive much more memory than
  18127.      they would under Windows 2.0
  18128.  
  18129.    ■ the ability to run MS-DOS applications in overlapping windows, even
  18130.      so-called "ill-behaved" applications that do not rely on MS-DOS or
  18131.      the ROM BIOS for screen output
  18132.  
  18133.    ■ exchange of screen data between both standard MS-DOS and Windows
  18134.      applications
  18135.  
  18136.    ■ emulation of the Lotus/Intel/Microsoft Expanded Memory Specification
  18137.      Version 4.0
  18138.  
  18139.  Windows/386 requires either an 80386-based PC AT-compatible or an AT&T(R)
  18140.  6300 computer, an EGA, VGA, or CGA monitor, at least 1Mb of RAM (2Mb are
  18141.  recommended), and a fixed disk. The 80287 or 80387 numeric coprocessors
  18142.  are also supported.
  18143.  
  18144.  
  18145.  An Intel Retrospective
  18146.  
  18147.  Nearly all of the capabilities of Windows/386 depend on a feature of the
  18148.  80386 called virtual 86 mode. To fully appreciate this particular facet of
  18149.  the 80386, it is necessary to digress for a moment and review the
  18150.  characteristics of the 80386's ancestors, the Intel 8086/88 and 80286
  18151.  microprocessors. Each successive generation of Intel processors has
  18152.  supported the software that was written for its predecessors by means of
  18153.  "execution modes," and virtual 86 mode is the logical culmination of this
  18154.  approach to software compatibility.
  18155.  
  18156.  The first Intel 16-bit processors, the 8086 and 8088 (announced in 1978
  18157.  and 1979 respectively), can address a maximum of 1Mb of memory. When
  18158.  memory is referenced, the contents of one of the four segment registers is
  18159.  shifted left four bits and combined with a 16-bit offset to form a 20-bit
  18160.  physical address; the segment registers simply act as base pointers. The
  18161.  8086 and 8088 have no provision for memory protection, virtual memory, or
  18162.  privilege levels; any program can access any location in memory or any I/O
  18163.  device without restriction.
  18164.  
  18165.  The Intel 80286 (first shipped in 1982) represents a major increase in
  18166.  speed and capabilities over the 8086/88. It can run in either of two
  18167.  modes: real or protected. In real mode, the 80286 acts like a fast 8086
  18168.  with a few additional machine instructions. It can run virtually all
  18169.  8086/88 software and is limited to 1Mb of memory.
  18170.  
  18171.  In protected mode, the 80286's mapping of addresses is altered to add a
  18172.  level of indirection. The value in a segment register is a selector, which
  18173.  is an index to an entry in a descriptor table that contains the base
  18174.  address and length of a physical memory segment, segment attributes
  18175.  (executable, read-only, or read-write), and privilege information. Each
  18176.  time a program references memory, the hardware accesses the associated
  18177.  descriptor to generate the physical address and simultaneously checks to
  18178.  make sure that the memory access is valid.
  18179.  
  18180.  This method of protected-mode address generation allows the 80286 to
  18181.  support memory protection and virtual memory management within a physical
  18182.  address space of 16Mb and a virtual address space of 1Gb. Four levels of
  18183.  privilege are also provided, allowing the operating system to be protected
  18184.  from applications programs and those programs from each other.
  18185.  
  18186.  When the Intel 80286 was designed, the dominant software base consisted of
  18187.  CP/M(R) applications such as WordStar(R) and dBASE II(R). At the time the 802
  18188.  was released, the IBM PC was just a few months old. Hence Intel engineers
  18189.  had no way of foreseeing the incredible success of the IBM PC and its
  18190.  compatibles, or the eventual need of an enormous user base to make a
  18191.  smooth transition from real-mode (8086/88) to protected-mode (80286)
  18192.  environments. Although the 80286 was designed to start up in real mode for
  18193.  compatibility with 8086 software, and although machine instructions were
  18194.  included to switch from real mode to protected mode, no mechanism was
  18195.  built into the chip to allow a return from protected mode to real mode
  18196.  under operating system control without halting the processor.
  18197.  
  18198.  This single omission proved to be a hideous technical problem during the
  18199.  development of the Microsoft OS/2, since one of the overriding design
  18200.  goals for the new operating system was to allow "old" (real-mode)
  18201.  applications to run alongside "new" (protected-mode) applications.
  18202.  Although experimentation and painstaking optimization eventually led to an
  18203.  acceptable solution for the necessary mode switching, another drawback
  18204.  remains: the protected-mode operating system cannot be shielded against
  18205.  bad behavior on the part of a real-mode program. By the very nature of
  18206.  real mode, such a program has a free hand with the hardware and can easily
  18207.  crash the machine.
  18208.  
  18209.  
  18210.  The Four Modes
  18211.  
  18212.  The Intel 80386 (introduced in 1985) is a true 32-bit processor that
  18213.  supports a 4Gb physical address space and a 64Tb virtual address space. To
  18214.  ensure compatibility with previous processors and to solve the problems of
  18215.  support for real-mode applications that were encountered with the 80286,
  18216.  the 80386 has no less than four different execution modes. The first is
  18217.  the familiar real mode, wherein the 80386 functions as a fast 8086/88-
  18218.  compatible processor with some bonus opcodes. Like the 80286, the 80386
  18219.  always powers up in real mode and can therefore run any existing 8086
  18220.  operating systems and software.
  18221.  
  18222.  In protected mode, the 80386 can take on two different personalities. It
  18223.  can execute a logical superset of the 80286 protected-mode instructions
  18224.  and run 16-bit programs, or it can run in its native mode, which offers
  18225.  32-bit instructions, registers, and stacks, and allows individual memory
  18226.  segments as large as 4Gb. In either case, the 80386 translates selectors
  18227.  and offsets to linear addresses using descriptor tables in much the same
  18228.  manner as the 80286. However, an additional level of address
  18229.  translation──supported in hardware by page tables──allows much greater
  18230.  flexibility in mapping the linear addresses onto physical memory.
  18231.  
  18232.  Unlike the 80286, the 80386 allows the operating system to switch smoothly
  18233.  and quickly back from protected mode to real mode when necessary. But it
  18234.  is unlikely that this capability will find much use because of the 80386's
  18235.  fourth operating mode: virtual 86 mode.
  18236.  
  18237.  
  18238.  Four Modes of the Intel 80386 Microprocessor
  18239.  
  18240.  Real Mode            Functions as a very fast 8086/88-compatible processor.
  18241.  
  18242.  Protected Mode       Functions in protected mode as an enhanced 286
  18243.  (16-bit)             processor.
  18244.  
  18245.  Protected Mode       Functions in protected mode using full 32-bit
  18246.  (32-bit, native      instructions, registers, and stacks.
  18247.  mode)
  18248.  
  18249.  Virtual 86 Mode      Runs multiple, protected-mode, virtual 8086 machines,
  18250.                       each with its own 1Mb of memory space.
  18251.  
  18252.  
  18253.  Device Virtualization
  18254.  
  18255.  A protected-mode 80386 operating system can create special memory
  18256.  segments──up to 1Mb in size──that have a remarkable characteristic: programs
  18257.  that run within these segments execute as though they were running on an
  18258.  8086/88 in real mode. Each such segment is called a virtual 8086 machine,
  18259.  and each has its own address space, I/O port space, and interrupt vector
  18260.  table. Multiple virtual machines can be running simultaneously, each under
  18261.  the illusion that it is in complete control of the computer.
  18262.  
  18263.  The crucial difference between real mode and virtual 86 mode is that
  18264.  memory protection, virtual memory, and privilege-checking mechanisms are
  18265.  still in effect when a virtual machine is running. Thus, a program
  18266.  executing in a virtual 8086 machine cannot interfere with the operating
  18267.  system or damage other processes. If the program reads or writes memory
  18268.  addresses that have not been mapped into its virtual machine, or if it
  18269.  manipulates I/O ports to which it has not been allowed access, an
  18270.  exception (hardware interrupt) is generated, and the operating system
  18271.  regains control.
  18272.  
  18273.  The operating system's exception handler can choose to carry out the
  18274.  operation on behalf of the program running in the virtual machine,
  18275.  possibly substituting other port or memory addresses; it is also able to
  18276.  arbitrate requests from multiple virtual machines directed at the same I/O
  18277.  port or memory address. This process of intercepting I/O operations,
  18278.  where, for example, the operating system creates the illusion of a
  18279.  separate disk controller or video controller for each virtual machine
  18280.  while only one physical device is present in the system, is called device
  18281.  virtualization.
  18282.  
  18283.  A program that runs in the 80386's native 32-bit protected mode and
  18284.  oversees a set of virtual machines is called a 386 control program, or
  18285.  virtual machine monitor. Windows/386 is a such a virtual machine monitor;
  18286.  it also provides complete device virtualization for the PC's disk
  18287.  controller, video adapter, keyboard, mouse, timer chip, and 8259
  18288.  programmable interrupt controllers.
  18289.  
  18290.  
  18291.  The User Interface
  18292.  
  18293.  At first glance, the user interface of Windows/386 appears identical to
  18294.  that of Windows Version 2.0: it has the same MS-DOS Executive window,
  18295.  pull-down menus, "hot keys," and utilities (Cardfile, Terminal, and the
  18296.  like). A program is launched by selecting its executable file, its PIF
  18297.  file, or one of its data files with the arrow keys or by double-clicking
  18298.  with the mouse, just like under Windows. An open window can be brought to
  18299.  the foreground with Alt-Tab or Alt-Esc or by clicking on the window with
  18300.  the mouse; also, a window may be resized or moved by clicking and dragging
  18301.  its borders (see Figure 1).
  18302.  
  18303.  The first inkling that something different is going on comes when a
  18304.  standard application (that is, an MS-DOS application that is not written
  18305.  specifically for Windows) is started. Under Windows 2.0, only those
  18306.  relatively rare, "well-behaved" MS-DOS programs that perform all of their
  18307.  input and output through standard MS-DOS calls, strictly observe the
  18308.  system's memory management conventions, and avoid all direct access to
  18309.  hardware can run in a window. "Ill-behaved" programs that access the
  18310.  hardware directly for performance reasons──a category that includes nearly
  18311.  every popular MS-DOS application──must run full-screen: Windows 2.0 simply
  18312.  gets out of the way until such programs terminate, and its multitasking
  18313.  comes to a screeching halt.
  18314.  
  18315.  A standard application under Windows/386 initially comes up full-screen
  18316.  just as it does under Windows 2.0. But if the user presses Alt-Enter, the
  18317.  program is suddenly running in an overlapping graphics-mode window right
  18318.  alongside the true Windows application (see Figure 2). The user can use
  18319.  the mouse or the cursor keys to select and copy data from a window
  18320.  containing a standard application to any other window and can resize,
  18321.  move, or even "iconize" such a window. Furthermore, the user can toggle
  18322.  between windowed and full-screen mode with Alt-Enter at any time. When an
  18323.  application is full-screen, Windows/386 can place it into a text-display
  18324.  mode to allow faster displays, and the application's intrinsic mouse
  18325.  support works as though it were running under MS-DOS alone.
  18326.  
  18327.  Windows/386 associates a drop-down control menu, activated with the
  18328.  keycode Alt-Space, with each standard application that can be used to
  18329.  affect its window size, position, and behavior (as shown in Figure 2). The
  18330.  Settings dialog box (see Figure 3), which is reached through the control
  18331.  menu, allows the user to suspend or resume a standard application, specify
  18332.  whether it should run full-screen or in a window, and control its
  18333.  multitasking behavior. The initial settings for a program can also be
  18334.  determined by creating a PIF file for it.
  18335.  
  18336.  There are three multitasking options available for a standard application:
  18337.  foreground, background, and exclusive. When foreground is chosen, the
  18338.  application runs only when it is displayed full-screen or when its window
  18339.  has been selected, but other applications are allowed to run in the
  18340.  background at the same time. The background option means that the
  18341.  application will continue to run even when some other application is
  18342.  selected (of course, this is not useful for word processors and other
  18343.  programs that have nothing to do unless they are receiving keyboard
  18344.  input). When the exclusive option is picked, the application runs only
  18345.  when it is in the foreground, but while it is active it receives all of
  18346.  the CPU time and no other applications are allowed to run at all. Hence
  18347.  the exclusive option permits the application to perform as if it were in a
  18348.  single-tasking MS-DOS environment.
  18349.  
  18350.  Figures 4 and 5 show the process of defining an area of the standard
  18351.  application for copying, bringing up another application, and transferring
  18352.  the defined area.
  18353.  
  18354.  A little experimentation leads to an additional pleasant surprise: there
  18355.  seems to be much more RAM left for a standard application than is usual
  18356.  under Windows. In fact, if COMMAND.COM and then CHKDSK are run in a
  18357.  window, approximately 580Kb is seen to be available──as though Windows
  18358.  weren't present at all. And when multiple standard applications are
  18359.  launched, each runs in a separate memory space as large as 640Kb, until
  18360.  the physical memory of the system is exhausted. As a fringe benefit,
  18361.  devotees of RAM-resident applications (TSRs) can find relief from the
  18362.  phenomenon of "RAM cram" by starting multiple copies of COMMAND.COM to
  18363.  establish several MS-DOS sessions, and loading a different selection of
  18364.  TSRs into each session. This is the magic of the 80386's virtual 86 mode
  18365.  in action.
  18366.  
  18367.  
  18368.  How Windows/386 Works
  18369.  
  18370.  The Windows/386 system actually consists of three separate, mutually
  18371.  interdependent elements. The first is a regulation copy of MS-DOS or PC-
  18372.  DOS, Version 3.0 or later. The second is a copy of Windows 2.0, which is
  18373.  included in the Windows/386 product. The third is the core of the
  18374.  Windows/386 product, the Virtual DOS Machine Manager (VDMM).
  18375.  
  18376.  The MS-DOS component supplies the routine file, time, and date, as well as
  18377.  memory allocations services to the application programs running in each
  18378.  virtual machine; the MS-DOS SHARE.EXE module can optionally be loaded for
  18379.  networklike file locking and sharing support if multiple applications will
  18380.  be accessing the same files. Windows 2.0 provides the graphical user
  18381.  interface and pointer device support. The VDMM runs in the 80386's native
  18382.  32-bit protected mode and oversees the multitasking and protected-memory
  18383.  aspects of the system's operation. The VDMM also takes advantage of the
  18384.  80386's paging capability to supply expanded (Lotus/Intel/Microsoft EMS)
  18385.  memory to applications that require it. The EMS services furnished by
  18386.  Windows/386 are compatible with the recently announced EMS 4.0
  18387.  specification and do not require loading of any special driver.
  18388.  
  18389.  Windows/386 is started on a system that has already been booted up under
  18390.  MS-DOS or PC-DOS in the usual manner. When Windows/386 gains control, it
  18391.  loads the Virtual DOS Machine Manager into extended memory (the memory
  18392.  above the 1Mb boundary), creates an initial virtual machine (referred to
  18393.  as VM1), maps the existing copy of MS-DOS into it using the 80386's page
  18394.  tables, and then loads Windows 2.0 on top of MS-DOS in the first virtual
  18395.  machine. Control is then passed to Windows 2.0, which reads its
  18396.  configuration file (WIN.INI) and presents the familiar MS-DOS Executive to
  18397.  the user.
  18398.  
  18399.  True Windows applications, such as Cardfile and Terminal, run together in
  18400.  the virtual machine containing Windows itself (VM1). Windows presents such
  18401.  programs with exactly the same applications program interface as in real
  18402.  mode, and multiple Windows applications running concurrently are scheduled
  18403.  for execution by the internal Windows multitasker──that is, nonpreemptively.
  18404.  Just as in real mode, the Windows kernel can use expanded memory pages to
  18405.  swap the code and data segments belonging to Windows applications, so that
  18406.  the number of Windows applications that can be loaded simultaneously is not
  18407.  limited by the 640Kb address space.
  18408.  
  18409.  When a standard application is started, the VDMM creates a new virtual
  18410.  machine and maps the image of MS-DOS, the ROM BIOS data area, and other
  18411.  vital structures into it using the 80386 page tables, and then loads the
  18412.  application into the virtual machine (see Figures 6 and 7). If the
  18413.  application does not have a PIF file, the size of the new virtual machine
  18414.  is under the control of the windowmemsize= entry in the WIN.INI file and
  18415.  defaults to 640Kb. If a PIF file exists for the program, the size of the
  18416.  new virtual machine depends on the Kb required and Kb desired fields in
  18417.  the PIF, and on the amount of physical memory actually available (there is
  18418.  no demand paging in Windows/386 as in OS/2).
  18419.  
  18420.  While initializing a new virtual machine, the VDMM also creates a
  18421.  corresponding instance of a special application called VMDOSAP in VM1
  18422.  (multiple instances share the same code segment, so the overhead in the
  18423.  Windows VM for each additional standard application VM is minimal).
  18424.  VMDOSAP is analogous to the WINOLDAP module of real-mode Windows; when the
  18425.  standard application is running in a window instead of full-screen,
  18426.  VMDOSAP is responsible for any necessary translation and clipping of its
  18427.  output for the window shape, size, and graphics-display mode. It also
  18428.  translates input on behalf of the application when necessary (for example,
  18429.  when data is being pasted from another window). The operation of VMDOSAP
  18430.  is completely transparent to the application, and VMDOSAP does not occupy
  18431.  any memory space within the application's virtual machine.
  18432.  
  18433.  The final step in initializing the new virtual machine is to allocate a
  18434.  "shadow" video buffer for the application and select the application's
  18435.  initial display and multitasking status based on the contents of its PIF
  18436.  file. The shadow video buffer lies outside the VM's address space and
  18437.  holds a copy of the application's virtual screen; it is used to restore
  18438.  the display when the application is brought to the foreground and allows
  18439.  scrolling when the application is running in a window. If no PIF file
  18440.  exists for the program, it begins execution with a full-screen display and
  18441.  with its multitasking option set to "foreground"──that is, the application
  18442.  is suspended when it is in the background.
  18443.  
  18444.  The complete isolation of VMDOSAP from the application, and of standard
  18445.  applications from each other, is made possible by Windows/386's
  18446.  virtualization of the system's input and output devices, as well as by its
  18447.  control over each virtual machine's interrupt vector table and I/O port
  18448.  address space. When an application executes a software interrupt (to
  18449.  invoke an MS-DOS or ROM BIOS service), reads from or writes to an I/O
  18450.  port, or accesses a memory address that lies outside its virtual machine
  18451.  space, an exception is generated that is fielded by the VDMM.
  18452.  
  18453.  The VDMM disposes of the exception by examining the machine instruction
  18454.  that provoked it and the contents of the CPU registers. For example,
  18455.  requests for input and output can be funneled to VMDOSAP, while calls for
  18456.  file system services revert to the virtual machine's image of MS-DOS for
  18457.  processing. An application that accesses the hardware to select a video
  18458.  mode not supported by Windows/386 is not terminated, but is forced to run
  18459.  full-screen.
  18460.  
  18461.  
  18462.  Windows/386 Multitasking
  18463.  
  18464.  The Windows/386 multitasking scheduler uses the standard PC AT 18.2-Hz
  18465.  timer tick. Its allocation of CPU cycles across multiple virtual machines
  18466.  is determined by the multitasking setting on each application's control
  18467.  menu or from its PIF file. In the simplest case, where each active program
  18468.  has the "background" option selected, the currently selected application
  18469.  gets two-thirds of the CPU cycles, and the remaining one-third are divided
  18470.  among the other virtual machines. Applications that hook the ROM BIOS
  18471.  timer tick interrupt vector (Interrupt 1CH) receive a proportionate number
  18472.  of timer interrupts.
  18473.  
  18474.  There are also two extreme cases of multitasking worth mentioning. The
  18475.  first is when a standard application that runs under the exclusive option
  18476.  is selected. Such an application receives all the CPU cycles, and all
  18477.  other virtual machines are suspended. The other is encountered when no
  18478.  standard applications are running and only the Windows VM is active. In
  18479.  this case the internal Windows scheduler apportions the CPU cycles between
  18480.  various Windows applications, and the VDMM plays no multitasking role at
  18481.  all.
  18482.  
  18483.  
  18484.  Many Machines, One DOS
  18485.  
  18486.  One of the most interesting aspects of Windows/386 is its relationship to
  18487.  MS-DOS. The 80386's paging tables allow a single physical instance of MS-
  18488.  DOS to be mapped into each virtual machine's address space at the same
  18489.  apparent address. Windows/386 uses internal knowledge of MS-DOS and
  18490.  Windows to maintain the integrity of the file system and to prevent
  18491.  different applications from executing concurrently within MS-DOS's
  18492.  critical regions. It also maintains a copy of the Windows and MS-DOS
  18493.  control structures (such as the MS-DOS system file table and the ROM BIOS
  18494.  video driver data area) for each virtual machine, and swaps these in and
  18495.  out of the MS-DOS image to supply the proper context for the application
  18496.  that is running. These tables and structures are referred to as the MS-DOS
  18497.  kernel instance data.
  18498.  
  18499.  Since simply copying the kernel instance data back and forth on every
  18500.  timer tick would burn up a significant number of CPU cycles, and since
  18501.  applications may execute for prolonged periods (several context switches)
  18502.  without referencing MS-DOS or the ROM BIOS, the VDMM uses an interesting
  18503.  trick to reduce the multitasking overhead. Before a program is given
  18504.  control at the beginning of its time slice, the memory pages in its
  18505.  virtual machine that contain MS-DOS instance data, the interrupt vector
  18506.  table, and the ROM BIOS data area are marked "not present" in the CPU's
  18507.  page table. Only a few pages within the MS-DOS that contain instance data
  18508.  are marked "not present" and cause page faults. Each page is demanded
  18509.  independently of the others. This way many accesses to DOS cause no
  18510.  context switching overboard at all, and when an instance switch is
  18511.  required, its overhead is as small as possible.
  18512.  
  18513.  If the program attempts to access these pages (for example, by inspecting
  18514.  the current cursor location in the ROM BIOS data area or executing an
  18515.  Interrupt 21H), a page fault is generated that suspends the program and
  18516.  transfers control to the VDMM's interrupt handler. The VDMM handles the
  18517.  fault for that page by moving in the MS-DOS kernel instance data, marking
  18518.  the MS-DOS memory page as "present" in the page table to prevent further
  18519.  faults during the same time slice, and restarting the instruction that
  18520.  caused the fault. On the other hand, if the program runs to the end of its
  18521.  time slice without referencing instance memory, no harm has occurred, and
  18522.  the overhead of moving the data needlessly has been avoided.
  18523.  
  18524.  
  18525.  Communications
  18526.  
  18527.  The techniques used in Windows/386 to virtualize the video controller and
  18528.  asynchronous communications controller are also particularly interesting.
  18529.  Since direct hardware access to these devices by standard applications for
  18530.  performance reasons is common, Windows/386 must stay out of the way as
  18531.  much as possible. This ensures that the application's throughput will not
  18532.  not be impaired, and that other programs will not be disrupted while still
  18533.  providing for preemptive multitasking.
  18534.  
  18535.  As mentioned earlier, each virtual machine is allocated a "shadow" video
  18536.  buffer that is used to save a copy of its complete screen image when the
  18537.  application is running in a window or in the background. The buffer is
  18538.  located in extended memory, outside the virtual machine's 1Mb address
  18539.  space, and may range in size from 16Kb to 256Kb depending on the display
  18540.  mode selected by the program.
  18541.  
  18542.  When an application is running in the background or in a window, the VDMM
  18543.  uses the 80386's page tables to map the virtual machine's video refresh
  18544.  buffer addresses (segment A000H, B000H, or B800H) onto the shadow buffer.
  18545.  The application can modify what it perceives to be the video buffer at
  18546.  will (see Figure 8), but the physical screen is not directly affected. At
  18547.  intervals, the VDMM checks the "dirty bits" in the page table──which are
  18548.  set by the hardware when an address within the page is written to──to
  18549.  determine if the application has modified its video buffer. If a write is
  18550.  detected, the buffer is compared against an earlier copy. The changes are
  18551.  sent to VMDOSAP, which renders them into an appropriate pattern of pixels
  18552.  and displays them in the visible portion of the application's window.
  18553.  
  18554.  When an application is running full-screen, the virtual addresses of the
  18555.  video refresh buffer within its virtual machine are mapped onto the
  18556.  physical memory belonging to the video controller, and the shadow buffer
  18557.  is not used as an intermediary. Thus, the program's control of the video
  18558.  display is direct, just as if it were running in real mode and there is no
  18559.  speed degradation. When the user switches away from the full screen
  18560.  application, the VDMM simply copies the video controller's physical buffer
  18561.  to the shadow buffer (the 80386's double-word string move instruction,
  18562.  which transfers 32 bits at a time, is used to advantage here) before
  18563.  restoring the screen image of the Windows/386 desktop or the next full-
  18564.  screen application to be selected.
  18565.  
  18566.  Applications that run in certain EGA graphics modes receive special
  18567.  treatment. The virtual machine's video buffer addresses are mapped onto
  18568.  the second 128Kb of the EGA controller's physical refresh buffer. This
  18569.  lets the application use all of the EGA features without the overhead of
  18570.  emulation, conserves system memory, and allows a rapid switch to full-
  18571.  screen operation. (Instead of copying the shadow buffer to the physical
  18572.  buffer, the VDMM selects the second graphics page as the active display.)
  18573.  
  18574.  In contrast to the video controller, the asynchronous communications
  18575.  controller, serial port, is fully virtualized at all times. The VDMM
  18576.  always handles all communications interrupts and input or output
  18577.  operations, and maintains an internal queue of serial port data and status
  18578.  information. Even the initialization of the serial port (baud rate, word
  18579.  length, parity, and number of stop bits) is virtualized, and this
  18580.  information is maintained separately for each virtual machine.
  18581.  
  18582.  An application signals its intention to use the communications controller
  18583.  by attempting to read or write one of the controller's data ports, change
  18584.  its interrupt mask bit on the 8259 Programmable Interrupt Controller, or
  18585.  capture its interrupt vector. Any of these operations will generate an
  18586.  exception that is processed by the VDMM. If the serial port is not in use
  18587.  by another program, the VDMM assigns "ownership" of the virtualized
  18588.  controller to the program that caused the exception; otherwise, a dialog
  18589.  box is displayed and the user is allowed to decide which program will
  18590.  retain access to the device.
  18591.  
  18592.  Once a program establishes ownership of the communications controller, it
  18593.  can perform input and output operations, and service communications
  18594.  interrupts in a normal manner from its point of view. In actuality, the
  18595.  interrupts and I/O operations are simulated by the VDMM, which transfers
  18596.  data between the virtual machine and its internal queue. In this fashion,
  18597.  the operation of the physical device, which is asynchronous and can
  18598.  generate an interrupt at any time, is decoupled from the virtual 86
  18599.  machine──which can only execute during its time slice.
  18600.  
  18601.  
  18602.  Portents for the Future
  18603.  
  18604.  In summary, Windows/386 exploits the 80386's unique capabilities to
  18605.  furnish preemptive multitasking, a windowing user interface, data exchange
  18606.  between any two applications, and efficient use of large amounts of RAM in
  18607.  conjunction with existing MS-DOS and Windows applications. In doing so, it
  18608.  overcomes the two most common objections to real-mode Windows: the
  18609.  latter's inability to coexist with popular ill-behaved programs such as
  18610.  Lotus(R) 1-2-3(R) or Microsoft Word, and its hunger for memory. In addition,
  18611.  although Microsoft bills Windows/386 as an interim product, it has some
  18612.  interesting implications──both short-term and long-term.
  18613.  
  18614.  In the near future, until protected-mode applications appear for OS/2 that
  18615.  fully exploit its virtual memory management, multitasking, and
  18616.  interprocess communications facilities, Windows/386 offers more to 80386-
  18617.  based PC owners than does OS/2. Windows/386 allows multiple MS-DOS
  18618.  applications to run concurrently, while OS/2 supports only one real-mode
  18619.  application at a time. In addition, the Windows/386 memory overhead in
  18620.  each virtual machine is typically 80 to100Kb less than the OS/2 overhead
  18621.  in the DOS 3.x Box. Finally, since Windows/386 uses the "real" MS-DOS as a
  18622.  substrate──unlike OS/2, which in effect emulates MS-DOS──Windows/386 is
  18623.  compatible with a broader range of existing applications than is OS/2.
  18624.  
  18625.  Taking the longer view, the availability of a true 32-bit version of OS/2
  18626.  and the Presentation Manager for the 80386 will probably threaten
  18627.  Windows/386's market niche. However, Windows/386 has an important role to
  18628.  play here too. It serves as a technology testbed for memory management,
  18629.  multitasking, device virtualization, and MS-DOS virtual-machine techniques
  18630.  that can eventually be absorbed into its successor──a system that will run
  18631.  multiple MS-DOS applications, 16-bit 80286 protected mode applications,
  18632.  and 32-bit 80386 protected-mode applications simultaneously.
  18633.  
  18634.  
  18635.  ───────────────────────────────────────────────────────────────────────────
  18636.  A Comparison of Current and Future Windows Versions
  18637.  ───────────────────────────────────────────────────────────────────────────
  18638.  
  18639. ╓┌──────────────────────┌──────────────┌──────────────┌───────────┌──────────
  18640.                         Microsoft      Microsoft                  MS OS.2
  18641.                         Windows        Windows        Windows     Presentation
  18642.                         1.03 & 1.04    2.0            386         Manager
  18643.  
  18644.  Presentation Spaces    Tiled          Overlapped     Overlapped  Overlapped
  18645.  
  18646.  More Consistent
  18647.  User and Keyboard
  18648.  Interfaces             ──             Yes            Yes         Yes
  18649.  
  18650.                         Microsoft      Microsoft                  MS OS.2
  18651.                         Windows        Windows        Windows     Presentation
  18652.                         1.03 & 1.04    2.0            386         Manager
  18653. 
  18654.  Processor              8088           8088           ──          ──
  18655.  Environments           8086           8086           ──          ──
  18656.                         286            286            ──          286
  18657.                         386            386            386         386
  18658.  
  18659.  Large Memory
  18660.  Support                ──             EMS/EEMS       EMS/EEMS    16Mb
  18661.  
  18662.  Multitasking           Nonpreemptive  Nonpreemptive  Fully       Fully
  18663.                                                       Preemptive  Preemptive
  18664.  
  18665.  Enhanced Display
  18666.  Performance            ──             Yes            Yes         Yes
  18667.  
  18668.  Runs Exisiting
  18669.  Windows (1.03)
  18670.  Applications           Yes            Yes            Yes         No
  18671.                         Microsoft      Microsoft                  MS OS.2
  18672.                         Windows        Windows        Windows     Presentation
  18673.                         1.03 & 1.04    2.0            386         Manager
  18674. Applications           Yes            Yes            Yes         No
  18675.  
  18676.  Graphics API           GDI            GDI            GDI         GPI
  18677.  
  18678.  Multiple Document
  18679.  Interface              ──             Yes            Yes         Yes
  18680.  
  18681.  Device Drivers         ──             Enhanced       Enhanced    New Model
  18682.  
  18683.  Old Application
  18684.  Support                ──             Improved       Improved    Improved
  18685.  
  18686.  Integral Part of OS    ──             ──             ──          Yes
  18687.  
  18688.  Protected Mode
  18689.  Applications
  18690.  Execution              ──             ──             Yes         Yes
  18691.  
  18692.                         Microsoft      Microsoft                  MS OS.2
  18693.                         Windows        Windows        Windows     Presentation
  18694.                         1.03 & 1.04    2.0            386         Manager
  18695. 
  18696.  New Development API    ──             ──             ──          Yes
  18697.  
  18698.  New User "Shell" and
  18699.  Keyboard Interface     ──             ──             ──          Yes
  18700.  
  18701.  Virtual 86 Mode        ──             ──             Yes         ──
  18702.  
  18703.  
  18704.  Figure 6:  This diagram shows the relationship between VDMM, MS-DOS, Windows,
  18705.             the VMDOSAP module, and standard applications.
  18706.  
  18707.      ┌───────────────────────────┐          ╔═════════════╗ ╔═════════════╗
  18708.      │The Windows/386 Environment│          ║  MS-DOS 3   ║ ║  MS-DOS 3   ║
  18709.      └───────────────────────────┘          ║ Application ║ ║ Application ║
  18710.                                             ╚═════════════╝ ╚═════════════╝
  18711.      ╔═════════════╗ ╔═════════════╗                              
  18712.      ║     WIN     ║ ║     WIN     ║        ╔═════════════╗ ╔═════════════╗
  18713.      ║ Application ║ ║ Application ║        ║ VMDSAP DOS  ║ ║ VMDOSAP DOS ║
  18714.      ║             ║ ║             ║        ║  Interface  ║ ║  Interface  ║
  18715.      ╚═════╤═══════╝ ╚═══════╤═════╝        ╚══╤════════╤═╝ ╚╤╤═══════════╝
  18716.            │                 │                 │   ┌────│────┘│
  18717.                                                          
  18718.   ┌──────────────────────────────────────────────────┬──────────┐
  18719.   │                                                  │          │
  18720.   │                   Windows                        │          │
  18721.   │                                                  │          │
  18722.   ├──────────────────────────────────────────────────┤          │
  18723.   │                                                  │          │
  18724.   │                   DOS 3.x                        │          │
  18725.   │                                                  │          │
  18726.   ├──────────────────────────────────────────────────┘          │
  18727.   │                                                             │
  18728.   │               Virtual DOS Machine Monitor (VDMM)            │
  18729.   │                                                             │
  18730.   └─────────────────────────────────────────────────────────────┘
  18731.  
  18732.  
  18733.  Figure 7:  Memory occupied by the DOS and other fixed items, such as the ROM
  18734.             BIOS, is shared between all virtual machines. Thus the system
  18735.             consumes physical memory sufficient for only one copy of each of
  18736.             these items and shares this memory between all the virtual
  18737.             machines using the memory management capabilities of the 386.
  18738.  
  18739.                            Components Within the
  18740.                            Address Space of a VDM
  18741.  
  18742.                           ╔═══════════════════════╗ FFFFFH
  18743.                           ║          ROM          ║
  18744.                           ║                       ║
  18745.                           ╠═══════════════════════╣
  18746.                           ║       EGA, etc.       ║
  18747.                           ╠═══════════════════════╣ 9FFFFH
  18748.                           ║                       ║
  18749.                           ║      Application      ║
  18750.                           ║         Area          ║
  18751.                           ║                       ║
  18752.                           ╠═══════════════════════╣
  18753.                           ║                       ║
  18754.                           ║          DOS          ║
  18755.                           ║                       ║
  18756.                           ╠═══════════════════════╣
  18757.                           ║       Int Vect        ║
  18758.                           ║       BIOS Data       ║ 00000H
  18759.                           ╚═══════════════════════╝
  18760.  
  18761.  
  18762.  Figure 8:  This diagram shows how a display adapter is virtualized.
  18763.  
  18764.                            ┌────────────────────┐
  18765.                            │Virtual Video Buffer│
  18766.  ╔═════════════════╗       └────────────────────┘
  18767.  ║                 ║
  18768.  ║    ROM BIOS     ║                                      ╔════════════════╗
  18769.  ║                 ║    ╔═══════════════╗                 ║      VDMM      ║
  18770.  ╚═════════════════╩═══║               ╟──────────────┐  ║    & Virtual   ║
  18771.                         ║               ║  Gets Changes╞═║ Display Driver ║
  18772.  ┌─────────────────────┐║ Virtual Video ╟──────────────┘  ╚═══════╦════════╝
  18773.  │Writes into Video RAM│║      RAM      ║                         ║
  18774.  └─────────────────────┘║               ║                         ║
  18775.  ╔═════════════════╦═══║               ║                 ┌───────╨────────┐
  18776.  ║                 ║    ╚═══════════════╝                 │Sends Changes to│
  18777.  ║                 ║                                      │ Windows/VMDOSAP│
  18778.  ║       DOS       ║                                      └───────╥────────┘
  18779.  ║   Application   ║                                              ║
  18780.  ║                 ║  ╔══════════════════╗                        ║
  18781.  ║                 ║  ║ ░░░░░░░░░░░░░  ≡≡║                ╔═══════════════╗
  18782.  ║                 ║  ║ ░░░░░░┌──────────╨────────────────╢                ║
  18783.  ╚═════════════════╝  ║ ░░ ══╡Renders Changes into Window║    VMDOSAP     ║
  18784.                       ║ ░░░░░░└──────────╥────────────────╢                ║
  18785.                       ║ ░░░░░░░░░░░░░    ║                ╚════════════════╝
  18786.                       ╚══════════════════╝
  18787.  
  18788.  ████████████████████████████████████████████████████████████████████████████
  18789.  
  18790.  Programming in C the Fast and Easy Way with Microsoft QuickC
  18791.  
  18792.  Augie Hansen
  18793.  
  18794.  The widespread use and popularity of the C language, which began in the
  18795.  early 1970s with researchers at Bell Laboratories, is likely to endure. C
  18796.  has matured in the marketplace and is becoming standardized, just as
  18797.  traditional languages like FORTRAN and COBOL did.
  18798.  
  18799.  The original purpose of C was to provide a malleable high-level assembler
  18800.  for reprogramming the UNIX(R) operating system for portability. Programmers
  18801.  have found the language and its support tools to be an inviting and highly
  18802.  compliant medium for system-level work and applications programming.
  18803.  
  18804.  The typical environment for C programmers, whether working on large
  18805.  multiuser systems or alone on PCs, has been to use a set of separate
  18806.  programs for each step in the process of translating source code into
  18807.  executable programs. The sequence is edit, compile (and sometimes
  18808.  assemble), link-edit object files and library modules, and then test.
  18809.  Depending on the results, the sequence might need to be repeated a number
  18810.  of times.
  18811.  
  18812.  Development can be tedious, even with a reasonable set of support tools,
  18813.  and is usually time-consuming. Making C programming faster and easier than
  18814.  is possible with traditional methods has become an important objective for
  18815.  vendors of C programming products.
  18816.  
  18817.  In the past few years, we have seen the introduction of C interpreters as
  18818.  front-end training, testing, and development systems. Interpreters
  18819.  typically give quick feedback to the user by avoiding the edit-compile-
  18820.  link-test cycle. However, such systems usually lack the flexibility for
  18821.  handling large programming projects.
  18822.  
  18823.  When an interpreter is used in program development, the source code
  18824.  produced is subsequently used as input to more traditional C compiler
  18825.  systems, which offer speed and size optimizations, the ability to handle
  18826.  multiple modules, provisions for custom libraries, and other convenient
  18827.  features.
  18828.  
  18829.  C compiler environments, the next stage in the evolution of C programming
  18830.  systems, are now the rage, and for a good reason: they provide the desired
  18831.  quickness on both ends of the development process. Creating a program is
  18832.  easier and faster than ever, and the final program runs at warp one.
  18833.  
  18834.  
  18835.  Introducing QuickC
  18836.  
  18837.  QuickC is an integrated C programming environment that provides all of the
  18838.  features you would expect in a mature program-development system while
  18839.  offering you the benefit of quick feedback and appropriate hand-holding
  18840.  when needed. The QuickC compiler can be obtained as a separate product or
  18841.  packaged with the Microsoft C Compiler Version 5.00.
  18842.  
  18843.  QC.EXE is the QuickC integrated environment program. It is great for
  18844.  learning C, useful in prototyping, and completely compatible with
  18845.  Microsoft C 5.00. The integrated environment consists of a tightly coupled
  18846.  full-screen editor, compiler, linker, and debugger. QuickC also has a
  18847.  built-in "make" feature that automates most of the process of compiling
  18848.  and linking programs.
  18849.  
  18850.  QCL.EXE, which is the QuickC equivalent of the C 5.00 CL control program,
  18851.  controls standalone operation. You can use the QCL program together with
  18852.  MAKE and the special version of the linker, LINK, to compile and link
  18853.  programs in the traditional manner.
  18854.  
  18855.  The use of separate programs is slower than working with the integrated
  18856.  environment because the number of disk accesses is significantly greater,
  18857.  but the approach lends itself well to automation via batch files and make
  18858.  files. Using a separate MAKE program gives you more latitude in preparing
  18859.  a program. It also provides greater access to other programs and features
  18860.  of the operating system than the built-in program-building feature of the
  18861.  integrated environment.
  18862.  
  18863.  QuickC is compatible with Microsoft C 5.00 in several ways. The compilers
  18864.  accept the same source code, use the same object file formats, and link
  18865.  with compatible libraries.
  18866.  
  18867.  Four memory models are supported: small, compact, medium, and large.
  18868.  Unless in-structed otherwise, the medium model is the default model used
  18869.  by QuickC. QuickC has no huge keyword to objects that exceed 64Kb in size,
  18870.  nor does it support the huge memory model.
  18871.  
  18872.  The near and far addressing keywords, however, are sup-ported. They are
  18873.  not part of the formal definition of C, but they are useful when you are
  18874.  pro-gramming the segmented archi-tecture of the Intel(R) 8086/80286
  18875.  microprocessor family.
  18876.  
  18877.  The few differences between QuickC and C 5.00 will affect those
  18878.  programmers who have exacting requirements for optimization of execution
  18879.  speed, for program size, or who need greater addressing flexibility than
  18880.  QuickC offers. Using QuickC for fast prototyping and then moving to the
  18881.  Microsoft Optimizing C Compiler to fine-tune program performance creates a
  18882.  comfortable working environment. The optimizing compiler has features for
  18883.  improving code size and execution speed.
  18884.  
  18885.  
  18886.  Integrated Environment
  18887.  
  18888.  The QuickC integrated environment, contained in QC.EXE, is shown
  18889.  schematically in Figure 1. The integrated environment is a collection of
  18890.  essential components that work together to increase the speed of creating
  18891.  and testing C programs.
  18892.  
  18893.  The built-in full-screen editor accepts input from both a keyboard and a
  18894.  mouse. It is a complete WordStar(R) compatible programmer's editor that
  18895.  accepts WordStar commands (a Ctrl-key combination) and IBM-compatible
  18896.  keyboard commands such as those found in Microsoft Word and many other
  18897.  visual text editors. Also, it is integrated with the compiler so that you
  18898.  can find errors and fix them quickly.
  18899.  
  18900.  Compiling at 7000 lines per minute, the QuickC in-memory compiler can
  18901.  capture information about errors (up to 26 of them) in a single
  18902.  compilation and feed the information back to the editor. For each error
  18903.  found, the editor places the cursor on the location in the source file
  18904.  where the error occurred.
  18905.  
  18906.  The compiler can produce a memory-based program or a disk-based
  18907.  executable; the choice is yours. Usually you will run entirely in memory
  18908.  to get the greatest speed advantage during development and then save the
  18909.  program to disk for later use as a standalone program.
  18910.  
  18911.  To help you find program bugs, QuickC incorporates a source-level debugger
  18912.  that is a comprehensive subset of the well-known CodeView(R) debugger. The
  18913.  debugger lets you single-step program execution, establish watch
  18914.  expressions and display their values, set multiple breakpoints, and search
  18915.  for functions by name. In addition, you can switch between displays of the
  18916.  program and its output by using a simple keypress command.
  18917.  
  18918.  Multimodule programs necessitate a significant amount of bookkeeping work.
  18919.  Such work is best left to computer programs, and QuickC obliges. A built-
  18920.  in program maintainer patterned after MAKE automatically prepares a make
  18921.  file from a program file list that you provide. As you modify your
  18922.  program, QuickC will keep each object module up to date with respect to
  18923.  its source file and controls linking if the executable file is to be
  18924.  preserved as an EXE file on disk.
  18925.  
  18926.  
  18927.  Core Library
  18928.  
  18929.  QuickC has a set of run-time library routines built right into the
  18930.  compiler. If a program uses only core routines, all external references
  18931.  can be resolved in memory, resulting in very high-speed program
  18932.  preparation. Figure 2 lists the core routines.
  18933.  
  18934.  If the program needs any routines that are not in the core set, they must
  18935.  be read from a library file on disk. QuickC uses the medium-memory model
  18936.  by default, so it normally looks for the library file MLIBCE.LIB or
  18937.  MLIBC7.LIB in the default library directory. The library to be chosen
  18938.  depends on whether floating-point emulation or 8087/80287 routines are
  18939.  used. Alternatively, QuickC looks for the component medium-model files if
  18940.  you choose not to create a combined library during installation. The
  18941.  component libraries are MLIBC.LIB, MLIBFP.LIB, EM.LIB or 87.LIB, and
  18942.  LIBH.LIB.
  18943.  
  18944.  Because searching a large file like MLIBCE.LIB takes a lot of time, you
  18945.  can speed up the process by creating a small library that contains only
  18946.  the routines your program needs. QuickC searches for a file named
  18947.  QUICK.LIB, or one you name on the QC invocation command line, if it cannot
  18948.  resolve external references by using the core routines.
  18949.  
  18950.  The QuickC system interacts with you via a menu bar and pull-down menus
  18951.  that you can control from the keyboard and a mouse, if one is installed.
  18952.  All frequently used selections can also be made by a shortcut method, such
  18953.  as pressing a function key, a Ctrl-key or Alt-key combination, or some
  18954.  other special key.
  18955.  
  18956.  Some of the available menu windows can be seen in Figures 3, 4, 5, and 6.
  18957.  
  18958.  Figure 3, which shows the editing window, adorned with scroll bars, and
  18959.  the File menu, indicates how the QuickC drop-down menus appear. Once a
  18960.  menu is selected, either from the keyboard by typing the first letter of
  18961.  its name while holding down the Alt key or by clicking a mouse button on
  18962.  the menu name, you can use either the arrow keys plus Enter or a mouse
  18963.  click to select an option.
  18964.  
  18965.  The figure shows the highlight on the "Set Program List" option. When you
  18966.  select it, this option leads to a dialog box (signified by "...") on which
  18967.  you select the names of files that comprise the program you want to
  18968.  compile. The dialog box lists all source files in the current or specified
  18969.  directory. You must indicate which of them comprise the sources of your
  18970.  program by using a "point and shoot" selection process. The selected files
  18971.  make up the program list that QuickC uses to build the program.
  18972.  
  18973.  
  18974.  In-Memory Compilation
  18975.  
  18976.  The advantage of in-memory compilation is that all source files, temporary
  18977.  files, and most library routines are held in high-speed, primary memory.
  18978.  This dramatically cuts the time it takes to produce an executable program
  18979.  compared with the traditional approach, which is usually very disk-
  18980.  intensive.
  18981.  
  18982.  The integrated environment facilitates debugging with close editor-
  18983.  debugger interaction. By placing the cursor on an error in your source
  18984.  code, you are spared the time and effort it takes to reload the editor,
  18985.  read in the source file, and move the cursor to the location of the
  18986.  error.
  18987.  
  18988.  In the integrated environment, errors are described in sufficient detail
  18989.  so that reference to manuals is usually unnecessary. For example, Figure 4
  18990.  shows an error message in the pop-up window at the bottom of the screen.
  18991.  The message is detailed enough to be helpful, and the cursor points to the
  18992.  line containing the error.
  18993.  
  18994.  Raw compilation speed on a given machine is about three-to-one in favor of
  18995.  QuickC compared with a standard C compiler. The programmer perceives the
  18996.  improvement in speed to be even greater because there is no need for
  18997.  continually loading and exiting a series of programs and suffering the
  18998.  concomitant disk-access times.
  18999.  
  19000.  
  19001.  Library Support
  19002.  
  19003.  The standard run-time library provided with QuickC includes all of the
  19004.  routines that accompany the Microsoft Optimizing C Compiler. The library,
  19005.  compatible with XENIX(R), provides extensive DOS support (bdos and intdos,
  19006.  for example), and includes routines that conform to the draft-proposed
  19007.  ANSI standard for C.
  19008.  
  19009.  A new graphics library is included in the QuickC package. It contains more
  19010.  than forty graphics routines that handle configuration, palette and
  19011.  attribute settings, outputting text and images, and a variety of other
  19012.  color and monochrome graphics tasks.
  19013.  
  19014.  A new set of DOS and BIOS interface routines add considerably to the
  19015.  support for PC hardware interactions. While not portable to other hardware
  19016.  environments, these routines allow you to get the most from a PC and its
  19017.  peripheral devices.
  19018.  
  19019.  
  19020.  Standalone Operation
  19021.  
  19022.  For programmers who prefer the traditional approach, QuickC provides
  19023.  separate programs for compiling and linking. QCL.EXE reads a list of
  19024.  optional control switches and filenames from the DOS command line and
  19025.  produces standard Microsoft format object files that you can put into
  19026.  libraries (use LIB.EXE) or link with other objects and libraries in order
  19027.  to produce executable programs.
  19028.  
  19029.  MAKE can control the entire process; it reads a list of instructions from
  19030.  a make file, and uses the time stamps on files to determine which files
  19031.  need to be recompiled and linked. MAKE is more flexible than the program
  19032.  maintenance feature built into the QuickC integrated environment.
  19033.  
  19034.  The library maintainer, LIB.EXE, permits you to add modules to a library,
  19035.  delete modules, copy modules to disk files, and combine libraries. It
  19036.  allows you to create your own custom libraries and modify existing
  19037.  libraries to meet special needs. For instance, to create a custom
  19038.  QUICK.LIB file of routines for a given program, you can use LIB to extract
  19039.  copies of the needed routines and then use LIB again to prepare the
  19040.  QUICK.LIB file.
  19041.  
  19042.  
  19043.  Sample Session
  19044.  
  19045.  Figures 7 and 8 show the two source files from which the TOUCH program, a
  19046.  useful adjunct to the MAKE program provided with the compiler, is built.
  19047.  TOUCH updates the date and time stamps associated with a file to the
  19048.  current date and time. This gives you a convenient way to force all or
  19049.  selected files for a program to be remade, even if the files were not
  19050.  modified since the last successful compile.
  19051.  
  19052.  TOUCH accepts command-line options -c and -v. TOUCH normally creates a
  19053.  named file if the file does not exist. The -c option tells TOUCH not to
  19054.  create any files. The -v option tells TOUCH to be verbose, giving the user
  19055.  a play-by-play description of what it is doing. This option caters to
  19056.  those programmers who like chatty programs.
  19057.  
  19058.  Figure 9 lists the make file and linker response files produced by the
  19059.  automatic program maintainer feature of QuickC. The make file is
  19060.  compatible with and can be used by the standalone MAKE command. The linker
  19061.  response file provides information needed by the LINK program when it
  19062.  combines object modules and libraries to produce an executable program.
  19063.  
  19064.  The source code is presented in its final form. However, to demonstrate
  19065.  the debugging capabilities of QuickC, we will introduce several deliberate
  19066.  errors into TOUCH.
  19067.  
  19068.  Program errors fall into two general categories: syntactic and semantic.
  19069.  Syntax errors result from how a program is put together. The rules for
  19070.  writing programs in C, as is the case for nearly all computer languages,
  19071.  are precise and rigid. Compilers have to be unforgiving in insisting on
  19072.  correct syntax. A missing semicolon, which effectively concatenates two
  19073.  perfectly good statements into one illegal one, is one of the more common
  19074.  syntax errors.
  19075.  
  19076.  Semantic errors result from the contextual meaning of a program's
  19077.  statements. Does the program run to completion and produce correct
  19078.  results? If not, it probably contains semantic errors, which can be
  19079.  further categorized into execution errors or logic errors.
  19080.  
  19081.  Execution errors are requests written in a perfectly acceptable way that
  19082.  are impossible to fulfill, for example, dividing by zero. A program
  19083.  containing such an error aborts with a run-time error. Logic errors also
  19084.  get by the compiler because the syntax is correct, and the program
  19085.  containing them runs fine, but it produces incorrect results. Such a
  19086.  program is not likely to become a best-seller.
  19087.  
  19088.  QuickC contains the tools to detect and correct both syntax errors and
  19089.  semantic errors. Syntax errors are caught during compilation and reported
  19090.  one at a time in a pop-up window. Up to 26 compile-time errors are
  19091.  maintained in a list that can be scanned in either direction.
  19092.  
  19093.  The "Warning Level" setting in the compile dialog box controls the level
  19094.  of checking and reporting to be done by QuickC (see Figure 6). The higher
  19095.  the warning level (they range from 0 to 4), the more detailed the checking
  19096.  and reporting. The resulting list of warnings and error messages will
  19097.  guide you in wringing out all compile-time errors.
  19098.  
  19099.  Figure 4 shows an error caught by the compiler. It is the common error of
  19100.  using assignment where the programmer really intended a test for equality.
  19101.  The statement
  19102.  
  19103.    if (fclose(fp) = EOF)
  19104.      {  ...  }
  19105.  
  19106.  should use "==" instead of "=". The error is detected because a function
  19107.  is not an lvalue, so nothing can be assigned into it. If the left-hand
  19108.  side of the assignment were an 1value, the compiler would be happy and
  19109.  compile without comment. Your program would execute, but probably
  19110.  incorrectly.
  19111.  
  19112.  Another common error is a missing parameter in an fprintf statement. Try
  19113.  leaving the stderr parameter out of an error message line. The compiler
  19114.  will detect the problem because it uses function prototypes to determine
  19115.  how many parameters a function takes and what data types the parameters
  19116.  have. In this case, QuickC will report incorrect parameter types. Because
  19117.  fprintf takes a variable number of arguments, the compiler cannot
  19118.  determine that the number of parameters is incorrect.
  19119.  
  19120.  You could still have semantic errors lurking about in your program, which
  19121.  the compiler cannot detect. Traditionally, C programmers have resorted to
  19122.  inserting printf statements in their programs to check values at critical
  19123.  locations during test runs. This is tedious, time-consuming, and
  19124.  unnecessary because of the built-in debugger provided in the QuickC
  19125.  integrated environment.
  19126.  
  19127.  To use the debugger, which contains an extensive subset of CodeView
  19128.  features, you must first compile your program with the Debug option set so
  19129.  that symbolic information needed by the debugger is preserved in the
  19130.  executable program (see Figure 6). Select the Run item (Alt-R) from the
  19131.  menu bar and then the Compile(C) dialog box. Use the Tab key to select
  19132.  the Debug option and the Spacebar to toggle it on. Press Enter to compile
  19133.  the program.
  19134.  
  19135.  After successful compilation, select the Debug item (Alt-D) from the menu
  19136.  bar and set watch expressions and breakpoints. A watch expression can be
  19137.  as simple as a variable name or as complex an expression as you need to
  19138.  check program operation. You can set watch expressions one at a time or
  19139.  several at once by separating them from each other with a semicolon. Watch
  19140.  expressions are displayed in a separate watch window at the top of the
  19141.  screen during program execution (see Figure 5).
  19142.  
  19143.  A breakpoint is a line in the program at which execution should stop to
  19144.  let you observe the values of watch expressions or just to observe the
  19145.  displayed output using the screen-switching feature of QuickC. It is
  19146.  simple, effective, and a lot easier and faster than placing printfs all
  19147.  over your program.
  19148.  
  19149.  You can set multiple watch expressions and breakpoints. If the ones you
  19150.  set initially don't do the job, it is a simple matter to clear them and
  19151.  set new expressions and breakpoints. There is no need to recompile as
  19152.  there would be with embedded printf statements.
  19153.  
  19154.  Introducing deliberate errors into TOUCH.C will help you to learn how to
  19155.  use the debugger. Try changing the sense of some logical tests (use "!="
  19156.  instead of "==") and observe the effect on the output.
  19157.  
  19158.  Deleting the two lines in TOUCH.C just before the first while loop
  19159.  produces an interesting result. The program will not skip the program name
  19160.  argument (argv[0]), and all following arguments will be treated as
  19161.  filenames even if they are preceded by a dash, which is the specified
  19162.  option flag. To fix the problem, you could set watch expressions on the
  19163.  command-line argument parameters, argc and *argv, by typing "argc; *argv"
  19164.  in the watch dialog box and put breakpoints on statements where these
  19165.  values should have just changed, such as the first statement inside each
  19166.  loop.
  19167.  
  19168.  You can provide a command line by selecting the Set Runtime Options (O)
  19169.  from the Run (Alt-r) menu before you run the program. Type only the
  19170.  command-line arguments; do not type the program name──it is already known
  19171.  to QuickC. Use something like
  19172.  
  19173.    -c one two three
  19174.  
  19175.  as a command line. After running the program, you can use the DOS Shell
  19176.  (D) selection of the File menu (Alt-F) to get to a temporary DOS command
  19177.  line. Run a DIR command and check the list of files. Don't be surprised to
  19178.  find that TOUCH created files named "-c", "one", "two", and "three", even
  19179.  though we instructed it not to create any files.
  19180.  
  19181.  The thorough syntax-checking and integrated debugging features provided by
  19182.  QuickC are as important to a programmer as the swiftness of the compiler.
  19183.  Become familiar with these easy-to-use tools and you will never want to be
  19184.  without them.
  19185.  
  19186.  
  19187.  Summary
  19188.  
  19189.  For easy learning and use of a C compiler, raw compilation speed, and
  19190.  overall flexibility, it's hard to beat QuickC. Compiling in the integrated
  19191.  environment makes trying out an idea exhilarating. Modifying the program
  19192.  until it does exactly what you want is as simple as playing "what if" with
  19193.  a spreadsheet. It's about time programmers received the same kind of help
  19194.  that financial analysts have had for years.
  19195.  
  19196.  When we took the big step from batch processing to interactive processing
  19197.  on mainframes back in the 1960s, programmer productivity received a great
  19198.  boost. The move to swift and capable integrated programming environments
  19199.  on personal computers provides the opportunity to make similar, perhaps
  19200.  even greater, productivity gains in the 1980s.
  19201.  
  19202.  
  19203.  Figure 1:  You can use separate libraries insteaad of the combined library.
  19204.             The default memory model is medium. You can also select small,
  19205.             compact, or large.
  19206.  
  19207.               ┌──────────────────────────────────────────────────┐
  19208.               │ The QuickC Integrated Environment and Libraries. │
  19209.               └──────────────────────────────────────────────────┘
  19210.        ╔═══════════════════════════╗        ╔════════════════════════════╗
  19211.        ║         QC.EXE            ║█       ║        MLIBCE.LIB*         ║█
  19212.        ╠═══════════════════════════╣█       ╠════════════════════════════╣█
  19213.        ║    Full-screen Editor     ║█       ║Combined Library File Merges║█
  19214.        ╟───────────────────────────╢█       ║ the following libraries:   ║█
  19215.        ║        Compiler           ║█       ║                            ║█
  19216.        ╟───────────────────────────╢█       ║     MLIBC.LIB             ║█
  19217.        ║   Source-level Debugger   ║█       ║     MLIBFP.LIB            ║█
  19218.        ╟───────────────────────────╢█       ║     LIBH.LIB              ║█
  19219.        ║  Program List Maintainer  ║█       ║     EM.LIB (or 87.LIB)    ║█
  19220.        ╟───────────────────────────╢█       ╚════════════════════════════╝█
  19221.        ║                           ║█         █████████████████████████████
  19222.        ║                           ║█       ╔════════════════════════════╗
  19223.        ║ "Core Subroutine Library" ║█       ║          QUICK.LIB         ║█
  19224.        ║                           ║█       ╠════════════════════════════╣█
  19225.        ║                           ║█       ║        Quick Library       ║█
  19226.        ╚═══════════════════════════╝█       ╚════════════════════════════╝█
  19227.          ████████████████████████████         █████████████████████████████
  19228.  
  19229.  
  19230.  Figure 2:  The core library routines are defined in QC.EXE and speed up
  19231.             linking time by avoiding unnecessary disk accesses.
  19232.  
  19233.                            Core Library Routines
  19234.  
  19235.      abort        _fmalloc       isatty         puts         strlen
  19236.      access       _fmsize        itoa           read         strlwr
  19237.      atof         fopen          kbhit          realloc      strncat
  19238.      atoi         fprintf        longjump       remove       strncmp
  19239.      atol         fputc          lseek          rewind       strncpy
  19240.      bdos         fputs          ltoa           rmdir        strnset
  19241.      brk          fread          malloc         rmtmp        strpbrk
  19242.      calloc       free           _memavl        sbrk         strrchr
  19243.      chdir        _freect        memccpy        scanf        strrev
  19244.      chmod        fscanf         memchr         segread      strset
  19245.      clearerr     fseek          memcmp         setbuf       strspn
  19246.      close        fstat          memcpy         setjmp       strtok
  19247.      creat        ftell          _memmax        setmode      strupr
  19248.      dosexterr    fwrite         _memset        setvbuf      system
  19249.      eof          agetch         mkdir          signal       tell
  19250.      _exit        getche         movedata       spawnl       time
  19251.      exit         getcwd         msize          spawnv       tmpfile
  19252.      _expand      _getdate       _nfree         sprintf      tmpnam
  19253.      fclose       _gettime       _nheapchk      sscanf       tolower
  19254.      fflush       getenv         _nheapset      strcat       toupper
  19255.      _ffree       gets           _nheapwalk     strchr       tzset
  19256.      _fheapchk    halloc         _nmalloc       strcmp       ultoa
  19257.      _fheapset    hfree          _nmsize        strcmpi      unlink
  19258.      _fheapwalk   int86          onexit         strcpy       vfprintf
  19259.      fgetc        int86x         open           strcspn      vprintf
  19260.      fgets        intdos         printf         strdup       vsprintf
  19261.      filelength   intdosx        putch          stricmp      write
  19262.      flushall
  19263.  
  19264.  
  19265.  Figure 7:  TOUCH.C
  19266.  
  19267.  /*
  19268.   * PROGRAM: TOUCH
  19269.   *
  19270.   * DESCRIPTION:  Update the last modification time of a file or a
  19271.   *     group of files to the current time.
  19272.   *
  19273.   * ENTRY:  A list of files to "touch".  The filenames can be preceded
  19274.   *     by one or both of the following options:
  19275.   *
  19276.   *          -c     don't create any files
  19277.   *          -v     operate in verbose mode (report activity)
  19278.   *
  19279.   * SYNTAX:
  19280.   *      touch [-cv] filename ...
  19281.   */
  19282.  
  19283.  #include <stdio.h>
  19284.  #include <stdlib.h>
  19285.  #include <string.h>
  19286.  #include <ctype.h>
  19287.  #include <io.h>
  19288.  #include <errno.h>
  19289.  #include <sys\types.h>
  19290.  #include <sys\stat.h>
  19291.  #include <sys\utime.h>
  19292.  
  19293.  #define ERR     0x7FFF
  19294.  #define MAXNAME 8
  19295.  
  19296.  typedef enum { FALSE, TRUE } BOOLEAN;
  19297.  extern void usage(char *);
  19298.  
  19299.  int
  19300.  main(argc, argv)
  19301.  int argc;
  19302.  char *argv[];
  19303.  {
  19304.       int ch;                  /* character buffer */
  19305.       char *cp;                /* character pointer */
  19306.       int badcount;            /* number of files that cannot */
  19307.                                /* be successfully updated */
  19308.       struct stat statbuf;     /* buffer for stat results */
  19309.       BOOLEAN errflag;         /* error flag */
  19310.       BOOLEAN cflag;           /* creation control flag */
  19311.       BOOLEAN vflag;           /* verbose control flag */
  19312.       FILE *fp;                /* file pointer */
  19313.  
  19314.       static char pgm[MAXNAME + 1] = { "touch" };
  19315.  
  19316.       /*
  19317.        * Initialize flags and variables
  19318.        */
  19319.       errflag = cflag = vflag = FALSE;
  19320.       badcount = 0;
  19321.  
  19322.       /*
  19323.        * Move past the command name argument and look for
  19324.        * optional arguments (signaled by a leading dash)
  19325.        */
  19326.       ++argv;
  19327.       --argc;
  19328.       while (argc > 1) {
  19329.            cp = *argv;
  19330.            if (*cp != '-')
  19331.                 /* not an option flag */
  19332.                 break;
  19333.  
  19334.            /*
  19335.             * Having seen an option flag ('-'), look at
  19336.             * any option letters that follow it in the
  19337.             * current argument string
  19338.             */
  19339.            for (++cp; *cp != '\0'; ++cp)
  19340.                 switch (*cp) {
  19341.                 case 'c':
  19342.                      /* don't create files */
  19343.                      cflag = TRUE;
  19344.                      puts("CREATION flag set");
  19345.                      break;
  19346.                 case 'v':
  19347.                      /* verbose -- report activity */
  19348.                      vflag = TRUE;
  19349.                      break;
  19350.                 default:
  19351.                      fprintf(stderr, "%s: unknown option %c\n",pgm, *cp);
  19352.                      usage(pgm);
  19353.                      exit(ERR);
  19354.                 }
  19355.            ++argv;
  19356.            --argc;
  19357.       }
  19358.  
  19359.       /*
  19360.        * Update modification times of files
  19361.        */
  19362.       for (; argc-- > 0; ++argv) {
  19363.            if (stat(*argv, &statbuf) == -1) {
  19364.                 /* file doesn't exist */
  19365.                 if (cflag == TRUE) {
  19366.                      /* don't create it */
  19367.                      ++badcount;
  19368.                      continue;
  19369.                 }
  19370.                 else if ((fp = fopen(*argv, "w")) == NULL) {
  19371.                      fprintf(stderr, "%s: Cannot create %s\n",
  19372.                           pgm, *argv);
  19373.                      ++badcount;
  19374.                      continue;
  19375.                 }
  19376.                 else {
  19377.                      if (fclose(fp) == EOF) {
  19378.                           perror("Error closing file");
  19379.                           exit(ERR);
  19380.                      }
  19381.                      if (stat(*argv, &statbuf) == -1) {
  19382.                           fprintf(stderr, "%s: Can't stat %s\n", pgm, *argv);
  19383.                           ++badcount;
  19384.                           continue;
  19385.                      }
  19386.                 }
  19387.            }
  19388.            if (utime(*argv, NULL) == -1) {
  19389.                 ++badcount;
  19390.                 perror("Error updating date/time stamp");
  19391.                 continue;
  19392.            }
  19393.            if (vflag == TRUE)
  19394.                 fprintf(stderr, "Touched file %s\n", *argv);
  19395.       }
  19396.  
  19397.       return (badcount);
  19398.  }
  19399.  
  19400.  
  19401.  Figure 8:  USAGE.C
  19402.  
  19403.  /*
  19404.   * usage()
  19405.   *
  19406.   * DESCRIPTION: Display an informative usage message using the
  19407.   *  actual program name, which may have been changed by the user.
  19408.   *
  19409.   * ENTRY: A pointer to the program name.
  19410.   */
  19411.  
  19412.  #include <stdio.h>
  19413.  
  19414.  void
  19415.  usage(pname)
  19416.  char *pname;
  19417.  {
  19418.      fprintf(stderr, "Usage: %s [-c] [-v] file ...\n", pname);
  19419.      fprintf(stderr, "\t-c  Do not create any files\n");
  19420.      fprintf(stderr, "\t-v  Verbose mode -- report activities\n");
  19421.  }
  19422.  
  19423.  
  19424.  Figure 9:  Make File
  19425.  
  19426.  #
  19427.  # Program: Touch
  19428.  #
  19429.  
  19430.  .c.obj:
  19431.       qcl -c  -Iusage.c -W0 -Ze -Zid -AM $*.c
  19432.  
  19433.  usage.obj : usage.c
  19434.  
  19435.  touch.obj : touch.c
  19436.  
  19437.  Touch.exe : usage.obj touch.obj
  19438.       del Touch.lnk
  19439.       echo usage.obj+ >>Touch.lnk
  19440.       echo touch.obj  >>Touch.lnk
  19441.       echo Touch.exe >>Touch.lnk
  19442.       echo Touch.map >>Touch.lnk
  19443.       link @Touch.lnk;
  19444.  
  19445.  
  19446.  Link Information - Touch.lnk
  19447.  ============================
  19448.  touch.obj+
  19449.  usage.obj
  19450.  Touch.exe
  19451.  Touch.map
  19452.  
  19453.  ████████████████████████████████████████████████████████████████████████████
  19454.  
  19455.  Character-Oriented Display Services Using OS/2's VIO Subsystem
  19456.  
  19457.  ───────────────────────────────────────────────────────────────────────────
  19458.  Also see the related article:
  19459.  A Survey of OS/2 Display Services
  19460.  ───────────────────────────────────────────────────────────────────────────
  19461.  
  19462.  Ray Duncan
  19463.  
  19464.  OS/2 provides the programmer with a fast and powerful set of video I/O
  19465.  functions. This particular group of API calls is important because the
  19466.  average user bases his assessment of the quality of application software
  19467.  largely on the ease and speed of his interaction with the program. He has
  19468.  neither the opportunity nor the experience necessary to peek into the
  19469.  source code and appreciate the elegance of its structure or the
  19470.  appropriateness of its algorithms, but the rapidity with which the
  19471.  application builds and updates its screen displays speaks for itself.
  19472.  Naturally, control of the video display has become one of the major areas
  19473.  of concern for the PC programmer.
  19474.  
  19475.  Unfortunately, the built-in video drivers available in MS-DOS(R) have done
  19476.  little to aid the programmer in his quest for competitive performance. The
  19477.  speed with which MS-DOS transfers text to the screen might be adequate for
  19478.  EDLIN, but it certainly doesn't suffice for sophisticated word processors,
  19479.  while the degree of control MS-DOS provides over character colors and
  19480.  attributes, display mode, and cursor shape and position is rudimentary at
  19481.  best. Consequently, the authors of most popular MS-DOS applications have
  19482.  felt justified in abandoning portability, bypassing the operating system
  19483.  altogether, and directly manipulating the video controller to achieve the
  19484.  desired throughput.
  19485.  
  19486.  In the real mode, single-tasking world of MS-DOS, such hardware-dependent
  19487.  display techniques rarely cause any significant problems, but the same
  19488.  cannot be said for the multitasking, protected-mode OS/2 environment. A
  19489.  program that tries to commandeer the video controller will either be
  19490.  terminated with a protection fault, or, if it manages to circumvent the
  19491.  memory-protection mechanisms, wreak havoc among the other processes that
  19492.  are active in the system. The designers of MS(R) OS/2 correctly perceived
  19493.  that they could head off such anarchy only by removing the motivation for
  19494.  programmers to go around the operating system and placed a high priority
  19495.  on the creation of a battery of video services rich enough and efficient
  19496.  enough to satisfy the needs of any reasonable application program.
  19497.  
  19498.  And that is just what they did. MS OS/2 provides application programs with
  19499.  a huge assortment of display functions that cover the full range of
  19500.  hardware independence, from the sophisticated graphical and rich text
  19501.  capabilities of the Microsoft(R) OS/2 Presentation Manager to the ability of
  19502.  an application to temporarily lock onto the logical or physical display
  19503.  buffer and modify its contents. (See "A Survey of OS/2 Display Services.")
  19504.  This article will focus specifically on the VIO subsystem, which occupies
  19505.  the middle ground (both in complexity and power) of OS/2's video cap-
  19506.  abilities. The other techniques for driving the display under OS/2 will be
  19507.  discussed in future articles.
  19508.  
  19509.  
  19510.  The VIO Subsystem
  19511.  
  19512.  OS/2's VIO subsystem provides character-oriented display services suitable
  19513.  for program editors, simple pop-up utilities, compilers and interpreters,
  19514.  and the like. The VIO function calls can be regarded as a superset of
  19515.  those available from MS-DOS and the IBM(R) PC ROM BIOS in real mode, and
  19516.  their actions can be grouped into the following general categories:
  19517.  
  19518.    ■ transferring strings of text to the screen at any position
  19519.    ■ controlling such text attributes as blink, underline, and color
  19520.    ■ controlling the cursor shape and position
  19521.    ■ scrolling and clearing the screen
  19522.    ■ setting the display mode
  19523.    ■ support for partial or full-screen pop-up windows
  19524.  
  19525.  The VIO subsystem is implemented as a dynamic link library (VIOCALLS.DLL)
  19526.  that resides on the system disk. Programs are bound to the VIO routines
  19527.  when they are loaded for execution, rather than at link time. Thus, the
  19528.  VIO library can be replaced at any time without recompiling or even
  19529.  relinking the client applications.
  19530.  
  19531.  Although the VIO subsystem does not support graphics operations aside from
  19532.  providing a means to get in and out of graphics display modes, programs
  19533.  that confine themselves to the use of VIO calls and do not access the
  19534.  logical or physical screen buffer will run properly in a window under the
  19535.  Presentation Manager without any changes. How is this possible? Under the
  19536.  Presentation Manager, the normal VIO dynalink library is replaced by a new
  19537.  "Windows-aware" library that maps each character and its attributes into
  19538.  an appropriate pattern of pixels and clips the output appropriately for
  19539.  the application's current window size.
  19540.  
  19541.  Figure 1 shows a summary of the VIO function calls. We'll take a close
  19542.  look at some of the most commonly used VIO services, together with sample
  19543.  C and MASM source code. Note that the C code shown assumes use of the
  19544.  large model, and the parameter called a "VIO handle" in the function
  19545.  descriptions is always zero in the Software Development Kit version of MS
  19546.  OS/2.
  19547.  
  19548.  
  19549.  Displaying Text
  19550.  
  19551.  There are a number of different VIO functions that can be used to place
  19552.  text on the screen. The simplest is VIOWRTTTY, which is called with the
  19553.  address and length of a string and a VIO handle and returns a status code
  19554.  of zero if the write succeeded or an error code if it failed. The control
  19555.  characters line feed, carriage return, backspace, and bell code are
  19556.  properly handled, automatic line wrap and screen scrolling are provided
  19557.  (unless they are intentionally disabled), and the cursor position is
  19558.  updated to lie at the end of the newly displayed text. Figure 2 is an
  19559.  example of the use of VIOWRTTTY.
  19560.  
  19561.  The VIOWRTTTY call is functionally similar to a call to DOSWRITE with the
  19562.  stdout handle (the preassigned handle for the standard output device), but
  19563.  is much faster and immune to redirection. As with DOSWRITE, and unlike the
  19564.  other VIO calls, ANSI escape sequences can be included in the text stream
  19565.  to control cursor position and character attributes.
  19566.  
  19567.  Ordinarily, the only error code returned by VIOWRTTTY is an invalid VIO
  19568.  handle. Providing the function with an invalid string address or length
  19569.  does not result in an error code but may well cause a General Protection
  19570.  Fault (Trap D), terminating the process.
  19571.  
  19572.  Three additional VIO calls are available for displaying text:
  19573.  VIOWRTCHARSTR (write character string), VIOWRTCHARSTRATT (write character
  19574.  string with attribute), and VIOWRTCELLSTR (write string of
  19575.  character/attribute pairs).
  19576.  
  19577.  These services are faster than VIOWRTTTY and offer direct control over
  19578.  screen placement. They are not sensitive to the presence of such control
  19579.  characters as carriage return or line feed; any control characters
  19580.  embedded in the string are simply displayed as their graphics character
  19581.  equivalents. These functions do not affect the current cursor position and
  19582.  only support limited line wrapping: if a string is too long for the
  19583.  current line, it will wrap onto the next line. However, if the end of the
  19584.  screen is reached, any remaining characters are discarded, the screen is
  19585.  not scrolled, and an error code is not returned.
  19586.  
  19587.  VIOWRTCHARSTR is the simplest of the three functions listed above. It
  19588.  accepts the address and length of a string, the screen position at which
  19589.  to begin writing the string, and a VIO handle. The new text assumes the
  19590.  attributes of the characters that were previously displayed at the same
  19591.  screen positions.
  19592.  
  19593.  VIOWRTCHARSTRATT is similar to the VIOWRTCHARSTR function, except for one
  19594.  additional parameter: the address of an attribute byte that is applied to
  19595.  every character in the string (see Figure 3). On monochrome adapters, the
  19596.  attribute byte specifies normal or reverse video, blink, underline, and
  19597.  intensity (see Figure 4). On color adapters in text modes, the attribute
  19598.  byte contains the background color in the upper four bits and the
  19599.  foreground color in the lower four bits (see Figure 5).
  19600.  
  19601.  VIOWRTCELLSTR displays a string that consists of alternating character and
  19602.  attribute bytes.VIOWRTCELLSTR is designed to be used in combination with
  19603.  VIOREADCELLSTR to restore an area of the display that was previously saved
  19604.  into a buffer. A programmer would not ordinarily select this function for
  19605.  normal text output, because generation of initialized strings with
  19606.  embedded attribute bytes is awkward in most languages, such strings are
  19607.  relatively bulky, and it is rare that an application needs to display a
  19608.  string where the attribute byte of each successive character is
  19609.  different.
  19610.  
  19611.  The VIO functions VIOWRTNCHAR (write string of identical characters),
  19612.  VIOWRTNATTR (write string of identical attribute bytes), and VIOWRTNCELL
  19613.  (write string of identical characters and attributes) offer some special
  19614.  capabilities that supplement the previously discussed text display
  19615.  functions and require similar parameters. VIOWRTNATTR replicates an
  19616.  attribute byte across a selected area without changing the text at that
  19617.  position and can rapidly alter the appearance of a display field.
  19618.  VIOWRTNCHAR and VIOWRTNCELL replicate a character or a character/attribute
  19619.  pair respectively and allow extremely efficient drawing of borders and
  19620.  similar screen objects.
  19621.  
  19622.  
  19623.  Scrolling and Clearing
  19624.  
  19625.  The four kernel services VIOSCROLLUP, VIOSCROLLDN, VIOSCROLLLF, and
  19626.  VIOSCROLLRT can clear a window of arbitrary size and position or scroll it
  19627.  up, down, left, or right any number of columns or rows. Any desired
  19628.  character and attribute byte can be specified to fill the newly blanked or
  19629.  scrolled lines. All four functions have the following parameters: the
  19630.  screen coordinates of the upper-left corner and lower-right corner of a
  19631.  window, the number of lines to be scrolled or blanked, the address of a
  19632.  character and attribute pair to be used to fill the blanked lines, and a
  19633.  VIO handle.
  19634.  
  19635.  Clearing the entire display in any mode, without first determining the
  19636.  screen dimensions, can be accomplished as a special case call of any of
  19637.  the four scroll functions, using an upper-left coordinate of (0,0), a
  19638.  lower-right coordinate of (-1,-1), and the value -1 as the number of lines
  19639.  to scroll. Figure 6 demonstrates clearing the screen to ASCII blanks with
  19640.  a normal video attribute. Scrolling a selected portion of the screen is
  19641.  also easy, as shown in Figure 7.
  19642.  
  19643.  
  19644.  Cursor Control
  19645.  
  19646.  The function VIOSETCURPOS positions the cursor, while the parallel
  19647.  function VIOGETCURPOS allows a program to obtain the current cursor
  19648.  location. Both VIOSETCURPOS and VIOGETCURPOS use text coordinates and
  19649.  assume a home position of (0,0) at the upper-left corner of the screen;
  19650.  neither function can be called in graphics modes. Figures 8 and 9 show
  19651.  examples of typical VIOSETCURPOS and VIOGETCURPOS calls.
  19652.  
  19653.  The functions VIOGETCURTYPE and VIOSETCURTYPE are used to get or alter the
  19654.  cursor height, width, and hidden/visible attributes. Both functions use a
  19655.  four-word data structure with the same format to communicate the attribute
  19656.  values──quite convenient when the cursor must be altered or hidden
  19657.  temporarily (see Figure 10).
  19658.  
  19659.  
  19660.  Display Mode Control
  19661.  
  19662.  The functions VIOGETMODE and VIOSETMODE allow programs to query or select
  19663.  the video display mode. Both calls use a data structure that contains
  19664.  flags for the adapter type, color burst enable, and text versus graphics
  19665.  mode; the number of displayable colors; the number of alphanumeric (text)
  19666.  columns and rows; and the number of pixel columns and rows. Another
  19667.  function, VIOGETCONFIG, returns the type of monitor and video adapter
  19668.  installed in the system and the amount of memory present on the adapter.
  19669.  
  19670.  This approach is open-ended: it allows the application to deal with the
  19671.  adapter on the basis of its capabilities and doesn't require the
  19672.  programmer to remember an ever-expanding list of "mode numbers" that are
  19673.  less and less related to the display modes they represent. For example,
  19674.  the code in Figure 11 selects 80 X 25, 16-color text mode with color
  19675.  burst enabled.
  19676.  
  19677.  
  19678.  Pop-up Support
  19679.  
  19680.  The functions VIOPOPUP and VIOENDPOPUP allow background processes to
  19681.  temporarily assert control of the screen and interact with the user.
  19682.  These processes, launched with the DETACH command, are placed in a
  19683.  special "black box" screen group; ordinarily the programs in this group
  19684.  cannot perform KBD or MOV input calls, and thus their output to the screen
  19685.  is discarded.
  19686.  
  19687.  In order to gain access to the screen, the background process first calls
  19688.  VIOPOPUP. This function has a wait/no-wait option: if wait is specified,
  19689.  the calling process is suspended until the screen is available; if no-wait
  19690.  is specified, the function returns immediately with an error code if the
  19691.  screen cannot be preempted, for example, if another background process has
  19692.  already called VIOPOPUP. If the VIOPOPUP call is successful, the current
  19693.  contents of the screen, which belong to the foreground process, are saved
  19694.  away, the screen is blanked and placed into an 80 X 25 text mode, and all
  19695.  further VIO calls by the background process that issued VIOPOPUP are
  19696.  directed to the active display.
  19697.  
  19698.  At this point, the background process can interact freely with the user.
  19699.  Other processes continue to run normally with their output going to the
  19700.  usual virtual screen buffer, until they require input or call VIOPOPUP, at
  19701.  which point they can be suspended. When the background process is finished
  19702.  with its pop-up activities, it must call VIOENDPOPUP to release control of
  19703.  the screen. The display belonging to the foreground process is then
  19704.  automatically restored. Figure 12 shows an example of this procedure.
  19705.  
  19706.  Since screen group switching is disabled during a VIOPOPUP...VIOENDPOPUP
  19707.  sequence and the abrupt transition from a normal display to the largely
  19708.  blank display of a pop-up (with a possible concomitant change in display
  19709.  mode) can be startling and disruptive to the user, use of VIOPOPUP should
  19710.  be kept to a minimum and reserved for true background processes. An
  19711.  ordinary application that wishes to use pop-up windows as part of its
  19712.  interaction can achieve more pleasant results by using the VIOREADCELLSTR
  19713.  and VIOWRTCELLSTR functions to save and restore small portions of the
  19714.  display.
  19715.  
  19716.  
  19717.  Summary
  19718.  
  19719.  MS OS/2's VIO subsystem provides a set of character-oriented display
  19720.  functions for use by so-called "kernel apps," programs that use only OS/2
  19721.  kernel services and do not rely on the presence of the Presentation
  19722.  Manager. Most of the programs initially ported to OS/2 from MS-DOS will
  19723.  likely be based on VIO services exclusively. This is because the VIO calls
  19724.  do not require any drastic changes to the converted program's internal
  19725.  logic; they are sufficiently powerful to make direct hardware access
  19726.  unnecessary and can be invoked directly from high-level languages. Also,
  19727.  the Presentation Manager itself will not be available during the
  19728.  introductory period.
  19729.  
  19730.  
  19731.  ───────────────────────────────────────────────────────────────────────────
  19732.  A Survey of OS/2 Display Services
  19733.  ───────────────────────────────────────────────────────────────────────────
  19734.  
  19735.  The MS OS/2 Presentation Manager is based on Microsoft(R) Windows and
  19736.  provides applications with a uniform graphical user interface. It supports
  19737.  text fonts, windowing and clipping, pull-down menus, pop-up dialog boxes,
  19738.  pointing devices, and a broad range of high-performance graphic drawing
  19739.  and painting operations. Applications written to take full advantage of
  19740.  the Presentation Manager's services must have a special structure and must
  19741.  abide by an intricate set of conventions. However, the programmer's payoff
  19742.  for making this effort is complete portability between machines and output
  19743.  devices that support the Presentation Manager, transparent adjustment of
  19744.  the program's output to compensate for the display's resolution and aspect
  19745.  ratio, the ability to exploit available fonts, and a shortened learning
  19746.  curve for the user.
  19747.  
  19748.  Character-oriented applications can avoid the complexity of the
  19749.  Presentation Manager graphical interface, while retaining device
  19750.  independence and the ability to run in a window, by using the function
  19751.  DOSWRITE together with the preassigned handles for the standard output (1)
  19752.  and standard error (2) devices to send their output to the screen. ANSI
  19753.  escape sequences embedded in the output allow for control of the video
  19754.  mode, foreground and background colors, and cursor position. This method
  19755.  is analogous to the use of Interrupt 21H Function 40H (Write to File or
  19756.  Device) under MS-DOS Versions 2.x and 3.x and is most appropriate for
  19757.  filters and other utility programs where redirectability of the output is
  19758.  an important consideration.
  19759.  
  19760.  For more flexibility and higher performance, character-oriented
  19761.  applications can employ the OS/2 kernel's VIO family of services. The VIO
  19762.  functions allow scrolling in all four directions, control over the cursor
  19763.  shape, more versatile assignment of character attributes and colors, and
  19764.  reading strings back from the screen buffer, among other things. The VIO
  19765.  calls are roughly analogous to the ROM BIOS video driver (Interrupt 10H)
  19766.  calls available under MS-DOS 2.x and 3.x in that they are immune to
  19767.  redirection of the standard output and mostly ignore control codes
  19768.  embedded in the text. Applications that use VIO calls and avoid other
  19769.  hardware dependence will still run in a window under the Presentation
  19770.  Manager.
  19771.  
  19772.  Finally, we come to two hardware-dependent display methods that are
  19773.  allowed by MS OS/2. Applications that need to present a graphics display
  19774.  without the aid of the Presentation Manager, drive the controller in a
  19775.  mode or resolution not supported by OS/2's built-in screen driver, or have
  19776.  other special requirements might use these methods.
  19777.  
  19778.  The first hardware-dependent display technique is to obtain a selector
  19779.  from OS/2 that gives the application direct access to the screen group's
  19780.  Logical Video Buffer. After making modifications to the contents of the
  19781.  buffer, the program issues an additional command to refresh the actual
  19782.  video display──this call has no effect if the application's screen group is
  19783.  not currently in the foreground. Such applications will obviously not run
  19784.  in a window under the Presentation Manager and may also have unexpected
  19785.  effects on other programs running in the same screen group, but they will
  19786.  not conflict with the operation of programs in other screen groups.
  19787.  
  19788.  The second, and potentially more destructive, hardware-dependent display
  19789.  technique is to obtain the actual physical address of the video refresh
  19790.  buffer from OS/2. After "locking" the screen with a function call, the
  19791.  application can write to the refresh buffer and modify the state of the
  19792.  video controller directly, "unlocking" the screen with another function
  19793.  call when it is finished. While the lock is in effect, the user is
  19794.  prevented from switching to another screen group, and background tasks are
  19795.  unable to obtain control of the screen so as to attract the user's
  19796.  attention.
  19797.  
  19798.  
  19799.  Figure 1:  VIO Subsystem Services at a Glance
  19800.  
  19801.  Text Display Functions
  19802.  
  19803.  VioWrtTty          Write text string to display, then reposition cursor
  19804.                     at end of string. Line feeds, carriage returns, tabs,
  19805.                     and backspaces are interpreted properly. Line-wrap
  19806.                     and screen scrolling are provided.
  19807.  
  19808.  VioWrtCellStr      Write string of alternating characters and attribute
  19809.                     bytes to screen at specified position. Cursor
  19810.                     position is not affected.
  19811.  
  19812.  VioWrtCharStr      Write character string to screen at specified
  19813.                     position. Each character takes on the attribute of the
  19814.                     previous characters at the same position. Cursor
  19815.                     position is not affected.
  19816.  
  19817.  VioWrtCharStrAtt   Write character string to screen at specified
  19818.                     position, applying same attribute byte to each
  19819.                     character. Cursor position is not affected.
  19820.  
  19821.  VioReadCellStr     Read string of characters and attributes from
  19822.                     specified position in display buffer to local buffer.
  19823.  
  19824.  VioReadCharStr     Read string of characters from specified position in
  19825.                     display buffer to local buffer.
  19826.  
  19827.  Replication Functions
  19828.  
  19829.  VioWrtNAttr        Replicate attribute byte on screen n times, starting
  19830.                     at specified position. Cursor position is not affected.
  19831.  
  19832.  VioWrtNCell        Replicate character and attribute byte on screen n
  19833.                     times, starting at specified position. Cursor position
  19834.                     is not affected.
  19835.  
  19836.  VioWrtNChar        Replicate character on screen n times, starting at
  19837.                     specified position. Cursor position is not affected.
  19838.  
  19839.  Cursor Size and Position
  19840.  
  19841.  VioGetCurPos       Get cursor position.
  19842.  
  19843.  VioSetCurPos       Set cursor position.
  19844.  
  19845.  VioGetCurType      Get cursor shape and size.
  19846.  
  19847.  VioSetCurType      Set cursor shape and size.
  19848.  
  19849.  Scroll or Clear Screen
  19850.  
  19851.  VioScrollDn        Scroll entire screen or portion of screen down by 1
  19852.                     to n lines, filling new lines with specified character
  19853.                     and attribute, or erase part or all of screen.
  19854.  
  19855.  VioScrollLf        Scroll left or clear screen as above.
  19856.  
  19857.  VioScrollRt        Scroll right or clear screen as above.
  19858.  
  19859.  VioScrollUp        Scroll up or clear screen as above.
  19860.  
  19861.  Display Mode Control
  19862.  
  19863.  VioSetANSI         Turn interpretation of ANSI escape sequences on
  19864.                     or off.
  19865.  
  19866.  VioSetCP           Select code page used to display text data.
  19867.  
  19868.  VioSetFont        Downloads a display font into the video adapter and
  19869.                     defines the dimensions of a character cell.
  19870.  
  19871.  VioSetMode         Select current display mode.
  19872.  
  19873.  VioSetState       Set palette registers, border color, or
  19874.                     blink/intensity toggle.
  19875.  
  19876.  Mode Information
  19877.  
  19878.  VioGetANSI         Get state of ANSI driver (on or off).
  19879.  
  19880.  VioGetBuf          Get selector for logical video buffer of current
  19881.                     screen group and buffer length.
  19882.  
  19883.  VioGetConfig       Get information about adapter type, display type,
  19884.                     and amount of memory on adapter board.
  19885.  
  19886.  VioGetCP           Get identifier for current code page in use for text
  19887.                     display.
  19888.  
  19889.  VioGetFont        Get character cell dimensions and address of bit
  19890.                     table for current or specified font.
  19891.  
  19892.  VioGetMode         Get current display mode. Information returned
  19893.                     includes adapter type, number of colors, vertical and
  19894.                     horizontal resolution in both characters and pixels.
  19895.  
  19896.  VioGetPhysBuf     Get selector for physical video buffer.
  19897.  
  19898.  VioGetState       Get current settings of palette registers, border
  19899.                     color, and blink/intensity toggle.
  19900.  
  19901.  Miscellaneous Functions
  19902.  
  19903.  VioPrtSc           Print screen.
  19904.  
  19905.  VioPrtScToggle     Turn echo of display to print device on or off.
  19906.  
  19907.  VioScrLock        Disable screen switching (used by a process that is
  19908.                     updating the physical video display buffer directly).
  19909.  
  19910.  VioScrUnLock      Enable screen switching (used when a process is
  19911.                     finished with direct access to physical display
  19912.                     buffer).
  19913.  
  19914.  VioShowBuf         Force update of physical display buffer from logical
  19915.                     buffer.
  19916.  
  19917.  Pop-up Support
  19918.  
  19919.  VioPopUp           Allocate full-screen text-mode pop-up display (used
  19920.                     by background process, for example, a TSR
  19921.                     program, or to notify user of errors while process's
  19922.                     screen group is not selected).
  19923.  
  19924.  VioEndPopUp        Deallocate pop-up display screen.
  19925.  
  19926.  VioSavReDrawWait  Allows a process to be notified when its screen
  19927.                     should be saved or redrawn. Used by graphics mode
  19928.                     program to recover from screen group switch or a
  19929.                     pop-up by another program.
  19930.  
  19931.  VioSavReDrawUndo  Cancels a VioSavReDrawWait call by another thread
  19932.                     within the same process.
  19933.  
  19934.  VioModeWait       Allows a process to be notified when it should
  19935.                     restore the video display mode. Used by graphics
  19936.                     mode program to recover from screen group switch
  19937.                     or a pop-up by another program.
  19938.  
  19939.  VioModeUndo       Cancels a VioModeWait call by another thread
  19940.                     within the same process.
  19941.  
  19942.  VIO Function Replacement
  19943.  
  19944.  VioRegister       Register video subsystem. Allows replacement of
  19945.                     system's default routine for any or all VIO functions
  19946.                     with new driver routines.
  19947.  
  19948.  VioDeRegister     Deregister video subsystem.
  19949.  
  19950.  
  19951.  Figure 2:  Using VIOWRTTTY to write the string "Hello World" to the screen
  19952.             at the current cursor position, then move to a new line (scrolling
  19953.             if necessary). The cursor position is updated appropriately after
  19954.             the write.
  19955.  
  19956.  Microsoft Macro Assembler:
  19957.                                      ∙
  19958.                                      ∙
  19959.                                      ∙
  19960.            push ds             ; address of string to display
  19961.            push offset DGROUP:msg
  19962.            push msg_length     ; length of string
  19963.            push 0              ; VIO handle (reserved)
  19964.            call VIOWRTTTY      ; transfer to OS/2
  19965.            or   ax,ax          ; did write succeed?
  19966.            jnz  error          ; jump if write failed
  19967.                                      ∙
  19968.                                      ∙
  19969.                                      ∙
  19970.  msg       db   'Hello World',0dh,0ah
  19971.  msg_length equ $-msg
  19972.  
  19973.  Microsoft C:
  19974.  
  19975.  extern unsigned far pascal VIOWRTTTY(char far *, unsigned, unsigned);
  19976.                                      ∙
  19977.                                      ∙
  19978.                                      ∙
  19979.       int status;
  19980.       static char msg[]="Hello World\r\n";
  19981.                                      ∙
  19982.                                      ∙
  19983.                                      ∙
  19984.       status=VIOWRTTTY(msg, sizeof(msg)-1, 0);
  19985.                                      ∙
  19986.                                      ∙
  19987.                                      ∙
  19988.  
  19989.  
  19990.  Figure 3:  Using the VIOWRTCHARSTRATT function to write to the screen. This
  19991.             code displays the string "Hello World" in reverse video at cursor
  19992.             location (10,5), column 10, row 5. The VIOWRTxxx calls support
  19993.             high-speed output of strings of any length (up to the size of the
  19994.             screen), with simultaneous control over character position and
  19995.             attributes.
  19996.  
  19997.  Microsoft Macro Assembler:
  19998.                                      ∙
  19999.                                      ∙
  20000.                                      ∙
  20001.            push ds             ; address of string
  20002.            push offset DGROUP:msg
  20003.            push msg_length     ; length of string
  20004.            push 5              ; Y position
  20005.            push 10             ; X position
  20006.            push ds             ; address of reverse video attrib
  20007.            push offset DGROUP:rev_attr
  20008.            push 0              ; VIO handle (reserved)
  20009.            call VIOWRTCHARSTRATT    ; transfer to OS/2
  20010.            or   ax,ax          ; did write succeed?
  20011.            jnz  error          ; jump if write failed
  20012.                                      ∙
  20013.                                      ∙
  20014.                                      ∙
  20015.  msg       db   'Hello World'
  20016.  msg_length equ $-msg
  20017.  
  20018.  rev_attr  db   70h            ; reverse video attribute
  20019.  
  20020.  Microsoft C:
  20021.  
  20022.  extern unsigned far pascal VIOWRTCHARSTRATT(char far *, unsigned,
  20023.       unsigned, unsigned, char far *, unsigned);
  20024.                                      ∙
  20025.                                      ∙
  20026.                                      ∙
  20027.       int status;
  20028.       static char msg[]="Hello World";
  20029.       char rev_attr=0x70;
  20030.                                      ∙
  20031.                                      ∙
  20032.                                      ∙
  20033.       status=VIOWRTCHARSTRATT(msg,sizeof(msg)-1,5,10,&rev_attr,0);
  20034.                                      ∙
  20035.                                      ∙
  20036.                                      ∙
  20037.  
  20038.  
  20039.  Figure 4:  Attribute byte for monochrome display adapter (MDA), EGA in
  20040.             monochrome text mode, or Hercules(R) Graphics Card in text mode.
  20041.  
  20042.                        7 │ 6   5   4 │ 3 │ 2   1   0
  20043.                       BL │background │I  │forground
  20044.  
  20045.                        BL = blink bit
  20046.                        I  = intensity (highlight or bold)
  20047.  
  20048.                        Background  Foreground  Display
  20049.                        000         000         no display
  20050.                        000         001         underline
  20051.                        000         111         normal video
  20052.                        111         000         reverse video
  20053.  
  20054.  
  20055.  Figure 5:  Attribute byte for graphics adapter in text mode is divided into
  20056.             two four-bit fields that control the foreground and background
  20057.             colors. The color assignments show are immutable for the original
  20058.             Color/Graphics Adapter and are the defaults for the Enhanced
  20059.             Graphics Adapter (other color assignments can be obtained by
  20060.             programming the EGA palette with VIOSETSTATE).
  20061.  
  20062.                            7   6   5   4 │ 3   2   1   0
  20063.                           background     │foreground
  20064.  
  20065.            Color Assignments
  20066.                    0     black
  20067.                    1     blue
  20068.                    2     green
  20069.                    3     cyan
  20070.                    4     red
  20071.                    5     magenta
  20072.                    6     brown
  20073.                    7     white
  20074.                    8     dark gray
  20075.                    9     light gray
  20076.                   10     light green
  20077.                   11     light cyan
  20078.                   12     light red
  20079.                   13     light magenta
  20080.                   14     yellow
  20081.                   15     intensified white
  20082.  
  20083.  
  20084.  Figure 6:  Clearing the screen to ASCII blanks with a normal video attribute,
  20085.             using one of the four VIO scroll functions. This special case call
  20086.             with an upper-left window coordinate of (0,0), lower-right
  20087.             coordinate of (-1,-1), and -1 as the number of lines to scroll
  20088.             does not require that the programmer first determine the
  20089.             dimensions of the active display.
  20090.  
  20091.  Microsoft Macro Assembler:
  20092.                                      ∙
  20093.                                      ∙
  20094.                                      ∙
  20095.            push 0              ; Y upper-left
  20096.            push 0              ; X upper-left
  20097.            push -1             ; Y lower-right
  20098.            push -1             ; X lower-right
  20099.            push -1             ; number of blank lines
  20100.            push ds             ; address of character/attrib
  20101.            push offset DGROUP:init_cell
  20102.            push 0              ; VIO handle (reserved)
  20103.            call VIOSCROLLUP    ; transfer to OS/2
  20104.            or   ax,ax          ; did scroll call succeed?
  20105.            jnz  error          ; jump if call failed
  20106.                                      ∙
  20107.                                      ∙
  20108.                                      ∙
  20109.  init_cell db   20h,07h        ; ASCII blank, normal video
  20110.  
  20111.  Microsoft C:
  20112.  
  20113.  extern unsigned far pascal VIOSCROLLUP(unsigned, unsigned, unsigned,
  20114.       unsigned, unsigned, char far *, unsigned);
  20115.                                      ∙
  20116.                                      ∙
  20117.                                      ∙
  20118.       int status;
  20119.       static char init_cell[2];     /* initialization parameter */
  20120.       init_cell[0]=0x20;            /* character = ASCII blank */
  20121.       init_cell[1]=0x07;            /* attribute = normal video */
  20122.                                      ∙
  20123.                                      ∙
  20124.                                      ∙
  20125.       status=VIOSCROLLUP(0, 0, -1, -1, -1, init_cell, 0);
  20126.                                      ∙
  20127.                                      ∙
  20128.                                      ∙
  20129.  
  20130.  
  20131.  Figure 7:  Scrolling selected portions of the screen in any direction can be
  20132.             accomplished very easily with the VIOSCROLLxx functions──a useful
  20133.             building block for windowing packages. For example, this code
  20134.             scrolls the bottom half of the screen up one line, filling the
  20135.             newly blanked line with ASCII blanks having a normal video
  20136.             attribute, and leaving the top half of the screen untouched.
  20137.  
  20138.  Microsoft Macro Assembler:
  20139.                                      ∙
  20140.                                      ∙
  20141.                                      ∙
  20142.            push 12             ; Y upper-left
  20143.            push 0              ; X upper-left
  20144.            push 24             ; Y lower-right
  20145.            push 79             ; X lower-right
  20146.            push 1              ; lines to scroll
  20147.            push ds             ; address of character/attrib
  20148.            push offset DGROUP:init_cell  ; for new line
  20149.            push 0              ; VIO handle
  20150.            call VIOSCROLLUP    ; transfer to OS/2
  20151.            or   ax,ax          ; did scroll succeed?
  20152.            jnz  error          ; jump if error
  20153.                                      ∙
  20154.                                      ∙
  20155.                                      ∙
  20156.  init_cell db   20h,07h        ; ASCII blank, normal video
  20157.  
  20158.  Microsoft C:
  20159.  
  20160.  extern unsigned far pascal VIOSCROLLUP(unsigned, unsigned, unsigned,
  20161.       unsigned, unsigned, char far *, unsigned);
  20162.                                      ∙
  20163.                                      ∙
  20164.                                      ∙
  20165.       int status;
  20166.       static char init_cell[2];     /* initialization parameter */
  20167.       init_cell[0]=0x20;            /* character = ASCII blank */
  20168.       init_cell[1]=0x07;            /* attribute = normal video */
  20169.                                      ∙
  20170.                                      ∙
  20171.                                      ∙
  20172.       status=VIOSCROLLUP(12, 0, 24, 79, 1, init_cell, 0);
  20173.                                      ∙
  20174.                                      ∙
  20175.                                      ∙
  20176.  
  20177.  
  20178.  Figure 8:  Cursor positioning with the VIO calls. This code locates the
  20179.             cursor on the first column of the last line of the screen.
  20180.  
  20181.  Microsoft Macro Assembler:
  20182.                                      ∙
  20183.                                      ∙
  20184.                                      ∙
  20185.            push 24             ; Y coordinate, row
  20186.            push 0              ; X coordinate, column
  20187.            push 0              ; VIO handle (reserved)
  20188.            call VIOSETCURPOS   ; transfer to OS/2
  20189.            or   ax,ax          ; did function succeed?
  20190.            jnz  error          ; jump if function failed
  20191.                                      ∙
  20192.                                      ∙
  20193.                                      ∙
  20194.  
  20195.  Microsoft C:
  20196.  
  20197.  extern unsigned far pascal VIOSETCURPOS(unsigned, unsigned, unsigned);
  20198.                                      ∙
  20199.                                      ∙
  20200.                                      ∙
  20201.       int status;
  20202.                                      ∙
  20203.                                      ∙
  20204.                                      ∙
  20205.       status=VIOSETCURPOS(24,0,0);
  20206.                                      ∙
  20207.                                      ∙
  20208.                                      ∙
  20209.  
  20210.  
  20211.  Figure 9:  Reading the cursor position with VIOGETCURPOS. The cursor
  20212.             location is returned in text coordinates that assume a home
  20213.             position of (0,0).
  20214.  
  20215.  Microsoft Macro Assembler:
  20216.                                      ∙
  20217.                                      ∙
  20218.                                      ∙
  20219.            push ds             ; address to receive row (Y)
  20220.            push offset DGROUP:CurY
  20221.            push ds             ; address to receive column (X)
  20222.            push offset DGROUP:CurX
  20223.            push 0              ; VIO handle
  20224.            call VIOGETCURPOS   ; transfer to OS/2
  20225.            or   ax,ax          ; was cursor position obtained?
  20226.            jnz  error          ; jump if function failed
  20227.                                      ∙
  20228.                                      ∙
  20229.                                      ∙
  20230.  CurY      dw   ?
  20231.  CurX      dw   ?
  20232.  
  20233.  Microsoft C:
  20234.  
  20235.  extern unsigned far pascal VIOGETCURPOS(unsigned far *,
  20236.      unsigned far *, unsigned);
  20237.                                      ∙
  20238.                                      ∙
  20239.                                      ∙
  20240.       int curx,cury,status;
  20241.                                      ∙
  20242.                                      ∙
  20243.                                      ∙
  20244.       status=VIOGETCURPOS(&cury,&curx,0);
  20245.                                      ∙
  20246.                                      ∙
  20247.                                      ∙
  20248.  
  20249.  
  20250.  Figure 10:  Example of altering the cursor to a block and then back to its
  20251.              original shape.
  20252.  
  20253.  Microsoft Macro Assembler:
  20254.                                      ∙
  20255.                                      ∙
  20256.                                      ∙
  20257.            push ds             ; address of structure to
  20258.            push offset DGROUP:CurData    ; receive cursor information
  20259.            push 0              ; VIO handle
  20260.            call VIOGETCURTYPE  ; transfer to OS/2
  20261.            or   ax,ax          ; did we get cursor info?
  20262.            jnz  error          ; jump if call failed
  20263.  
  20264.            mov  ax,CurStart    ; save cursor starting line
  20265.            mov  CurPrev,ax
  20266.  
  20267.            mov  CurStart,0     ; force starting line to
  20268.                                ; zero to get block cursor
  20269.  
  20270.            push ds             ; address of cursor data block
  20271.            push offset DGROUP:CurData
  20272.            push 0              ; VIO handle
  20273.            call VIOSETCURTYPE  ; transfer to OS/2
  20274.            or   ax,ax          ; did we change cursor?
  20275.            jnz  error          ; jump if call failed
  20276.                                      ∙
  20277.                                      ∙
  20278.                                      ∙
  20279.            mov  ax,CurPrev     ; restore original cursor shape
  20280.            mov  CurStart,ax
  20281.  
  20282.            push ds             ; address of cursor data block
  20283.            push offset DGROUP:CurData
  20284.            push 0              ; VIO handle
  20285.            call VIOSETCURTYPE  ; transfer to OS/2
  20286.            or   ax,ax          ; did we change cursor?
  20287.            jnz  error          ; jump if call failed
  20288.                                      ∙
  20289.                                      ∙
  20290.                                      ∙
  20291.  CurData   label byte          ; cursor data structure
  20292.  CurStart  dw   ?              ; starting scan line
  20293.  CurEnd    dw   ?              ; ending scan line
  20294.  CurWidth  dw   ?              ; width (0=default)
  20295.  CurAttr   dw   ?              ; attribute (0=visible, -1=hidden)
  20296.  
  20297.  CurPrev   dw   ?              ; previous starting line for cursor,
  20298.                                ; used to restore shape later
  20299.  
  20300.  Microsoft C:
  20301.  
  20302.  struct CursorData {
  20303.       unsigned cur_start;
  20304.       unsigned cur_end;
  20305.       unsigned cur_width;
  20306.       unsigned cur_attribute;
  20307.       };
  20308.  
  20309.  extern unsigned far pascal VIOGETCURTYPE(struct CursorData far *, unsigned);
  20310.  extern unsigned far pascal VIOSETCURTYPE(struct CursorData far *, unsigned);
  20311.                                      ∙
  20312.                                      ∙
  20313.                                      ∙
  20314.       struct CursorData CurData;
  20315.       int status,CurPrev;
  20316.                                      ∙
  20317.                                      ∙
  20318.                                      ∙
  20319.       status=VIOGETCURTYPE(&CurData,0);  /* get current cursor data */
  20320.  
  20321.       CurPrev=CurData.cur_start;         /* save cursor start line */
  20322.       CurData.cur_start=0;               /* set start line to 0 */
  20323.  
  20324.       status=VIOSETCURTYPE(&CurData,0);  /* force block cursor */
  20325.                                      ∙
  20326.                                      ∙
  20327.                                      ∙
  20328.       CurData.cur_start=CurPrev;         /* previous cursor start line */
  20329.  
  20330.       status=VIOSETCURTYPE(&CurData,0);  /* restore cursor size */
  20331.                                      ∙
  20332.                                      ∙
  20333.                                      ∙
  20334.  
  20335.  
  20336.  Figure 11:  Using VIOWRTTTY to write the string "Hello World" to the screen
  20337.              at the current cursor position, then move to a new line
  20338.              (scrolling if necessary). The cursor position is updated
  20339.              appropriately after the write.
  20340.  
  20341.  Microsoft Macro Assembler:
  20342.                                      ∙
  20343.                                      ∙
  20344.                                      ∙
  20345.            push ds             ; address of mode data structure
  20346.            push offset DGROUP:TextMode
  20347.            push 0              ; VIO handle (reserved)
  20348.            call VIOSETMODE     ; transfer to OS/2
  20349.            or   ax,ax          ; did mode set succeed?
  20350.            jnz  error          ; jump if mode set failed
  20351.                                      ∙
  20352.                                      ∙
  20353.                                      ∙
  20354.  TextMode  dw   8              ; length of data passed
  20355.            db   1              ; non-MDA, text mode, color enabled
  20356.            db   4              ; 16 colors
  20357.            dw   80             ; 80 text columns
  20358.            dw   25             ; 25 text rows
  20359.  
  20360.  Microsoft C:
  20361.  
  20362.  struct ModeData {
  20363.       unsigned length;
  20364.       unsigned char type;
  20365.       unsigned char color;
  20366.       unsigned col;
  20367.       unsigned row;
  20368.       unsigned hres;
  20369.       unsigned vres;
  20370.       };
  20371.  
  20372.  extern unsigned far pascal VIOSETMODE(struct ModeData far *, unsigned);
  20373.                                      ∙
  20374.                                      ∙
  20375.                                      ∙
  20376.       int status;
  20377.       struct ModeData TextMode;     /* init mode params */
  20378.                                      ∙
  20379.                                      ∙
  20380.                                      ∙
  20381.       TextMode.length=8;            /* amount of data passed */
  20382.       TextMode.type=1;              /* non-MDA, text mode, color */
  20383.       TextMode.color=4;             /* 16-color mode */
  20384.       TextMode.col=80;              /* 80 columns */
  20385.       TextMode.row=25;              /* 25 rows */
  20386.  
  20387.       status=VIOSETMODE(&TextMode,0);    /* force mode change */
  20388.                                      ∙
  20389.                                      ∙
  20390.                                      ∙
  20391.  
  20392.  
  20393.  Figure 12:  Example of the use of VIOPOPUP and VIOENDPOPUP by a background
  20394.              process to interact with the user. Note that if a successful
  20395.              VIOPOPUP call is made, the program must be careful not to take
  20396.              any branch away from the main line of execution toward
  20397.              VIOENDPOPUP.
  20398.  
  20399.  Microsoft Macro Assembler:
  20400.                                      ∙
  20401.                                      ∙
  20402.                                      ∙
  20403.                                ; First put up pop-up Window...
  20404.            push ds             ; address of Wait flags
  20405.            push offset DGROUP:WaitFlag
  20406.            push 0              ; VIO handle
  20407.            call VIOPOPUP       ; transfer to OS/2
  20408.            or   ax,ax          ; did we capture display?
  20409.            jnz  error          ; no, jump to error handler
  20410.  
  20411.                                ; now display pop-up message...
  20412.            push ds             ; address of message
  20413.            push offset DGROUP:msg
  20414.            push msg_len        ; length of message
  20415.            push 12             ; Y
  20416.            push (80-msg_len)/2 ; X (center it)
  20417.            push 0              ; VIO handle
  20418.            call VIOWRTCHARSTR  ; transfer to OS/2
  20419.  
  20420.            push 0              ; pause for 5 seconds
  20421.            push 5000
  20422.            call DOSSLEEP       ; transfer to OS/2
  20423.  
  20424.                                ; Take down pop-up window...
  20425.            push 0              ; VIO handle
  20426.            call VIOENDPOPUP    ; (should never fail)
  20427.                                      ∙
  20428.                                      ∙
  20429.                                      ∙
  20430.  msg       db   'Hello World'
  20431.  msg_len   equ  $-msg
  20432.  
  20433.  WaitFlag  dw   1              ; bit 0=1 if Wait until pop-up
  20434.                                ; is available, =0 if no wait
  20435.  
  20436.  Microsoft C:
  20437.  
  20438.  extern unsigned far pascal DOSSLEEP(unsigned long);
  20439.  extern unsigned far pascal VIOWRTCHARSTR(char far *, unsigned,
  20440.       unsigned, unsigned, unsigned);
  20441.  extern unsigned far pascal VIOPOPUP(unsigned far *, unsigned);
  20442.  extern unsigned far pascal VIOENDPOPUP(unsigned);
  20443.                                      ∙
  20444.                                      ∙
  20445.                                      ∙
  20446.       static char msg[]="Hello World";
  20447.                                      ∙
  20448.       status=VIOPOPUP(&WaitFlag, 0);     /* pop-up screen */
  20449.  
  20450.                                          /* display msg at center screen */
  20451.       status=VIOWRTCHARSTR(msg,sizeof(msg)-1,12,(80-sizeof(msg))/2,0);
  20452.  
  20453.       status=DOSSLEEP(5000L);            /* pause for 5 seconds */
  20454.  
  20455.       status=VIOENDPOPUP(0);             /* release pop-up screen */
  20456.                                      ∙
  20457.                                      ∙
  20458.                                      ∙
  20459.  
  20460.  ████████████████████████████████████████████████████████████████████████████
  20461.  
  20462.  Dynamic Allocation Techniques for Memory Management in C Programs
  20463.  
  20464.  Steve Schustack
  20465.  
  20466.  Most end-user PCs have between 64Kb and 640Kb of memory, and it is safe to
  20467.  assume that many of these machines will, at some point, have more RAM
  20468.  added on. Though we know what the lower and upper bounds of installed
  20469.  memory are, we can never be certain about how much memory any given
  20470.  machine might have.
  20471.  
  20472.  Programs can be written to run in specific, fixed-size environments, or
  20473.  they can be designed to dynamically adjust to any given amount of RAM.
  20474.  Programs that support such flexible memory management capability certainly
  20475.  offer an extra measure of utility. I began to consider this when my
  20476.  favorite word processor did not recognize the 128Kb of additional memory
  20477.  I had just installed in my own machine; instead it completely ignored the
  20478.  additional RAM and merrily continued to access the PC's disk.
  20479.  
  20480.  Had the programmers who wrote my word processing program utilized dynamic
  20481.  memory allocation techniques, the program would have been able to use the
  20482.  additional memory I had installed to hold more of my text, and perhaps to
  20483.  hold more of the application as well.
  20484.  
  20485.  Dynamic allocation allows programs to adapt to their environments, taking
  20486.  advantage of any memory available on an as-needed basis. Such programs
  20487.  dynamically allocate memory to hold array and linked list data rather than
  20488.  using fixed array dimensions, which are set by the programmer in array
  20489.  declarations. The only limitation on the array sizes becomes the amount of
  20490.  memory available on the user's system.
  20491.  
  20492.  This article will focus on using C's standard library functions to write C
  20493.  programs that employ data structures that grow and shrink dynamically
  20494.  during program execution. We will implement these functions through
  20495.  variable-sized arrays and linked lists, which are applications of dynamic
  20496.  memory allocation.
  20497.  
  20498.  Standard library functions such as malloc and free are among the
  20499.  programmer's tools explored here. The function malloc, for example,
  20500.  accepts as its argument the number of bytes of memory needing to be
  20501.  dynamically allocated, and returns a pointer to a block of free memory at
  20502.  least that large, provided enough memory is available. A call to the
  20503.  function free returns dynamically allocated memory to the pool of memory
  20504.  available to a program.
  20505.  
  20506.  
  20507.  The Problem
  20508.  
  20509.  Like most programming languages, C requires that when you declare an array
  20510.  you must specify its size. This often becomes a limitation of the program,
  20511.  however, because it sets an upper bound on the amount of data the program
  20512.  can process. Why not simply make the arrays so large that they are sure
  20513.  to satisfy even the largest uses?
  20514.  
  20515.  Regrettably, memory is not limitless. The more memory a program needs, the
  20516.  larger the system required to run it. As terminate-and-stay-resident (TSR)
  20517.  programs become more popular and operating system memory requirements
  20518.  grow, it makes a great deal of sense for programs to conserve memory by
  20519.  using only what is needed. A PC's memory has, in fact, become a shared
  20520.  resource. What are a programmer's options for reserving memory to hold a
  20521.  program's data?
  20522.  
  20523.  
  20524.  Memory Organization
  20525.  
  20526.  A program's memory is organized into areas known as heap, stack, data, and
  20527.  code. The heap is at the top of a program's memory space and is the source
  20528.  of dynamically allocated memory. The size of the heap depends on the sizes
  20529.  of the other areas, because the heap consists of all available memory not
  20530.  assigned to one of the other areas.
  20531.  
  20532.  The stack is just below the heap in memory; its size is set by LINK,
  20533.  either to 2048 bytes by default, or to the size given to LINK with the
  20534.  /STACK option. The data area contains static and external variable data.
  20535.  Executable machine instructions fill the code area.
  20536.  
  20537.  
  20538.  Allocation of Memory
  20539.  
  20540.  When a program obtains memory for its exclusive use, the memory is said to
  20541.  be allocated to that program. C programs have two options for memory
  20542.  allocation. The most common way memory is allocated is by simple
  20543.  declaration of variables. The alternative is to dynamically allocate
  20544.  memory by calling certain standard library functions, such as malloc,
  20545.  calloc, and realloc.
  20546.  
  20547.  Memory for static and external variables is allocated and initialized only
  20548.  once, prior to execution of the program, at the time the program is loaded
  20549.  from disk into RAM. It remains allocated until the program terminates its
  20550.  execution.
  20551.  
  20552.  All other variables, namely those of auto or register storage class, are
  20553.  allocated each time the function (or block) in which they are declared is
  20554.  entered. The memory for auto and register variables is released
  20555.  (deallocated) when the flow of control leaves the declaring function (or
  20556.  block).
  20557.  
  20558.  Because memory for statics and externals is allocated throughout
  20559.  execution, limiting the use of static and external variables tends to
  20560.  conserve RAM. Auto and register variables, on the other hand, need only be
  20561.  allocated when a given function is active (on the calling stack). A
  20562.  function is active when it is called by main or any other function.
  20563.  Lifetime, scope (visibility), and initialization are other factors to
  20564.  consider when selecting storage classes for your variables.
  20565.  
  20566.  In order to dynamically allocate memory, the amount of memory needed must
  20567.  first be known. The C keyword operator sizeof is used to find the number
  20568.  of bytes allocated to any variable that has been declared. The sizeof
  20569.  operator may also be used on data types that are enclosed in parentheses.
  20570.  This means that a program can expect the value of the expression "sizeof
  20571.  (int)" to be two or four, depending on whether it was compiled and whether
  20572.  it is executing on a 16-bit or on a 32-bit CPU (see Figure 1).
  20573.  
  20574.  
  20575.  Variable-length Arrays
  20576.  
  20577.  Dynamic allocation of a block of memory takes place during execution. The
  20578.  size of the dynamic block is specified using arguments to standard library
  20579.  functions such as malloc, calloc, and realloc. This is in contrast to the
  20580.  dimension of an array, which is fixed by a constant or by a constant
  20581.  expression in a declaration.
  20582.  
  20583.  The elementary program SUM_NUM, listed in Figure 2, introduces the
  20584.  standard library function calloc. Within SUM_NUM, calloc returns the
  20585.  address of a block of memory whose size is specified by passing the number
  20586.  of cells (elements) to be allocated, along with the size in bytes of each
  20587.  cell. Simply declare a pointer to the type of data to be stored, and
  20588.  assign the return value from calloc to that pointer variable, as follows:
  20589.  
  20590.    long *nums;
  20591.            ∙
  20592.            ∙
  20593.            ∙
  20594.    nums = (long *) calloc(how_many,sizeof(long));
  20595.  
  20596.  These two statements create a dynamically allocated array of "how_many"
  20597.  long integers, and cause the pointer "nums" to point to that array.
  20598.  
  20599.  The "(long *)" before calloc, which is a cast operator indicating "pointer
  20600.  to type long," is optional and specifies a conversion that will avoid
  20601.  compiler warnings such as "different levels of indirection." If a function
  20602.  is not declared prior to being called, or if it is not explicitly declared
  20603.  as being of a particular type (like "long"), the compiler assumes that
  20604.  "int" is the type of value returned by that function. Assigning "nums" a
  20605.  value from calloc without the "(long *)" cast would therefore result in
  20606.  trying to assign an int value to a variable declared as long, resulting in
  20607.  a mismatch of levels of indirection and causing a compiler warning
  20608.  message.
  20609.  
  20610.  If the amount of memory requested is not available, then calloc returns
  20611.  the value of NULL, which happens to be defined in stdio.h as zero. If you
  20612.  don't test for this special null pointer value and store data at address
  20613.  0, you will get a "null pointer assignment" error when your program
  20614.  terminates. Clobbering data in memory not dynamically allocated and not
  20615.  allocated to any variable, such as data at address 0, can easily go
  20616.  unnoticed and create hard-to-find errors, hard-to-find because the program
  20617.  may only misbehave while handling some rare and unusual case not tested
  20618.  during program development.
  20619.  
  20620.  
  20621.  Pointers and Arrays
  20622.  
  20623.  An understanding of C's use of pointers and arrays is essential,
  20624.  especially in relation to memory management schemes. The pointer and array
  20625.  data types are very closely related──perhaps more than you realized.
  20626.  Whenever you refer to an array in C by using that array in an expression,
  20627.  such as passing it to a function or getting the value of one of its
  20628.  elements, the value of the name of the array is taken to be the starting
  20629.  address of that array. So if you have a pointer variable that has been
  20630.  made to point to an array of the same data type, you can use that pointer
  20631.  in expressions as if it were the array (see Figure 3).
  20632.  
  20633.  The fact that pointers behave very much like arrays means that you can
  20634.  operate on elements of the array of data pointed to by the variable nums
  20635.  in SUM_NUM.C (in Figure 2), as if nums were an ordinary array of long
  20636.  integers. The address of an element pointed to by nums in the expression
  20637.  "&nums[inum]" is passed to the function scanf. The value of the element is
  20638.  added to sum in the statement "sum += nums[inum];".
  20639.  
  20640.  Even if you are a newcomer to C's pointer operations, you have been
  20641.  applying this concept every time you've written a function that operated
  20642.  on elements of an array that was passed to it. The parameters of a
  20643.  function that receive data from an array are declared using empty
  20644.  brackets, as in the expression get_param[ ]. What C actually passes to the
  20645.  function is the array's starting address. The function treats the
  20646.  parameter as an array, even though the parameter is in fact a pointer. One
  20647.  way to tell the array itself apart from a pointer to it is by using the
  20648.  sizeof operator. In Figure 3, the sizeof operator returns different values
  20649.  for p_longs and long_array, as indicated.
  20650.  
  20651.  A consequence of this type of argument-passing is that a called function
  20652.  cannot directly tell the dimension of an array that was passed to it as an
  20653.  argument. This is because the array itself is not available to it, so its
  20654.  size cannot be tested. Instead, the value passed to the function is a
  20655.  pointer to the array's first element, and as a pointer variable, it is
  20656.  either 2 or 4 bytes in size (an int or a long in a 16-bit machine).
  20657.  
  20658.  
  20659.  Free Memory
  20660.  
  20661.  When a program terminates, all of the memory allocated to it is released.
  20662.  This includes memory allocated dynamically by calling C library functions,
  20663.  like calloc. When allocated memory is released, it is deallocated and
  20664.  becomes available for future allocations. If your program requires
  20665.  repeated dynamic allocations, then you can call the function free to avoid
  20666.  running out of memory. Call free to release blocks of memory when they are
  20667.  no longer needed. The free function expects as its argument the same
  20668.  address that was returned by a previous call to calloc, malloc, or
  20669.  realloc, for example:
  20670.  
  20671.    nums = (long *) calloc(how_many,sizeof(long));
  20672.    free(nums); /* deallocates memory nums points to */
  20673.  
  20674.  Never pass an address to free that was not first given to you by calloc,
  20675.  malloc, or realloc; otherwise things will get nasty. Each block of
  20676.  dynamically allocated memory is preceded by internal system maintenance
  20677.  information, which includes the size of that block, as well as a pointer
  20678.  to the next block. If that maintenance information has been clobbered by
  20679.  storing data off the end of another block or the beginning of this block,
  20680.  or the information is simply not there, then the pool of available memory
  20681.  will become "polluted" by that call to free. Library dynamic allocation
  20682.  functions will erroneously determine that free memory is already
  20683.  allocated, or even worse, that already allocated memory is free to be
  20684.  reallocated.
  20685.  
  20686.  To illustrate the concept of dynamically allocating data we will look at a
  20687.  particular application, namely sorting a directory. Directory information
  20688.  is an unknown and unpredictable quantity. A directory sorting program
  20689.  would therefore have to do one of two things: either some specific size
  20690.  would have to be predetermined, or the sorting routine could be designed
  20691.  to dynamically allocate whatever memory it might need. Though the program
  20692.  may seem simple, it provides an excellent example of dynamic memory
  20693.  allocation and lends itself well to the memory management technique called
  20694.  linked list processing.
  20695.  
  20696.  
  20697.  Problem Definition
  20698.  
  20699.  The number of files in a directory can vary and change in unpredictable
  20700.  ways. Directories can vary in size from as little as two files ("." and
  20701.  ".."──an empty directory) to whatever upper bound the most current release
  20702.  of MS-DOS(R) allows. Because this upper bound may change with the next
  20703.  release of the operating system, a utility to sort an MS-DOS directory
  20704.  should be able to deal with this change in a manner that will not
  20705.  necessitate rewriting the program.
  20706.  
  20707.  The DIR command outputs information about files in a directory.
  20708.  Unfortunately, the order in which files are listed is not very useful. A
  20709.  solution to both problems will be discussed.
  20710.  
  20711.  The MS-DOS filter program SORT can be used to reorder the listing of
  20712.  files, to be sorted by file name and extension, for instance. Just tell
  20713.  SORT what column to begin sorting on, and it does the job. Using a pipe
  20714.  with the SORT and DIR command provides a somewhat useful but rather
  20715.  limited directory listing and sorting capability.
  20716.  
  20717.  Furthermore, a number of things are poorly handled by SORT. For example, a
  20718.  file modified in the early afternoon will be listed ahead of a file
  20719.  modified late in the morning of the same day, because MS-DOS uses "a" and
  20720.  "p" to represent A.M. and P.M., rather than a 24-hour clock. Combining DIR
  20721.  and SORT can be useful, but something better is needed.
  20722.  
  20723.  
  20724.  Linked Lists
  20725.  
  20726.  Linked lists are a very powerful and flexible way of using dynamically
  20727.  allocated memory. They are suited to applications in which data of
  20728.  different types (integer, character, and floating point) can be combined
  20729.  in chainlike structures of unpredictable lengths. Each element of a linked
  20730.  list is like a record in a memory-resident file of records. A record holds
  20731.  keys for other records, which it points to. These records link together to
  20732.  form structures ranging from a simple single chain to exotic networks of
  20733.  chains.
  20734.  
  20735.  Linked lists come in a variety of sizes and shapes, with some common
  20736.  characteristics and terminology. They can become quite complex in
  20737.  structure. We will be looking at the simplest type, the singly linked
  20738.  list, which consists of a series of elements, called nodes, that are
  20739.  joined to form a chain. It is only possible to move along this type of
  20740.  list in one direction, from the starting node of the list, its head, to
  20741.  the end node, its tail.
  20742.  
  20743.  The ease of inserting a new link between two links in a list makes linked
  20744.  lists great for storing data in a variety of different orderings. If each
  20745.  new link added to a list is inserted at the head of the list, then the
  20746.  list behaves like a push-down stack (also known as a last-in-first-out
  20747.  (LIFO) queue). If new links are stored only at the tail of the list, a
  20748.  first-in-first-out (FIFO) queue is the result. Any ordering is possible if
  20749.  new links can be inserted anywhere in the list. Sorting text data
  20750.  alphabetically is easily accomplished.
  20751.  
  20752.  The data in Figure 4 was output by the DIR command and will be used to
  20753.  construct two kinds of linked lists: first a push-down stack, then a
  20754.  sorted list. The evolution of a push-down stack from an empty list is
  20755.  demonstrated in Figures 5, 6, 7, 8, 9, and 10. The list itself is
  20756.  displayed in Figure 11. A memory diagram showing the state of the
  20757.  list after each code segment is executed follows each segment in
  20758.  Figures 5, 6, 7, 8, 9, and 10.
  20759.  
  20760.  The link structure tag, s_dir_node, is declared in Figure 5, along with
  20761.  p_head and p_new, which are both declared to be pointers to that type of
  20762.  structure. The member "next", the first member of an s_dir_node structure,
  20763.  is also a pointer to an s_dir_node structure. This type of structure is
  20764.  called self-referencing because it contains a pointer to its own data
  20765.  type. The member "next" is the means for joining structure links to form a
  20766.  linked list. The pointer to the head of the list, p_head, is assigned the
  20767.  null pointer NULL (from stdio.h) to create an empty list.
  20768.  
  20769.  Enough memory to hold a single s_dir_node structure is dynamically
  20770.  allocated by calling the standard library function malloc in Figure 6. The
  20771.  argument passed to malloc is the number of bytes required to be allocated.
  20772.  The pointer returned by malloc is the address of the newly allocated
  20773.  block, but it is type pointer to char, so it must be converted to type
  20774.  pointer to an s_dir_node structure using the "(struct s_dir_node *)" cast
  20775.  operator. The type-converted address is assigned to p_new as the final
  20776.  action of this first statement. The memory diagram indicates that the new
  20777.  link is at address 3800.
  20778.  
  20779.  The remaining two statements in Figure 6 assign values to the members of
  20780.  the allocated link by using the arrow "->" operator. The expression
  20781.  "p_new->dir_line" refers to the member dir_line in the s_dir_node
  20782.  structure pointed to by p_new. NULL is assigned to "p_new->next" to make
  20783.  it the tail of the list. This link is also the head of the list.
  20784.  
  20785.  The variable p_head is made to point at the same link that p_new points to
  20786.  via the assignment in Figure 7. In Figure 8, another new link is allocated
  20787.  at address 3900. The combined actions in Figures 9 and 10 place this
  20788.  second new link at the head of the list, in front of the previous head, in
  20789.  push-down stack fashion.
  20790.  
  20791.  The "for" loop in Figure 11 lists each link in the list, starting at the
  20792.  head and moving, one link per iteration, to the tail. One interesting
  20793.  aspect of the loop is that, rather than counting up or down, it moves
  20794.  through a chain of pointers. The expression "p_new = p_new->next"
  20795.  accomplishes that movement by assigning to p_new the value of the member
  20796.  "next" in the link p_new points to.
  20797.  
  20798.  Notice that in the format string passed to printf, inside the loop, the %u
  20799.  (unsigned integer) format specifier is used to display addresses, since
  20800.  addresses are never negative.
  20801.  
  20802.  Now that you've had a taste of building linked lists and operating on
  20803.  them, take a moment to examine the comparison between linked lists and
  20804.  arrays in Figure 12. Linked lists facilitate handling unpredictable
  20805.  amounts of data. It is easy to change the order of the data, by inserting
  20806.  a new link or chain of links between two existing links on the list; it is
  20807.  just as simple to remove a single link, or chain of links.
  20808.  
  20809.  Accessing the different elements of an ordinary array is quite fast and
  20810.  provides random direct access. The elements in a linked list must be
  20811.  accessed in order, from the head, moving link-by-link, to the tail.
  20812.  Different paths through (or orderings of) a single list can be achieved by
  20813.  adding additional next-link pointers to the link structure, however, which
  20814.  speeds up the process somewhat. The term "doubly linked list" refers to a
  20815.  linked list that contains a pointer to the previous link as well as a
  20816.  pointer to the next one. The overhead of storing link pointers in each
  20817.  link is another price paid for linked lists, but not for arrays. Keeping
  20818.  those facts in mind, let's proceed to a more real-world application of
  20819.  linked lists.
  20820.  
  20821.  
  20822.  BY_TIME Program
  20823.  
  20824.  The BY_TIME program may be used to sort DIR command output for all files
  20825.  in a directory, or only those selected by a wildcard file specification.
  20826.  The file information is placed in order with the most recently created or
  20827.  modified first. The sort is accomplished by inserting a link for each
  20828.  file's information, read from standard input, into its correct position in
  20829.  the list so that the list is always in order. A DOS command to invoke
  20830.  BY_TIME to list the *.EXE files in date and time sequence appears in
  20831.  Figure 13, along with some sample output.
  20832.  
  20833.  The organization and structure of the program BY_TIME demonstrates a
  20834.  coding style that is appropriate for developing larger applications. The
  20835.  main function hides the details of the data manipulations that take place
  20836.  in the lower-level functions, but it does give a clear overall picture of
  20837.  the processing to be performed. Each function is short and concise, doing
  20838.  a single job. No external data is used; instead the paths through which
  20839.  the data flows are made more clear by being passed as arguments.
  20840.  
  20841.  The BY_TIME program is built by separately compiling each of the C source
  20842.  files and then linking the resulting object files (see Figures 14, 15,
  20843.  16, 17, 18, 19, and 20).
  20844.  
  20845.  Each node in BY_TIME's linked list is a familiar structure with two
  20846.  members, a pointer to another node and a string of information, output by
  20847.  DIR, about each file. The structure of the nodes is defined in the header
  20848.  file, BY_TIME.H in Figure 14, so that it can be #include'd in each source
  20849.  file that needs it.
  20850.  
  20851.  The first action taken by the main function in Figure 15 is a call to the
  20852.  function bgn_list, passing the address of a dummy head node that was
  20853.  declared as an s_dir_node structure. The function bgn_list in Figure 16
  20854.  assigns dummy data (not for a file from DIR) into that node, with a date
  20855.  value that is high enough to ensure that that node will always be the
  20856.  head, given the date and time ordering requirements for this list. This
  20857.  special head helps keep the code clean, because it prevents the head node
  20858.  from being treated as a special case when inserting new links. The head of
  20859.  the list is not dynamically allocated──it is an ordinary auto structure
  20860.  type variable.
  20861.  
  20862.  The initialized list is now ready to grow with the user's file
  20863.  information. The "while" loop in main alternates between calls to get_dir
  20864.  and sav_dir, until get_dir returns zero. Each call to get_dir (listed in
  20865.  Figure 17) places one line of file information in the buffer that was
  20866.  passed to it. It returns 1 if successful; otherwise it returns 0,
  20867.  indicating that the end of input has been reached.
  20868.  
  20869.  Once DIR information for the first file has been input by the function
  20870.  get_dir, the first block of dynamically allocated memory is requested and
  20871.  put to use in sav_dir, as shown in Figure 18. The function sav_dir is
  20872.  responsible for inserting new links in such a way that the list of files
  20873.  is always in most-recently-created-or-modified-first sequence.
  20874.  
  20875.  The insertion process begins with a loop that searches the list for the
  20876.  correct insertion point. The function time_b4 in Figure 19 is called in
  20877.  the test expression of the sav_dir "for" loop. The time_b4 function
  20878.  compares the date and time in the input buffer with the date and time in a
  20879.  link in the list and returns 1 if the buffer time is earlier; otherwise it
  20880.  returns 0.
  20881.  
  20882.  Memory for the new node is dynamically allocated by a call to malloc in
  20883.  sav_dir. Notice that a test for NULL is made to verify that enough memory
  20884.  is available for the new node. The last two statements in sav_dir break
  20885.  the connection between the two appropriate links and splice in the new
  20886.  link, thereby creating a list that is one node longer, with all nodes in
  20887.  the correct order.
  20888.  
  20889.  Each DIR output line is displayed by the function sho_list in Figure 20,
  20890.  as it moves from the head to the tail of the list. This takes place after
  20891.  the end of input has been reached and the list is built. The code in
  20892.  sho_list loops through the list, displaying the data portion of each link,
  20893.  just as the loop in Figure 11 did.
  20894.  
  20895.  
  20896.  Beware of Dangling Pointers
  20897.  
  20898.  As with the free function previously mentioned, care must be taken when
  20899.  dealing with memory management C code, especially when referencing memory
  20900.  through pointers. The program DANGLE.C in Figure 21, for example, is
  20901.  guilty of memory mismanagement, because it refers to values in unallocated
  20902.  memory. The results don't make sense at first glance, until you realize
  20903.  that a nasty bug is at work. The output consists of three different lines
  20904.  of garbage. Try it and see for yourself.
  20905.  
  20906.  The memory pointed to by p_text was not allocated in the three cases where
  20907.  its address was passed to puts and printf for output. It was allocated
  20908.  while str_dang was executing, but after str_dang returned the address of
  20909.  its variable auto_string, the memory for auto_string became deallocated.
  20910.  Automatic variables within a function become deallocated when that
  20911.  function returns to its caller. Next, the auto_string memory became
  20912.  reallocated for a new use, so the string pointed to by p_text changed, as
  20913.  if by magic. The new use was for printf, or one of its subfunctions.
  20914.  
  20915.  Memory for all of a program's static and external variables remains
  20916.  allocated for the entire time the program is executing. If the declaration
  20917.  of auto_string were preceded by the word "static," then instead of three
  20918.  different lines of junk, you would see three identical lines of 123456789.
  20919.  
  20920.  
  20921.  Summary and Beyond
  20922.  
  20923.  Programs that process unpredictable amounts of data residing in memory can
  20924.  benefit from dynamically allocated arrays. These arrays may be made up of
  20925.  simple single-element data, or they may be arrays of complex structures.
  20926.  Once allocated, these dynamic arrays may be operated on by using the
  20927.  square brackets "[ ]" operator to access individual elements such as
  20928.  ordinary arrays.
  20929.  
  20930.  If changes in the ordering of the data are part of the data's processing,
  20931.  then linked lists should be considered to hold the data. The trade-offs in
  20932.  this decision involve how the data will be accessed. Will random accesses
  20933.  be frequent, or will the list only be processed in sequence? Will the
  20934.  overhead of node pointers for each link be acceptable? Are the people who
  20935.  will maintain this code fluent C programmers who understand pointers,
  20936.  dynamic allocation, and linked list theory?
  20937.  
  20938.  Only the singly linked list was examined here in detail. The doubly linked
  20939.  list, which can be traversed forward or backward, received a mention. A
  20940.  tree is another, more complex, type of linked list. The head of a tree may
  20941.  have two or more links it points to, which are sometimes called children,
  20942.  as in a family tree. Each of the child nodes may also have one or more
  20943.  children. Nodes without children are called leaves. No loops within a tree
  20944.  are permitted, just as a person cannot be his own parent or grandparent.
  20945.  Applications of trees include sorting, parsing expressions in compilers
  20946.  and translators, and processing directories of files and their
  20947.  subdirectories.
  20948.  
  20949.  For more information about managing memory, dynamic allocation, and linked
  20950.  lists, I encourage you to read the chapter "Efficient Use of Memory" in my
  20951.  book Variations in C (Microsoft Press: 1985).
  20952.  
  20953.  How well memory is managed can make a real difference in the value of a
  20954.  program to those who use it. May your pointers not dangle and your lists
  20955.  stay linked.
  20956.  
  20957.  
  20958.  Figure 1:  Using sizeof to Determine Memory Needs
  20959.  
  20960.  main()
  20961.          {
  20962.          short counter;          /* 2 bytes of RAM allocated.   */
  20963.          static char name_list[1000][25] = '\0'; /* 25000 bytes */
  20964.          int status_vector[10];  /* 20 bytes on 16-bit CPU's,   */
  20965.                                  /* 40 bytes on 32-bit CPU's.   */
  20966.  
  20967.          printf("sizeof counter %d, name_list %d, status_vector %d\n",
  20968.                  sizeof counter, sizeof name_list,
  20969.                  sizeof status_vector);
  20970.  
  20971.          printf("int's are %d bytes, I'm a %d-bit CPU\n",
  20972.                  sizeof (int), 8 * sizeof (int));
  20973.          }
  20974.  
  20975.  This output will result from the program above:
  20976.  
  20977.       sizeof counter 2, name_list 25000, status_vector 20
  20978.       int's are 2 bytes, I'm a 16-bit CPU
  20979.  
  20980.  
  20981.  Figure 2:  SUM_NUM.C
  20982.  
  20983.  /****************************************************************
  20984.   * SUM_NUM.C: Input and total a variable number of integers.
  20985.   *   Show list of numbers and cumulative subtotals in output.
  20986.   */
  20987.  main()
  20988.          {
  20989.          long *nums;      /* Pointer to array of numbers to sum */
  20990.          short how_many;  /* Number of numbers to input and sum */
  20991.          short inum;      /* Counter to index into nums array   */
  20992.          long sum;        /* Sum of numbers in nums array       */
  20993.  
  20994.          printf("How many numbers to sum? ");
  20995.          if (1 != scanf("%hd", &how_many) || how_many < 2)
  20996.                  exit(1);         /* Terminate sum_num program  */
  20997.  
  20998.          /* Dynamically allocate memory for how_many long's     */
  20999.          nums = (long *) calloc(how_many, sizeof (long));
  21000.  
  21001.          for (inum = 0; inum < how_many; ++inum) /* Input nums  */
  21002.                  {
  21003.                  printf("Enter #%d: ", inum + 1);
  21004.                  if (1 != scanf("%ld", &nums[inum]))
  21005.                          how_many = inum;        /* Quit early  */
  21006.                  }
  21007.          for (sum = inum = 0; inum < how_many; ++inum)
  21008.                  {
  21009.                  sum += nums[inum];      /* Add number to sum   */
  21010.                  printf("%3d:  %10ld %10ld\n", /* Show results  */
  21011.                          inum + 1, nums[inum], sum);
  21012.                  }
  21013.          printf("\nThe sum of the %d numbers entered is %ld\n",
  21014.                  how_many, sum);
  21015.          }
  21016.  
  21017.  
  21018.  Figure 3:  Use of Pointers with Arrays
  21019.  
  21020.  main()
  21021.          {
  21022.          long long_array[100];   /* Ordinary array of long's    */
  21023.          long *p_longs;          /* Pointer to type long        */
  21024.  
  21025.          p_longs  = &long_array[0]; /* point at long_array      */
  21026.          p_longs[8] = 32;
  21027.          printf("%d equals %d\n", p_longs[8], long_array[8]);
  21028.          printf("sizeof p_longs %d, sizeof long_array %d\n",
  21029.                  sizeof p_longs, sizeof long_array);
  21030.          }
  21031.  
  21032.  Which outputs:
  21033.  
  21034.       32 equals 32
  21035.       sizeof p_longs 2, sizeof long_array 400
  21036.  
  21037.  
  21038.  Figure 4:  Sample Data from Original DIR Command
  21039.  
  21040.       MEMO     TXT       46  11-18-86   6:26p
  21041.       DIROUT   TXT     1206   1-17-87   7:25p
  21042.  
  21043.  
  21044.  Figure 5:  Declaring the Link Structure Tag
  21045.  
  21046.  #include <stdio.h>                    /* For #define of NULL pointer */
  21047.  #define DIR_LINE_LEN 39               /* DIR output line length      */
  21048.  struct s_dir_node                     /* Linked list node structure  */
  21049.          {
  21050.          struct s_dir_node *next;      /* Next node pointer */
  21051.                                        /*   or NULL (0)     */
  21052.          char dir_line[DIR_LINE_LEN + 1];  /* DIR output line   */
  21053.          }
  21054.  
  21055.  struct s_dir_node *p_head;            /* Pointer to head of list */
  21056.  struct s_dir_node *p_new;             /* Pointer to new link     */
  21057.  p_head = NULL;                        /* List is empty now       */
  21058.  
  21059.              ┌──────┐
  21060.    p_head    │ 0000 │
  21061.              └──────┘
  21062.              ┌──────┐
  21063.    p_new     │ ???? │
  21064.              └──────┘
  21065.  
  21066.  
  21067.  Figure 6:  Allocating for a New Link
  21068.  
  21069.  /* Dynamically allocate a new link and assign data to it  */
  21070.  
  21071.  p_new = (struct s_dir_node *) malloc (sizeof (struct s_dir_node));
  21072.  strcpy (p_new->dir)line,
  21073.            "DIROUT    TXT    1206    1-17-87    7:25p");
  21074.  p_new->next = NULL;    /* No next node - this is the tail */
  21075.  
  21076.              ┌──────┐
  21077.    p_head    │ 0000 │
  21078.              └──────┘
  21079.              ┌──────┐
  21080.    p_new     │ 3800 │
  21081.              └───┬──┘  ┌──────┐
  21082.                  └─────┤ 0000 │  DIROUT    TXT    1206    1-17-87    7:25p
  21083.                        └──────┘
  21084.  
  21085.  
  21086.  Figure 7:  First Link
  21087.  
  21088.  p_head = p_new;     /* Empty list grows to a single link  */
  21089.  
  21090.              ┌──────┐
  21091.    p_head    │ 3800 ├─────┐
  21092.              └──────┘     │
  21093.              ┌──────┐     │
  21094.    p_new     │ 3800 │     │
  21095.              └───┬──┘  ┌──┴───┐
  21096.                  └─────┤ 0000 │  DIROUT    TXT    1206    1-17-87    7:25p
  21097.                        └──────┘
  21098.  
  21099.  
  21100.  Figure 8:  Allocating for Additional Links
  21101.  
  21102.  /* Allocate a second link and assign data to it      */
  21103.  
  21104.  p_new = (struct s_dir_node *) malloc(sizeof (struct s_dir_node));
  21105.  strcpy(p_new->dir_line,
  21106.          "MEMO      TXT      46   11-18-86    6:26p");
  21107.  p_new->next = NULL;     /* No next node yet          */
  21108.  
  21109.              ┌──────┐
  21110.    p_head    │ 0000 │
  21111.              └───┬──┘  ┌──────┐
  21112.                  └─────┤ 0000 │  DIROUT    TXT    1206    1-17-87    7:25p
  21113.                        └──────┘
  21114.              ┌──────┐
  21115.    p_new     │ 3900 │
  21116.              └───┬──┘  ┌──────┐
  21117.                  └─────┤ 0000 │  MEMO      TXT      46   11-18-86    6:26p
  21118.                        └──────┘
  21119.  
  21120.  
  21121.  Figure 9:  Second Link
  21122.  
  21123.  p_new->next = p_head;   /* New link points to list's head link   */
  21124.  
  21125.              ┌──────┐
  21126.    p_head    │ 0000 │
  21127.              └───┬──┘  ┌──────┐
  21128.                  └─────┤ 0000 │  DIROUT    TXT    1206    1-17-87    7:25p
  21129.                        └───┬──┘
  21130.              ┌──────┐      │
  21131.    p_new     │ 3900 │      │
  21132.              └───┬──┘  ┌───┴──┐
  21133.                  └─────┤ 3800 │  MEMO      TXT      46   11-18-86    6:26p
  21134.                        └──────┘
  21135.  
  21136.  
  21137.  Figure 10:  Completed Links
  21138.  
  21139.  p_head = p_new;    /* List grows again, now 2 links long   */
  21140.  
  21141.              ┌──────┐
  21142.    p_head    │ 0000 ├─────┐
  21143.              └──────┘  ┌──┴───┐
  21144.                        │ 3800 │  MEMO      TXT      46   11-18-86    6:26p
  21145.                        └──┬───┘
  21146.              ┌──────┐     │
  21147.    p_new     │ 3900 │     │
  21148.              └───┬──┘     │
  21149.                  │     ┌──┴───┐
  21150.                  └─────┤ 0000 │  DIROUT    TXT    1206    1-17-87    7:25p
  21151.                        └──────┘
  21152.  
  21153.  
  21154.  Figure 11:  Output Result from the Linked List
  21155.  
  21156.  /* Now that the linked list is built, display it in a for loop */
  21157.  for (p_new = p_head; p_new != NULL; p_new = p_new->next)
  21158.          printf("At addr: %4u, next: %4u, dir_line: %-.12s\n",
  21159.                  p_new, p_new->next, p_new->dir_line);
  21160.  
  21161.  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *  *
  21162.  Output:
  21163.  
  21164.  At addr: 3900, next: 3800, dir_line: MEMO     TXT
  21165.  At addr: 3800, next:    0, dir_line: DIROUT   TXT
  21166.  
  21167.  
  21168.  Figure 12:  Linked Lists vs Arrays
  21169.  
  21170.  Tradeoffs exist between storing data in linked lists and in arrays.
  21171.  
  21172.  Linked lists                  Arrays
  21173.  
  21174.  Variable length               Fixed dimension with
  21175.                                maximum size
  21176.  
  21177.  Will use memory added         Ignores memory added
  21178.  by user                       by user
  21179.  
  21180.  Fast to insert or remove      Slow to insert or
  21181.  a link                        remove an element
  21182.  
  21183.  No random access, sequential  Random direct
  21184.  access to lists only          accesses are fast
  21185.  
  21186.  Memory overhead for node      No pointer memory
  21187.  pointers                      overhead
  21188.  
  21189.  Code to manipulate lists      Code to manipulate
  21190.  is harder to read             arrays is easier to read
  21191.  
  21192.  
  21193.  Figure 13:  Sample Output of BY_TIME Program
  21194.  
  21195.  DIR *.EXE | BY_TIME
  21196.  
  21197.  BY_TIME  EXE      5654    1-26-87    8:44p
  21198.  VWC      EXE     16886    1-17-87    8:22p
  21199.  GREP     EXE      9336    4-06-87    6:43p
  21200.  PR       EXE     11604    3-16-86   11:07p
  21201.  CFIND    EXE      8146    6-20-85    5:30p
  21202.  CHARCNT  EXE      8146    4-07-87    9:53p
  21203.  LISTING  EXE      6266    4-07-85    9:27p
  21204.  VISIBLE  EXE      6402    4-07-85    9:24p
  21205.  
  21206.  
  21207.  Figure 14:  BY_TIME.H
  21208.  
  21209.  /****************************************************************
  21210.   * BY_TIME.H:  Header file for BY_TIME program, define linked
  21211.   *   list structure as next node pointer and line of DIR text
  21212.   */
  21213.  #define DIR_LINE_LEN 39         /* DIR output line length      */
  21214.  struct s_dir_node               /* Linked list node structure  */
  21215.          {
  21216.          struct s_dir_node *next;         /* Next node pointer  */
  21217.                                           /*   or NULL (0)      */
  21218.          char dir_line[DIR_LINE_LEN + 1]; /* DIR output line    */
  21219.          };
  21220.  
  21221.  
  21222.  Figure 15:  BY_TIME.C
  21223.  
  21224.  /****************************************************************
  21225.   * BY_TIME.C: Filter sorts DIR output in linked list, most recent
  21226.   * first. This is the main function of the program.
  21227.   */
  21228.  
  21229.  #include <stdio.h>                      /* For BUFSIZ symbol   */
  21230.  #include "by_time.h"                    /* Linked list struct  */
  21231.  main()
  21232.          {
  21233.          struct s_dir_node head;         /* First node in list  */
  21234.          char in_buf[BUFSIZ];            /* Input buffer for    */
  21235.                                          /*   DIR output        */
  21236.          bgn_list(&head);                /* Initialize list     */
  21237.          while (get_dir(in_buf))         /* Input DIR info for  */
  21238.                                          /*   next file         */
  21239.                  sav_dir(in_buf, &head); /* Save file info in   */
  21240.                                          /*  sorted linked list */
  21241.          sho_list(&head);                /* Show sorted list    */
  21242.          }
  21243.  
  21244.  
  21245.    Figure 16 BGN_LIST.C
  21246.  
  21247.  /****************************************************************
  21248.   * BGN_LIST.C: Initialize head of list to dummy highest value
  21249.   */
  21250.  #include <stdio.h>              /* For NULL (zero) pointer     */
  21251.  #include "by_time.h"            /* For linked list struct      */
  21252.  
  21253.  bgn_list(p_head)
  21254.  struct s_dir_node *p_head;      /* Pointer to head of list     */
  21255.          {
  21256.          /* Date in head is greater than DIR date for any file  */
  21257.          strcpy(p_head->dir_line,
  21258.                  "ZZZZZZZZ ZZZ    99999  99-99-99  99:99p");
  21259.          p_head->next = NULL;    /* No next node - empty list   */
  21260.          }
  21261.  
  21262.  
  21263.  Figure 17:  GET_DIR.C
  21264.  
  21265.  /****************************************************************
  21266.   * GET_DIR.C:  Input DIR info for next file from stdin
  21267.   */
  21268.  #include <stdio.h>              /* For NULL (zero) pointer     */
  21269.  
  21270.  int get_dir(buf)
  21271.  char *buf;              /* Buffer for read and pass line back  */
  21272.          {
  21273.          char *rtn;                      /* save gets() return  */
  21274.  
  21275.          /* Loop:  Input lines until no more input or got line  */
  21276.          /*   starting with an uppercase letter (has file data) */
  21277.          while ((rtn = gets(buf)) &&     /* Input a DIR line    */
  21278.                  (buf[0] < 'A' || buf[0] > 'Z'))   /* For file? */
  21279.                  ;
  21280.          return (rtn != NULL);  /* Return 1 if got data, else 0 */
  21281.          }
  21282.  
  21283.  
  21284.  Figure 18:  SAV_DIR.C
  21285.  
  21286.  /****************************************************************
  21287.   * SAV_DIR.C: Allocate new node in list, save DIR info in it
  21288.   */
  21289.  #include <stdio.h>              /* For NULL (zero) pointer     */
  21290.  #include "by_time.h"            /* For linked list struct      */
  21291.  
  21292.  sav_dir(buf, p_head)
  21293.  char *buf;                      /* Line of DIR output to save  */
  21294.  struct s_dir_node *p_head;      /* Pointer to head of list     */
  21295.          {
  21296.          struct s_dir_node *p_next;      /* Pointer to next     */
  21297.                                          /*   node in list      */
  21298.          struct s_dir_node *old_p_next;  /* Pointer to previous */
  21299.                  /*   next node, parent of current next node    */
  21300.          struct s_dir_node *p_new;       /* Pointer to new node */
  21301.  
  21302.          /* Loop: for each node in list until end of list or    */
  21303.          /*   insert point that will keep list sorted is found  */
  21304.          for (p_next = p_head->next, old_p_next = p_head;
  21305.                  p_next && time_b4(buf, p_next);
  21306.                  old_p_next = p_next, p_next = p_next->next)
  21307.                  ;
  21308.          /* Dynamically allocate memory for new node - DIR output
  21309.           *   line. Note use of the cast (struct s_dir_node *)
  21310.           *   operator in the assignment to avoid this message:
  21311.           *         warning 47: '=' : different levels of
  21312.           *                 indirection
  21313.           */
  21314.          p_new = (struct s_dir_node *)
  21315.                  malloc(sizeof (struct s_dir_node));
  21316.          if (p_new == NULL)      /* malloc() failed, out of RAM */
  21317.                  {
  21318.                  puts("Out of memory!!!");
  21319.                  return;
  21320.                  }
  21321.          strcpy(p_new->dir_line, buf);   /* Save DIR line in    */
  21322.                                          /*  newly alloc'd node */
  21323.          p_new->next = old_p_next->next; /* New node points to  */
  21324.                                          /*   rest of list      */
  21325.          old_p_next->next = p_new;       /* Insert new node in  */
  21326.                                          /*   list              */
  21327.          }
  21328.  
  21329.  
  21330.  Figure 19:  TIME_B4.C
  21331.  
  21332.  /****************************************************************
  21333.   * TIME_B4.C: Return 1 if date and time in buf is before date
  21334.   *   and time in node p_next points to, otherwise return 0.
  21335.   */
  21336.  #include "by_time.h"            /* For linked list struct      */
  21337.  int time_b4(buf, p_next)
  21338.  char *buf;                      /* Line of DIR output to find  */
  21339.                                  /*   insert point in list for  */
  21340.  struct s_dir_node *p_next;      /* Pointer to node in list to  */
  21341.          {                       /*   compare time in buf with  */
  21342.          int rtn;                /* Return value from strncmp() */
  21343.  
  21344.          /* compare year, month, day, am/pm, hour, and minute   */
  21345.  
  21346.          if (rtn = strncmp(&buf[29], &(p_next->dir_line)[29], 2))
  21347.                   return (rtn < 0);      /* Years differ        */
  21348.          if (rtn = strncmp(&buf[23], &(p_next->dir_line)[23], 2))
  21349.                   return (rtn < 0);      /* Months differ       */
  21350.          if (rtn = strncmp(&buf[26], &(p_next->dir_line)[26], 2))
  21351.                   return (rtn < 0);      /* Days differ         */
  21352.          if (buf[38] != (p_next->dir_line)[38])  /* am/pm's     */
  21353.                  return (buf[38] == 'a');        /* differ      */
  21354.          if (rtn = strncmp(&buf[33], &(p_next->dir_line)[33], 2))
  21355.                   return (rtn < 0);      /* Hours differ        */
  21356.          if (rtn = strncmp(&buf[36], &(p_next->dir_line)[36], 2))
  21357.                   return (rtn < 0);      /*  Minutes differ     */
  21358.          return (0);             /* Dates and times are equal   */
  21359.          }
  21360.  
  21361.  
  21362.  Figure 20:  SHO_LIST.C
  21363.  
  21364.  /****************************************************************
  21365.   * SHO_LIST.C: Show sorted linked list - output it to stdout
  21366.   */
  21367.  #include <stdio.h>              /* For NULL (zero) pointer     */
  21368.  #include "by_time.h"            /* Linked list struct          */
  21369.  
  21370.  sho_list(p_head)
  21371.  struct s_dir_node *p_head;      /* Pointer to head of list     */
  21372.          {
  21373.          struct s_dir_node *p_next;      /* Pointer to next     */
  21374.                                          /*   node in list      */
  21375.  
  21376.          for (p_next = p_head->next;     /* Start at first node */
  21377.                  p_next != NULL;         /* Still more list?    */
  21378.                  p_next = p_next->next)  /* Move down a node    */
  21379.  
  21380.                  puts(p_next->dir_line); /* Output a DIR line   */
  21381.          }
  21382.  
  21383.  
  21384.  Figure 21:  DANGLE.C──Beware of Dangling Pointers
  21385.  
  21386.  /****************************************************************
  21387.   * DANGLE.C: Danger of referencing a dangling pointer. That is a
  21388.   *      pointer to deallocated memory.
  21389.   */
  21390.  main()
  21391.          {
  21392.          char *p_text;
  21393.  
  21394.          p_text = str_dang();
  21395.          puts(p_text);
  21396.          printf("%s\n", p_text);
  21397.          printf("%s\n", p_text);
  21398.          }
  21399.  
  21400.  str_dang()
  21401.          {
  21402.          char auto_string[10];
  21403.  
  21404.          strcpy(auto_string, "123456789");
  21405.          return (&auto_string[0]);
  21406.          }
  21407.  
  21408.  ████████████████████████████████████████████████████████████████████████████
  21409.  
  21410.  CD ROM Technology Opens the Doors on a New Software Market
  21411.  
  21412.  Tony Rizzo
  21413.  
  21414.  Along with the introduction of major new CD ROM applications from
  21415.  Microsoft(R) and other software publishers, the confident glow of success
  21416.  that usually follows a "good year" can be seen on the faces of all the
  21417.  participants in the CD ROM industry these days. After a somewhat shaky
  21418.  entry into the personal computing environment, a new stability has settled
  21419.  on the CD ROM world that is finally fostering the growth of a new PC
  21420.  software market──and expectations for dramatic growth are running high.
  21421.  
  21422.  Two factors in particular contribute to this new stability. First, the
  21423.  commercial success of compact audio discs give CD ROM a strong, low-cost
  21424.  manufacturing capability, prompting considerable interest from potential
  21425.  users of the medium. Second, the creation of a standardized logical disc
  21426.  format and new systems software from Microsoft allows CD ROM drives to
  21427.  easily interface with MS-DOS(R)-based personal computers so as to provide a
  21428.  high level of software and hardware compatibility.
  21429.  
  21430.  CD ROM applications extend the use of PCs from information processing to
  21431.  include information accessing. It has proved to be a perfect medium for PC
  21432.  applications requiring:
  21433.  
  21434.    ■ a substantial amount of data space
  21435.  
  21436.    ■ wide and inexpensive distribution of medium-volatile data, such as a
  21437.      parts catalog──it is considerably less expensive to press and ship new
  21438.      discs in quantity than it is to print new copies of reference works or
  21439.      update looseleaf references
  21440.  
  21441.    ■ random searching and accessing of large databases
  21442.  
  21443.    ■ distribution of large standardized reference works, such as dictionaries
  21444.      and specification catalogs, to be integrated into a PC user's
  21445.      environment
  21446.  
  21447.  Additionally, state-of-the-art hardware now gives developers the ability
  21448.  to integrate text with video and audio information, which will ultimately
  21449.  give rise to multimedia PC applications.
  21450.  
  21451.  
  21452.  The Audio Connection
  21453.  
  21454.  CD ROM evolved from Sony and Philips's development of the audio compact
  21455.  disc. Their new optical format offered the ability to store very large
  21456.  quantities of digital information on a relatively small and damage-
  21457.  resistant disc. More importantly, their combined work led to a definition
  21458.  for the physical format of CD ROM that has become a de facto industry
  21459.  standard.
  21460.  
  21461.  As with compact audio discs, a major advantage of CD ROM is the fact that
  21462.  once a master disc is made, any number of discs can be quickly and
  21463.  inexpensively pressed from it. The success of compact audio discs led to
  21464.  the creation of a growing number of pressing facilities, which, in turn,
  21465.  has considerably reduced the cost of mass-producing both compact audio and
  21466.  CD ROM discs.
  21467.  
  21468.  The production process involves the following:
  21469.  
  21470.    ■ all data relevant to a given application is assembled
  21471.  
  21472.    ■ the data is indexed and structured for retrieval
  21473.  
  21474.    ■ a CD ROM image in High Sierra format is created from the data files──
  21475.      this step can be done by a service bureau or by using special systems
  21476.      such as CD Publisher
  21477.  
  21478.    ■ a High Sierra data image is then mixed with control information such as
  21479.      error correction code and recorded on a master tape (premastering)
  21480.  
  21481.    ■ the master tape is shipped to a pressing plant, where the master disc is
  21482.      then created (mastering)
  21483.  
  21484.    ■ the desired number of CD ROM discs is pressed from the master disc
  21485.  
  21486.  After production of the master disc, which can cost from $3,000 to
  21487.  $10,000, distribution discs can be pressed for as little as $3 per disc in
  21488.  quantities of 100 or more. If one considers that a single CD ROM disc can
  21489.  hold 660 Mb of data──more than 1500 floppy disks──it is a very cost
  21490.  effective and efficient way to distribute information.
  21491.  
  21492.  The CD ROM drive market also benefits from the success of compact audio
  21493.  discs. Technological gains made in audio hardware lead directly to
  21494.  improvements in CD ROM drive technology. More than ten manufacturers
  21495.  currently make CD ROM drives. Prices, which now average less than $1,000,
  21496.  will continue to drop as volume picks up.
  21497.  
  21498.  
  21499.  The CD ROM Disc
  21500.  
  21501.  The specification Sony and Philips created for CD ROM discs was included
  21502.  in a document called "CD ROM," informally known as the Yellow Book. Though
  21503.  not an officially recognized standard, its universal acceptance by hardware
  21504.  developers has created a stable and well-defined physical environment.
  21505.  
  21506.  The specification states that a CD ROM disc contains a stream of micron-
  21507.  size pits that are read by a laser. The pits, laid out in a spiral, define
  21508.  bit patterns that are grouped into bytes. Bytes are then grouped to form
  21509.  sectors that are each 2352 bytes long: 304 bytes are dedicated to providing
  21510.  system and hardware information (for example, error correction code), and
  21511.  the remaining 2048 bytes contain user data (see Figure 1).
  21512.  
  21513.  
  21514.  CD ROM Drives
  21515.  
  21516.  Most magnetic disks store information on concentric tracks of information.
  21517.  Each track is divided into an equal number of sectors, each of which holds
  21518.  the same amount of information; that is, the largest outer sectors hold the
  21519.  same amount of information as the smallest inner sectors. As sectors get
  21520.  smaller, information is more densely packed.
  21521.  
  21522.  Because the drives spin at a constant rate, because the number of sectors
  21523.  is the same on each track, and because the outer tracks move at a faster
  21524.  rate than the inner tracks, information on the largest sectors must be
  21525.  spaced out far enough so that it takes the same amount of time to read
  21526.  them as it does to read the smallest sectors (see Figure 2). This "spacing
  21527.  out" results in the loss of a significant amount of possible data space,
  21528.  but because the drive spins at a constant rate, and track and sector
  21529.  location is specific and constant, the seek times of the read/write heads
  21530.  can be very fast.
  21531.  
  21532.  CD ROM works differently. There is only one continuous spiral track. Each
  21533.  sector is the same physical size, allowing for substantially more data to
  21534.  be stored──there is no wasted sector space (see Figure 3). To read data
  21535.  stored in this format, however, the drive must spin at a variable rate.
  21536.  
  21537.  As the head moves towards the inner layers of the spiral the rotational
  21538.  speed increases, and as the head move towards the outer layer the
  21539.  rotational speed decreases. This change in rotational speed is required to
  21540.  maintain a constant linear velocity (CLV), which ensures that the speed
  21541.  with which the outer and inner layers of the spiral travel, relative to
  21542.  the read head, is the same. The drive first seeks to the approximate
  21543.  location of a sector within the spiral, and then must read through the
  21544.  sectors in order to find the exact sector it is looking for.
  21545.  
  21546.  This mechanical characteristic of CD ROM results in relatively slow seek
  21547.  times, generally ranging from .2 to 1 second (200 to 1000 ms). The benefit
  21548.  of the design is a much denser and substantially greater data space (see
  21549.  Figure 4). Good CD ROM layout and design means placing related data close
  21550.  together──this ensures that a good deal of the time an application will
  21551.  seek information that is nearby. Seeks that are close to each other can
  21552.  have a minimum seek time of as little as 10 ms.
  21553.  
  21554.  
  21555.  The Pioneers
  21556.  
  21557.  Most of the pioneer CD ROM projects involved the distribution of text-
  21558.  based information. Aside from building the applications themselves, early
  21559.  CD ROM developers had to deal with a number of issues over which they did
  21560.  not have much control.
  21561.  
  21562.  The major problem was compatibility. Though a physical disc format
  21563.  existed, there was no equivalent standard for logical disc formats. Worse,
  21564.  there was no standard hardware or software PC interface for CD ROM drives,
  21565.  and each project required the writing of system software to access the CD
  21566.  ROM drive from a PC. This resulted in a number of proprietary logical disc
  21567.  formats and interfacing techniques. Users found themselves in the position
  21568.  of having to invest in expensive hardware that was incompatible with any
  21569.  software not specifically designed for it. Developers had to face the
  21570.  probability that proprietary hardware and software would not be widely
  21571.  accepted.
  21572.  
  21573.  
  21574.  The Road to Success
  21575.  
  21576.  It became clear that, if CD ROM was to succeed, the industry would require
  21577.  hardware and software compatibility, as well as the ability to easily
  21578.  interface CD ROM drives to MS-DOS-based PCs.
  21579.  
  21580.  An industry committee of dedicated CD ROM participants that became known
  21581.  as the High Sierra Group (HSG) was formed to iron out the particulars of
  21582.  creating a standard logical disc format. At the same time, Microsoft
  21583.  undertook development of the MS-DOS CD ROM Extensions, software so as to
  21584.  interface CD ROM drives to MS-DOS.
  21585.  
  21586.  
  21587.  The HSG Proposal
  21588.  
  21589.  The High Sierra Group work is based on the Sony/Philips physical format
  21590.  specification. The HSG logical format provides two levels of CD ROM disc
  21591.  definitions. The first level deals with the volume as a whole, and the
  21592.  second deals with the actual files and directories that make up a volume's
  21593.  information base. The full proposal also provides for several levels of
  21594.  operating system support. The lowest level (level 1) supports MS-DOS and
  21595.  similar operating systems, while the higher levels support operating
  21596.  systems like XENIX(R) and VMS.
  21597.  
  21598.  The HSG logical format limits itself to using only the 2048-byte user data
  21599.  segment of the physical sector, which it calls a logical sector (see
  21600.  Figure 5). The proposal anticipates larger sector sizes on future media.
  21601.  The underlying hardware, device driver and system software will accommodate
  21602.  such changes, maintaining compatibility with older formats while taking
  21603.  advantage of newer formats.
  21604.  
  21605.  The HSG format defines specifications for file identifiers, file
  21606.  directories, and subdirectories. File layout is defined in terms of
  21607.  logical blocks, extents, and optional XARs (eXtended Attribute Records).
  21608.  Files containing actual information can be grouped within hierarchical
  21609.  directories, just as they are in XENIX or MS-DOS. Directories, which are
  21610.  themselves files, contain entries that provide the information necessary
  21611.  for locating the entire contents of every file. Directories can contain
  21612.  subdirectories nested to a maximum of eight levels.
  21613.  
  21614.  The HSG specification takes into account the seek-performance limits of CD
  21615.  ROM drives. For example, the proposal does not define an absolute location
  21616.  for directories. This means directories can be placed close to the most
  21617.  commonly used files, which would help keep seek time to a minimum, thereby
  21618.  optimizing read/seek performance.
  21619.  
  21620.  Another optimization in the HSG file structure is the Path Table. Because
  21621.  of the design of CD ROM drives, finding and tracking subdirectories can be
  21622.  a painfully slow process, especially when information is located deep
  21623.  within a hierarchy──for example, within the sixth, seventh or eighth level
  21624.  of subdirectory──or at distant parts of the spiral track itself.
  21625.  
  21626.  The Path Table is an index into the hierarchical directories (see
  21627.  Figure 6). It provides a way of quickly determining the starting sector of
  21628.  any directory at any level in the hierarchy without having to traverse the
  21629.  entire directory path, thus keeping the number of seeks necessary to look
  21630.  up a directory or file to a minimum. The sectors that contain the Path
  21631.  Table will be cached in memory by the MS-DOS CD ROM Extensions,
  21632.  significantly improving seek performance.
  21633.  
  21634.  From there, the system can search the directory to find actual files that
  21635.  it needs. Because searching for directories should be very fast, while
  21636.  searching for files can be very slow, the designer is cautioned against
  21637.  using directories with lots of files in them. Otherwise, an application
  21638.  might need to scan many sectors of a given directory to find a particular
  21639.  file, resulting in seek time penalties. A single CD ROM directory sector
  21640.  can hold about 40 directory entries.
  21641.  
  21642.  The specification also defines Volume Descriptors, which provide
  21643.  information for the entire contents of a CD ROM volume. Volume Descriptors
  21644.  are the only "fixed-location" entities defined by the HSG specification,
  21645.  from which any other information can be traced. Each CD ROM volume must
  21646.  have at least one Volume Descriptor that provides directory and Path Table
  21647.  information. Types of information that can be kept in the Volume
  21648.  Descriptors include the standard ASCII character set (Standard File
  21649.  Structure Volume Descriptor), author names, and creation dates.
  21650.  
  21651.  A specification is also provided for handling multidisc applications,
  21652.  where data exceeds the 660Mb capacity of one volume.
  21653.  
  21654.  The HSG completed its work on May 28, 1986. Since that time, most CD ROM
  21655.  vendors have embraced the group's set of specifications.
  21656.  
  21657.  
  21658.  The High Sierra Groups Initial Members
  21659.  
  21660.    Apple Computer
  21661.    Digital Equipment Corp.
  21662.    Hitachi
  21663.    LaserData
  21664.    Microsoft Corp.
  21665.    Phillips
  21666.    Reference Technologies Inc.
  21667.    Sony Corp.
  21668.    3M
  21669.    TMS Inc.
  21670.    VideoTools
  21671.    XEBEC
  21672.  
  21673.  
  21674.  The MS-DOS CD ROM Extensions
  21675.  
  21676.  The MS-DOS CD ROM Extensions are designed to allow a user to purchase any
  21677.  CD ROM drive from any manufacturer, to install the drive on any MS-DOS-
  21678.  based personal computer, and to read any publisher's discs that use the
  21679.  HSG format in that drive or in any other CD ROM drive.
  21680.  
  21681.  Microsoft has implemented the Extensions through a terminate-and-stay-
  21682.  resident (TSR) program known as MSCDEX.EXE, which is installed on top of
  21683.  MS-DOS and handles the interfacing of CD ROM Device Drivers to MS-DOS
  21684.  itself. The set of existing device driver commands was enhanced so that
  21685.  they would support the unique characteristics of the CD ROM drives.
  21686.  
  21687.  Microsoft has opted to distribute the MS-DOS CD ROM Extensions through CD
  21688.  ROM drive manufacturers. Each drive manufacturer provides its own device
  21689.  driver(s) and a copy of Microsoft's MSCDEX.EXE program with the CD ROM
  21690.  drive itself, both supplied on a setup diskette. CD ROM application
  21691.  developers need not supply any systems software with their applications.
  21692.  This approach is analagous to the floppy and hard disk market. Software
  21693.  developers can simply make the same assumptions about CD ROM drives that
  21694.  they make about magnetic drives and disks.
  21695.  
  21696.  Once the hardware is in place, the setup program is run by the user and
  21697.  makes the necessary modifications to CONFIG.SYS (to install the device
  21698.  driver) and AUTOEXEC.BAT (to include the call to MSCDEX.EXE). Once the
  21699.  system is rebooted the PC will recognize the newly installed CD ROM drive,
  21700.  and users and application programs can access the information on any CD
  21701.  ROM disc adhering to the HSG specifications.
  21702.  
  21703.  For a technical discussion of the MS-DOS CD ROM Extensions, see the
  21704.  accompanying article MS-DOS CD ROM Extensions: A Standard PC Access Method.
  21705.  
  21706.  
  21707.  Development Issues
  21708.  
  21709.  A number of features unique to the CD ROM environment must be taken into
  21710.  consideration during application design. The first and foremost is that CD
  21711.  ROM is read-only. Most software assumes, for example, that the current
  21712.  disk is always writable; this will create obvious problems if the current
  21713.  drive is a CD ROM drive. And of course, index and search strategies, file
  21714.  layouts and judicious use of directories must be considered.
  21715.  
  21716.  One must also keep in mind that MS-DOS is, in High Sierra format terms, a
  21717.  level 1 operating system──for example, filenames are limited to eight
  21718.  characters plus a three-character extension. The MS-DOS CD ROM Extensions
  21719.  will eventually support most features of the High Sierra format, such as
  21720.  multivolume sets.
  21721.  
  21722.  The HSG specification and the MS-DOS CD ROM Extensions have made the
  21723.  development process substantially easier. Many of the obstacles faced by
  21724.  the pioneer CD ROM developers and marketers have been cleared: The MS-DOS
  21725.  CD ROM Extensions have eliminated most of the systems-level programming
  21726.  that was previously required, and the HSG specification ensures logical
  21727.  disc compatibility. The developer need only concentrate on the application
  21728.  itself.
  21729.  
  21730.  
  21731.  Applications
  21732.  
  21733.  Typical CD ROM applications include parts catalogs, information databases
  21734.  such as those offered by on-line database services, specialized applications
  21735.  like medical diagnoses, and applications requiring the distribution of very
  21736.  large texts, such as an encyclopedia. According to Carl Stork, Microsoft's
  21737.  director of marketing for CD ROM, 150 vertical applications using CD ROM are
  21738.  currently shipping, and the use of the High Sierra format is booming.
  21739.  
  21740.  Microsoft itself has developed what can be called the first general-
  21741.  purpose CD ROM product available to the PC user. Microsoft(R) Bookshelf(TM)
  21742.  offers ten reference works on a single CD ROM disc. Bookshelf is designed
  21743.  to work as either a standalone program or interactively with programs such
  21744.  as word processors, allowing immediate on-line access to the ten reference
  21745.  works while creating or editing a document.
  21746.  
  21747.  Applications will soon include video and audio components. Digital Video
  21748.  Interactive (DVI) is a new technology that is ideally suited for CD ROM; it
  21749.  allows for full-motion video and graphics at the same data rates as those
  21750.  of a CD ROM.
  21751.  
  21752.  Optical media technology has finally hit its stride, and CD ROM is leading
  21753.  the way through its vastly improved drive technology, the High Sierra format
  21754.  specification, and the MS-DOS CD ROM Extensions.
  21755.  
  21756.  Developers with existing applications that lend themselves to CD ROM
  21757.  technology or with applications that would benefit from the CD ROM format
  21758.  now have the entire PC market available to them. And those exciting new
  21759.  applications that CD ROM is ideally suited for can now be brought to this
  21760.  market, offering personal computer users new and added functionality, as
  21761.  well as increased productivity.
  21762.  
  21763.  The combination of desktop personal computers and CD ROM is proving to be a
  21764.  winning hand. Disc compatibility has paved the way for mass market
  21765.  acceptance of CD ROM, which is bound to encourage the creation of a new
  21766.  software market and provide this new technology with a large degree of
  21767.  success.
  21768.  
  21769.  
  21770.  Figure 1:  The physical sector is 2352 bytes long; 2048 bytes can contain
  21771.             user data.
  21772.  
  21773.  Type of Information           Allocated Space
  21774.  
  21775.  Synchronization Data            12 bytes
  21776.  Header Data                      4 bytes
  21777.  User Data                     2048 bytes
  21778.  Error Detection Code (EDC)       4 bytes
  21779.  Unused Space                     8 bytes
  21780.  Error Correction Code (ECC)    276 bytes
  21781.  
  21782.  
  21783.  Figure 4:  Performance and storage characteristics of several types of
  21784.             media.
  21785.  
  21786. ╓┌─────────────┌────────────┌─────────┌────────┌──────────┌────────────┌─────╖
  21787.               │ Small      │ Large   │        │          │ Large      │     ▌
  21788.               │ Winchester │ Optical │ Floppy │ Magnetic │ Winchester │ CD  ▌
  21789.         Media │ Disk       │ ROM     │ Disk   │ Tape     │ Disk       │ ROM ▌
  21790.  ─────────────┼▀▀▀▀▀▀▀▀▀▀▀▀┼▀▀▀▀▀▀▀▀▀┼▀▀▀▀▀▀▀▀┼▀▀▀▀▀▀▀▀▀▀┼▀▀▀▀▀▀▀▀▀▀▀▀┼▀▀▀▀▀┤
  21791.         Media ▌            │         │        │          │            │     │
  21792.          Cost ▌            │         │        │          │            │ 10- │
  21793.   (in U.S. $) ▌ N/A        │ 15-30   │ 1-5    │ 10-20    │ N/A        │ 20  │
  21794.  ─────────────┼────────────┼─────────┼────────┼──────────┼────────────┼─────┤
  21795.         Drive ▌            │         │        │          │            │     │
  21796.          Cost ▌            │ 7,000-  │ 200-   │ 3,000-   │ 10,000-    │ 500-│
  21797.   (in U.S. $) ▌ 500-3,000  │ 100,000 │ 1,500  │ 15,000   │ 150,000    │ 2500│
  21798.  ─────────────┼────────────┼─────────┼────────┼──────────┼────────────┼─────┤
  21799.      Capacity ▌            │ 1,000-  │ 0.36-  │          │            │ 550 │
  21800.               │ Small      │ Large   │        │          │ Large      │     ▌
  21801.               │ Winchester │ Optical │ Floppy │ Magnetic │ Winchester │ CD  ▌
  21802.         Media │ Disk       │ ROM     │ Disk   │ Tape     │ Disk       │ ROM ▌
  21803.  ─────────────┼▀▀▀▀▀▀▀▀▀▀▀▀┼▀▀▀▀▀▀▀▀▀┼▀▀▀▀▀▀▀▀┼▀▀▀▀▀▀▀▀▀▀┼▀▀▀▀▀▀▀▀▀▀▀▀┼▀▀▀▀▀┤
  21804.     Capacity ▌            │ 1,000-  │ 0.36-  │          │            │ 550 │
  21805.       (in Mb) ▌ 5-50       │ 4,000   │ 1.20   │ 30-300   │ 50-4000    │ 680 │
  21806.  ─────────────┼────────────┼─────────┼────────┼──────────┼────────────┼─────┤
  21807.         Media ▌            │         │        │          │            │     │
  21808.     Size (in) ▌ 5.25       │ 12.0    │ 5.25   │ 10.50    │ 14.0       │ 4.72│
  21809.  ─────────────┼────────────┼─────────┼────────┼──────────┼────────────┼─────┤
  21810.   Access Time ▌            │ 0.03-   │ 0.03-  │          │            │ 0.40│
  21811.     (in sec.) ▌ 0.03-0.30  │ 0.40    │ 0.05   │ 1-40     │ 0.01-0.08  │ -1  │
  21812.  ─────────────┼────────────┼─────────┼────────┼──────────┼────────────┼─────┤
  21813.       Density ▌            │         │        │          │            │ 35, │
  21814.    (bits/in.) ▌ 15,000     │ 35,000  │ 10,000 │ 6,250    │ 15,000     │  000│
  21815.  ─────────────┼────────────┼─────────┼────────┼──────────┼────────────┼─────┤
  21816.     Data Rate ▌            │         │        │          │            │     │
  21817.     (Kb/sec.) ▌ 625        │ 300     │ 31     │ 500      │ 2,500      │ 150 │
  21818.  ▀▀▀▀▀▀▀▀▀▀▀▀▀┴────────────┴─────────┴────────┴──────────┴────────────┴─────┘
  21819.  
  21820.  
  21821.  
  21822.  Figure 5:  The HSG proposal focuses only on the 20448-byte user data space of
  21823.             the 2352-byte physical sector defined by Sony and Philips. The
  21824.             logical sector can then be further divided into logical blocks as
  21825.             per the HSG proposed definition.
  21826.  
  21827.                               ╔═══════════════════╗
  21828.                               ║  Logical Sector   ║
  21829.                               ╚═════════╤═════════╝
  21830.                                         │
  21831.                         ╔═══════════════╧═════════════════╗
  21832.                         │                                 │
  21833.     ┌────────┬──────────┬─────────────────────────────────┬───────╥───────┐
  21834.     │  Sync  │  Header  │       2048 bytes user data      │  EDC  ║  ECC  │
  21835.     └────────┴──────────┴─────────────────────────────────┴───────╨───────┘
  21836.     │                                                                     │
  21837.     ╚═══════════════════════════════════╤═════════════════════════════════╝
  21838.                                         │
  21839.                               ╔═════════╧═════════╗
  21840.                               ║  Physical Sector  ║
  21841.                               ╚═══════════════════╝
  21842.  
  21843.  
  21844.  Figure 6:  The Path Table provides the means to move directly to the location
  21845.             of the first logical sector of any directory and subdirectory.
  21846.  
  21847.                 VOLUME DESCRIPTOR
  21848.                         ├──────────────────────────────────────────┐
  21849.                                                                   
  21850.                       ROOT                                       ╔═══╗
  21851.          ┌─────┬─────┬──┴──┬─────┬─────┐                         ║   ║
  21852.          │ ┌─────┬─────┬─────┬─────┬─────┬───────────────────────║ P ║
  21853.          │ │   │ │   │ │   │ │   │ │   │ │                       ║ A ║
  21854.                                                      ║ T ║
  21855.         ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐                      ║ H ║
  21856.         │ A │ │ B │ │ C │ │ D │ │ E │ │ F │                      ║   ║
  21857.         └┬─┬┤ └───┘ └───┘ └───┘ └┬─┬┤ └───┘                      ║ T ║
  21858.          │ │└──────┐             │ │└──────┐                     ║ A ║
  21859.                                                            ║ B ║
  21860.      ┌───┐ ┌───┐ ┌───┐       ┌───┐ ┌───┐ ┌───┐                   ║ L ║
  21861.      │ G │ │ H │ │ I │       │ J │ │ K │ │ L │                   ║ E ║
  21862.      └───┘ └───┘ └───┘       └───┘ └───┘ └───┘                   ║   ║
  21863.                                                            ║   ║
  21864.        │     │     │           └─────┴─────┴─────────────────────║   ║
  21865.        └─────┴─────┴─────────────────────────────────────────────╚═══╝
  21866.  
  21867.  ████████████████████████████████████████████████████████████████████████████
  21868.  
  21869.  MS-DOS CD ROM Extensions: A Standard PC Access Method
  21870.  
  21871.  Tony Rizzo
  21872.  
  21873.  Developers of CD ROM applications need not concern themselves with the
  21874.  details of interfacing CD ROM drives to the MS-DOS environment.
  21875.  Microsoft(R), working closely with CD ROM drive manufacturers, has developed
  21876.  the software necessary to accomplish this. The MS-DOS CD ROM Extensions,
  21877.  in conjunction with the High Sierra logical disc format, free developers
  21878.  to concentrate solely on their applications and eliminates dependence on
  21879.  any particular manufacturer's drive technology.
  21880.  
  21881.  The MS-DOS CD ROM Extensions consist of a device driver for a CD ROM and a
  21882.  RAM-resident program called MSCDEX.EXE that interfaces with MS-DOS. Together
  21883.  they provide an interesting solution to some rather thorny MS-DOS/CD ROM
  21884.  interfacing problems.
  21885.  
  21886.  
  21887.  The Dilemma
  21888.  
  21889.  CD ROM device drivers are of concern primarily to manufacturers of CD ROM
  21890.  drives, who write and package them with their products in accordance with
  21891.  Microsoft's "CD ROM Device Driver Specification." However, an understanding
  21892.  of how these drivers work will benefit CD ROM application developers.
  21893.  
  21894.  Unlike conventional hard and floppy disks, CD ROM discs are read-only,
  21895.  with file structures that can occupy up to 660Mb of data space, part of
  21896.  which might be audio or video information. There is no File Allocation
  21897.  Table (FAT) as there is on a normal MS-DOS file system, and there is no
  21898.  need to dynamically track disc space for allocating data. Furthermore, CD
  21899.  ROM drives cannot be accessed via the standard MS-DOS Interrupt
  21900.  25h/Interrupt 26h (read/write disk sector) mechanism, and they cannot be
  21901.  looked at by the operating system at a physical level because they are not
  21902.  in MS-DOS file format. Files and directories are not organized as MS-DOS
  21903.  expects them, which is why CHKDSK, FORMAT, and other MS-DOS utilities will
  21904.  not work on CD ROMs.
  21905.  
  21906.  MS-DOS interfaces to standard magnetic media through block device drivers
  21907.  that deal with disk drives at the hardware level and transfer blocks of
  21908.  data in multiples of a given sector size. Also, block drivers are able to
  21909.  support multiple devices (for example, two or more hard disks at once), as
  21910.  well as removable media.
  21911.  
  21912.  MS-DOS makes some assumptions about block device drivers. It assumes there
  21913.  is a FAT, and it will attempt to read one when the device driver is
  21914.  initialized. MS-DOS also assumes that it will need to assign drive letters
  21915.  to each unit supported by a particular device driver. Neither of these
  21916.  assumptions is relevant to CD ROM drives, nor can MS-DOS deal with CD
  21917.  ROM's 660Mb data space, since MS-DOS can support a maximum disk space of
  21918.  only 32Mb.
  21919.  
  21920.  MS-DOS also accepts a character device driver, which deals with
  21921.