home *** CD-ROM | disk | FTP | other *** search
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.
- Microsoft Systems Journal Volume 2
-
- ────────────────────────────────────────────────────────────────────────────
-
- Vol. 2 No. 1 Table of Contents
-
- ────────────────────────────────────────────────────────────────────────────
-
- IRMA: A 3278 Terminal Emulator for Micro-to-Mainframe Communication
-
- The development team at Digital Communications Associates (DCA) took the
- IBM(R) PC and grafted an EBCDIC-speaking synchronous coaxial cable
- system into it. The result, IRMA(R), is a precedent-setting
- PC-to-mainframe link reflecting innovative systems design and
- hardware/software engineering.
-
-
- Upgrading Applications for Multi-user Environments
-
- Software developers who hesitated to write multi-user versions of their
- products have a clearer upgrade path to follow with the appearance of IBM's
- PC Network and software modules such as MS-DOS(R) 3.x, MS-Net, and the
- NetBIOS. We look at rewriting applications for multi-user environments.
-
-
- Expanded Memory: Writing Programs That Break the 640K Barrier
-
- Once, 640K of memory seemed like more than enough, but as programs written
- for DOS grew larger, the 640K limit was just too small. The LIM Expaneded
- Memory Specifications (EMS) defines a standard that allows developers
- to access expanded memory beyond 640K.
-
-
- Keep Track of Your Windows Memory with FREEMEM
-
- Not every Microsoft(R) Windows program is long and complex. This short
- (fewer than 100 lines of code), simple program not only displays the amount
- of available memory in an icon at the bottom of the screen and updates it
- every second, it also reveals a good deal about programming in Windows.
-
-
- A Guide to Debugging with CodeView
-
- The CodeView(TM) debugger included with the Microsoft(R) C Compiler,
- Version 4.00, provides flexible commands and a higher degree of integration
- than previous debuggers. This behind-the-scenes view of CodeView's
- development focuses on the design of the debugger and its use in a
- typical situation.
-
-
- Page Description Languages: High-Level Languages for Printer Independence
-
- Page description languages (PDLs) are becoming popular as a precise
- and formalized means of controlling printer output. We take a look at
- the three major languages that appear to be serious market
- contenders──Postscript(TM), Interpress(TM) and Document Description
- Language (DDL).
-
-
- DIAL 2.0 Provides Software Developers With Integrated Support System
-
- As part of its commitment to upgrade technical support to developers and
- programmers, Microsoft offers a new, improved version of Direct Information
- Access Line (DIAL) service, which includes access to bulletin boards,
- direct technical assistance, and interactive develper forums.
-
-
- Rich Text Format Standard Makes Transferring Text Easier
-
- Microsoft is proposing the Rich Text Format (RTF) as a standard for moving
- formatted text between applications. RTF allows the transfer of text from
- one application to another without losing the formatting and without
- worrying about translating text to each application's unique file format.
-
-
- Ask Dr. Bob
-
-
- Carl's Toolbox
-
-
- EDITOR'S NOTE
-
- We are pleased to present what might reasonably be called the first "real"
- issue of Microsoft Systems Journal. This issue is twice as large as previous
- issues and contains a good deal more of the technical support material that
- we had planned from the beginning. We believe that MSJ is already on its way
- to becoming an important source of information and support for the
- development community, but in order to achieve this goal we need your
- feedback. We hope that you will write us at the address below, or via MCI
- Mail (MSJ), with your comments, questions, criticisms, or complaints.
-
- One of MSJ's prime objectives is to promote excellence and innovation in
- software design and development. Therefore, we are especially proud to
- present a programmer-oriented article on IRMA, the revolutionary board from
- Digital Communications Associates (DCA) that enables an IBM PC to emulate
- IBM 3278 or 3279 terminals, allowing communication between mainframes and
- PCs. IRMA, first released in 1983, took the industry by storm: DCA has sold
- over 350,000 boards. Though considered a hardware product, IRMA's success is
- really due to its supporting software and firmware, representing a triumph
- of engineering and design.
-
- Standardization and communication between different systems is a hot topic
- these days, and many developers are looking for data exchange standards to
- help ease the disparity among different systems. This issue of MSJ has an
- article on the Rich Text Format, a standard proposed by Microsoft to encode
- formatted text. To fully illustrate RTF's utility, we include details of the
- specs used by three page description language (PDL) vendors to create a
- universally formatted text design.
-
- As a quick read of the abstracts on the cover suggests, Microsoft Systems
- Journal wants to explore any development or technology that is of interest
- to the community of professional developers. We plan to cover many diverse
- topics in forthcoming issues. If there are any specific topics that you
- think we should include, please write to us.──Ed.
-
-
- Masthead
-
- JONATHAN D. LAZARUS
- Editor and Publisher
-
- EDITORIAL
-
- BARRY OWEN
- Managing Editor
-
- CHRISTINA G. DYAR
- Associate Editor
-
- GERALD CARNEY
- Staff Editor
-
- DIANA PERKEL
- Editorial Assistant
-
- ART
-
- MICHAEL LONGACRE
- Art Director
-
- VALERIE MYERS
- Associate Art Director
-
- CIRCULATION
-
- WILLIAM B. GRANBERG
- Circulation Manager
-
- L. PERRIN TOMICH
- Assistant to the Publisher
-
- BETSY KAUFER
- Office Manager
-
- Copyright(C) 1987 Microsoft Corporation. All rights reserved; reproduction
- in part or in whole without permission is prohibited.
-
- Microsoft Systems Journal is a publication of Microsoft Corporation, 16011
- NE 36th Way, Box 97017, Redmond, WA 98073-9717. Officers: William H.
- Gates, III, Chairman of the Board and Chief Executive Officer; Jon Shirley,
- President and Chief Operating Officer; Francis J. Gaudette, Treasurer;
- William Neukom, Secretary.
-
- Microsoft Corporation assumes no liabilty for any damages resulting from the
- use of the information contained herein.
-
- Microsoft, the Microsoft logo, MS-DOS and XENIX are registered trademarks
- and CodeView is a trademark of the Microsoft Corporation. IBM is a
- registered trademark of International Business Machines Corporation.
- PageMaker is a registered trademark of Aldus Corporation. PostScript
- is a registered trademark of Adobe Systems, Inc. dBase II is a registered
- trademark of Ashton-Tate. Crosstalk is a registered trademark of Microstuf,
- Inc. WordStar is a registered trademark of MicroPro International. Above is
- a trademark of Intel Corporation. Advantage! is a trademark of AST
- Research, Inc.
-
- ████████████████████████████████████████████████████████████████████████████
-
- IRMA: A 3278 Terminal Emulator for Micro-to-Mainframe Communications
-
- Frank J. Derfler, Jr., and Edward Halbert
-
- Computer products, like plants, evolve in many different ways. Some new
- species of plants mutate from a single root stock, but others are hybridized
- by cross-pollinating different strains. One of the earliest attempts to
- combine an old proven stock with the wild new personal computer resulted in
- a product whose name is now synonymous with micro-to-mainframe
- communications──IRMA.
-
- The developers of the IRMA board took the PC as it was produced by IBM and
- programmed by Microsoft──an ASCII device with only asynchronous
- communications capability──and grafted an EBCDIC-speaking synchronous
- coaxial cable system into it. While designing IRMA, they had to make several
- decisions and solve many technical problems.
-
-
- What IRMA Is
-
- IRMA (the name is not an acronym and was chosen because it is memorable) is
- a circuit board that can be installed in one of the slots in the IBM(R) PC.
- The card has a BNC connector and is attached to the coaxial cable coming
- from a communications controller in front of an IBM mainframe computer.
- IRMA's software allows the PC to emulate IBM 3278 or 3279 terminals and to
- transfer files between a mainframe and a PC.
-
- IRMA is truly integrated into the PC's hardware and operating system. It is
- a standalone coprocessor system hosted by the PC. If you place an IRMA card
- on a table and connect 5 volts and the coaxial cable to it, the IBM 3274
- communications controller talks to it just like any other terminal.
-
-
- IRMA's Lineage
-
- IRMA is now marketed by Digital Communications Associates, Inc. DCA markets
- products for microcomputer communications (the company recently acquired
- Microstuf and its Crosstalk communications package) and large-scale network
- communications. The full DCA product line includes network processors,
- statistical multiplexers, high-speed time-division multiplexers, protocol
- converters, X.25 PADs, integrated software products for network management
- and control, and modems.
-
- DCA acquired IRMA in 1983 as a result of a merger with Technical Analysis
- Corp. (TAC). TAC started engineering design on IRMA in 1982 and released the
- product in 1983.
-
- The man behind IRMA is Andrew Miyakawa. He joined TAC as a programmer in
- 1972; in 1978, he became TAC's director of hardware engineering. Miyakawa
- designed the first IRMA printed circuit card. In fact, if you have an old
- IRMA board you can see his signature in the corner, which was later thought
- to be a scratch on the photo negative and removed.
-
- The engineering staff at TAC did a lot of custom work with the IBM coax A
- and coax B interfaces to develop the Agile printer interface for IBM
- systems. The coax A interface uses polling for terminal communications. The
- Agile interface uses a microprocessor to control communications between the
- polled coax system and the dumb printer. There is an obvious parallel
- between this system and the PC, and the engineering team's experience with
- this communications interface was important in the development of IRMA.
-
- The version of the IBM PC that was available in August 1982, when the IRMA
- team started work, was a limited machine with even more limited development
- tools. The PC's eight interrupts and four DMA channels made it difficult to
- enhance the machine──even if most of the interrupts had not already been
- assigned. The PC's character set was, and still is, small, and only the most
- primitive assembler was available. Finally, the 8088 just isnot up to the
- job of responding to the 3270 communications scheme. The coax A polling
- system was designed to interact with terminals using hard-wired logic and
- allows only 5.5 microseconds for a terminal to respond. The interrupt
- latencies on an 8088 don't allow it to recover quickly enough to respond to
- the communications channel.
-
- IRMA's designers forged ahead to make the interface work, almost in spite of
- IBM's product. They decided to add a coprocessor to the PC that is
- considerably faster than the 8088. Having decided upon the design approach,
- the IRMA development team divided the project into four major areas:
- coprocessor hardware, microcode for the hardware, terminal emulation
- software, and the user interface.
-
- The IRMA team spent most of its development time on implementation of the
- interface, rather than on design. TAC received no significant help from IBM
- other than the standard documents available to the general public. IBM's
- Technical Reference manual contained much information about the user
- interfaces, such as the monitor, the keyboard, and memory. This information
- was crucial to the development of the IRMA board.
-
-
- The Hardware
-
- The IRMA board consists of four major components. The Signetics 8X305,
- IRMA's processor, is the large chip on the left side of the board. This
- microprocessor has the power to handle the 3270 protocol and the associated
- polling, data transfer, and handshaking. Even though the chip runs at 4
- MIPS, it can only execute roughly 22 instructions in the 5.5 milliseconds
- allowed for generating a response to the 3274 communications controller.
-
- The IRMA board functions as an intelligent buffer processor and interface
- between the coaxial cable information flow and the IBM PC. Signals from the
- coax travel through the DP8340 and DP8341 3270 Coax Transmitter/Receiver
- Interface, which occurs in the two large chips along the top of the card.
- The serialization and deserialization of data takes place in these two
- chips. The chips also provide the interface between the coaxial cable and
- the microprocessor.
-
- The independence of the IRMA processor in the PC is made possible by the
- Decision Support Interface (DSI). DSI is implemented in the microcode of
- IRMA's processor and support program. DSI allows the 8X305 system to meet
- the requirements of the 3270 protocol by managing streams of buffered data
- and handling all of the timing. The technique that moves the data between
- the processors is called a mailbox structure (see Figure 1).
-
- The mailbox is a 4-byte, dual-ported register array (4 74LS670) located in
- addresses 220H to 223H. The dual port array is the bridge between the 8088
- and the 8X305 processors. Flags are used to detect idle states within the
- DSI structure; an idle state lasts only 5.5 microseconds between messages
- from the controller. When DSI isn't doing anything, it looks to this array
- for any commands that need processing. The command words sent by the IRMA
- program read or write bytes of data in the screen buffer, process
- keystrokes, and call other special features.
-
- Data is transmitted between the two processors through the use of four
- address locations (220H─223H). The processor traffic is controlled by the
- Command Request flag located at a higher address (226H). This flag is
- cleared by either processor when information in the array has been
- successfully read. There is also an Attention Request flag (227H). The
- commands that are passed through these four registers are parameter driven.
- The command is placed in the base address, or word 0, at 220H, and up to
- three arguments can be specified at 221H─223H, depending on the command.
- Before issuing any command on the array, the Command Request flag is set to
- high. The command is then placed in the register(s). After it has been
- picked up by the receiving processor, the request flag is cleared. Address
- locations 224H and 225H are reserved for future use.
-
- There are sixteen commands supported by DSI:
-
- Code Command Definition
-
- 0 Read buffer data
- 1 Write buffer data
- 2 Read status/cursor position
- 3 Clear main status bits
- 4 Send keystroke
- 5 Light pen transmit
- 6 Execute Power-on Reset
- 7 Load trigger data and mask
- 8 Load trigger address
- 9 Load attention mask
- 10 Set terminal type
- 11 Enable auxiliary relay
- 12 Read terminal information
- 13 No-op
- 14 Return revision ID and OEM number
- 15 Reserved; Do not use The Video Interface
-
- Even with its standalone power, the IRMA coprocessor board needs the PC as a
- keyboard input and display device. However, the PC lacks many of the
- features and the flexibility found in 3270-series terminals, so the
- designers had to work around these limitations. Special techniques are used
- to move video, characters and colors are substituted on the screen, and
- keyboard emulation alternatives are provided.
-
- The use of an on-board screen buffer is very important for the IRMA/PC video
- interface. Data on the IRMA's video buffer is always active and being
- updated regardless of whether or not the user is looking at it. Only when
- the user switches to the emulation mode is the data moved into the PC's
- video buffer.
-
- The screen buffer is controlled by DSI and occupies a total of 8K of fast
- RAM on the IRMA board. This RAM resides in the four large chips located
- along the bottom edge of the card. The IRMA board supports displays of up to
- 132 columns by 43 lines, such as those used by some 3278 models. This
- requirement drove a RAM with a size of 3K for the characters and another 3K
- for the attributes. This is because each character needs 16 bits in memory:
- 8 bits for the ASCII code and another 8 bits for the attributes. The
- remaining 2K are used by the 8X305 for local storage.
-
- The attribute characters that are supported by IRMA include protected/
- nonprotected, numeric/alpha/both, light pen detect, tab stops,
- intensified/normal, nondisplay type fields, and modified data tag.
-
- The Extended Attribute Characters control character type (normal, blinking,
- reverse video, underlined), character color, and character set.
-
- Using the 8X305 processor, DSI converts the keystrokes and attribute
- characters into 3270 functions and emulates those same functions on the PC.
- The field attributes are mapped to the colors on the PC, corresponding
- approximately to those on the 3279 terminal. If an IRMA board is in a PC
- with a color display, each color represents a different type of field. The
- attribute characters on the PC translate to the field types in Figure 2.
-
- In order for a field to be unprotected, numeric data only, highlighted, and
- detectable, the program would have to send a 11011000B, D8H to the screen
- buffer. This field attribute continues to be in effect until another field
- type is specified.
-
- IRMA replicates some of the quirks of 3270 functions such as "attribute
- wrap," which occurs when the programmer fails to end a field. The attribute
- for that field not only affects screen locations after it, but also wraps
- around and affects locations before it on the screen. Programmers have to be
- just as careful with IRMA as they are with real 3278/9 terminals.
-
- In the 3278/9 terminals, a specific bit within the attribute byte, known as
- the Modified Data Tag (MDT), is changed whenever a field is modified. If the
- byte had been sent out as D8H and read back as D9H, the program would be
- signaled that the operator has modified the field. The IRMA software must
- interpret the position of the data on the screen of the PC and then convert
- it to the appropriate coordinates for the 3278/9 fields. The Extended
- Attribute Bytes (EAB) are defined in Figure 3.
-
- The IRMA screen-handling software has features designed to allow developers
- to see screen attributes that might not otherwise be displayed. A single
- keystroke shows the attributes of a field. Unprotected fields can be
- temporarily filled to view all of their locations.
-
- Attributes are normally displayed as blanks and take up a byte on the screen
- just as attributes on the 3278/9 terminals do. Addressing for text in the
- IRMA board starts at 50H and increases in increments of 80 for each line up
- to 780H on line 24 during emulation of MOD 2 terminals. Emulation of MOD 3
- terminals requires 32 lines and ends at A00H. Emulation for MOD 4 terminals
- must support 43 lines, and the last line starts at D70H. The storage for the
- status line in the emulation of all models starts at 0H in lower memory, but
- the line is displayed at the bottom of the screen.
-
-
- User Interface
-
- There were some significant problems in the development of the user
- interface for the IRMA board. More than half of the problems involved
- keyboard operation. The major problem was figuring out how to emulate
- special 3270 series terminal keys on the PC's limited keyboard. The PC's
- keyboard has fewer keys to work with, and the PC's BIOS does not allow all
- combinations of keys on the keyboard to generate a scan code. This problem
- was eventually solved by including software that takes control of and
- reprograms the entire keyboard.
-
- The 3270 terminals have 24 PF keys and more than 30 other keys that the PC
- must satisfy. At first, the developers felt it would be best to keep the
- positions of the special function keys to be emulated on the PC as close to
- their positions on a 3278 as possible. Most of the 3270's PF keys were
- originally mapped to the numeric keypad on the right side of the PC's
- keyboard. This was later changed because of conflicts with the use of the
- numeric keypad. The PF keys 1─12 are mapped to Alt-1 through Alt-=, and PF
- keys 13─24 are mapped to Ctrl-1 through Ctrl-=.
-
- Either IRMA's developers had great foresight, or they have set a standard
- for PF keys, because most current PC-to-mainframe applications now map
- PF13-PF24 to the PC's control key sequence. Most of the terminal control
- keys on the left side of the 3278 keyboard have been faithfully duplicated
- on the PC's keyboard for IRMA. In all cases, the normal functions of the
- PC's keyboard are unmodified, and the 3270 emulated functions are reached
- through the Alt and Ctrl keys.
-
- The only keys that were located far from their normal positions on the 3278
- keyboard are the Dup, Field Mark, PA1, and PA2 keys, which are mapped to
- Ctrl-G, Ctrl-H, Ctrl-J, and Ctrl-K, respectively. Other special functions
- not offered by a 3278 terminal are the Show Attribute and Display All
- Unprotected Fields functions. The designers left these in for the benefit of
- users and developers.
-
- Another problem faced by the IRMA design team was screen display emulation.
- The 3278 uses icons for certain actions and messages. These icons cannot be
- recreated with the standard IBM ASCII character set. The designers
- considered drawing these icons with the IBM PC graphic display system, but a
- little experimentation showed that the system would be too slow for good
- emulation. Also, using graphic characters would limit users to graphic
- terminals. The compromise was to use a combination of ASCII characters to
- emulate the special characters available on the IBM terminal. An example of
- some major substitutions are listed in Figure 4.
-
- Through this selection of characters, the designers came up with a
- reasonable facsimile of those used in 3278-type terminals. Screen emulation
- was made easier by the fact that many operators of 3270-series terminals
- recognize characters by where and when they come up as much as by what they
- look like. Thus, almost anything appearing in the same general location on
- the screen at the right time would be interpreted by the operator as the
- same message. The alternative characters developed by the IRMA team have
- become standard and are used by most other manufacturers of terminal
- emulators, including IBM.
-
- Color selection during emulation of the 3279 color terminal was also a
- problem, because the IBM Color Graphics Adapter has a much smaller palette
- than the 3279. Until just recently, the normal protected fields were dark
- blue and hard to read. DCA provided a patch in the setup menu to substitute
- cyan for blue.
-
-
- Converging Limitations
-
- The board was nearly complete when the IRMA team discovered a strange quirk
- in the IBM 3274 controller. The 3274 would not send color or graphic
- characters to the IRMA board. After an extensive investigation, the
- designers discovered that the controller looks for a special color
- convergence test feature found only on the 3279 terminals to determine if
- the terminal at the distant end is really capable of using color. This color
- convergence test allows the user to align the red, blue, and green guns by
- using the cursor keys until they form a single white dot. This dot signifies
- correctly aligned electron guns in the CRT. The presence of data in a
- special convergence test video buffer tells the 3274 that the terminal is
- aligned and ready to accept color graphics. Since the 3274 controller
- couldn't get a color convergence test response from the IRMA board or the
- PC, it would not transmit color graphics.
-
- The design team had to program the IRMA board to fake the controller into
- thinking that it had this test feature. The controller only checks for this
- test at terminal power-up, so the test is faked at the board's
- initialization. The test code is temporarily overlaid in undisplayed video
- buffers.
-
-
- Locked Out
-
- Another problem cropped up in early 3276 controllers. The phase-locked loop
- devices in the 3276 controllers were not consistent, and individual machines
- had significant timing differences during data transmission. At first, IBM
- field technicians consistently blamed problems on improper cable. Since
- changing the length of the cable can adjust the timing of received data,
- this fix often worked. The programming of the IRMA board was modified to
- work around the 3276 problem by addressing the data separator at specific,
- gated times. The bug was fixed on the newer models of the 3276, and
- modifications were developed for older models.
-
-
- User Relations
-
- A major issue in the development of the user interface was user-developed
- software. DCA chose not to copy-protect its software. DCA feels that copy
- protection is just not worth the ill will that it brings.
-
- DCA ships the source code for the terminal emulation and file transfer
- programs with every IRMA board. DCA includes the source code for its
- programs because everyone benefits if someone can improve the code. The user
- manual includes programs written in BASIC so that even the novice programmer
- can see how the board communicates. The BASIC programs were never really
- intended for use in applications because they run very slowly under a BASIC
- interpreter, but many people have tried them anyway.
-
- The design team assumed that sophisticated users would provide their own
- file transfer protocol or use a third-party protocol, but they found that
- they had to supply a more complete system. The file transfer software
- provided for the mainframe link has been updated from the slow FT78X to the
- FTCMS and FTTSO programs. The old FT78X used the VM/CMS Xeditor and just
- copied files byte by byte from disk through a fake keyboard buffer to the
- file on the host. The new program can send large blocks of data and is much
- faster. The new programs are written in PL/1 and require that the PL/1
- transient library installed on the host.
-
- The IRMA package also includes an extensive technical reference manual with
- all the functions and commands documented. Miyakawa speaks for all MS-DOS
- developers when he expresses his wish that someone had defined MS-DOS user
- interfaces or that some of the BIOS responsibility was taken off the
- developer.
-
- DCA has gained much from such large users of the IRMA board as Texaco, Coca
- Cola, and Blue Cross and Blue Shield. Miyakawa feels that user feedback is
- extremely valuable and provides the best information for user interface
- development.
-
-
- IRMA's Future
-
- A new program called IRMAX has been developed to enable the IRMA card to run
- as what IBM calls a Distributive Function Terminal. This allows an IBM PC to
- emulate the 3270 PC, running multiple sessions on a single coax connection.
- This configuration is more desirable because the 3270 PC has had
- compatibility problems running PC software. IRMAX will maintain
- compatibility while allowing the full capabilities of the Distributive
- Function Terminal.
-
- IRMAX functions by removing most of the work from the 3274 controller and
- placing it on the IRMA card. The card carries the SNA, a 3270 display
- support program, and a terminal control program similar to the one in the
- 3270 PC.
-
- DCA's commitment to communications will carry it into the LAN market with
- IRMA LAN, which will operate with any manufacturer's version of the network
- basic input/output software (NetBIOS) using the function calls originally
- developed by IBM and Sytek.
-
- IRMA LAN is configured with an IRMA card in a network server, acting as what
- is functionally termed a network gateway. A modified version of the IRMA
- software runs on a PC operating as a network workstation and communicates to
- the IRMA card through the NetBIOS. In an efficient high-speed network, the
- person using the workstation can't tell that the IRMA card is located in a
- remote server.
-
- In some cases (IBM, Sytek, 3COM, and other companies), a "real" NetBIOS lies
- between the networking software and DOS. Other companies, such as Novell and
- Banyan, emulate NetBIOS as part of a complete operating system. In either
- case, the IRMA LAN software relies on NetBIOS to carry data messages to the
- gateway server, where they are addressed to the IRMA card.
-
- In the LAN environment, the IRMA software generates a stream of code called
- a network control block (NCB), which activates NetBIOS and passes data
- through a set of buffers established by NetBIOS and controlled by the NCB.
- The software generates an interrupt 5CH that calls NetBIOS. Other fields of
- the NCB point to a message buffer address and tell NetBIOS where to send the
- information in the buffer. This is a standard implementation of the
- capabilities provided by the NetBIOS communications interface.
-
-
- IRMA Showed How
-
- IRMA is an excellent example of the integration of an advanced
- communications capability into the somewhat limited PC system. The
- development team was innovative and employed sound engineering techniques
- to develop a precedent-setting link between PCs and mainframe computer
- systems.
-
-
- Figure 1: Mailbox Structure
-
- ┌──COAXIAL─CABLE───
- ┌────────────────────────────────────────────────────│──(TO 327X CONTROLLER)
- │ ┌────────┴────┐ │
- │ │ │ │
- │ ╔═══════════╗ ╔═════╧═════╗ ╔════╧══════╗│
- │ ║ 8K RAM ║ ║ COAX ║ ║ COAX ║│
- │╔══════════════╗ ║ BUFFER ║ ║TRANSMITTER║ ║ RECEIVER ║│
- │║ ║ ║ MEMORY ║ ║ DP8340 ║ ║ DP8340 ║│
- │║ ║ ╚══╤═════╤══╝ ╚══╤═════╤══╝ ╚══╤═════╤══╝│
- │║ ║ │ │ │ │ │ │ │
- │║ ╟─────────┘ └────────┘ └────────┘ │ │
- │║MICROPROCESSOR║ │ │
- │║ 8X305 ║ │ │
- │║ ╟───────────────────────┐ ┌───────────────┘ │
- │║ ║ │ │ │
- │║ ║ │ │ │
- │║ ║ │ │ │
- │╚══════════════╝ ╔══════╤════╧══╤══╧═══╤══════╗ │
- │ ║ │ │ │ ║ │
- │ ║ DUAL PORTED REGISTER ║ │
- │ ║ │ │ │ ║ │
- │ ╚══════╧════╤══╧══╤═══╧══════╝ IRMA │
- └───────────────────────────────────────│─────│───────────────────┘
- SYSTEM UNIT
- │ BUS │
-
- ████████████████████████████████████████████████████████████████████████████
-
- Upgrading Applications For Multi-user Environments
-
- ───────────────────────────────────────────────────────────────────────────
- Also see the related article:
- MS-Net Lock (Function 5CH, Code 00H)
- ───────────────────────────────────────────────────────────────────────────
-
- Robert Cowart
-
- With the appearance of IBM's PC Network and the accompanying software
- modules (MS-DOS 3.x; the Network Program Microsoft-Networks, popularly known
- as MS-Net; and the NetBIOS) as at least semi-stable industry performers,
- software developers who were previously hesitant to release multi-user
- versions of their products now have a clearer upgrade path to follow.
-
- This development raises several questions: What's involved in rewriting
- existing applications for use with MS-DOS 3.x and these new standards, or
- for those many products that adhere to them? Is it worth the effort? How
- long might it take? Do you have to rewrite your entire program? And most
- importantly, if you do end up deciding to go multi-user, how should the
- logistics of implementation be handled?
-
- Obviously, the first point to consider is whether your application stands to
- gain from adaptation to a multi-user environment. There are certainly
- applications that don't lend themselves to multi-user operations. Word
- processors are a case in point; it's hardly advantageous or even advisable
- for more than a single user to be editing a given document. But for such
- applications as database management, accounting systems, manufacturing
- control, or even certain spreadsheet packages, upgrading your product to
- multi-user capability may serve to increase sales and fill a need among
- users, particularly as the market's interest in networking swells. IBM's
- clear commitment to the fully integrated and networked office environment of
- the future reinforces this trend.
-
-
- Single-user Upgrades
-
- In making this decision, you should bear in mind the distinction between
- single-user and multi-user modes of operation on the same PC network.
- Running an existing program in single-user mode allows one or more people to
- load the program into their own computers and run it, thus creating their
- own independent data files. So-called multi-user operation allows users to
- share those same data files. The multi-user program must, in this case,
- offer sufficiently intelligent data management to prevent users from
- accidentally clobbering each other's data.
-
- Most existing single-user applications run without concern for these matters
- if they are in a single-user mode under the current network operating
- systems. More often than not, few, if any, adjustments are necessary to get
- such an application running reasonably well under MS-Net. At the most,
- certain considerations pertaining to file access modes must be taken into
- account to prevent data corruption. This boils down to deciding what
- status──the most frequently used settings are read/write or read-only──each
- file used by a program should be given when a user is running the program on
- a network.
-
- According to Gary Stroud, PC systems programmer for Berkeley, Calif.-based
- Centram Systems West, makers of the TOPS network,
-
- "The first thing to do when adapting a single-user application on a network
- is to set your primary program file and all its overlay files to read-only
- mode. This way, any number of people can run the program, and nobody can
- accidentally erase one of the necessary program files while somebody else is
- in the middle of using them. That can cause a catastrophe. Microsoft Word
- sets its files to read-only automatically, for example. Next, it's
- imperative that, if your application uses temporary scratch files when users
- are creating documents, you ensure that the names for these files are always
- unique. Otherwise MS-DOS will confuse, say, my temporary file and your
- temporary file, and we'd have a mess. Starting with MS-DOS 3.0, there is a
- DOS call for obtaining a unique filename. Also, there is a new DOS file
- create call that will fail if a file of the same name already exists. You
- should use these facilities."
-
- If your current program uses the old-fashioned FCB (file control block)
- method of opening and closing files, you should convert your program to the
- newer (MS-DOS 2.0 or later) calls for file handling. Otherwise, files may
- not be closed properly, which would prevent other people from accessing
- them. This was typical of programs ported over from CP/M or written for MS-
- DOS 1.x. For WordStar(R) and dBASE II(R), for example, the use of FCB was
- the only way to open and close files.
-
-
- Multi-user Upgrades
-
- More-serious philosophical and logistical considerations come into play when
- the developer intends to provide real simultaneous multi-user access to data
- files on the LAN. A deeper understanding of some technical aspects of data
- sharing and real-world LAN usage is necessary. Although MS-DOS 3.1, with its
- extended function calls that are available through Int 21, provides all the
- control necessary for most typical LAN-based data-sharing scenarios, using
- the calls properly and effectively takes some planning and some reading of
- the MS-DOS 3.x Technical Reference manual.
-
- The developer should determine how many users would typically be sharing
- data at one time. How many files would actually be involved? Where might
- bottlenecks crop up in the system? Every command or function of the
- single-user version of the program must be closely scrutinized to determine
- the demands that the multi-user environment will introduce.
-
- To assist in the upward migration process and encourage developers to write
- for MS-Net, Microsoft offers an Independent Software Vendor (ISV) kit for
- $50. The kit includes a variety of published material about MS-Net, along
- with the programmer's reference manual (normally $50) and a selection of
- applications notes.
-
-
- Microrim's R:base(R)
-
- As a case in point, I've investigated the migration of Microrim's R:base
- from single-user PC-DOS 2.x to the multi-user MS-Net environment. (Figure 1
- shows R:base on a multi-user network.) Since Microrim essentially used the
- same network interface module for its System V, the strategies apply to that
- product as well.
-
- As is typical of many database companies, Microrim postponed its multi-user
- implementation until the Microsoft/IBM standard began to take hold and users
- asked for a multi-user, "record-locking" version of R:base. At this point,
- Microrim began the work, which, according to Collin Miller, Microrim's
- director of product development, required about 8 person-months to complete.
-
- The first task was to understand the distinction between true multi-user
- environments and a PC-based LAN, and what impact that would have on product
- design.
-
- "A true multi-user environment employs some central intelligence to control
- traffic flow through a database," says Miller. "The central control, which
- would run in the server, would know which database and record each user was
- using at any given time, on all nodes. On PCs, users are really only sharing
- the same data, not the R:base program itself. R:base is running individually
- in each user's PC."
-
-
- More Intelligence?
-
- Microrim considered writing a central module that would be RAM-resident in
- the server to better arbitrate who was using files and records. "That would
- have given us something more approaching the classical multi-user system,"
- says Miller. "This way we could have established levels of priorities for
- individual user access. We opted not to do that, just on philosophical
- bases."
-
- There were several reasons for this choice. First, writing a multi-user
- system would have added extra levels of complexity for PC users. "PC users
- don't want to get into the degree of complication necessary in really
- sophisticated multi-user databases," says Miller. "We simply wanted
- something that ran as easily as the existing R:base program. Second, this
- meant the single-user version of R:base is essentially identical to the
- multi-user version in both user interface and data file structure, making it
- easier to maintain compatibility for users."
-
- Also, Microrim felt that attempting to write a true multi-user system runs
- counter to the IBM philosophy of PCs──that everybody has his own processor
- and copy of the program running independently. IBM encourages developers to
- keep things as station-independent as possible to prevent unnecessary
- complications.
-
-
- MS-DOS vs. NetBIOS
-
- There are primarily two levels of compatibility to which a network
- application can be written──MS-DOS 3.1 and the NetBIOS (Network Basic Input
- Output System). The DOS level handles most file and record operations
- adequately enough for most purposes and acts on a higher level than the
- NetBIOS, which is the interface between MS-Net/MS-DOS and the particular
- networking card.
-
- "Someone like Microrim has a choice. They can write to DOS level calls, or
- to the lower-level NetBIOS interface," says Dave Melin, product marketing
- manager for networking products at Microsoft. "Both are standards and both
- are popular, but the official recommendation from IBM and Microsoft, as is
- always the case, is to write to the highest possible level in terms of
- programming interfaces. If MS-DOS will do what you need to do, use it. If
- you do need to go below it for some reason such as concurrency or
- proprietary security, then go ahead, but most applications won't require
- it."
-
- It is a common belief that writing to the NetBIOS provides a performance
- advantage. This probably comes from programmers' experience that
- circumventing some other MS-DOS calls by writing directly to hardware does
- increase speed. According to Melin,
-
- "It is true that performance may be improved under the right conditions by
- bypassing the DOS and writing to the NetBIOS directly. By writing to the
- NetBIOS, programmers have more control over network session──related
- parameters. Of course, they must deal with the added complexity of writing
- to a low-level interface. Some people are writing software that doesn't
- require any networking operating system, such as MS-Net, to be present.
- Primarily to minimize memory requirements, they do not want to require the
- presence of all that networking software, such as the redirector. They want
- to provide all the services themselves within their application. In this
- case, they will likely write directly to the NetBIOS."
-
- However, there are hazards involved when writing directly to the NetBIOS,
- particularly because contention management at the NetBIOS level is weak. At
- this level, applications can bump into each other if the NetBIOS is not
- handled very carefully. Considering that this is what DOS is really for, it
- makes sense to employ it whenever possible. "If there is no reason to use
- the NetBIOS, it's not worth it," says Melin. "Besides, the MS-DOS interface
- is what stays stable between revisions, whereas lower-level things have a
- history of changing, jeopardizing your compatibility over time."
-
- In light of these facts, Microrim intentionally worked exclusively on the
- DOS level in implementing the R:base upgrade. Also, it wanted to keep the
- number of extended MS-DOS calls to a minimum. Fewer than five new calls were
- used, according to Miller.
-
-
- Avoiding Corruption
-
- More troublesome to Microrim were concerns over its implementation of file
- and record handling within R:base. Because of the peculiarities of R:base
- files, Microrim faced some unique roadblocks when it was planning this angle
- of its strategy. Unlike dBASE, for example, where database files are
- essentially standalone entities, a series of R:base data files is controlled
- by two more-complex files. The first one (file 1) contains the "schema" or
- database structure, information about the size and layout of each table and
- their relationships to one another in a multitable database. The actual data
- of all related tables is stored in a second, larger file (file 2).
-
- Regulating traffic flow through these two files posed some interesting
- challenges for network implementation on MS-Net. For example, say two people
- attempt to change the structure or information about the number of records
- in a table in the database, that is, modifying file 1. Normally, the
- approach would be to lock the file temporarily for use by the person
- changing it. But this file could bring other users to a screaming halt
- because access to any of the tables in file 2 requires access to file 1
- first. "We wanted to have a bottleneck in file 1, since everyone has to read
- it," says Miller. "We wanted to lock it as little as possible, so we had to
- come up with some other solutions."
-
- Microrim decided that four discrete levels of locking were necessary for the
- amount of flexibility desired and for the type of command a user might be
- issuing.
-
- The first level is the Database lock. It essentially allows a single user to
- have exclusive read/write access to selected files. This lock is necessary
- because it allows for modification of file 1 and packing (eliminating
- unwanted records from) a database. Assurance of single-user access is
- mandatory for these two activities, since the database is often
- significantly reorganized during these periods, and these processes may take
- some time.
-
- The next level is used for a very quick update to the schema (part of
- file 1) and is called a Schema or combination lock. Microrim offers this
- as an alternative to the complete lockout mentioned above. Examples of
- situations in which this lock would be used include defining a new relation,
- joining tables, or doing a database "add" or "subtract," that is, creating
- a new database that is a subset of an existing one. At this level, file 1
- is momentarily locked and then quickly released for use by others. The new
- table(s) being built is/are the only thing(s) locked.
-
- The third level is a Table level lock, allowing for a load or a browse/edit
- on one table in a database. Loading allows a user to make a batch update to
- a table; browse/edit allows a user to edit a screenful of records at one
- time. In either case, other users are allowed read/write access to other
- tables in the database, and may read, but not write to, the locked table.
-
- Finally, Microrim incorporated a Record level lock, which it calls
- concurrency control, for use in managing individual records under multi-user
- table access. (See Figure 2.) Normally, this would be done by using MS-DOS's
- range-locking function.
-
- "MS-DOS 3.1 allows you to lock a record," says Microrim's Collins. "But if
- it's locked, other users have no access to it for any reason, even for
- reading. If you're editing a record in a table and go out to lunch, everyone
- else is locked out of that record. Let's say someone is running a report on
- outstanding orders, while someone else has a record locked in the same order
- table. The report could be inaccurate because one record wasn't accessible.
-
- "You could call this technique collision avoidance, to use networking
- terminology, because it prevents the possibility of collisions. But we
- wanted everyone to always be able to read the database, so we developed a
- collision detection scheme."
-
- Microrim's detection technique operates only if two or more users actually
- do try to alter the same record simultaneously. The scheme keeps track of
- where each user is in the database, allowing access to data records at any
- time. Two people can even be reading or editing the same record at the same
- time. Only for the split second that a user actually writes the change out
- to disk is the record locked. If a change is actually made to the record by
- one user, the second user is then notified that a change has been made since
- his last read. At this point, the second user may want to reread it before
- making his edit.
-
- "Actually, the likelihood of two or more users editing the same record at
- the same time is not that great when you only have five or six users on the
- network," says Miller. "With a small number of users, collision detection is
- fine. But when you try to support a larger number of users, collision
- avoidance would be the better choice, because collision detection begins to
- slow down exponentially with increases in retries."
-
- Microrim's attitude is that collision avoidance comes with a high price tag,
- because it prevents concurrent access to the database records. However,
- Microrim admits that it's a scheme that works well, particularly with a
- large number of users on the network. "By contrast," notes Miller, "our
- [collision detection] scheme has more overhead during a collision. The
- second person has to physically resolve the collision himself, through user
- intervention."
-
- Karl Schulmeisters, software design engineer for systems software at
- Microsoft, says that the exclusive locking procedure Microrim has
- circumnavigated to some degree, is a protective mechanism and intentionally
- designed into MS-DOS to avoid loss of data integrity and to prevent
- incomplete data from being transferred to different users. "There are pros
- and cons to Microrim's solution," Schulmeisters notes. "You really only have
- to lock a record for a brief moment during a read or write of a critical
- area." (See Figure 3.)
-
-
- Problems and Fixes
-
- There are three other more or less generic problems that developers
- have to consider addressing when upgrading their applications. The
- first of these could be called queuing. If a number of users want
- access to a particular resource, whether it is a printer, a file, or
- a modem, some arbitration mechanism may be desirable to handle these
- requests are on a first-come-first-served basis.
-
- Related to the queuing question is the problem of waiting. How do you
- control how long a process waits for an unavailable resource? When
- encountering an error, such as a locked record or file, DOS performs error
- retries only for a short period of time. But say you want to go to lunch and
- let the machine retry a batch update until the resource is available.
- Multi-user R:base allows the user, or a custom program, to retry for as long
- as 4.5 hours, determined by an R:base command, SET WAIT.
-
- Finally, there is the nefarious "deadly embrace." (See Figure 4.) This
- impasse occurs when one user is waiting for resources, such as data tables,
- that are already in exclusive use by the other user. It can result in an
- endless tie-up, in which your program appears to be hung. Microrim solved
- the deadly embrace problem by providing a command, SET LOCK, that the user
- can issue. The command lets a user lock a number of tables in advance of
- beginning a batch process that would involve all those tables. If the tables
- are not all available, the transaction stops there and gives the user a
- message to that effect.
-
-
- Conclusion
-
- There are many points to consider before embarking on any serious rewrite of
- an application for LAN use, not all of which could be discussed here. I have
- tried to bring up the most salient issues. Keep in mind that the planning
- process is the hardest and most critical stage. You may want to begin by
- analyzing how your program works in single-user mode. Then you should
- consider what control mechanisms would be necessary to guarantee lack of
- data corruption and minimize intrusions by users upon each other.
-
- If your application includes a programming language, as R:base does, you
- have to map out each of the commands in the language and analyze what and
- when resources will have to be locked for each command. If your application
- lacks a programming language, things should be that much easier. All this
- planning may well take up the biggest part of your development time, but it
- should pay off in the long run.
-
- For broader system compatibility, try to stick to the MS-DOS 3 function
- calls, unless you are writing a really esoteric application. Finally, the
- MS-DOS 3.1 Technical Reference manual is complete enough to answer most
- questions. According to Microrim's Collins, "We lived and died by the 3.1
- Technical Reference. I don't think we ever had to phone up Microsoft."
-
-
- Figure 1: This sample network configuration shows several combinations
- typical of networks.
-
- ╔══════════════════ SERVER ╗ ┌────────────────────────────────────────┐
- ║ ┌────────────┐ ║ │ ┌──────────────────────────────┐ │
- ║ │ Database │ ║ │ │ ╔══════════════════════════│══════│════╗
- ║ ┌─┤ directory ├───│──┘ ║ ┌────┐ ┌─────────────┐ ┌─┴──┐ ┌─┴──┐ ║
- ║ │ │ c:\dbfiles ├───│──┐ ║ │ a: │ │ c:\ │ │ g: │ │ e: │ ║
- ║ │ │ (g:) │ ║ │ │ ║ └────┘ │ ┌─┴────┐ │ └────┘ └────┘ ║
- ║ │ └────────────┘ ║ │ │ ║ Local │ Other │ │ └──┬──┘ ║
- ║ │ ┌────────────┐ ║ │ │ ║ Floppy │ directory│ │ Remote ║
- ║ │ │ R:base │ ║ │ │ ║ Drive │ │ │ Drives ║
- ║ ┌─────┐ │ │ System V │ ║ │ │ ║ │ c:\rbfiles │ ║
- ║ │ │ ├─┤ directory ├─────┐│ ║ └─────────────┘ ║
- ║ │ │ │ │ c:\rbfiles │ ║ │ ││ ║ Local Hard Drive ║
- ║ │ │ │ │ (f:) │ ║ │ ││ ╚═══════════════════════ WORKSTATION 2 ╝
- ║ │ C:\ ├─┤ └────────────┘ ║ │ │└───────────────────────────────────┐
- ║ │ │ │ ┌────────────┐ ║ │ │ │
- ║ │ │ │ │ User's │ ║ │ └─────────────────────────────┐ │
- ║ │ │ │ │ private │ ║ │ ┌──────────────────────┐ │ │
- ║ └─────┘ ├─┤ directory ├───┘ │ ╔═════════════════│══════│══════│══════╗
- ║ │ │ c:\user2 │ ║ │ ║ ┌────┐ ┌────┐ ┌─┴──┐ ┌─┴──┐ ┌─┴──┐ ║
- ║ │ │ (e:) │ ║ │ ║ │ a: │ │ b: │ │ e: │ │ f: │ │ g: │ ║
- ║ │ └────────────┘ ║ │ ║ └────┘ └────┘ └────┘ └────┘ └────┘ ║
- ║ │ ┌────────────┐ ║ │ ║ └──┬──┘ └──────┬─────┘ ║
- ║ │ │ User's │ ║ │ ║ Local Floppy Remote ║
- ║ │ │ private │ ║ │ ║ Drives Drives ║
- ║ └─┤ directory ├─────┘ ╚═══════════════════════ WORKSTATION 1 ╝
- ║ │ c:\user1 │ ║
- ║ └────────────┘ ║
- ╚══════════════════════════╝
-
-
- Figure 2: Conflict in Updating. When multiple users are accessing a
- database, they must take care when updating to avoid conflicts.
- As shown above, it is not enough just to facilitate multiple
- access;interlocks must be provided to isolate the entire update
- process.
-
- ┌─────────────┐ ┌─────────────┐
- │ USER 1 │ VALUE IN DATABASE │ USER 2 │
- └─────────────┘ ┌───────────────┐ └─────────────┘
- ┌──────────────────┤ │█
- Read Value │ 100 │█ Read Value
- ┌────────────────┐ │ │█─────────────────┐
- │ Type in change │█ │ 100 │█ │
- │ (subtract 20) │█ │ │█
- │ ┌──────────┐ │█ │ 100 │█ ┌────────────────┐
- │ │ Process │ │█ │ ┌─────────┐ │█ │ Type in change │█
- │ │ change │ │█ │ │ │ │█ │ (subtract 10) │█
- │ │ (100-20) │ │█───────│ │ 80 │ │█ │ ┌──────────┐ │█
- │ └──────────┘ │█ │ │ │ │█ │ │ Process │ │█
- │ │█ │ └─────────┘ │█ │ │ change │ │█
- │ Write │█ │ ┌─────────┐ │█ │ │ (100-10) │ │█
- │ change │█ │ │ │ │█ │ └──────────┘ │█
- └────────────────┘█ │ │ 80 │ │█ ───────┤ │█
- █████████████████ │ │ │ │█ │ Write │█
- │ └─────────┘ │█ │ change │█
- └───────────────┘█ └────────────────┘█
- ████████████████ █████████████████
-
-
- Figure 3: R:base Concurrency Control and Locking
-
- ╓┌───────────────┌──────────────────┌────────────────────────────────────────╖
- Command Control/Lock Description
-
- EDIT form Concurrency Allows concurrency control
- ENTER form for other users and items
- unless a table lock is issued.
-
- Command Control/Lock Description
- APPEND Concurrency and Table lock interrupts
- CHANGE Table Lock concurrency control while
- DELETE ROWS data is modified; table lock
- may also exclude other table
- locks issued after it.
-
- EDIT ALL Table Lock Table lock set if concurrency
- ENTER form control is not in effect for
- FROM filespec any part of the table. When
- LOAD set, the lock excludes other
- FORMS table locks issued after it.
- REPORTS
- SET LOCK ON
- UNLOAD...
- FOR tblname
- VIEW
-
- DEFINE Database Lock Locks all tables and allows no
- EXPAND other locks to be set while it
- Command Control/Lock Description
- EXPAND other locks to be set while it
- RELOAD is in effect. This lock will not
- REDEFINE be set while a table lock is in
- REMOVE effect.
- REMOVE COLUMNUN
- UNLOAD
-
- BUILD Table Lock Used together for some
- FORMS Database Lock operations. While the
- INTERSECT database structure is
- JOIN modified, a database lock is
- PROJECT in effect. After the structure
- RENAME is modified, the database
- REPORTS lock is replaced by a table
- SUBTRACT lock.
- UNION
- VIEW
-
-
- Figure 4: Deadlock can occur when two of more users who have access to some
- tables wait for access to others. Microrim solved this problem
- with the SET LOCK command that lets a user lock a number of
- tables before beginning a batch process.
-
- User 2
- cannot reach
- ┌──────────────────┐ CUSTOMERS
- ┌──────────────┤ CUSTOMERS │ because user 1
- │ │ ───────── │ has locked it.
- │ │ User 1 locks │
- │ │ CUSTOMERS │ X ─────────┐
- │ │ waits for │ │
- │ │ TRANSACTIONS │ │
- │ └──────────────────┘ │
- │ │
- │ │
- │ ┌──────────────────┐ │
- │ │ TRANSACTIONS ├───────────────┘
- │ │ ──────────── │
- │ │ User 2 locks │
- └──────── X │ TRANSACTIONS │
- │ waits for │
- User 1 │ CUSTOMERS │
- cannot reach └──────────────────┘
- TRANSACTIONS
- because user 2
- has locked it.
-
-
- ───────────────────────────────────────────────────────────────────────────
- MS-Net Lock (Function 5CH, Code 00H)
- ───────────────────────────────────────────────────────────────────────────
-
- This excerpt opens a file named DATABASE in Deny None mode and locks two
- portions of it: the first 128 bytes and bytes 1024 through 5119. After some
- (unspecified) processing, it unlocks the same portions and closes the file.
- Function 5CH, Code 00H denies all access (read or write) by any other
- process to the specified region of the file. BX must contain the handle of
- the file that contains the region to be locked. CX:DX (a 4-byte integer)
- must contain the offset in the file of the beginning of the region. SI:DI
- (a 4-byte integer) must contain the length of the region.If another process
- attempts to use (read or write) a locked region, MS-DOS retries three
- times; if the retries fail, MS-DOS issues Interrupt 24H for the requesting
- process. You can change the number of retries with Function 44H, Code 0BH
- (IOCTL Retry).If a program closes a file that contains a locked region or
- terminates with an open file that contains a locked region, the result is
- undefined.Programs should not rely on being denied access to a locked
- region. A program can determine the status of a region (locked or unlocked)
- by attempting to lock the region and examining look at the error code.
-
- Call
- AH = 5CH
- AL = 00H
- BX Handle
- CX:DX Offset of region to be locked
- SI:DI Length of region to be locked
-
- Return
- Carry set:
- AX 1 = Invalid function code
- 6 = Invalid handle
- 33 = Lock violation
- 36 = Sharing buffer exceeded
- Carry not set: No error
- Macro Definition:
-
- OPEN_HANDLE macro path, access
- mov dx,offset path
- mov al, access
- mov ah, 3DH
- int 21H
- endm
- ;
- WRITE_HANDLE macro handle,buffer,bytes
- mov bx,handle
- mov dx,offset buffer
- mov cx,bytes
- mov ah,40H
- int 21H
- endm
- ;
- LOCK macro handle,start,bytes
- mov bx, handle
- mov cx, word ptr start+2
- mov dx, word ptr start
- mov si, word ptr bytes+2
- mov di, word ptr bytes
- mov al, O
- mov ah, 5CH
- int 21H endm
- ;
- UNLOCK macro handle,start,bytes
- mov bx,handle
- mov cx,word ptr start+2
- mov dx,word ptr start
- mov si,word ptr bytes+2
- mov di,word ptr bytes
- mov al,1
- mov ah,5CH
- int 21H
- endm
- ;
- CLOSE_HNDL macro handle
- mov bx,handle
- mov ah,3EH
- int 21H
- endm
-
- stdout equ 1
- ;
- start1 dd 0
- lgthl dd 128
- start2 dd 1023
- lgth2 dd 4096
- file db "DATABASE", 0
- op_msg db " opened.",ODH,OAH
- 11_msg db "First 128 bytes locked.",ODH,OAH
- 12_msg db "Bytes 1024-5119 locked.",ODH,OAH
- u1_msg db "First 128 bytes unlocked.",ODH,OAH
- u2_msg db "Bytes 1024-5119 unlocked."ODH,OAH
- c1_msg db "closed.",ODH,OAH
- handle dw ?
- ;
- begin: open_handle file,01000010b
- jc open_error
- mov handle,ax ; save handle
- write_handle stdout, file,8
- jc write error
- write_handle stdout,op_msg,10
- jc write error
- lock handle,start1,lgth1
- jc lock_error
- write_handle stdout,11_msg,25
- jc write_error
- lock handle, start2,lgth2
- jc lock_error
- write_handle stdout,12_msg,25
- jc write_error
- ;
- ; (Further processing here)
- ;
- unlock handle,start1,lgth1
- jc unlock_error
- write_handle stdout,u1_msg,27
- jc write_error
- unlock handle,start2,lgth2
- jc unlock_error
- write_handle stdout,u2_msg,27
- jc write_error
- close_handle handle
- jc close_error
- write_handle stdout,file,8
- jc write_error
- write_handle stdout,cl_msg,10
- jc write_error
- ;
- ; (Further processing here)
- ;
- open_error ; routine not shown
- write error ; routine not shown
- lock_error ; routine not shown
- unlock_error ; routine not shown
- close_error ; routine not shown
-
- ████████████████████████████████████████████████████████████████████████████
-
- Expanded Memory: Writing Programs That Break the 640K Barrier
-
- Marion Hansen, Bill Krueger, and Nick Stuecklen
-
- When the size of conventional memory was set at 640K, that seemed like all
- the memory that anyone with a PC could ever use. But as programs written for
- DOS grew larger, and the amount of data they could handle increased, what
- had once seemed inexhaustible pinched like a pair of size 8 shoes on size 10
- feet. Swapping to disk, or the use of overlays, is a solution, but it often
- limits performance to unacceptable levels.
-
- That's why Lotus Development Corp., Intel Corp., and Microsoft Corp. got
- together to do something about DOS's 640K memory limit. Together they came
- up with the Lotus/Intel/Microsoft Expanded Memory Specification (EMS). The
- programming examples accompanying this article use the EMS and will run
- under the AST Research Enhanced Expanded Memory Specification (EEMS), a
- variation of the EMS, as well.
-
- Expanded memory is memory beyond DOS's 640K limit. Just as DOS manages
- conventional memory, the Expanded Memory Manager (EMM) manages expanded
- memory. The EMM can manage up to 8 megabytes (MB) of expanded memory.
- Programs that adhere to the EMS can use expanded memory without fear of
- conflict.
-
- Contrary to what you may have heard, you can put code as well as data into
- expanded memory. Programs can store anything in expanded memory except their
- stacks, which should reside in conventional memory. While placing the stack
- in expanded memory is theoretically possible, managing a paged stack is
- generally very difficult.
-
- Expanded memory is implemented in one of two ways. One way is an expanded
- memory board, where expanded memory physically resides on an add-in board.
- Intel's Above(TM) Board and AST's Advantage(TM) are examples of expanded
- memory boards. The other way is a LIMulator, such as the Compaq Deskpro
- 386's CEMM (Compaq Expanded Memory Manager), running on a 386-based system.
- A LIMulator emulates expanded memory in extended memory (which is memory
- from 1MB to 16MB) using the 80386 paging hardware.
-
- Application programs can't use expanded memory automatically. This article
- explains how to write programs that take advantage of expanded memory,
- including programming techniques and examples, and the EMM functions.
-
-
- Expanded Memory
-
- In the current DOS environment, code and data can reside in one of three
- memory locations. Each memory type has advantages and disadvantages.
-
- Conventional Memory: Conventional memory is always available, except
- whatever is used by application programs and resident software, and it's
- easily accessible. Moving about in conventional memory, whether through code
- or data, requires very little overhead. Segment register updates (when the
- software crosses segment boundaries) are the only substantial software
- overhead. Segment register updates are common to all three types of memory
- and as such are not a limitation unique to conventional memory. Conventional
- memory's drawback is its 640K limit. Large application programs, network
- software, and resident spelling checkers, to name just three types of
- software a typical user might have, consume prodigious amounts of
- conventional memory.
-
- Disk Memory: There's more than enough room on a disk for any software,
- but the constant paging in and out of data and code in even the simplest
- applications creates a great deal of overhead. This makes disk memory
- undesirable for speed-sensitive applications.
-
- DOS is not re-entrant, and you can invoke a terminate-and-stay-resident
- (TSR) program in the middle of a DOS function. For this reason, TSR programs
- sometimes have difficulties using DOS for disk I/O.
-
- Expanded Memory: Like conventional memory, expanded memory is nearly always
- available. And with fully populated expanded memory boards, it is sufficient
- for most applications. Accessing expanded memory requires slightly more
- overhead than accessing conventional memory but significantly less overhead
- than accessing disk memory. When an application stays within a single 64K
- page, expanded memory overhead is comparable to conventional memory
- overhead.
-
- Expanded memory is especially suitable for four types of software: TSR
- programs, graphics packages, databases, and network software.
-
- TSR programs permanently consume the memory they occupy. If a TSR program is
- large in code or data, it consumes a great deal of conventional memory. A
- TSR program that is designed to use expanded memory effectively keeps most
- of its code and data in expanded memory, while maintaining a small kernel in
- conventional memory for housekeeping chores, such as trapping interrupts,
- and activating the rest of the TSR program in expanded memory.
-
- Drawing and drafting packages frequently have to maintain multiple copies of
- their graphics bit map. Secondary drawings, double buffers for animations,
- and additional menus are all stored for later retrieval. Because recall
- speed is essential, these bit maps must be maintained in memory. Just one
- monochrome (1 bit per pixel) bit map with 640-by-350 resolution requires
- nearly 28K of storage. Several such bit map copies can eat up conventional
- memory, but they are easily accommodated in expanded memory.
-
- Database programs sort huge volumes of data, typically much more than
- conventional memory are able to handle. Expanded memory can be used to
- store and sort large databases and is much faster than swapping to disk.
-
- Network software creates large tables and volumes of resident data.
- Although network software may be used infrequently──usually just for
- peripheral sharing and file transfers──it can consume up to 50 percent of
- available conventional memory. Putting network software in expanded memory
- frees conventional memory for software that you use more frequently.
-
- Using application software efficiently is a trade-off between the
- convenience of generous amounts of expanded memory and the overhead of
- paging in 64K blocks of it at a time. You should consider two questions
- when deciding whether to use expanded or conventional memory for your
- applications.
-
- First, does the code execute a large number of far calls or jumps relative
- to the time it spends executing other instructions? If it does, put the code
- in conventional memory. If it doesn't, put the code in expanded memory.
-
- Second, does the application's data require segment register initialization
- each time it is accessed? If it does, use conventional memory. If it doesn't
- use expanded memory.
-
- As a rule of thumb, use expanded memory if both the time spent using data or
- executing code and the preparation overhead are large.
-
-
- The Page Frame
-
- Expanded memory is managed the same way, whether it resides on an add-in
- board or is emulated in extended memory. The Lotus/Intel/Microsoft EMS
- defines a 64K segment of memory that resides between 640K and 1MB. This page
- frame is a window into expanded memory (see Figure 1).
-
- Just after the application program starts executing, it allocates a certain
- number of 16K pages of expanded memory for its own use. Four pages of
- expanded memory can be mapped into the expanded memory page frame at one
- time. By mapping pages in and out of the page frame, the program can access
- any area of the expanded memory that it allocated.
-
- The EEMS allows the page frame to reside at any unused memory address
- between 0K and 1,024K. Theoretically, this allows a page frame length of
- 1MB. Practical considerations, such as DOS and application programs, which
- use conventional memory, and the BIOS and ROM on add-in boards, which use
- memory above 640K, restrict the page frame to fewer than the possible 64
- pages. Generally, in a typical AT system with an EGA, the maximum number of
- mappable pages that DOS doesn't rely on is six 16K pages.
-
- When the EMM software is installed, the user selects where in memory (above
- 640K) the page frame resides. The page frame address is user-selectable, so
- that if another device uses memory at a particular address, the user can
- then relocate the page frame.
-
-
- Checking for Memory
-
- Before an application program can use expanded memory, it must determine
- if expanded memory and the EMM are present. There are two methods of
- determining if the EMM is present: the open-handle technique and the
- get-interrupt-vector technique.
-
- Because the EMM is implemented as a device driver, in the open-handle
- technique the program issues an open handle command (DOS function 3FH) to
- determine whether the EMM device driver is present.
-
- In the get-interrupt-vector technique, the program issues a get-interrupt-
- vector command (DOS function 35H) to get the contents of interrupt vector
- array entry number 67H. The pointer thus obtained accesses information that
- tells the program whether the EMM is installed. The get-interrupt-vector
- technique is easier to implement. Most programs can use either technique,
- but if a program is a device driver or if it interrupts DOS during file
- system operations, it must use the get-interrupt-vector technique.
-
-
- Residents, Transients
-
- Application programs that use expanded memory can be classified as either
- resident or transient. A transient application program is resident only as
- long as it executes. When it is finished running, the memory it used is
- available for other programs. Examples of resident application programs
- include spreadsheets, word processors, and compilers.
-
- A resident application program remains in memory after it executes. Resident
- application programs are usually invoked by a hardware interrupt, such as a
- keystroke, or a software interrupt, such as a RAMdisk. Pop-up desktop
- programs, RAMdisk drives, and print spoolers are examples of resident
- application programs.
-
- Resident programs and transient programs handle expanded memory differently.
- Resident programs may interrupt transient programs that might be using
- expanded memory, so resident programs must save and restore the state of the
- page-mapping registers when they use expanded memory.
-
- Transient programs don't interrupt other programs, so they don't need to
- save and restore state. A resident program typically keeps the EMM handles
- assigned to it and the expanded memory pages allocated to it by the EMM
- until the system is rebooted. A transient program, in contrast, should
- return its handle and pages just before it exits to DOS.
-
-
- EMM Functions
-
- The EMM functions, summarized in Figure 2, provide the tools that
- application programs need to use expanded memory. Functions 1 through 7 are
- general-purpose functions. Functions 8 and 9 are for interrupt service
- routines, device drivers, and other memory-resident software. Functions 10
- and 11 are reserved. Functions 12 through 14 are for utility programs.
- Finally, Function 15 is for multitasking operating systems, although it can
- be used for interrupt service routines as easily as Functions 8 and 9.
-
- To use expanded memory, programs must perform these steps in the following
- order:
-
- 1. Check for the presence of the EMM by using the get-interrupt-vector or
- open-handle techniques.
-
- 2. Check whether the EMM's version number is valid (only if the
- application is EMM version-specific)──Function 7 (Get EMM Version).
-
- 3. Determine if enough unallocated expanded memory pages exist for the
- program──Function 3 (Get Unallocated Page Count).
-
- 4. Save the state of expanded memory hardware (only if it is a resident
- program)──Function 8 (Save Page Map) or Function 15 (Get/Set Page Map).
-
- 5. Allocate the number of 16K expanded memory pages needed by the
- program──Function 4 (Allocate Pages).
-
- 6. Map the set of expanded memory pages (up to four) into the page
- frame──Function 5 (Map Handle Page).
-
- 7. Determine the expanded memory page frame base address──Function 2 (Get
- Page Frame Address).
-
- 8. Read/write to the expanded memory segment within the page frame, just
- as you read or write to conventional memory.
-
- 9. Deallocate the expanded memory pages when the program is finished using
- them──Function 6 (Deallocate Pages).
-
- 10. Restore the state of expanded memory hardware (only if it is a memory-
- resident program)──Function 9 (Restore Page Map) or Function 15
- (Get/Set Page Map).
-
- Each EMM function's number is passed in register AX. The EMM will return the
- function's status in the same register.
-
- Programs use Int 67 to invoke the EMM. This works like DOS Int 21: preload
- certain registers and issue an Int 67. All required registers are rigidly
- specified, and certain conventions exist; for example, the AX register
- always returns status.
-
-
- Programming
-
- The following two examples contain programs that have both code and data in
- expanded memory. The first example (written in Microsoft C, Version 3.00)
- illustrates how expanded memory can be used to save and restore data. The
- main program (see Figure 3) calls a series of subprocedures that allocate
- one 16K page of expanded memory, save the video RAM area (the user's screen)
- to expanded memory, clear the screen, and then restore the screen from
- expanded memory. The program assumes the user has a monochrome display
- adapter operating in text mode (nongraphics) and video page zero is
- displayed.
-
- The program contains four subprocedures. The detect_emm subprocedure (see
- Figure 4) determines whether the EMM software is installed. If it is
- installed, the subprocedure returns to the caller. If the EMM software isn't
- installed, the subprocedure generates an error message and exits the
- program.
-
- The get_expanded_memory_page subprocedure (see Figure 6) returns a pointer
- to the expanded memory page and a 16-bit tag or handle associated with that
- page. The subprocedure uses the EMM to allocate a page of expanded memory.
- If an unallocated page exists, the procedure allocates it and maps it in and
- returns the EMM handle that is associated with that page.
-
- The check_status subprocedure (see Figure 5) is called after each EMM
- function to verify that no EMM errors have occurred. The
- release_expanded_memory_page subprocedure (see Figure 7) releases expanded
- memory pages by deallocating the handle associated with those pages.
-
- The second example illustrates one program loading another program into
- expanded memory, which is especially applicable for developers of terminate-
- and-stay-resident (TSR) applications. Both programs are written in Microsoft
- Macro Assembler, Version 4.0.
-
- The first program, expanded_memory_dispatcher_kernel (see Figure 8), loads a
- set of subprocedures into expanded memory, from where they can be invoked at
- any time. The set of loaded subprocedures is called a pseudo-overlay. This
- program loads only one pseudo-overlay and immediately invokes all the
- subprocedures contained in it. You can easily load as many pseudo-overlays
- as you want by allocating additional pages in expanded memory, mapping up to
- four of the newly allocated pages into the page frame, and then loading
- additional pseudo-overlays.
-
- The program has one subprocedure, test_for_EMM (see Figure 9), which
- determines whether the EMM software is installed and returns the appropriate
- status.
-
- The kernel program loads the program OVERLAY.EXE (see Figure 10) into
- expanded memory. A pseudo-overlay can't be larger than 64K because of the
- four-page EMM page frame, so the developer must decompose the program into
- separate modules that contain code or data no larger than 64K. You can have
- up to 8MB of expanded memory and, therefore, up to 128 overlays.
-
- Although the DOS "load overlay" function (DOS function 4B03H) is used to
- load the pseudo-overlays, the code and any data loaded remain resident after
- the load takes place. The subprocedures contained in the pseudo-overlay can
- be accessed by using the list of pointers returned to the kernel by the
- initialization code in the pseudo-overlay.
-
- The pseudo-overlay program has five subprocedures. If the pseudo-overlay
- program is invoked from the command line, then the command_line_entry_point
- subprocedure (see Figure 11) tells the user that this is a pseudo-overlay
- and thus can't be executed.
-
- The initialization subprocedure (see Figure 13) is critical. The kernel
- calls this subprocedure after the program is loaded. The initialization
- subprocedure passes back to the kernel the data segment environment, a count
- of the number of callable subprocedures in the overlay, and a far pointer to
- each subprocedure.
-
- The sum and diff subprocedures are examples of typical applications. The sum
- subprocedure (see Figure 14) adds the numbers in the AX and DX registers and
- displays the result, while the diff subprocedure (see Figure 15) subtracts
- the numbers in the AX and DX registers and displays the result. The
- display_result procedure (see Figure 16) converts the result into
- printable ASCII form and then displays it.
-
- The pseudo_overlay program places data into expanded memory. The data
- segment for the pseudo_overlay program is shown in Figure 12. The common
- data area for both programs is shown in Figure 17.
-
-
- To Get EMS
-
- If you're interested in developing application programs that use expanded
- memory, call Intel for a free copy of the Lotus/Intel/Microsoft Expanded
- Memory Specification. In the continental United States, but outside Oregon,
- call (800) 538-3373. In Oregon, Alaska, Hawaii, or outside the United States
- (except Canada), call (503) 629-7354. In Canada, call (800) 235-0444. For
- more information on the AST EEMS, contact the AST Product Information Center
- at (714) 863-1480.
-
-
- Figure 1: The Lotus/Microsoft EMS defines a 64K segment of memory that
- resides between 640K and 1MB.
-
- 16M┌─────────────────────┐
- │ │
- │ │ ┌───────────┐
- │ │ ┌──┴─────────┐ │
- ≈ Extended Memory ≈ ┌──┴──────────┐ │ │
- │ │ ┌───┴───────────┐ │ │ │
- │ │ │ Expanded │ │ │ │
- 1M├─────────────────────┤ .│ Memory │ │ │ │
- │ Reserved by IBM │ . │ │ │ │ │
- 896K├─────────────────────┤ . │ │ │ │ │
- ├─────────────────────┤ . │ Available to │ │ │ │
- │ 64K Page Frame │. │DOS application│ │ │ │
- ├─────────────────────┤ . │ programs │ │ │ │
- 786K├─────────────────────┤ . │ adhering to │ │ │ │
- │ Reserved by IBM │ . │ LIM Expanded │ │ │ │
- 640K├─────────────────────┤ .│ Memory │ │ ├─┘
- │ Conventional │ │ Specification │ ├─┘
- │ Memory │ │ ├─┘
- │ │ └───────────────┘
- │ Managed by DOS │
- 0K└─────────────────────┘
-
-
- Figure 2: EMM Functions
-
- EMM functions provide the tools that application programs need to use
- expanded memory.
-
- ╓┌────────┌──────────────┌──────────────────┌────────────────────────────────╖
- Function Function AX Action
- Number Name Register
-
- 1 AH: 40 Get Status Returns a status code to tell you
- whether the EMM is present and
- the hardware/software is working
- Function Function AX Action
- Number Name Register
- the hardware/software is working
- correctly.
-
- 2 AH: 41 Get Page Frame Gives the program the location of
- Address the page frame.
-
- 3 AH: 42 Get Unallocated Tells the program the number of
- Page Count unallocated pages and the total
- number of pages in expanded
- memory.
-
- 4 AH: 43 Allocate Pages Allocates the number of expanded
- memory pages requested by the
- program; assigns a unique EMM
- handle to the set of pages
- allocated.
-
- 5 AH: 44 Map Handle Page Maps the specified logical page
- in expanded memory to the
- Function Function AX Action
- Number Name Register
- in expanded memory to the
- specified physical page within
- the page frame.
-
- 6 AH: 45 Deallocate Pages Deallocates the pages currently
- allocated to an EMM handle.
-
- 7 AH: 46 Get EMM Version Returns the version number of the
- EMM software.
-
- 8 AH: 47 Save Page Map Saves the contents of the page
- mapping registers of all expanded
- memory boards.
-
- 9 AH: 48 Restore Page Map Restores the contents of the page
- mapping registers.
-
- 10 AH: 49 Reserved.
-
- Function Function AX Action
- Number Name Register
- 11 AH: 4A Reserved.
-
- 12 AH: 4B Get EMM Handle Returns the number of active EMM
- Count handles.
-
- 13 AH: 4C Get EMM Handle Returns the number of pages
- Pages allocated to a specific EMM
- handle.
-
- 14 AH: 4D Get All EMM Returns the active EMM handles
- Handle Pages and the number of pages allocated
- to each one.
-
- 15 AH:4E; AL:00 Get/Set Saves and restores the mapping
- AH:4E; AL:01 Page Map context of the active EMM handle.
- AH:4E:AL:02
-
-
- Figure 3: Main Program
-
- The main program allocates one 16K page of expanded memory, saves the video
- RAM area to expanded memory, clears the screen and then restores the screen
- to expanded memory.
-
- #include <dos.h>
- #include <stdio.h>
-
- #define EMM_INT 0x67 /* EMM interrupt number */
- #define GET_PAGE_FRAME_BASE 0x41 /* EMM func = get page frame base address *
- #define GET_FREE_COUNT 0x42 /* EMM Func = get unallocated pages count *
- #define ALLOCATE_PAGES 0x43 /* EMM Func = allocates pages */
- #define MAP_PAGES 0x44 /* EMM Func = map pages */
- #define DEALLOCATE_PAGES 0x45 /* EMM Func = deallocate pages */
- #define GET_INT_VECTOR 0x35 /* DOS func = get interrupt vector */
- #define DEVICE_NAME_LEN 8 /* Number of chars in device driver name
- field */
- #define VIDEO_RAM_SIZE 4000 /* Total bytes in video RAM (char/attr) */
- #define VIDEO_RAM_BASE 0xB0000000 /* Video RAM start address (MDA) */
-
- union REGS input_regs, output_regs; /* Regs used for calls to EMM and DOS */
- struct SREGS segment_regs;
- unsigned int emm_status; /* Status returned by EMM */
-
- main ()
-
- {
- unsigned int i;
- long target_time, current_time;
- char *video_ram_ptr = {VIDEO_RAM_BASE}; /* Pointer to video RAM */
- unsigned int emm_handle; /* EMM handle */
- char *expanded_memory_ptr; /* Pointer to expanded memory */
-
- /* Ensure that the Expanded Memory Manager software is installed on the
- user's system. */
-
- detect_emm();
-
- /* Get a page of expanded memory. */
-
- get_expanded_memory_page (&expanded_memory_ptr, &emm_handle);
-
- /* Copy the current video RAM contents to expanded memory. */
-
- memcpy (expanded_memory_ptr, video_ram_ptr, VIDEO_RAM_SIZE);
-
- /* Clear the screen to nulls. */
-
- memset (video_ram_ptr, '\0', VIDEO_RAM_SIZE);
-
- /* Delay for 1 second so the user can see the blanked screen. */
-
- time (¤t_time);
- target_time = current_time + 1;
- while (current_time < target_time)
- {
- time (¤t_time);
- }
-
- /* Restore the video RAM contents from expanded memory. */
- memcpy (video_ram_ptr, expanded_memory_ptr, VIDEO_RAM_SIZE);
-
- /* Deallocate the expanded memory page */
-
- release_expanded_memory_page (emm_handle);
-
- exit(0);
- }
-
-
- Figure 4: Detect_EMM Subprocedure
-
- The detect_emm subprocedure determines whether the EMM driver software is
- installed.
-
- detect_emm ()
-
- {
- static char EMM_device_name [DEVICE_NAME_LEN] = {"EMMXXXX0"};
- char *int_67_device_name_ptr;
-
-
- /* Determine the address of the routine associated with INT 67 hex. */
-
- input_regs.h.ah = GET_INT_VECTOR; /* DOS function */
- input_regs.h.al = EMM_INT; /* EMM interrupt number */
- intdosx (&input_regs, &output_regs, &segment_regs);
- int_67_device_name_ptr =
- (segment_regs.es * 65536) + 10; /* Create ptr to device name field */
-
- /* Compare the device name with the known EMM device name. */
-
- if(memcmp(EMM_device_name,int_67_device_name_ptr,DEVICE_NAME_LEN) !=0)
- {
- printf ("\x07Abort: EMM device driver not installed\n");
- exit(0);
- }
- }
-
-
- Figure 5: Check_Status Subprocedure
-
- The check_status subprocedure is called after each EMM function to make
- sure that no EMM errors have occurred.
-
- check_status (emm_status)
- unsigned int emm_status;
- {
- static char *emm_error_strings[] = {
- "no error",
- "EMM software malfunction",
- "EMM hardware malfunction",
- "RESERVED",
- "Invalid EMM handle",
- "Invalid EMM function code",
- "All EMM handles being used",
- "Save/restore page mapping context error",
- "Not enough expanded memory pages",
- "Not enough unallocated pages",
- "Can not allocate zero pages",
- "Logical page out of range",
- "Physical page out of range",
- "Page mapping hardware state save area full",
- "Page mapping hardware state save area already has handle",
- "No handle associated with the page mapping hardware state save area",
- "Invalid subfunction"
- };
-
- /* IF EMM error, THEN print error message and EXIT */
-
- if (emm_status != 0) /* IF EMM error... */
- {
- emm_status -= 0x7F; /* Make error code zero-based */
- printf ("\x07Abort: EMM error = "); /* Issue error prefix */
- printf ("%s\n", emm_error_strings[emm_status]);
- /* Issue actual error message */
- exit(0); /* And then exit to DOS */
- }
- }
-
-
- Figure 6: Get_Expanded_Memory_Page Subprocedure
-
- The get_expanded_memory_page subprocedure returns a pointer to the expanded
- memory page and a 16-bit tag or handle associated with that page.
-
- get_expanded_memory_page (expanded_memory_ptr_ptr, emm_handle_ptr)
-
- unsigned int *emm_handle_ptr; /* 16 bit handle returned by EMM */
- char *(*expanded_memory_ptr_ptr); /* Pointer to expanded memory page */
-
- {
- unsigned int page_frame_base; /* Expanded memory page frame base */
- unsigned int physical_page = {0}; /* Physical page number */
-
- /* Get unallocated pages count. */
-
- input_regs.h.ah = GET_FREE_COUNT; /* EMM function */
- int86x (EMM_INT, &input_regs, &output_regs, &segment_regs);
- emm_status = output_regs.h.ah;
- check_status(emm_status); /* Check for errors */
- if (output_regs.x.bx < 1) /* Check unallocated page count */
- {
- printf ("\x07Abort: insufficient unallocated expanded memory pages\n");
- exit(0);
- }
-
- /* Allocate the specified number of pages. */
-
- input_regs.h.ah = ALLOCATE_PAGES; /* EMM function */
- input_regs.x.bx = 1; /* Number of pages to allocate */
- int86x (EMM_INT, &input_regs, &output_regs, &segment_regs);
- emm_status = output_regs.h.ah;
- check_status(emm_status); /* Check for errors */
- *emm_handle_ptr = output_regs.x.dx; /* Get EMM handle */
-
- /* Map the logical page into physical page 0. */
-
- input_regs.h.ah = MAP_PAGES; /* EMM function */
- input_regs.h.al = 0; /* Logical page number */
- input_regs.x.bx = physical_page; /* Physical page number */
- input_regs.x.dx = *emm_handle_ptr; /* EMM handle */
- int86x (EMM_INT, &input_regs, &output_regs, &segment_regs);
- emm_status = output_regs.h.ah;
- check_status(emm_status); /* Check for errors */
-
- /* Determine the page frame address. */
-
- input_regs.h.ah = GET_PAGE_FRAME_BASE; /* EMM function */
- int86x (EMM_INT, &input_regs, &output_regs, &segment_regs);
- emm_status = output_regs.h.ah;
- check_status(emm_status); /* Check for errors */
- *expanded_memory_ptr_ptr =
- (output_regs.x.bx * 65536)
- + (physical_page * 16 * 1024); /* Set the expanded memory ptr */
- }
-
-
- Figure 7: Release_Expanded_Memory_Page Subprocedure
-
- The release_expanded_memory_page subprocedure releases the expanded memory
- pages by de-allocating the handle associated with those pages.
-
- release_expanded_memory_page (emm_handle)
-
- unsigned int emm_handle; /* Handle identifying which page set to
- deallocate */
- {
-
- /* Release the expanded memory pages by deallocating the handle
- associated with those pages. */
-
- input_regs.h.ah = DEALLOCATE_PAGES; /* EMM function */
- input_regs.x.dx = emm_handle; /* EMM handle passed in DX */
- int86x (EMM_INT, &input_regs, &output_regs, &segment_regs);
- emm_status = output_regs.h.ah;
- check_status(emm_status); /* Check for errors */
- }
-
-
- Figure 8: Kernel Model
-
- The pseudo-overlay is loaded into expanded memory by the kernel. The
- kernel then calls the initialization procedure within the pseudo-overlay
- that returns a data structure to the kernel. The data structure describes
- the first object that will be located in expanded memory starting at the
- data and extra segments of the pseudo-overlay, the number of subprocedure
- entry points in the pseudo-overlay, and a list of far pointers to each of
- the subprocedures contained in the pseudo-overlay. The developer must
- establish a convention for the sequence of far pointers and what the
- procedures they point to do. Other information could be passed in this
- structure as well, for example, number and types of parameters that are
- required by the subprocedures in the pseudo-overlay. This example uses a
- literal to determine the maximum number of far pointers that may be passed.
- To allocate additional space for a larger number of entries, simply
- increase the value of max_proc_entries. The example assumes a maximum of 64
- entries can be returned.
-
- CODE SEGMENT PARA PUBLIC 'CODE'
- ORG 100h
- ASSUME CS:CODE, DS:DATA, ES:NOTHING, SS:STACK
-
- max_proc_entries EQU 64
-
- pseudo_over_struct STRUC
- proc_data_segment DW ?
- proc_extra_segment DW ?
- proc_entry_count DW ?
- proc_entry_ptr DD max_proc_entries DUP (?)
- pseudo_over_struct ENDS
-
- main PROC NEAR
-
- MOV AX, DATA ; Segment initialization
- MOV DS, AX
-
- check_for_emm_loaded:
- CALL test_for_EMM ; Use the "interrupt vector"
- JE get_emm_page_frame ; technique to determine
- JMP emm_err_exit ; whether EMM is loaded
-
- get_emm_page_frame:
- MOV AH, 41h ; Get the page frame base
- INT 67h ; address from EMM
- OR AH, AH
- JZ allocate_64K
- JMP emm_err_exit
-
- allocate_64K:
- MOV exp_mem_segment, BX ; Allocate 4 pages of expanded
- MOV AH, 43h ; ed memory for this example.
- MOV BX, 4 ; More can be allocated
- INT 67h ; depending on the number of
- OR AH, AH ; overlays to be loaded.
- JZ map_64K ; Actually, in this case,
- JMP emm_err_exit ; only a single page is
- ; required because the example
- ; pseudo-overlay is extremely
- ; small.
- map_64K:
- MOV handle, DX ; Map in the first 4 logical
- MOV CX, 4 ; pages at physical pages 0
- map_pages_loop: ; through 3
- MOV AH, 44h ; logical page 0 at
- MOV BX, CX ; physical page 0
- DEC BX ; logical page 1 at
- MOV AL, BL ; physical page 1
- MOV DX, handle ; logical page 2 at
- INT 67h ; physical page 2
- OR AH, AH ; logical page 3 at
- LOOPE map_pages_loop ; physical page 3
- JE init_load_struct ; If additional overlays were
- JMP emm_err_exit ; required, each overlay
- ; would be loaded after
- ; mapping and a new set of
- ; logical pages would be
- ; mapped at the same
- ; physical pages.
-
- init_load_struct:
- MOV ES, exp_mem_segment ; Initialize pseudo-overlay
- MOV DI, 0 ; environment and procedure
- MOV CX, (SIZE pseudo_over_struct) ; pointer area. This structure
- MOV AL, 0 ; begins at the page
- REP STOSB ; frame segment address.
-
- MOV AX, (SIZE pseudo_over_struct) ; Compute the load address
- ADD AX, 000Fh ; within expanded memory for
- AND AX, 0FFF0h ; the overlay. The address is
- MOV CX, 4 ; rounded up to the next
- SHR AX, CL ; higher paragraph boundary
- ADD AX, exp_mem_segment ; immediately following the
- MOV parm_block.load_segment, AX ; pseudo-overlay environment
- MOV parm_block.reloc_factor, AX ; & procedure pointer
- ; structure. This computation
- ; tion takes into account
- ; the maximum number of
- ; procedure entry points
- ; which the pseudo-overlay
- ; is going to return to
- ; this program.
-
- MOV WORD PTR entry_point[0], 100h ; Build .COM file entry
- MOV WORD PTR entry_point[2], AX ; point
-
- MOV AH, 4Bh ; Load the pseudo-overlay
- MOV AL, 03h ; using the DOS "load
- LEA DX, pseudo_over_name ; overlay" function
- PUSH DS
- POP ES
- LEA BX, parm_block
- INT 21h
- JC emm_err_exit
-
- PUSH DS ; Transfer control to the
- PUSH ES ; loaded pseudo-overlays
- CALL DWORD PTR entry_point ; initialization code
- POP ES
- POP DS
- OR AH, AH
- JZ call_over_procedures
- JMP emm_err_exit
-
- call_over_procedures:
- MOV ES, exp_mem_segment ; As an example of passing
- MOV BX, 0 ; control to a procedure
- MOV DI, 0 ; existing in expanded
- MOV CX, ES:[BX].proc_entry_count ; memory, each procedure
- JCXZ deallocate_exp_memory ; contained in the overlay will
- ; be called in sequence.
- ; Obviously, a single procedure
- ; could be called just as
- ; easily.
-
- pseudo_over_call_loop:
- PUSH BX
- PUSH CX
- PUSH DI
- PUSH ES
- PUSH DS
-
- LDS AX, ES:[BX+DI].proc_entry_ptr
- MOV WORD PTR CS:tp_ent_ptr[0], AX
- MOV WORD PTR CS:tp_ent_ptr[2], DS
-
- MOV AX, 123 ; Pass 2 numbers to
- MOV DX, 23 ; the procedures
-
- MOV DS, ES:[BX].proc_data_segment ; Set up pseudo-overlays
- MOV ES, ES:[BX].proc_extra_segment ; segment environment
- CALL DWORD PTR CS:tp_ent_ptr ; Call each procedure
-
- POP DS
- POP ES
- POP DI
- POP CX
- POP BX
-
- ADD DI, 4 ; Adjust index to the next
- LOOP pseudo_over_call_loop ; procedure (4 bytes long)
- ; pointer & loop till all
- ; have been called
-
- deallocate_exp_memory:
- MOV AH, 45h ; Return the allocated
- MOV DX, handle ; pages to the expanded
- INT 67h ; memory manager
- OR AH, AH
- JNZ emm_err_exit
-
- exit:
- MOV AH, 4Ch ; Return a normal exit code
- MOV AL, 0
- INT 21h
-
- emm_err_exit:
- MOV AL, AH ; Display the fact that
- MOV AH, 09h ; an EMM error occurred
- LEA DX, emm_err_msg ; Go to the normal exit
- INT 21h
- JMP exit
-
- tp_ent_ptr DD ? ; CS relative far pointer
- ; used for transfer to the
- main ENDP ; procedures in the
- ; pseudo_overlay
-
-
- Figure 9: Procedure to Test for the Presence of EMM
-
- This procedure tests for the presence of the EMM in the system. The carry
- flag is set if the EMM is present. The carry flag is clear if the EMM is not
- present.
-
- test_for_EMM PROC NEAR
-
- MOV AX, 3567h ; Issue "get interrupt vector"
- INT 21h
-
- MOV DI, 000Ah ; Use the SEGMENT in ES
- ; returned by DOS, place
- ; the "device name field"
- ; OFFSET in DI.
-
- LEA SI, EMM_device_name ; Place the OFFSET of the EMM
- ; device name string in SI,
- ; the SEGMENT is already in DS.
-
- MOV CX, 8 ; Compare the name strings
- CLD ; Return the status of the
- REPE CMPSB ; compare in the ZERO flag
- RET
-
- test_for_EMM ENDP
-
- CODE ENDS
-
-
- Figure 10: Pseudo-overlay Module
-
- The kernel loads the pseudo-overlay into expanded memory.The kernel calls
- the initialization procedure within the pseudo-overlay. The initialization
- procedure returns a data structure to the kernel. The data structure
- describes the first object that will be located in expanded memory starting
- at the page frame segment address. It contains the data and extra segments
- of the pseudo-overlay, the number of subprocedure entry points in the
- pseudo-overlay, and a list of far pointers to each of the subprocedures
- contained in the pseudo-overlay.
-
- CODE SEGMENT PARA PUBLIC 'CODE'
- ASSUME CS:CODE, DS:DATA
- ORG 100h
-
- actual_proc_entries EQU 2
-
- overlay_entry_struct STRUC
- proc_data_segment DW ?
- proc_extra_segment DW ?
- proc_entry_count DW ?
- proc_entry_ptr DD actual_proc_entries DUP (?)
- overlay_entry_struct ENDS
-
-
- Figure 11: Procedure to Identify Overlay
-
- This procedure merely informs a user that this is the overlay and cannot be
- executed from the command line.
-
- command_line_entry_point PROC NEAR
-
- MOV AX, DATA ; Set up local data
- MOV DS, AX ; segment
-
- LEA DX, overlay_err_msg ; Display overlay error
- MOV AH, 09h ; message
- INT 21h
-
- MOV AX, 4C00h ; Exit back to DOS
- INT 21h
-
- command_line_entry_point ENDP
-
-
- Figure 12: Data Segment for the Pseudo-overlay Module
-
- This is the data segment for the pseudo-overlay program.
-
- DATA SEGMENT PARA PUBLIC 'DATA'
-
- sum_msg DB 0Dh, 0Ah, 'Sum of numbers = ', '$'
- diff_msg DB 0Dh, 0Ah, 'Difference of numbers = ', '$'
- overlay_err_msg DB 'Overlay cannot be executed via the command line$'
- powers_of_ten DW 10000, 1000, 100, 10, 1
-
- DATA ENDS
-
- END command_line_entry_point
-
-
- Figure 13: Pseudo-overlay Data Structure Initialization Procedure
-
- The initialization subprocedure is called by the kernel after the program is
- loaded. It passes to the kernel the data segment environment, a count of the
- number of callable subprocedures in the overlay, and a far pointer to each
- subprocedure.
-
- initialization PROC FAR
-
- MOV AX, DATA ; Set up a local
- MOV DS, AX ; data segment
-
- MOV AH, 41h ; Get the page
- INT 67h ; frame segment
- OR AH, AH ; address from EMM
- JNZ error
-
- MOV ES, BX ; Create pointer
- MOV DI, 0 ; to the page frame
- ; segment address
-
- MOV ES:[DI].proc_data_segment, DS ; Return local data
- MOV ES:[DI].proc_extra_segment, DS ; & extra segment
- ; back to the kernel
-
- MOV WORD PTR ES:[DI].proc_entry_count, 2 ; Return the number
- ; of callable
- ; procedures
- MOV WORD PTR ES:[DI].proc_entry_ptr[0], OFFSET sum ; Return
- MOV WORD PTR ES:[DI].proc_entry_ptr[2], SEG sum ; pointer to each
- MOV WORD PTR ES:[DI].proc_entry_ptr[4], OFFSET diff ; local callable
- MOV WORD PTR ES:[DI].proc_entry_ptr[6], SEG diff ; procedure in the
- ; pseudo-overlay
- ; back to kernel
-
- exit: MOV AH, 0 ; Set status in AH
- ; = passed
- error: RET ; Return status
- ; in AH
-
-
- Figure 14: Procedure to Add AX and DX
-
- This procedure adds AX and DX and displays the result.
-
- sum PROC FAR
- ADD AX, DX ; Add numbers
- PUSH AX ; Display sum message
- LEA DX, sum_msg
- MOV AH, 09h
- INT 21h
- POP AX
- CALL display_result ; Display sum
- RET
- sum ENDP
-
-
- Figure 15: Procedure to Subtract AX and DX
-
- This procedure subtracts AX and DX and displays the result.
-
- diff PROC FAR
-
- SUB AX, DX ; Subtract numbers
- PUSH AX ; Display difference message
- LEA DX, diff_msg
- MOV AH, 09h
- INT 21h
- POP AX
- CALL display_result ; Display difference
- RET
-
- diff ENDP
-
-
- Figure 16: Procedure to Display Number in AX in Decimal
-
- This procedure displays the number in AX in decimal
-
- display_result PROC NEAR
-
- LEA DI, powers_of_ten
- MOV CX, 5
- display_loop:
- XOR DX, DX ; Divide the number passed
- DIV WORD PTR [DI] ; in AX by descending powers of ten
- ADD AL, '0' ; Convert digit to ASCII
-
- PUSH DX ; Output the digit
- MOV DL, AL
- MOV AH, 02h
- INT 21h
- POP AX
-
- ADD DI, 2
- LOOP display_loop
- RET
-
- display_result ENDP
-
-
- Figure 17: Data and Stack Segment for the Kernel and the Pseudo-overlay
-
- This is the common data area for the kernel and psuedo-overlay programs.
-
- DATA SEGMENT PARA PUBLIC 'DATA'
-
- emm_err_msg DB 'EMM error occurred$' ; EMM diagnostic message
- pseudo_over_name DB 'OVERLAY.EXE', 0 ; Name of pseudo-overlay
- EMM_device_name DB 'EMMXXXX0' ; Standard EMM device name
- exp_mem_segment DW ? ; Temp for expanded
- ; memory page frame
- ; segment address
- handle DW ? ; Temp for handle allocated
- ; to the kernel
- entry_point DD ? ; Far pointer to the
- ; entry point for a .COM
- ; file
- parm_block_struct STRUC ; Structure definition
- load_segment DW ? ; for a "load overlay"
- reloc_factor DW ? ; parameter block
- parm_block_struct ENDS
- parm_block parm_block_struct <> ; The actual parameter
- ; block
-
- DATA ENDS
-
-
- STACK SEGMENT PARA STACK 'STACK'
- local_stack DW 256 DUP ('^^')
- STACK ENDS
-
- END main
-
- ████████████████████████████████████████████████████████████████████████████
-
- Keep Track of Your Windows Memory With FREEMEM
-
- Charles Petzold
-
- For the beginning Microsoft(R) Windows programmer, even simple, do-nothing
- Windows programs seem to be forbiddingly long and complex. You may have
- concluded that all Windows applications are monstrous collections of code.
-
- This is not true. Take the program called FREEMEM, for example, a complete
- and useful Microsoft Windows application in fewer than 100 lines of C code.
- The FREEMEM program displays the amount of available free memory in an icon
- at the bottom of the Windows screen. The free memory value is updated every
- second and is consistent with the value that is shown in the MS-DOS
- Executive "About" box.
-
- The short length of FREEMEM helps to clarify the structure of Windows
- applications and allows us to discuss extensively some details of Windows
- programming. And since FREEMEM is certainly an unusual Windows program,
- we'll explore a few of its tricks as well.
-
-
- Overall Structure
-
- FREEMEM.C contains only two functions: WinMain (see Figure 1) and WndProc
- (see Figure 2). Similar functions are found in most Windows applications.
-
- WinMain is the entry point to FREEMEM. WinMain is devoted mainly to
- performing all of the preliminary initialization chores needed to create and
- display a window.
-
- The WndClass structure passed to the RegisterClass function defines the
- window class. The most important item in the WndClass structure is
- lpfnWndProc, which specifies the function within FREEMEM that processes
- messages from Microsoft Windows. This is the WndProc function shown in
- Figure 2.
-
- The CreateWindow, ShowWindow, and UpdateWindow functions all cause Windows
- to generate messages to the WndProc function. Within the WndProc function,
- these messages are identified by names beginning with the letters WM. The
- names are simply macro identifiers defined in WINDOWS.H that conveniently
- hide the actual numbered codes.
-
- WndProc sorts out the messages through a case statement. Only a few of the
- many messages that Windows sends to WndProc are handled directly. The rest
- are sent on to the DefWindowProc function within Windows for default
- processing.
-
- CreateWindow will cause Microsoft Windows to generate a WM_CREATE message.
- ShowWindow usually generates a whole sequence of messages, among them
- WM_SIZE and WM_ERASEBKGND. These messages cause the window to be displayed
- on the screen and the background of the client area that is to be erased.
- When FREEMEM calls the function UpdateWindow, Microsoft Windows generates a
- WM_PAINT message, which instructs WndProc to paint the client area of the
- window.
-
- Following the UpdateWindow call, FREEMEM enters a message loop. The
- GetMessage call retrieves a message from FREEMEM's message queue. If no
- messages to FREEMEM are available, control can pass to another Windows
- application. Currently, Windows' nonpreemptive multitasking guarantees that
- a call to GetMessage is the only time that FREEMEM, in effect, stops
- running. It regains control when FREEMEM's message queue contains some
- messages and other applications' message queues are empty. It's really a
- little more complex than this, but I'll explain it later.
-
- TranslateMessage translates keystroke messages into character code messages.
- Although FREEMEM doesn't care about the keyboard, this translation is
- necessary to provide a keyboard interface with FREEMEM's system menu.
- DispatchMessage then sends the message to the WndProc procedure.
-
- FREEMEM will terminate when GetMessage retrieves a WM_QUIT message from the
- queue. The GetMessage function returns a zero in this case, and FREEMEM
- drops out of the message loop.
-
- So far, this routine is normal for most Windows applications, but let's take
- a look at the details.
-
-
- Creative Use of Icons
-
- Unlike most Windows programs, FREEMEM is intended to be displayed only as an
- icon. Once FREEMEM is running, you can use the keyboard or mouse to open the
- icon into a regular tiled window, but it won't give you any more information
- in that form.
-
- Most Windows applications have static pictorial icons. The programmer
- usually creates these icons with the ICONEDIT utility supplied with the
- Microsoft Windows Software Development Kit. The icon file is referenced by a
- name in the resource script file. When the window class structure is
- constructed, the icon is specified by the statement
-
- WndClass.hIcon = LoadIcon (hInstance, (LPSTR)szAppName) ;
-
- The LoadIcon function loads the icon from the section of the .EXE file where
- all the resources are stored and assigns a handle to it. (I'm assuming here
- that the icon has the same name as the application and that szAppName is a
- pointer to a character string with that name.)
-
- If instead you use the following statement:
-
- WndClass.hIcon = NULL ;
-
- then the application is responsible for drawing the icon. This lets your
- application create a dynamic icon that you can alter. The CLOCK application
- included with Microsoft Windows uses this same technique to display the
- current time even when the window is an icon.
-
- From the program's perspective, a NULL icon is just a tiny window that you
- can draw on in the same way that you draw on the client area of a normal
- window. If you need to know when your application is becoming an icon, you
- can find out the information from the WM_SIZE message. For instance, CLOCK
- takes note of this change so it can eliminate the second hand and update the
- clock every minute in the iconic form.
-
-
- Forcing the Icon
-
- Normally, to execute a Windows application from the MS-DOS Executive, you
- either press the Enter key while the cursor is on the program name, double-
- click the program name with the mouse, or select File Run from the menu. If
- you want to load a program as an icon instead, you just press Shift-Enter
- when the cursor is on the program name or select File Load from the menu.
- However, with FREEMEM, it doesn't matter what you do──it always loads as an
- icon.
-
- Most Windows applications execute the function
-
- ShowWindow (hWnd, nCmdShow) ;
-
- shortly before entering the message loop. The nCmdShow variable is passed to
- the program as a parameter to WinMain. If you RUN a program from the MS-DOS
- Executive, nCmdShow is set as equal to the handle of the window that the
- application is replacing on the display, which is usually the handle of the
- MS-DOS Executive main window. If you LOAD an application as an icon,
- nCmdShow is set equal to SHOW_ICONWINDOW. Your application usually doesn't
- have to figure this out; it simply passes this parameter on to ShowWindow.
-
- However, you don't have to use the nCmdShow variable with ShowWindow. In
- FREEMEM, we use this line instead:
-
- ShowWindow (hWnd, SHOW_ICONWINDOW) ;
-
- This forces the window to appear as an icon regardless of the value of
- nCmdShow.
-
- You can achieve other results with this technique. If you have an
- application that you always want to appear first as a "zoomed" full-screen
- display, you can use
-
- ShowWindow (hWnd, SHOW_FULLSCREEN) ;
-
- You can even force FREEMEM to occupy a particular icon position at the
- bottom of the display. If you replace the existing ShowWindow call in
- FREEMEM with
-
- ShowWindow (hWnd, (int) 0xFF8F) ;
-
- the icon will be positioned in icon slot 15, all the way over at the right
- of the display. This syntax is somewhat obscure, but it is documented in the
- Programmer's Reference manual included with the Software Development Kit.
-
-
- Keeping It an Icon
-
- As I mentioned before, you can easily use the keyboard or mouse to open up
- FREEMEM into a regular tiled window. If you'd like to prevent that and
- ensure that FREEMEM is always displayed as an icon, just add these two lines
- to the WndProc function:
-
- case WM_QUERYOPEN:
- break ;
-
- A reasonable place for these lines would be right above the line that reads
- "case WM_DESTROY." Now when you try to use the mouse to open the icon, the
- icon jumps back to the bottom of the display.
-
- These two lines may not seem to be doing very much, but a closer look will
- reveal what they're up to.
-
- Microsoft Windows will send a WM_QUERYOPEN message to a program when it
- wants to open an icon into a window. The documentation for the WM_QUERYOPEN
- message states that a windows procedure such as WndProc must return zero to
- prevent the icon from being opened. Under normal circumstances, the
- WM_QUERYOPEN message is passed on to the DefWindowProc function, which
- returns a nonzero value. (The DefWindowProc routine is provided with the
- Windows Software Development Kit in the file WINDWP.C.) Windows then opens
- the icon.
-
- With the two lines shown above, however, WndProc will return a value of zero
- for a WM_QUERYOPEN message. So, when Windows asks, "Do you want to be
- opened?", WndProc answers, "Zero," which in this case means "No thanks."
-
-
- The Timer Messages
-
- FREEMEM continues to update its display of free memory even while other
- applications are running. It performs this through the use of the Microsoft
- Windows timer, which sends WM_TIMER messages to the window procedure. In
- this respect, FREEMEM is similar to the CLOCK application. The timer permits
- a form of multitasking without requiring the application to hog precious
- processing time.
-
- In its WinMain function, FREEMEM requests a timer from Windows with the
- following statement:
-
- if (!SetTimer (hWnd, 1, 1000, NULL))
- return FALSE ;
-
- The third parameter to SetTimer indicates that FREEMEM wants a WM_TIMER
- message every 1,000 milliseconds, or once a second.
-
- If SetTimer returns zero, it means that Windows cannot assign a timer to the
- program. If you've ever tried to determine how many CLOCK applications can
- be loaded and running in Windows at the same time, you know that the limit
- is 15. Any additional SetTimer calls will return a zero. Actually, there is
- a way to get more than 15 timers out of Windows, but it's more complex.
-
- If FREEMEM cannot get a timer, the WinMain function must return a zero value
- (the value of FALSE), which simply terminates the application. As you'll
- note from the two other "return FALSE" lines in WinMain, FREEMEM also
- terminates if a previous instance is already running or if FREEMEM cannot
- register the window class. While prohibiting multiple instances of FREEMEM
- from running isn't necessary, there is really no reason to run FREEMEM more
- than once.
-
- If you prefer that FREEMEM tell you why it must terminate when SetTimer
- returns zero, you can instead use the code shown in Figure 3. This is more
- informative, if not exactly friendly.
-
- The second parameter in the SetTimer call is a "timer ID." When WndProc
- receives the WM_TIMER message, the wParam parameter contains this value. By
- using different timer IDs, a Windows application can set multiple timers and
- do different processing for each one. FREEMEM must also use the timer ID to
- relinquish the timer with the KillTimer call when WndProc receives a
- WM_DESTROY message.
-
- WM_TIMER messages are not asynchronous. Specifying 1,000 milliseconds in the
- SetTimer call does not guarantee that the window procedure will receive a
- WM_TIMER message precisely every second. The WM_TIMER messages are placed in
- the normal message queue and synchronized with all the other messages.
-
- If another application is busy for more than a second, FREEMEM does not get
- any WM_TIMER messages during that time. FREEMEM receives its next WM_TIMER
- message from the queue only when the other applications yield control by
- calling GetMessage, PeekMessage, or WaitMessage.
-
- In fact, like WM_PAINT messages, WM_TIMER messages are handled by Microsoft
- Windows as low-priority items. I said before that control passes to other
- applications only when a program's message queue is empty. Actually, if a
- program's message queue contains only WM_PAINT or WM_TIMER messages, and the
- message queue of another program contains any messages other than WM_PAINT
- or WM_TIMER, control will pass to the other application anyway.
-
- Moreover, Windows does not continue loading up the message queue with
- multiple WM_TIMER messages if another application is running during this
- time. In that case, Windows combines several WM_TIMER messages in the
- message queue into a single message so that the application doesn't get a
- bunch of them all at once.
-
- While Windows' handling of WM_TIMER messages is adequate for a program like
- FREEMEM, keep these points in mind if you ever do a clock application like
- the Windows CLOCK. A 1,000-millisecond timer value does not guarantee 3,600
- WM_TIMER messages over the course of an hour. When you get a WM_TIMER
- message, you'll want to determine the real time either with a C function or
- through MS-DOS directly. You can't keep time yourself solely with WM_TIMER
- messages.
-
-
- Calculating Memory
-
- When FREEMEM receives a WM_TIMER message, it must determine the amount of
- free memory. This little chore was the most difficult part of programming
- FREEMEM. I knew the information was available because the MS-DOS Executive
- "About" box showed a free memory value. But Microsoft Windows has around 400
- function calls, and finding the one you need is sometimes difficult.
-
- It turns out that the MS-DOS Executive gets a free memory value by calling
- GlobalCompact with a parameter of zero. Windows applications normally use
- GlobalCompact to generate some free memory from the global heap. If
- necessary, Windows compacts memory and frees up segments marked as
- discardable when GlobalCompact is called. (The global heap is comprised of
- memory outside of the program's local data segment.)
-
- A close reading of the documentation of GlobalCompact reveals that "if [the
- parameter] is zero, the function returns a value but does not compact
- memory." Thus, the value that FREEMEM displays is really a potential free
- memory size rather than actual free memory. It represents how much memory a
- Windows application can get from the global heap if it needs it.
-
- If you're familiar with the HEAPWALK utility included with the Software
- Development Kit, you can spend lots of time attempting to reconcile the
- value displayed by FREEMEM with value displayed by HEAPWALK. It's definitely
- not obvious. In general, you'll find that GlobalCompact returns
- approximately the size of the large chunk of free memory in the middle of
- the memory space maintained by Microsoft Windows, plus some discardable
- segments above that block of free memory. However, it stops short of the
- discardable code segment that contains FREEMEM's code. Obviously,
- GlobalCompact cannot discard FREEMEM's code if FREEMEM is calling the
- function.
-
-
- Drawing the Icon
-
- In FREEMEM, the section of WndProc that handles WM_TIMER messages does not
- itself update the icon display. Instead, the line
-
- InvalidateRect (hWnd, NULL, TRUE) ;
-
- notifies Windows that the contents of FREEMEM's window are now invalid and
- must be repainted. The InvalidateRect function causes Windows to put a
- WM_PAINT message in the message queue. WndProc actually updates the window
- only when it receives this WM_PAINT message.
-
- There are alternatives to this method. Instead of calling InvalidateRect,
- WM_TIMER can update the window directly. It would need to retrieve a
- display context with the statement
-
- hDC = GetDC (hWnd) ;
-
- and would then call GetClientRect and DrawText, just as in the WM_PAINT
- logic, but using hDC as the display context rather than ps.hdc. WM_TIMER
- would finally release the display context with
-
- ReleaseDC (hWnd, hDC) ;
-
- While this is certainly valid, I chose not to do it this way.
-
- WndProc must also process WM_PAINT messages which occur for reasons other
- than an updated free memory value. For instance, if you use the mouse to
- move the FREEMEM icon around the screen, Windows will eventually send
- FREEMEM a WM_PAINT message so that FREEMEM can repaint the icon.
-
- So, we'd either have to duplicate the paint code or move it to a separate
- subroutine. The InvalidateRect call really does this for us by generating
- the WM_PAINT message. It allows us to use the same paint code for all
- painting jobs.
-
- WM_PAINT messages are considered low priority by Microsoft Windows. They
- always go to the back of the message queue and are fetched from the queue
- only when no other message is present. There is a way around this, however.
- After calling InvalidateRect, WM_TIMER could then call
-
- UpdateWindow (hWnd) ;
-
- just as we did in WinMain. This instructs Windows to call WndProc directly
- with a WM_PAINT message, without going through the message queue.
-
- But that's really not necessary here. I wanted to keep FREEMEM operating as
- a low-priority task. If something else was going on in another application,
- I didn't want FREEMEM to hog time continually by repainting the icon.
-
- The DrawText function used by FREEMEM when processing the WM_PAINT message
- is very convenient for simple word-wrapped text. Note that the line within
- the DrawText code that reads
-
- strlen (strcat (itoa (mem, buffer, 10), "K Free"))
-
- does the same thing as the more-common construction
-
- sprintf (buffer, "%dK Free", mem)
-
- However, sprintf is a big function. By using strlen, strcat, and itoa
- instead, we can reduce the size of FREEMEM.EXE by about 3K──after all,
- every little bit helps.
-
-
- Creating FREEMEM
-
- Aside from the FREEMEM.C source code, you need two other files to create
- FREEMEM. The first is the module definition file, which is called
- FREEMEM.DEF (see Figure 4). The FREEMEM.DEF file contains the standard
- information that you'll see in definition files for most small Windows
- applications.
-
- The WINSTUB.EXE program specified in the STUB line of FREEMEM.DEF is
- included with the Software Development Kit. The program runs when you
- execute FREEMEM outside of Windows. It simply displays the message "This
- program requires Microsoft Windows."
-
- If you want to, you can create a normal MS-DOS program that displays a free
- memory value comparable to the value that CHKDSK calculates. If you decide
- to name this normal MS-DOS program DOSFREE.EXE, for instance, you can
- specify
-
- STUB 'DOSFREE.EXE'
-
- in the FREEMEM.DEF file. This little trick permits FREEMEM the use of both
- on the MS-DOS command level and inside Windows. FREEMEM.EXE would thus
- contain two related, but quite dissimilar, programs in one file.
-
- The FREEMEM "make-file" (which is called simply FREEMEM, without an
- extension) is shown in Figure 5. When you execute
-
- MAKE FREEMEM
-
- the FREEMEM.C source code will be compiled and linked with the appropriate
- Windows and C libraries, along with additional information from the
- FREEMEM.DEF module definition file.
-
-
- No Resource Script?
-
- Unlike many sample Windows applications, FREEMEM has no resource script
- file, which is a file with the extension .RC. FREEMEM doesn't need one.
- FREEMEM has no menu, no pictorial icon, and no dialog boxes. The only use we
- would have for a resource script is for storing text strings, but the only
- text string FREEMEM uses is the one with the word "Free" in it.
-
- Of course, for longer programs, the use of the resource script for all text
- strings is highly recommended since it will eventually make translation of
- the program into another language easier. But to tell the truth, loading
- strings stored as resources is a real nuisance for small programs. FREEMEM
- is short enough that translation is not a big problem, and we'd risk being
- meretricious by using a resource script for that single text string.
-
-
- Uses in Development
-
- I like to keep FREEMEM loaded and running in normal Microsoft Windows use
- just to give me an idea of how close Windows is getting to a low-memory
- situation. But it has more value in program development.
-
- If you're developing a Windows application, keep a watch on the FREEMEM icon
- during testing. If the free memory size keeps shrinking, you may be
- neglecting to free up some allocated memory within the program. You may then
- want to call on the HEAPWALK utility, also known as Luke Heapwalker, for
- some assistance in tracking down those orphaned memory blocks.
-
- However, don't assume that FREEMEM will display identical values before an
- application is run and after it terminates. The complexity of Windows'
- memory management makes this unlikely for most large programs.
-
-
- Figure 1: WinMain Function of FREEMEM.C
-
- The first half of FREEMEM.C contains the WinMain function, which performs
- necessary initialization and contains the message loop.
-
- /* FreeMem.C -- Windows application that displays free memory */
-
- #include <windows.h> /* all Windows functions */
- #include <stdlib.h> /* itoa */
- #include <string.h> /* strcat & strlen */
-
- long FAR PASCAL WndProc (HWND, unsigned, WORD, LONG) ;
-
- int PASCAL WinMain (hInstance, hPrevInstance, lpszCmdLine, nCmdShow)
- HANDLE hInstance, hPrevInstance ;
- LPSTR lpszCmdLine ;
- int nCmdShow ;
- {
- static char szAppName [] = "FreeMem" ;
- WNDCLASS WndClass ;
- HWND hWnd ;
- MSG msg ;
-
- if (hPrevInstance)
- return FALSE ;
-
- WndClass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
- WndClass.hIcon = NULL ;
- WndClass.cbClsExtra = 0 ;
- WndClass.cbWndExtra = 0 ;
- WndClass.lpszMenuName = NULL ;
- WndClass.lpszClassName = (LPSTR) szAppName ;
- WndClass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
- WndClass.hInstance = hInstance ;
- WndClass.style = CS_HREDRAW | CS_VREDRAW;
- WndClass.lpfnWndProc = WndProc ;
-
- if (!RegisterClass ((LPWNDCLASS) &WndClass))
- return FALSE ;
-
- hWnd = CreateWindow ((LPSTR) szAppName,
- (LPSTR) szAppName,
- WS_TILEDWINDOW,
- 0, 0, 0, 0,
- (HWND) NULL,
- (HMENU) NULL,
- (HANDLE) hInstance,
- (LPSTR) NULL) ;
-
- if (!SetTimer (hWnd, 1, 1000, NULL))
- return FALSE ;
-
- ShowWindow (hWnd, SHOW_ICONWINDOW) ;
- UpdateWindow (hWnd) ;
-
- while (GetMessage ((LPMSG) &msg, NULL, 0, 0))
- {
- TranslateMessage ((LPMSG) &msg) ;
- DispatchMessage ((LPMSG) &msg) ;
- }
- return (int) msg.wParam ;
- }
-
-
- Figure 2: WindProc Function of FREEMEM.C
-
- long FAR PASCAL WndProc (hWnd, message, wParam, lParam)
- HWND hWnd ;
- unsigned message ;
- WORD wParam ;
- LONG lParam ;
- {
- static int mem, lastmem ;
- char buffer [20] ;
- PAINTSTRUCT ps ;
- RECT rect ;
-
- switch (message)
- {
- case WM_TIMER:
- mem = (int) (GlobalCompact (0L) / 1024) ;
- if (mem != lastmem)
- InvalidateRect (hWnd, NULL, TRUE) ;
- lastmem = mem ;
- break ;
-
- case WM_PAINT:
- BeginPaint (hWnd, (LPPAINTSTRUCT) &ps) ;
- GetClientRect (hWnd, (LPRECT) &rect) ;
- DrawText (ps.hdc, (LPSTR) buffer,
- strlen (strcat (itoa (mem, buffer, 10), "K Free")),
- (LPRECT) &rect, DT_WORDBREAK) ;
- EndPaint (hWnd, (LPPAINTSTRUCT) &ps) ;
- break ;
-
- case WM_DESTROY:
- KillTimer (hWnd, 1) ;
- PostQuitMessage (0) ;
- break ;
-
- default:
- return DefWindowProc (hWnd, message, wParam, lParam) ;
- }
- return (long) 0 ;
- }
-
-
- Figure 3: Alternate SetTimer Logic
-
- This code causes FREEMEM to tell you why it must terminate when SetTimer
- returns zero.
-
- if (!SetTimer (hWnd, 1, 1000, NULL))
- {
- MessageBox (hWnd, "Hey! Too many timers!", NULL, MB_OK) ;
- return FALSE ;
- }
-
-
- Figure 4: Module Definition File FREEMEM.DEF
-
- The module definition file FREEMEM.DEF is used by LINK4 for information
- about the program not implied by the source code.
-
- NAME FreeMem
-
- DESCRIPTION 'Free Memory Display by Charles Petzold'
-
- STUB 'WINSTUB.EXE'
-
- CODE MOVEABLE
- DATA MOVEABLE MULTIPLE
-
- HEAPSIZE 1024
- STACKSIZE 4096
-
- EXPORTS
- WndProc @1
-
-
- Figure 5: FREEMEM Make-File
-
- This file, called simply FREEMEM, without an extension, is the "make-file"
- for generating FREEMEM.EXE from the source code.
-
- # Make file for FREEMEM -- assumes Microsoft C 4.0
- # ------------------------------------------------
-
- freemem.obj : freemem.c
- cl -c -d -Gsw -Os -W2 -Zdp freemem.c
-
- freemem.exe : freemem.obj freemem.def
- link4 freemem, /align:16, /map/line, slibw, freemem
- mapsym freemem
-
- ████████████████████████████████████████████████████████████████████████████
-
- CodeView:Debugging Philosophy And Step-by-Step Technique
-
- David Norris and Michael J. O'Leary
-
- Most MS-DOS programmers at one time or another have had to fire up the MS-
- DOS debugger DEBUG to patch another program or peek into memory. Some have
- actually debugged programs with it.
-
- All kidding aside, DEBUG has its uses, but it is usually woefully inadequate
- for doing any serious debugging, as shown by the many MS-DOS debuggers now
- on the market──we've counted more than a dozen.
-
- While debuggers are second only to editors as a computer "religious" issue,
- arguments often tend to revolve around user-interface issues or "my debugger
- has more features than your debugger" comparisons. Yet these are secondary
- to the main consideration: providing facilities that lend themselves well to
- established debugging techniques. If DEBUG fails, it is because it has no
- commands that adapt themselves to user queries such as "what is the value of
- iter?" and no commands like "stop when iter reaches 10."
-
- During the design of the Microsoft CodeView(TM) debugger, we surveyed these
- debugging techniques and created a requirements document for the "ultimate"
- debugger. By iterating these techniques and requirements, we hope to give
- you a fuller understanding of the complex world of debugging and aid the
- future evaluation of debugging tools, thus providing a debugger yardstick.
- We'll also introduce CodeView's philosophy and evolution, demonstrating its
- strengths and weaknesses with examples. Our goal is not to teach CodeView
- commands and syntax (that's what the manual is for), but rather to give you
- an understanding of the concepts behind the commands.
-
-
- Techniques
-
- The different debugging techniques can be categorized into two major groups:
- internal and external. External debugging implies an inspection of some
- sort, without specific interaction with the program being debugged. Internal
- debugging, on the other hand, requires the use of debugging tools that
- exercise control over the debugged program's environment.
-
- External debugging via code reviews by peers can uncover almost three-
- quarters of all software bugs for some kinds of errors. While the old adage
- about an ounce of prevention applies here, it has been our experience that
- scheduling peer code reviews usually plays second fiddle to meeting other
- product deadlines.
-
- Another external technique is simple post-mortem debugging. What did the
- program output? What should it have output? Simple formatting errors are
- usually found this way.
-
- Embedding debugging aids directly in the program is an anachronism from a
- time when adequate debugging aids were not available. In this case, the
- programmer decides where in the code he wants something done, such as
- testing an assertion (is this pointer valid?) and conditional compilation.
-
- None of these external methods requires any specialized debugging tools. On
- the other hand, internal debugging techniques involve the use of tools that
- give the user varying degrees of control over his program as it executes.
- This usually means a debugger, but includes a class of code-flow analysis
- tools such as profilers, event monitors, and path generators. A debugger can
- be internal to the program or it can be separate. Generally, the latter
- approach is used, as it is more desirable to debug the target program by
- itself, with minimal interference from the debugger.
-
-
- Requirements
-
- Let's examine some of the requirements of a debugger. This list contains
- general ideas about debugging, not specific commands.
-
- Size and Speed Efficiency: Obviously, you want a debugger to take up as
- little space as possible (especially when memory space is at a premium) and
- to be as fast as possible.
-
- Minimum impact on program being debugged: The debugger should not affect the
- behavior of the program it is debugging. As some have discovered (especially
- on unprotected systems, such as MS-DOS), the program may crash by itself but
- run fine when being debugged. This has been called the Heisenberg
- uncertainty principle when applied to debugging──the result being a
- "Heisenbug."
-
- Execution Control: The user should be provided with a wide range of commands
- for controlling the execution of his program, mostly in the form of "trap on
- x." Figure 1 lists the possible event traps.
-
- State Manipulation: Users of debuggers need to be able to meaningfully
- examine the state of a running, suspended, or dead program, including the
- determination of the cause of suspension, procedure call history,
- examination and alteration of code and data in its source form, and
- execution history.
-
- User interface: The debugger should be invisible to the user. The use of the
- debugger should be so comfortable and intuitive that users feel they are
- "in" the program they are debugging. It should be like driving a car──you
- don't drive the car around the corner, you just go around the corner.
-
- Portability: Programmers have met with various degrees of success in making
- debuggers portable. Still, it's always nice not to have to learn a new
- debugger when you move to a new computer system.
-
-
- CodeView Philosophy
-
- Microsoft needed a unified debugging tool strategy. The SYMDEB debugger, a
- symbolic extension of the MS-DOS DEBUG debugger, was partially able to
- fulfill the need for language independence, but attempts to make it portable
- or to separate user-interface code from system-dependent code would have
- been extremely difficult. We realized an entirely new debugger was needed.
-
- We knew we wanted the debugger to be language-independent; one hallmark of
- the Microsoft language series is that it is object-compatible. Therefore, a
- Microsoft debugger should be able to debug whatever programs users could
- come up with. We also needed the debugger to have a portable user interface
- and system-dependent code so that an SDB-compatible debugger could be
- created for the XENIX operating system. (See "ISLAND Design" diagram below)
-
- Above all, we wanted a user interface that everyone could be comfortable
- with. This proved to be the most difficult design task, as every individual
- had his own idea about user interfaces. We went through many iterations of
- the basic screen snapshots before we arrived at the present version.
-
- The main problem was presenting the proper information to the user given the
- limited amount of screen real estate. Thus, there are only two horizontal
- border bars on the basic screen (the original screen had borders around
- every window). In fact, the second horizontal bar, called the dialog bar,
- can be eliminated completely, allowing 22 lines of source information on the
- screen, and more on the EGA. The registers were placed to minimize the
- impact on the source window. Also, a pull-down menu system was used because
- it is a friendly, intuitive interface, and it conserves screen space when it
- is not used.
-
- Finally, we decided that rather than creating a new debugger syntax for
- people to learn, we would adhere to the syntax of the SYMDEB and DEBUG
- debuggers in the dialog window. This caused, and still causes, confusion for
- a number of reasons──mainly because the default radix can change. Our
- difficulty was that while we wanted compatibility with DEBUG commands
- (default radix of hex), we also wanted to use an expression evaluator that
- mirrored C as closely as possible (default decimal radix). To complicate
- matters, the default radix doesn't affect the dump command, which has its
- own set of types (byte, word, unsigned, etc.). We felt that programmers who
- dealt strictly in C might never use the lower-level commands and that
- assembly-language programmers wouldn't be using C expressions.
-
-
- Introducing CodeView
-
- CodeView is currently only available with the Microsoft C Compiler,
- Version 4.00 and Microsoft FORTRAN Optimizing Compiler, Version 4.0,
- although versions are planned for other languages and operating systems.
- After a brief overview of its features we'll go into detail on a few
- specific functions in an actual debugging session.
-
- A sample CodeView screen is shown in Figure 3. Four windows are available
- for display of information: the source window, the dialog window, the
- register window, and the watch window.
-
- The central and most important window is the source window and can be
- thought of as a read-only editor attached to debugging functions. For
- example, the user can scroll and search through source files, and the
- current line and active breakpoints are shown by a background blue bar and
- intense video that are imposed on the source text. Navigation through the
- program, which can be composed of more than one source file, is possible
- through a number of commands. The user can locate a program label, and the
- source window loads and displays the source file containing that label.
-
- The dialog window was our answer to commands that did not lend themselves to
- a simpler windowing scheme, such as variable-length data dumps. The dialog
- window can be thought of as the "glass-teletype" interface to CodeView;
- indeed, a CodeView switch (/T) forces CodeView to act as its predecessor
- SYMDEB did, in that a nonwindowing user interface is presented. The dialog
- window views only a portion of dialog text; scrolling commands that are
- similar to those in the source window can be used to examine data.
-
- The register window simply presents the machine registers. The only command
- available here is the ability to alter a flag value by clicking on it with
- the mouse.
-
- Finally, the watch window is available for viewing variables and data of
- interest to the user, so that it doesn't have to typed over and over. The
- format of the watch window entries is identical to the way they would be
- displayed in the dialog window; also, the command syntax for variable/data
- display between these two windows is orthogonal. For example, "?i" displays
- the variable i in the dialog window, while "w?i" displays it in the watch
- window.
-
- The pull-down menu was designed to mimic the Windows pull-down menu system,
- including the mouse and the keyboard.
-
- Let's look at a debugging session with CodeView in order to demonstrate some
- debugging concepts.
-
-
- Debugging WHERE
-
- WHERE is a utility program written in C (see Figure 2) and compiled with the
- Microsoft C 4.00 compiler, using the -Zi switch to enable CodeView
- information. It takes as arguments one or more program names and tries to
- find them as MS-DOS would try to execute them. Thus, the tool is useful in
- finding out whether the command "foo" would mean "C:\BIN\FOO.EXE" or
- "E:\FOO.BAT."
-
- Knowing that Real Programmers get it right the first time, we'll run WHERE
- and hope it doesn't trash our hard disk.
-
- A>WHERE WHERE.EXE
- WHERE.EXE 6224 10-30-86 11:23a
-
- Let's verify our program's output by using DIR:
-
- A>DIR WHERE.EXE
- WHERE.EXE 6224 10-30-86 11:23p
-
- Obviously, a.m./p.m. is incorrect. A quick glance at the printstats routine
- verifies that there is a typo; "<=" should have been ">=". Again, since
- we're Real Programmers, we'll remember to fix the bug later and forget the
- number of times we have forgotten to fix the bug later.
-
- But we haven't fully exercised WHERE. It should be able to find ambiguous
- names such as "DIR," and perform the name search in the same manner that MS-
- DOS would: .COM, .EXE, then .BAT. Let's try a simple case:
-
- A>WHERE WHERE
- A>
-
- WHERE can't find itself. How about an ambiguous or unambiguous filename
- somewhere in the path (but not in the current directory)?
-
- A>WHERE COMMAND
- A>
- A>WHERE COMMAND.COM
- A>
-
- It looks like we have two bugs; we can try to find them at the same time.
- Let's debug:
-
- A>CV WHERE COMMAND
-
- Figure 4 shows the initial CodeView screen. The register window is initially
- off when debugging C programs, and the watch window is initially empty.
- Typing "t" or clicking on the menu item "Trace!" with the mouse causes a
- blue highlighted line to appear in the source window on line 17, indicating
- the current instruction. Tracing one more time makes line 23 the current
- instruction.
-
- You're probably wondering why the current instruction started one line after
- "main" and why it skipped 5 lines. The C Compiler will only output line
- numbers for those lines that contain executable code. Lines 18 through 22
- are declarations that produce no code, while the declaration in line 23 has
- an assignment. We can show the relationship between source lines and emitted
- code (see Figure 5) by typing "u main", which instructs CodeView to display
- both source-code and assembly-language instructions in the source window.
- Type F3 to return CodeView to source-only mode.
-
-
- Single-stepping
-
- As the program is so brief, let's single-step through the program and see
- what happens. We can put variables of interest in the watch window so we
- don't have to retype them every time we want to see their values. The first
- interesting line is line 32; we can execute up to that line by typing "g
- .32" or by clicking the right mouse button on that line. Single-step by
- pressing the F10 key. To see the result of the getenv function, type
- "?envptr". CodeView displays the value 17552:4636; CodeView evaluated
- "envptr" as a C expression and so returned the value of the pointer (an
- address). The value of the expressions "&envptr", "envptr", and "*envptr"
- are different, and CodeView evaluates them properly. We are interested in
- looking at the null-terminated string pointed to by envptr, so our
- expression should be
-
- >w?envptr,s
-
- to put the string in the watch window (see Figure 6). Pointers and pointer
- expressions in C can be difficult to learn. Inexperienced users may find
- CodeView confusing in this respect, but no more so than writing in C itself;
- CodeView is a great learning tool for this purpose.
-
- The next two lines alter the variables ptr and namebuf, so let's trace the
- code and put the variables in the watch window. This can be done all in one
- line by separating the commands with semicolons:
-
- >t 2;w?ptr,s;w?namebuf,s
-
- The current line will execute the search_for_file function. We can use the
- "binary search," or divide-and-conquer, approach to debugging by stepping
- over the function using the Pstep command:
-
- >p
-
- The screen now looks like Figure 7. Note that both envptr and ptr are
- incorrect. The search_for_file function should not have been able to alter
- these variables, which are local to main. What we need is a way to stop
- execution when the variables change. CodeView accommodates us with the
- watchpoint and tracepoint commands.
-
- We don't know whether the string pointed to by envptr or envptr itself
- changed, so we need to trap on both conditions. First, we can reset the
- state of the program to just before the occurrence of the bug by typing
-
- >l;g .40
-
- To stop execution when envptr itself changes, we can use the watchpoint
- command:
-
- >wp?main.envptr!=4636
-
- We had to specify the function to which envptr belonged since we would be
- accessing the variable outside its normal scope (that of main).
- Alternatively, we could have used the Tracepoint command and typed "tpw
- envptr". In either case, CodeView is somewhat deficient because the user has
- to know either the initial value of envptr or the size of the pointer. We
- can break on the second condition by typing
-
- >tpb *envptr
-
- This command causes a trap when the first character pointed to by envptr
- changes. Now you continue by clicking on "Go!".
-
- After a couple of seconds──CodeView watchpoints and tracepoints can be
- quite slow, as they are emulated in software, although CodeView can utilize
- debugging hardware──the CodeView screen returns at line 82. The code just
- executed was a call to the strcat library routine. Before blaming the run-
- time library, though, let's take a look at its return value in fullpath:
-
- >?fullpath,s
-
- Oops (see Figure 8). The search_for_file function has been appending file
- extensions via strcat ad nauseum; we are overrunning the allocated length of
- the namebuf array and destroying the contents of other local variables of
- main. We know that envptr is being changed; unfortunately, the Microsoft C
- Compiler did not order main's local variables in the order given. In this
- case they are ordered envptr, ptr, found, namebuf, envpath, then
- pathbuf──completely different from the declaration order.
-
- The bug is easily fixed by removing the previous file extension using the
- strchr function just before the end of the file extension loop:
-
- *strchr(fullpath, '.') = '\0';
-
-
- Summary
-
- CodeView is not new. Many of its commands and features have already been
- seen in other debuggers. What is new about CodeView is the number of
- commands and degree of integration that it has provided. What is most
- important is that programmers have a full understanding of the nature of
- debugging and how they can best utilize the language and debugging tools
- available to them.
-
-
- ISLAND Design
-
- The ISLAND source code is divided into three major sections: user interface,
- core code, and system-dependent routines. By linking the core code with a
- given user interface and system-dependent code, a variety of debuggers can
- be created.
-
- ┌──────────────────────────┐
- │ CodeView │
- User Interface ┌───┴──────────────────────┬───┘
- │ SDB │
- ┌───┴──────────────────────┬───┘
- │ │
- │ │
- Core Code │ ISLAND │
- │ │
- │ │
- ├──────────────────────────┤
- │ DOS 2.x/3.x │
- └───┬──────────────────────┴───┐
- │ XENIX-286 │
- System Dependent └───┬──────────────────────┴───┐
- │ XENIX-386 │
- └───┬──────────────────────┴───┐
- │ Others │
- └──────────────────────────┘
-
-
- Figure 1: Debugger Event Traps
-
- Before/After execution of a specific instruction
- Before/After successful branch
- Single-step
- Data/Code Read/Write
- Procedure and Function Prologue/Epilogue
- Cross-Process
-
-
- Figure 2: WHERE Utility Program
-
- /* WHERE - shows where DOS is finding your executable */
- #include <stdio.h>
- #include <time.h>
- #include <sys\types.h>
- #include <sys\stat.h>
- #define NUMEXTS 3
- #define MAXFILELEN 12
- #define MAXPATHLEN 65
- struct stat file_stat;
- char *getenv(char *);
- char *strchr();
- main (argc, argv)
- int argc;
- char **argv;
- {
- int found;
- char pathbuf[MAXPATHLEN];
- char namebuf[MAXFILELEN];
- char *ptr;
- char *envpath;
- char *envptr;
- if (argc < 2) {
- printf("Usage: where filename[.ext]\n");
- exit(2);
- }
- if (!(envptr = getenv("PATH")))
- envptr = "";
- /* Loop thru filenames given */
- while (ptr = *++argv) {
- strcpy(namebuf, ptr);
- /* Try current directory first */
- if (!(found = search_for_file(namebuf, strchr(namebuf, '.')))) {
- /* Loop thru paths in path environment variable */
- envpath = envptr;
- while (*envpath) {
- ptr = pathbuf;
- /* Copy path into buffer */
- while (*envpath && *envpath != ';')
- *ptr++ = *envpath++;
- /* skip path separators */
- while (*envpath == ';')
- ++envpath;
- /* Don't append a '\' if looking thru a root directory */
- if (*(ptr-1) != '\\')
- *ptr++ = '\\';
- *ptr = '\0';
- strcat(pathbuf, namebuf);
- if (search_for_file(pathbuf, strchr(namebuf, '.'))) {
- printstats(pathbuf);
- break;
- }
- }
- }
- else printstats(namebuf);
- }
- exit(found == 0);
- }
- search_for_file (fullpath, specific_file)
- char *fullpath;
- int specific_file;
- {
- int found, next_ext;
- static char *extension[NUMEXTS] = {".COM", ".EXE", ".BAT"};
- if (specific_file)
- found = !stat(fullpath, &file_stat);
- else {
- for (next_ext = 0; next_ext < NUMEXTS; ++next_ext) {
- /* try .com .exe .bat extensions */
- strcat(fullpath, extension[next_ext]);
- if (found = !stat(fullpath, &file_stat))
- break;
- }
- }
- return(found);
- }
- /*
- * print out fullname from passed string, and stats (size, date..)
- * from global struct file_stat.
- */
- printstats (fullname)
- char *fullname;
- {
- struct tm *tmptr;
- tmptr = localtime(&file_stat.st_atime);
- printf("%s \t%ld\t%2d-%2d-%2d\t%d:%02d%c\n",
- strupr(fullname),
- file_stat.st_size,
- tmptr->tm_mon+1,
- tmptr->tm_mday,
- tmptr->tm_year,
- (tmptr->tm_hour < 13 ? tmptr->tm_hour : tmptr->tm_hour-12),
- tmptr->tm_min,
- (tmptr->tm_hour <= 12 ? 'p' : 'a'));
- }
-
- ████████████████████████████████████████████████████████████████████████████
-
- Page Description Languages: High-Level Languages for Printer Independence
-
- ───────────────────────────────────────────────────────────────────────────
- Also see the following illustrations of Page Description Languages:
- Adobe System's PostScript
- A Comparison of Three Page Description Languages
- ───────────────────────────────────────────────────────────────────────────
-
- Steve Rosenthal
-
- As printers get steadily smarter, more and more of them are supporting
- command languages similarly enhanced in power and flexibility. In
- particular, page description languages (PDLs) are gaining popularity as a
- preferred way of telling printers how to put images on paper. More than just
- a simple list of what elements to be printed next, PDLs offer a precise and
- formalized means of controlling printer output. PDL language statements,
- which are normally created and sent to the output device invisibly to the
- end user, can include a combination of operations, objects, dot locations,
- and references to typographic elements such as fonts.
-
- You can, in fact, think of PDLs as high-level languages for printer output,
- compared to the machine language or assembly language approaches that have
- until recently been the rule. Not surprisingly, many of the advantages and
- disadvantages of using a PDL parallel those for using a high-level language
- in other types of programming, and many of the arguments about which PDL is
- best echo similar discussions about the relative virtues of computationally-
- oriented programming languages.
-
- Unfortunately, you can't run out and get all those promised advantages on
- very many devices or from many programs just yet. Of the three major page
- description languages that are likely to become major forces in the personal
- computer market in the immediate future──PostScript, Interpress, and
- Document Description Language (DDL)──only PostScript and Interpress have
- been implemented on any production output devices so far, and only
- PostScript has been implemented on hardware and software for personal and
- desktop computers. So for now, we'll have to settle for an overall
- discussion of why PDLs might be attractive, what's needed in a PDL and its
- implementation, and a description of the three major languages that appear
- to be serious market contenders.
-
-
- Device Independence
-
- Backers of PDLs say that the foremost advantage we can expect from them is
- device independence. Theoretically, an application or system can produce one
- single set of statements in a page language for output on any output device
- that supports that page language, rather than having to produce a different
- version for each brand, resolution level, and technology of output.
-
- Using a PDL is like using a single language, such as BASIC, that works on a
- wide variety of different machines. Even though the internal operation of
- the various printing devices may be different, that's taken care of during
- the interpretation of the PDL statements by each individual printer. From
- the outside, each device seems to be logically equivalent.
-
- Yet having a single logical connection between programs and printers affects
- interfacing in a dramatic way: the number of drivers that must be written is
- reduced to the sum of the programs and printers at issue rather than their
- product. Furthermore, an application written to PDL standards continues to
- support new devices that were not available at the time that the software
- was created──a feat that is at best difficult with more-traditional
- approaches.
-
- While that's the theory, in practice the results aren't quite as pristine.
- In all the announced PDLs, there are some device dependencies that sometimes
- must be considered, just as there often are in programming BASIC or Pascal.
- You can usually take a PDL file meant for one device and output it on
- another, but you may have to clean up a few differences or accept some
- slight artifacts of the retargeting process.
-
- Furthermore, every page description language may not be optimized for all
- new print technologies, so future compatibility cannot really be totally
- guaranteed. Given that most users replace computer equipment when the
- available alternative becomes economically more worthwhile, not when the
- previous generation fails to work any longer, inefficiency can be as much of
- a limiting factor as inability.
-
- Ease of connectability could also vanish as a major advantage unless a
- single page description language, or at least a small set of languages,
- becomes the de facto standard. Right now, several different PDLs are
- competing for market favor, along with several typographic description
- languages that take a more character-oriented and smaller-region view of
- the page.
-
- In addition, all of these languages compete against the virtual device
- approach, which has all applications produce their output as a series of
- calls to functions on a theoretical device. The environment then translates
- these calls to the actual operations needed by each printer. This last
- method, by the way, is the one implemented in most operating environments,
- such as Microsoft's own Windows.
-
- Still, given all these cautions, all it takes to convince most people that
- the PDL approach to universality is worthwhile is one typesetting project on
- the Macintosh. Because many applications on the Mac (including Microsoft
- Word and Microsoft Works) can produce output in PostScript, the same files
- can be run off first inexpensively on the Apple LaserWriter, then, when
- proofed and ready, can be run in finished form on a true high-quality
- typesetting machine from Allied Linotype.
-
-
- Low Overhead
-
- Economy of description is the second major advantage that page description
- languages offer. In all current PDLs, an application can describe intended
- output, where appropriate, as a series of objects rather than characters or
- individual dots and lines. An object-oriented description often takes much
- less time to communicate, as well as less memory buffer space and other
- system resources.
-
- For example, if an application wants to draw a circle in the output with a
- PDL, it generally orders up a circle object of a certain center and radius,
- with the outline done in the current line width setting. In contrast, if it
- wants to draw a circle with ordinary dot graphics, the application has to
- send every point on the circumference to the printer.
-
- The same idea applies to boxes, lines, shading, and even typographic fonts.
- Because the PDL-equipped printer starts with a large store of knowledge
- about elements of graphics and type, an application usually needs to send
- only the pointers rather than the objects themselves.
-
- In most PDLs, fonts are considered a special class of graphics. All the
- regular PDL commands that apply to graphics──such as those governing
- movement, rotation, enlargement, tint, and so on──apply to typographic
- characters as well. In addition, all PDLs have font-handling commands that
- take into account the special nature of type.
-
- On the flip side, being able to work with regular objects is no help at all
- for photographic information, scanned images, or other graphic elements
- that, unlike line drawings, have no simple tonal structure. In fact, if a
- PDL has to communicate such a graphic as a series of dots that require a lot
- of overhead for their expression, it can take the PDL more time and space to
- communicate this type of picture than would a simpler approach.
-
- Division of labor is another benefit that PDLs offer. Since page languages
- describe output in terms of objects, an originating system that contains
- those objects internally does not have to translate from object to ink
- dot──a process called rasterizing. On most printer technologies, rasterizing
- means translating an object into a series of on/off dots placed in a raster
- pattern of successive lines sweeping across the page. Rasterizing is also a
- necessary step in printing various sizes, styles, and weights of type.
-
- However, rasterizing is computationally intensive, and──if the page creation
- sequence and timing are to be decoupled from the printing technology──it
- needs at least one bit of memory for every possible dot placed on the page.
- Using a PDL permits the main processor to generate the prerasterized image;
- the printer can then rasterize and store the resulting bit map.
-
- PDLs that are full programming languages can also let the printer do some of
- the computations that precede rasterization. Since PDL statements can
- describe operations as well as objects, programs have the ability to send
- complex expressions to the output device; these expressions, once they are
- reduced to simpler form, will generate either the described object or the
- parameters of the described object. The output device will then be able to
- do the computations while the main processor proceeds with other work.
-
-
- Three Main Ingredients
-
- Basically, you need three components to implement a PDL in some workable
- form.
-
- The first component is the quality of language itself, which allows it to
- meet a number of criteria pertaining to function. For example, a page
- description language must be clearly and completely defined if it is to be
- used as a consistent standard across multiple machines and systems. It must
- be sufficiently fast and efficient to make the overhead involved in using it
- affordable. If it isn't, developers will simply bypass the PDL to write
- native code drivers for each device.
-
- A workable PDL should also have some sort of hierarchical or layered
- structure. Although not every printer has every feature, users who buy the
- more capable devices want to put any extra functionality to use; hence a
- good PDL must be able to take care of most business with a set of core
- commands and functions, yet still retain the potential to make use of
- additional capabilities.
-
- To be device independent, a PDL must also maintain a universal coordinate
- system visible to the applications program that is translating the points to
- the printer's actual coordinate system for output. The coordinate system
- must be sufficiently large to cover the maximum defined page size at the
- maximum workable resolution of any supported output device.
-
- A successful PDL must also be perceived as acceptable. The primary value of
- any such language depends in large part on its pervasiveness, so any
- language must appeal to a large number of software developers and output
- device manufacturers. Products that are not seen as acceptable, no matter
- how technically proficient, won't reach the critical mass needed to make
- their use worthwhile.
-
- In most cases, the bid for popularity has led to having the PDL languages
- themselves placed into the public domain──that is, the actual verbs and
- structures that comprise the language can be used by anyone, even though the
- programs that create the code or translate it into printed images are
- proprietary.
-
-
- The Interpreter
-
- The second component of the PDL system is the interpreter and printer
- controller, which translate the PDL statements into the actual dot
- information that the printer's marking engine needs to produce the output
- image.
-
- Translation is usually done on the fly by an interpreter (compiling usually
- isn't worthwhile because documents frequently change between every
- printing). Strictly speaking, the output of the interpretation process is
- some result sent back to the host system, but its "side effect" is the
- creation of the actual sequence of bits needed to control the printer
- marking engine.
-
- The major PDLs for the PC market are all based on threaded stack languages,
- so the interpreters and languages all have a very Forth-like flavor. They
- use reverse Polish notation (RPN) and store almost all their data and
- working values on a push-down stack.
-
- Because the computational power needed to make this translation quickly is
- so great, most of the PDL-equipped printers are more high-powered than the
- main system for which they are ostensibly peripherals.
-
- They also need a lot of memory. All current implementations are for page
- printers (where the complete page is translated and stored in dot form
- before the actual marking process begins), and all expect to find a complete
- bit map in which to store the resulting page image. For the current standard
- of 300 dots per inch on an 8 1/2- by 11-inch page, that means slightly more
- than a megabyte.
-
- PDL-based printers also need a large ROM space or a great deal of extra
- memory for downloading the interpreter. A typical PDL interpreter for a
- laser or similar xerographic page printer takes up several hundred
- kilobytes.
-
- The third component, producing the translation code, is also a huge
- challenge. Page description languages are intended as a form of
- communication between program and printer and are not generally designed
- for ease of human use. Instead, applications are expected to produce the
- code to be sent to the printer without any direct human intervention.
-
- The PDL code emitter that most applications require is somewhat more complex
- than a standard printer driver, but still within the reach of most serious
- programmers. For maximum efficiency, the application must specify its output
- as certain types of objects, and there are similar incentives toward
- handling type in more compatible ways.
-
- It is possible to write your own PDL code emitter, and maybe a dozen or more
- firms have already included such a facility in current software products.
- The backers of most PDLs will also write the needed code sections on a
- contract basis or will recommend a firm that does so.
-
- A common code generator can also be shared by multiple applications that run
- in a common operating environment using a virtual device interface. On the
- Macintosh, for example, system and applications programs write to the
- Quickdraw ROM routines, and a single code generator that is loaded as a
- "printing resource" then changes the ROM calls to PostScript code.
-
- Similarly, in Windows and Digital Research's GEM, programs that want to
- write PostScript don't have to have their own embedded PostScript code. A
- system driver turns virtual device interface calls into the code needed for
- a PostScript-based printer.
-
- Given all these requirements, it's not surprising that the major page
- description languages exhibit many similarities. But in the case of these
- three languages, the resemblance is more than coincidental. PostScript,
- Interpress, and DDL are all outgrowths of work done at the Xerox Palo Alto
- Research Center (PARC). So, although the exact syntax and precise list of
- features characterizing each language have diverged because each language
- was developed at a different corporate home, the overall spirit and approach
- are very much the same.
-
-
- PostScript
-
- PostScript, from Adobe Corp., was the first page description language to be
- implemented for personal computer software and peripherals and is still the
- only one that is being delivered with actual commercial products. Although
- its first widely available applications were on the Apple Macintosh and the
- Apple LaserWriter printer, PostScript is now backed by scores of software
- packages on various machines and operating systems, as well as close to a
- half dozen or more different printers.
-
- PostScript is in the public domain, and there is even a functionally
- compatible language/interpreter set combination that can be mixed and
- matched with PostScript sold by Control-C Software of Portland, Oregon.
- Most implementations so far have been done by Adobe.
-
- As a language, PostScript is particularly rich in general programming
- capabilities as well as in graphic and typographic support elements; more
- than 250 PostScript verbs cover arithmetic, logical, and control categories,
- as well as graphics. PostScript is written entirely in ASCII (printable)
- characters, making it easier to debug final code and to send output
- descriptions across simple communications links. On the other hand, this
- slows the transmission of nongeometric images, since they must be translated
- from bit maps to hex representation and back to be sent as printable
- characters.
-
- PostScript supports outline (vector) fonts and has provision for both
- built-in fonts and downloadable supplements. All fonts are defined as one
- single point unit in height and are then scaled to any selected size. The
- PostScript language definition supports color as well as shading, but so far
- all the delivered devices have been monochrome. For creating halftone
- (shaded) images, such as those found in photographs, PostScript includes a
- number of facilities for creating small regular patterns.
-
- PostScript applications can be written to handle documents of most any
- length, but the language won't supply much built-in help for the more
- complex projects. Formatting beyond the structure of a single page, not
- included in the first language definition, is currently implemented as a
- series of structured comment lines.
-
- For debugging or constructing composite projects, PostScript contains a fair
- number of file and input control statements. The AppleWriter PostScript
- interpreter, for example, can be used interactively to let you improve
- various formulations of your procedures.
-
-
- Interpress
-
- Interpress is Xerox Corp.'s nomination as a standard page description
- language. So far, it has been implemented on several larger laser printers
- and on many Xerox minicomputer applications as part of the Xerox Network
- Architecture (XNS) system.
-
- However, applications in the personal computer field should begin to appear
- very shortly. Xerox's Ventura Publisher will support Interpress, as will
- Microsoft Windows. Xerox says that several personal computer printer
- manufacturers plan to announce Interpress support, starting in the first
- quarter of 1987. The language itself is also in the public domain.
-
- Compared with PostScript, Interpress has more facilities for controlling
- overall document structure and distribution but slightly less power for
- general computation. The language can be written in a Forth-like ASCII
- representation, but the actual code sent to Interpress printers is a binary
- representation. Bidirectional translators are available for debugging,
- testing, and learning.
-
- Using binary for Interpress files obviously saves space, but perhaps more
- importantly, it cuts transmission time for complex documents. Because
- Interpress was explicitly designed for use with networks, transmitting a
- file more than once was expected to be the rule rather than the exception.
-
- The most important document control feature is page independence. In an
- Interpress output file, each page is dependent only on an initial header and
- information local to that single page. This feature guarantees that printers
- that actually image in some order other than first through last will produce
- the right output, and many of the higher performance printers that do two-
- sided copying or binding do indeed print documents in various orders.
-
- Interpress also features an explicit mechanism for setting imaging priority.
- On many output devices the order with which overlapping images are laid down
- matters to the final result; on others, printing can be done faster by using
- an order that may be different from the order in which the instructions
- arrive. Hence, Interpress allows the user to specify strict sequencing when
- needed.
-
- As part of an overall network strategy, Interpress also links to a large
- number of complementary output-related Xerox standards. Many of these
- define a standard solution to issues that have yet to be addressed in the
- DOS and Macintosh environments. That includes standards for font handling
- and naming, encoding of scanned and other raster images, and character
- coding for extended character sets.
-
-
- DDL
-
- Document Description Language (DDL) is Imagen Corp.'s entry into the
- standard page description language derby. As the latest of the three
- contenders, and as Imagen's second generation of page description languages,
- it naturally combines features from the prior languages as well as adding
- some innovations of its own.
-
- DDL devices and programs will not be publicly available until sometime this
- year, but agreements between Imagen, Hewlett-Packard, and Microsoft ensure
- the language a substantial launch. It will be Hewlett-Packard's language of
- choice for sophisticated applications speaking to the HP Laserjet printer
- and will be supported as an output driver in Microsoft Windows.
-
- DDL shares with Interpress an enhanced emphasis on overall document
- structure as well as on the geometry of each page and perhaps goes even
- further in this direction. It also makes more-extensive use of caching to
- reduce the time required in repetitively translating objects from
- description to dot form.
-
- Document control includes page independence and an explicit mechanism for
- specifying page order for output. The latter feature is particularly
- important when multiple logical pages are imaged onto each physical page,
- and the resulting sheets have to be folded and assembled into a complete
- document.
-
- Caching attempts to use all available memory, which is constantly getting
- cheaper, to increase the speed of performance. Objects, including both fonts
- and geometric shapes, are held in memory in translated form as long as there
- is room; they can be reused if called again. That cuts down translation time
- substantially for repetitive elements.
-
- DDL files are transmitted in binary, which makes them more compact and
- speedier to transmit than an ASCII representation. Compactness is
- particularly important for scanned images and halftones (photographs), which
- are normally sent as arrays of dots. For debugging purposes, DDL printers
- will also accept an ASCII equivalent.
-
- DDL can also support both bit-mapped and outline fonts. When scaling bit-
- mapped fonts, which are made of lists of actual dot positions, the system
- applies various typographic design rules to produce a more intelligent
- result than does simple geometric multiplication. Besides the standard fonts
- found on both computerized and traditional systems, Imagen has licensed
- several special computer-oriented type families, including a face called
- Lucida that its originators, Bigelow & Holmes, claim is the first designed
- explicitly for electronic printing.
-
- For times when exact dot placement is important, such as in tiny fonts or in
- plotter-type output, DDL guarantees that if a target resolution is defined
- before image creation, dot placement at that same output resolution will
- follow the original exactly without any errors due to translation back and
- forth into universal coordinates. Close positioning control will increase
- greatly in importance when more color devices become available, especially
- if they are able to intermix colors to provide a wide range of hues and
- shades ("process color"). Tight dot positioning control also makes it
- possible to guarantee a close level of correspondence between screen and
- printed images.
-
- Like PostScript, DDL includes a full complement of control structures and is
- extensible, which makes it possible to write routines that handle both
- common and special requirements. While a wide variety of programs can be
- written in DDL, the expected use of this facility is for printer drivers or
- output filters and formatters.
-
-
- Making a Choice
-
- Although end users normally choose a page description language only
- implicitly through their choice of printers, developers and programmers face
- a tougher dilemma. They can pick a single PDL and support it as their output
- language of choice, support multiple PDLs, or rely on an operating
- environment for PDL support.
-
- The problem is made still more difficult by the rising expectations of end
- users. While the first laser printers that supported graphics and
- typographic-style fonts initially seemed sufficient, users are now asking
- for higher performance, faster output, greater detail, and increased ease of
- use.
-
- The makers of PDLs recognize this and are all working on improvements and
- refinements. But because the interpreters are normally implemented in ROM
- firmware, updating a PDL is a major market headache. PDLs are not changed
- very lightly.
-
- Furthermore, optimizing the choice of language itself is not the total
- solution. PDLs are part of a complex web that includes experienced
- programmers, supporting applications, tools, and output devices. The
- availability and popularity of resources and collateral material also
- matter.
-
- Which page description language is the best? Perhaps that question won't be
- settled any more than which programming language should be everyone's
- choice. At some point, it's not technology but theology.
-
- The good news is that developers writing programs for Microsoft Windows do
- not have to choose. Since Windows provides a device-independent interface, a
- developer can write an application that can output to a device using a PDL
- simply by having the appropriate device driver installed. In doing so,
- Windows permits innovation in PDLs while maintaining a standard application
- interface. A PostScript driver is already available, and DDL and Interpress
- drivers are expected to be ready in the near future.
-
- ───────────────────────────────────────────────────────────────────────────
- Adobe System's PostScript
- ───────────────────────────────────────────────────────────────────────────
-
- The "B" cube on page 50 of the printed version was created from the
- sample of Adobe's PostScript Language which follows.
-
- %! PS-Adobe- Adobe Systems Incorporated-Colophon 3 ShowPage Graphics 1986
- %% DocumentFonts: Palatino-Roman
- %% Title: B.cube.ps
-
- % B.cube.ps produces a cube drawn with a character on each face.
- % To change the font subtitute a chosen font for Palatino-Roman in the
- % definition of "masterfont".
-
- /cube {
- /masterfont /Palatino-Roman findfont def
-
- % Letter strings that allow you to assign particular letters to each cube
- face.
- /front exch def
- /leftside exch def
- /back exch def
- /rightside exch def
- /top exch def
- /bottom exch def
-
- % The height of the letter is reduced with respect to its angle. (stan)
- % Height is multiplied by sin/cos.
- % This helps for perspective.
-
- /stan {a sin a cos div sy mul} def
- /getfont {masterfont [sx 0 stan sy 0 0]
- makefont setfont} def
-
- % type (s)ize and (a)ngle of oblique
-
- gsave
- /sy 100 def
- /sx 100 def
- /a 0A def
- getfont
- 40 50 moveto
- .75 setgray
- back show
- grestore
-
- gsave
- /a 45 def
- /sy sy 2 sqrt div def
- /sx 70 def
- getfont
- 0 0 moveto
- .60 setgray
- bottom show
- grestore
-
- gsave
- getfont
- 70 0 moveto
- 45 rotate
- .1 setgray
- rightside show
- grestore
-
- gsave
- getfont
- 0 7 moveto
- 45 rotate
- .40 setgray
- leftside show
- grestore
-
- gsave
- /a 0 def
- /sy 100 def
- /sx 100 def
- getfont
- 0 0 moveto
- .20 setgray
- front show
- grestore
-
- gsave
- /sy sy 2 sqrt div def
- /a 45 def
- /sx 70 def
- getfont
- 0 70 moveto
- top show
- grestore
-
- } def %cube
-
- %% EndProlog
-
- % To set cube:
- % gsave
- % x y translate
- % x y scale ...no scale here gives a cube of 100 pts
- % (bottom) (top) (rightside) (back) (leftside) (front) cube
- % grestore
-
- gsave
- 100 200 translate
- 3 3 scale
- (B) (B) (B) (B) (B) (B) cube
- grestore
-
- ───────────────────────────────────────────────────────────────────────────
- A Comparison of Three Page Description Languages
- ───────────────────────────────────────────────────────────────────────────
-
- We asked three PDL vendors to create a sample output consisting of the words
- "Microsoft Systems Journal" in a box.
-
- ■ Postscript Sample
- ■ DDL Sample
- ■ Interpress Sample
-
- Postscript Sample
-
- The PostScript programming from Adobe produces "Microsoft Systems Journal"
- shown on page 53 of the printed version.(The misspelling of the corporate
- name was due to creative license on the part of the programmers.)
-
- !PS-Adobe-2.0
- %% Creator: pasteup
- %% CreationDate: Tue Dec 10 1986
- %% For: Microsoft Systems Journal
- %% Pages: 1
- %% DocumentFonts: Optima
- %% BeginProcSet: text_tools v1.0 revl.0
- /box { % takes relativeX relativeY on stack, draws a box
- dup 0 rlineto
- 0 3 -1 roll rlineto
- neg 0 rlineto
- closepath
- stroke
- } bind def
- % short names for frequently used PostScript operators:
- /s /show load def
- /m /moveto load def
- /S /save load def
- /RS { restore save } bind def
- /R /restore load def
- %% EndProcSet
- %% END Prolog
- %% Page: 1 1
- S
- 100 100 moveto
- 35 306 box
- RS
- 120 110 m
- /Optima findfont 24 scalefont setfont
- (MicroSoft Systems Journal) show
- R
- showpage
- %% Trailer
-
-
- DDL Sample
-
- The DDL output from Imagen Corporation appears on page 54 of the printed
- version.
-
- Uopbind.ddl (this program uses the standard DDL include file "opbind.ddl")
- "opbind.ddl" (specify the name of a file for the binding of operators and
- constants)
- :40 (open the specified file and push its source descriptor code on the
- stack)
- \srcn ; 3C (assign a name to the source descriptor code of specified file)
- srcn :3 (interpret the contents of specified source file)
- srcn :42 (close the specified source file)
-
- ; define an operator to convert points to image units
- { 24 POINTSPERINCH / ImageMetrics 5 index * }
- \pointstounits =
- @S (end of preamble and the start of section 1)
-
- @Uhelvr (this program uses the font file Helvetica Regular)
-
- "helvr" \SymbolStyle = ; set the state variable SymbolStyle to the name of
- the font file
- 24 pointstounits \SymbolDesignSize = ; convert 24 points into image units
-
- 128 array \CompositeMap = ; specify the size of the CompositeMap array
-
- 33 \i = ; set the starting index for the Composite map array to 33
- 95 {
- i symbol area\ CompositeMap i = ; store the graphic object for an
-
- i 1 + \i = ; ASCII character and increment the array index
- } repeat
-
- SymbolDesignSize 1 + 3 / relx \ CompositeMap 32 =
- ; calculate the width of the ASCII character SP (space)
-
- 656 \Xvalue = ; set the starting X coordinate for printing specified words
-
- 600 \Yvalue = ; set the starting Y coordinate for printing specified words
-
- Xvalue Yvalue absxy ! ; go to the coordinates Xvalue Yvalue
-
- "Microsoft Systems Journal" composite \text =
- ; create a composite object called text
-
- text ! ; print the words "Microsoft Systems Journal"
-
- text bbox ; calculate the smallest box that can enclose
- the specified three words
-
- \maxy = ; get the maximum Y coordinate of the bounding box from the DDL
- stack
- \miny = ; get the minimum Y coordinate of the bounding box from the DDL
- stack
- \maxx = ; get the maximum X coordinate of the bounding box from the DDL
- stack
- \minx = ; get the minimum X coordinate of the bounding box from the DDL
- stack
-
- maxy miny - \height = ; height of the bounding box
- maxx minx - \width = ; width of the bounding box
-
- height 2 / \offset = ; calculate the offset from the starting position
-
- width height = \width = ; increase the box width by box height
-
- Xvalue minx + offset - \Xvalue = ; calculate the starting X and Y
- coordinates
- Yvalue miny + offset - \Yvalue = ; the printing of a box
-
- Xvalue Yvalue absxy ! ; go to the starting coordinates
-
- width height height + rectangle line !
- ; print a box twice as high as the bounding box
-
- endimage
- @E
-
- Interpress Sample
-
- The Interpress output from Xerox Corporation appears on page 57 of the
- printed version.
-
- ──Date: 8-Dec-86 14:42:23 PST - Object file Name: MICROSOFT2.ip
-
- ── Object to Source Conversion parameters:
- ── Object File Name: MICROSOFT2.ip,
- ── Source File Name: MICROSOFT2.ial.
- ── Op codes: Source Only.
- ── File Results: SourceOnly, Sequence Data:
- Decimal/ASCII.
- ── Conversion: Normalized, Items Per Line:
- Multiple.
- ── Large Arrays: NotSupressed.
-
- Interpress/Xerox/2.2
- BEGIN
- {
- Identifier "Xerox" Identifier "XC1-1-1" Identifier
- "Modern" 3 MAKEVEC
- FINDFONT
- 99.576 SCALE
- MODIFYFONT
- O FSET
- }
-
- {
- 1/11811 SCALE
- CONCATT
- 375 2950 SETXY
- O SETFONT
- String "Microsoft"
- SHOW
- 40 SETXREL
- String "Systems"
- SHOW
- 40 SETXREL
- String "Journal"
- SHOW
- 5 15 ISET
- 350 2900 350 3050 MASKVECTOR
- 350 3050 1630 3050 MASKVECTOR
- 1630 3050 1630 2900 MASKVECTOR
- 1630 2900 350 2900 MASKVECTOR
- }
-
- END
-
- ████████████████████████████████████████████████████████████████████████████
-
- DIAL 2.0 Provides Software Developers with Integrated Support System
-
- Barbara Krasnoff
-
- Microsoft's on-line DIAL 2.0 support service, which supplies assistance for
- applications developers, has been recently upgraded and expanded. We asked
- Sunny Baker, Director of Planning and Marketing, to explain the new
- developments.
-
-
- MSJ: What exactly is DIAL?
-
- SUNNY BAKER: DIAL is an integrated set of on-line support services that use
- the PC as the interface to our own software. There are three components to
- DIAL. First, there is the on-line bulletin board system, a knowledge bank
- that is one of the most sophisticated bulletin board systems in the entire
- industry. It uses artificial intelligence techniques for keyword search
- capabilities. It's very fast and powerful.
-
- Second, we have an on-line Forum, similar to the ones you get through
- CompuServe, especially for the ISV (independent software vendor) community.
-
- Finally, we offer technical assistance through our TAR (technical assistance
- request processing) system. We supply both on-line TAR processing and
- callback for high-priority TARs in order to give people immediate assistance
- over the telephone. DIAL also transfers both binary and ASCII files, and we
- do furnish some DIAL software to subscribers.
-
-
- MSJ: When did DIAL first go on-line?
-
- BAKER: The first version went on-line at the end of 1984, and, quite
- honestly, it was not very capable. It was basically a prototype version. At
- that time, we limited access to DIAL to our OEM customers and some of the
- specialized ISVs. It wasn't until last year that we really opened DIAL up to
- the ISV community, and now we have a completely new version.
-
- DIAL 2.0 is very similar to what people have been using from the interface
- standpoint, but functionally and operationally, it's a completely new
- product. For example, we used to run DIAL on very small machines because of
- its prototype nature, and now we've moved the actual server for DIAL to a
- full-sized VAX.
-
- MSJ: What are some of the other differences between 2.0 and previous
- versions?
-
- BAKER: The Bulletin Board has been entirely restructured. It has keyword
- search capabilities, it contains much more information, and the organization
- and speed of the board are entirely different. The Forum is a totally new
- feature, and transmission error-checking capabilities have been added as
- well. The product now uses an ANSI driver rather than a VT52 driver, which
- is something that our customers asked for. The VT52 was a Microsoft standard
- that we used in-house, but nobody else uses it, so it was a big problem.
-
- MSJ: What kind of service does DIAL contribute to the community? Why is
- there a need for it?
-
- BAKER: There have been complaints in the user community for some time that
- Windows development has not been supported properly. DIAL is our effort to
- supply the high level of support that people who are doing software
- development need. Access to the Bulletin Board gives software developers
- immediate information for any known problem. We summarize every TAR that's
- presented to us from whatever source, and place it on the Bulletin Board,
- giving developers and programmers everywhere full access to bug lists for
- Microsoft products. There is immediate access to any new software updates;
- it's the most efficient way to get new tools to users. We also offer example
- software that they can download and then use in their own applications.
-
- From our point of view, the Bulletin Board is the fastest and most efficient
- way for subscribers to get support from Microsoft, since it contains answers
- to every question that somebody has asked before. The Bulletin Board is so
- quick that it's faster than calling somebody or sending in a technical
- assistance request. We try to get subscribers to DIAL to really use the
- Bulletin Board actively, especially when they first start developing
- applications with a Microsoft product.
-
-
- MSJ: Who is the typical DIAL subscriber?
-
- BAKER: Right now, our typical users are software developers doing Windows
- development, OEMs doing adaptations of both DOS and Windows to their own
- machines, and anybody who uses a Microsoft systems product──especially to
- create Windows applications. We are in the process of making DIAL available
- to any person who does systems development using any Microsoft programming
- language or programming tool kit, with emphasis on our most strategic
- products: Windows and DOS.
-
- MSJ: Approximately how many users do you have?
-
- BAKER: We have about 1,200 users today; and we're getting about 120 new
- users a month. Certainly we would like to penetrate the community even
- deeper than that. Right now, we know we have about 6,000 Windows Toolkit
- users out there; our goal is to have at least 75 percent of the active
- program developers on-line. And there are thousands of C programmers out
- there who are another target market for us. As our user base expands, the
- Forums will get better, because there are more and more people exchanging
- information.
-
-
- MSJ: How much information does Microsoft post on the Bulletin Board?
-
- BAKER: We delete things that are obsolete, but otherwise the Bulletin Board
- is constantly expanding. We now post about 150 items a week. Our goal for
- next month is to post 1,000 items, and our goal over the next 6-month period
- is to get up to the point where we will be posting 7,000 items a month,
- because we're going to support Microsoft's complete product line.
-
- MSJ: The Bulletin Board contains information only from Microsoft. Is there
- any on-line interaction between users?
-
- BAKER: That's what the Forum is all about. We just started it in January,
- and it looks like we're going to have a good quorum.
-
- Our Forums are just like the forums on GENIE or CompuServe. Let's say that
- somebody is looking for a local area network card. That person can ask the
- community, "Hey, do any of you know a good local area network card?" DIAL
- people can respond to the Forum and say, "Hey, I know so-and-so." Their
- anonymity is maintained throughout the Forum.
-
- Forum items that we think are noteworthy are also translated into Bulletin
- Board items, so that everybody can have access to them.
-
- The whole point is to make DIAL a one-stop support system that combines
- everything a user basically needs as far as support from Microsoft, support
- from his own community, and support from our knowledge base──all integrated
- into one system.
-
-
- MSJ: When does a subscriber use TAR?
-
- BAKER: Any question that relates to the operation of a product or the
- building of an application with a Microsoft product can be asked over the
- TAR system.
-
- However, to support the complex questions that we get, we really need to see
- the customer's code. TAR offers an immediate mechanism for transferring that
- code back and forth. Coupled with our callback on priority questions, we
- think we've developed the optimum support system for our ISV customers. It
- lets them have direct contact over the telephone with Microsoft, and gives
- them the chance to explain the problem in detail in writing, which is
- usually the best way to handle it.
-
- Every TAR is summarized and rewritten to become a Bulletin Board item after
- it's answered. Of course, the anonymity of the person who sent in the TAR is
- always maintained.
-
-
- MSJ: How many people do you have on staff right now?
-
- BAKER: In the system support area in Microsoft, there are 35 people
- supporting languages and operating environments. We have just gotten
- approval to double our operations staff from 10 to 20. We've also introduced
- a very interesting job rotation plan that allows engineers to work on DIAL
- and then rotate back into engineering.
-
-
- MSJ: How long does it usually take for a question on TAR to be answered?
-
- BAKER: In the past, because of the limited staff, the typical time was 2 or
- 3 days. Right now, our goal is to get it to within 24 hours on all TAR
- questions.
-
- MSJ: How much does DIAL cost?
-
- BAKER: $450 a year for unlimited service covering all of the MS-DOS system's
- products; XENIX Support is $600 per year.
-
-
- MSJ: Are there any arrangements for long-distance callers?
-
- BAKER: We have negotiated an arrangement for low-cost service with Telenet
- so that DIAL users who call long distance can reduce that charge.
-
-
- MSJ: How many lines do you have now?
-
- BAKER: We have 8 Telenet lines and 8 long-distance lines. We also monitor
- the usage at any particular time and add lines as they are needed.
-
-
- MSJ: What is your own frank evaluation of DIAL 2.0?
-
- BAKER: It's a good, solid support vehicle that still needs work. In the
- past, there weren't enough senior people staffed on DIAL. Of course, we're
- meeting that need now by adding more people.
-
- The knowledge base is probably the most outstanding keyword search bulletin
- board system in the entire computer industry──and we've looked at the
- systems that DEC and IBM are using as well, so we really have a
- technological lead with our system.
-
- I think that in certain areas, such as actual support delivery, it does need
- some enhancement, but Microsoft management is being very responsive in
- giving us the resources to make it a really great world-class system.
-
- ████████████████████████████████████████████████████████████████████████████
-
- Rich Text Format Standard Makes Transferring Text Easier
-
- ───────────────────────────────────────────────────────────────────────────
- Also see the following related articles:
- An RTF Sample
- Preliminary Rich Text Format Specification
- ───────────────────────────────────────────────────────────────────────────
-
- Nancy Andrews
-
- Back in the bad old days of punch cards, formatted text was never an issue.
- There were no formats; you couldn't even use lowercase. But we've come a
- long way since then. With bit-mapped screens, state-of-the-art word
- processors can handle numerous character, paragraph, and section formats.
- Not only can you get lowercase, you can have 16-point Helvetica in a
- paragraph with a 1/2-inch hanging indent. However, problems arise when you
- transfer text and formats to other applications. Rich Text Format (RTF)
- provides a solution to these problems.
-
- RTF is a way to encode formatted text. Microsoft is proposing RTF as a
- standard to make moving text between applications easier. With RTF, users
- can transfer text from one application to another and not lose any
- formatting. And developers can save documents in one format, RTF, and know
- that other applications can read this information without worrying about
- translating to each application's unique format.
-
-
- Why RTF
-
- Microsoft is pushing RTF because of Microsoft Windows. Currently, if you use
- Window's clipboard to transfer text between applications, you lose all
- formatting. For example, if you were transferring formatted text from
- Windows Write into Micrografx Draw, you'd have to reformat the text within
- Draw. A way to retain the format from one application when it is transferred
- to another is needed. If both Write and Draw used RTF, the clipboard could
- transfer the formats along with the text. Microsoft recognizes the need to
- exchange documents easily among its own products. It sees RTF as a solution
- for existing and future products.
-
- Current format standards such as IBM's Document Content Architecture (DCA),
- although widely supported, are not adequate. DCA, for example, lacks a good
- font strategy. It works with pitch only, doesn't consider point sizes, and
- has only one code to specify proportional spacing. DCA is most efficient for
- transferring entire documents, as opposed to short strings.
-
- Microsoft wanted a standard that could handle all existing formats and both
- entire documents and the rapid exchange of short formatted strings. This
- kind of information is usually transferred via the Windows clipboard. When
- you transfer short strings, you don't want to include the document name,
- creator, and date of last modification. But when you transfer entire
- documents, this information is essential. RTF has the flexibility to handle
- both types of transfer. Also, the standard had to be what Charles Simonyi,
- author of RTF and Microsoft's chief architect of applications, calls
- "forward-looking," that is, one that can handle existing applications and
- leaves room for future enhancements as well.
-
-
- What RTF Is
-
- RTF provides a standard format for interchanging text regardless of the
- output device, operating system, or operating environment. Text and format
- instructions are saved in 7-bit ASCII format so that they are easy to read,
- and you can send them over virtually any communications link.
-
- RTF uses "control words" to encode the formats. Control words provide the
- space so that RTF can be expanded later to include macros or additional
- formats. Control words use this form:
-
- \lettersequence
- <delimeter>
-
- If the delimeter is a digit or a hyphen (-), another parameter follows. A
- nonletter or digit terminates the control word.
-
- In addition to control words, RTF uses braces. A left brace ({) marks the
- beginning of a group of text, and a right brace (}) marks the end. Braces
- can be used to delineate footnotes, headers, and titles. In RTF, the control
- words, the control symbol (\), and the braces constitute control
- information. All other characters in RTF are plain text.
-
- A bit of RTF code might look like this:
-
- \rtf0\mac{\fonttbl\f1\ fromanBookman;}
- {\stylesheet {\s0 Normal;}
- {\s1\i\qj\snext2\f1 Question;}{\s2\qj\f1 Answer;}}
- {\s0\f1\b\qc Questions and Answers\par }
- {\s1\i\qj 1. What is the left margin of this document?\par}
- {\s2\qj\li720\f1 Since no document parameters were specified, the default
- of 1800 twips (1.25") is used.\par}}
-
- Here's what this information means. \rtf0\ indicates that this is an RTF
- document and the version number is 0. It uses the Macintosh, rather than the
- PC or ANSI, character set. Next comes information about the current font,
- which is from font table 1, the roman family, the Times font. After that is
- style sheet information. Three different styles are assigned. Style 0 is the
- normal character and paragraph style. Style 1 is italic, the paragraph is
- justified, it is always followed by a style 2 paragraph, uses font 1, and is
- called Question. Style 2 is justified, uses font 1, and is called Answer.
-
- Next is information about the first paragraph, followed by the text of that
- paragraph. It is normal text (style 0), uses font 1, and is bold and
- centered. The second paragraph is a question, uses the question style, and
- is italic and justified. The last section is the answer, which is based on
- style 2, is justified, and uses a left indent of 720 twips or 0.5". (Twips
- are 1/20th of a point; there are 1,440 twips per inch.)
-
- To read a stream of RTF you first need to separate the control information
- from the plain text and then act on the control information. When you
- encounter a left brace ({), you have to stack the current state and then
- continue. When you encounter a control symbol (\), collect the control word
- and parameter, if any, look up the word in the symbol table (a constant
- table), and follow the directions outlined there. For example, change a
- formatting property such as adding the bold format. Then continue writing
- characters to the current destination by using the current formatting
- properties. When you come to the right brace (}), unstack the current state.
- Working with RTF is straightforward──much simpler than decoding proprietary
- file formats of individual applications.
-
-
- Who's Playing
-
- RTF, like any standard, is as valuable as the people who support it.
- Microsoft believes that most Microsoft Windows applications developers will
- support RTF, since it is advantageous for them to be able to easily exchange
- formatted text. George Grayson, president of Micrografx, one of the first
- Windows applications developers, says that its products, In*a*Vision, Draw,
- and Graph, will all support RTF by June. Also, Microsoft currently has
- several Windows applications in development that will definitely support
- RTF.
-
- Aldus, developer of PageMaker for the Mac and PC, has not yet committed to
- RTF. But Ted Johnson, an Aldus engineer, says that although RTF was too late
- to be included in Version 1.0 of Aldus's PC PageMaker, versions after 1.0
- will definitely support RTF for clipboard transfers. PageMaker will also
- continue to accept formatted text from several word processors using the
- proprietary file format of each of these word processors. So it looks like
- RTF is well on its way to acceptance in the Microsoft Windows world.
-
-
- What Next
-
- Greg Slyngstad, program manager in Microsoft's word processing area and
- current keeper of the RTF standard, claims "this version is complete, but we
- expect to extend it in the future to meet the needs of Windows application
- developers (including Microsoft). Any future version will be superset of the
- current RTF." The goal of RTF is to retain all formatting in transfer. And
- Slyngstad's goal is to ensure that the RTF standard responds to the needs of
- developers.
-
-
- ───────────────────────────────────────────────────────────────────────────
- An RTF Sample
- ───────────────────────────────────────────────────────────────────────────
-
- {\rtf0\mac {\fonttbl{\f0\fswiss Chicago;}{\f3\fswiss Geneva;}{\f4\fmodern
- Monaco;}{\f13\fnil Zapf Dingbats;}{\f14\fnil Bookman;}{\f15\fnil N Helvetica
- Narrow;}{\f20\froman Times;}{\f21\fswiss Helvetica;}{\f22\fmodern
- Courier;}{\f23\ftech Symbol;}}
- {\stylesheet{\s243\tqc\tx4320\tqr\tx8640 \sbasedon0\snext243
- footer;}{\sbasedon222\snext0
- Normal;}}\margl1613\margr1080\margt720\widowctrl\ftnrestart {\headerl
- \pard\plain 1-4 see auditors notes{\|}\par
- \par
- }\sectd \linemod0\linex0\cols1\colsx0\endnhere {\footer \pard\plain
- {\i\f20\fs18 This document was prepared with Microsoft(R)
- Word}{\i\scaps\f20\fs18 }{\i\f20\fs18 Version }{\i\scaps\f20\fs18
- 3.0}{\i\f20\fs18 for Apple(R) Macintosh(TM) systems, \par
- and printed with an Apple LaserWriter(TM).}{\i\scaps\f20\fs18 \par
- }\pard\plain \s243\tqc\tx4320\tqr\tx8640 {\f150\fs18 \par
- }}\pard\plain \ri360 {{\pict\macpict\picw73\pich66
- 02b2012500af016700f81101a00082a0008c01000a0000000002d0024051013500b4016700f1
- 5809000000000000000071001e013500c5016200de016000dd013900c501
- ∙
- ∙
- ∙
- 00f3a0621670002a015300eb016000f7015400f3015700ee015c00eb016000ef015f00f60158
- 00f7015300f3015400f3a0008da00083ff}}{\b\f140\fs28 \par
- \par
- }\pard \ri187 {\b\f140\fs32 West Associates, Inc.\par
- }\pard \ri187\sb40\sl320\brdrt\tqr\tx9360 {\f139 Consolidated Balance
- Sheet}{\f21\fs20 \tab }{\i\f21\fs20 Current as of: December 31st, 1986\par
- }\pard \ri5947\sa40\brdrb {\f21\fs20 \par
- }\pard \ri187\brdrth\tx360\tx1080\tx6480\tqr\tx7560\tx8280\tqr\tx9360
- {\b\f140\fs20 Assets}{\b\f21\fs20 \tab \tab \tab }{\b\i\f21\fs20 1986\tab
- }{\i\f21\fs20 \tab 1985\par
- }\pard \ri187\sb40\brdrt\brdrth\tx360\tx1080\tx8280 {\i\f21\fs20 \tab
- }{\f21\fs20 Current Assets:\par
- }\pard \ri187\tx360\tx1080\tqr\tx7560\tqr\tx9360 {\f21\fs20 \tab \tab Cash
- and temporary cash investments}{\b\f21\fs20 \tab $114,888}{\f21\fs20 \tab
- $143,284\par
- \tab \tab Accounts receivable}{\b\f21\fs20 \tab 258,238}{\f21\fs20 \tab
- 136,420\par
- \tab \tab Inventories (at cost)}{\scaps\f21\fs16\up6 1}{\b\f21\fs20 \tab
- 264,619}{\f21\fs20 \tab 142,457\par
- }\pard \ri187\sb40\tx360\tx1080\tqr\tx7560\tqr\tx9360 {\f21\fs20 \tab \tab
- Prepaid Income Taxes}{\b\f21\fs20 \tab 26,751}{\f21\fs20 \tab 27,949\par
- }\pard \ri187\brdrb\tx360\tx1080\tqr\tx7560\tqr\tx9360 {\f21\fs20 \tab \tab
- Other current assets}{\b\f21\fs20 \tab 23,055}{\f21\fs20 \tab 18,883\par
- }\pard \ri187\sb40\brdrb\tx360\tx1080\tx1620\tqr\tx7560\tqr\tx9360
- {\f21\fs20 \tab \tab \tab }{\f138\fs20 Total Current Assets:}{\b\f21\fs20
- \tab $687,551}{\f21\fs20 \tab $468,993\par
- }\pard \ri187\tx360\tx1080\tx1620\tqr\tx7560\tqr\tx9360 {\f21\fs20 \tab
- Property, plant and equipment\tab \par
- \tab \tab Land and Buildings}{\b\f21\fs20 \tab 24,892}{\f21\fs20 \tab
- 19,993\par
- \tab \tab Machinery and equipment}{\b\f21\fs20 \tab 68,099}{\f21\fs20 \tab
- 51,445\par
- \tab \tab Office furniture and equipment}{\b\f21\fs20 \tab 30,575}{\f21\fs20
- \tab 22,628\par
- }\pard \ri187\sa60\tx360\tx1080\tx1620\tqr\tx7560\tqr\tx9360 {\f21\fs20 \tab
- \tab Leasehold improvements}{\scaps\f21\fs16\up6 2}{\b\f21\fs20 \tab
- 26,008}{\f21\fs20 \tab 15,894\par
- }\pard \ri187\brdrt\tx360\tx1080\tx1620\tqr\tx7560\tqr\tx9360 {\f21\fs20
- \tab \tab \tab }{\b\f21\fs20 \tab $149,574}{\f21\fs20 \tab $109,960\par
- }\pard \ri187\tx360\tx1080\tx1620\tqr\tx7560\tqr\tx9360 {\f21\fs20 \tab \tab
- Accumulated depreciation and amortization}{\b\f21\fs20 \tab
- (73,706)}{\f21\fs20 \tab (42,910)\par
- ∙
- ∙
- ∙
- \ri187\sb40\brdrb\brdrdb\tx360\tx1080\tx1620\tqr\tx7560\tqr\tx9360
- {\f21\fs20 \tab \tab \tab }{\b\f21\fs20 \tab $788,786}{\f21\fs20 \tab
- $556,579\par
- }\pard \ri187\brdrdb\tx360\tx1080\tx1620\tqr\tx7560\tqr\tx9360 {\f21\fs20
- \par
- }{\f138\fs18 All amounts shown in thousands (000)\par
- }}
-
- ───────────────────────────────────────────────────────────────────────────
- Preliminary Rich Text Format Specification
- ───────────────────────────────────────────────────────────────────────────
-
- RTF text is a form of encoding for various text formatting properties,
- document structures, and document properties, using the printable ASCII
- character set. The main encoding mechanism of "control words" provides a
- naming convention to expand the realm of RTF to include macros, programming,
- and so on. Special characters can also be thus encoded, although RTF does
- not prevent the utilization of character codes outside the ASCII printable
- set.
-
-
- BASIC INGREDIENTS
-
- A file or stream of RTF consists of "plain text" interspersed with control
- symbols, control words, and braces. Control words follow the format:
-
- \<alphabetic string> <delimiter>
-
- The delimiter can be a space or any other nonalphanumeric character. When a
- numeric parameter follows the control word, the first digit of the parameter
- (or the minus sign, in the case of a negative number) functions as the
- delimiter of the control word. The parameter is then in turn delimited by a
- space or any other nonalphanumeric character.
-
- Control symbols consist of a \ character followed by a single nonalphabetic
- character. They require no additional delimiting characters. Control symbols
- are compact, but there are not too many of them, whereas the number of
- possible control words is unlimited. The parameter is partially incorporated
- in control symbols, so a program that does not understand a control symbol
- can recognize and ignore the corresponding parameter as well.
-
- Each group begins with a control word that describes the contents of the
- group. { indicates the beginning of a text group and } indicates its end.
- The text grouping is used for formatting and to delineate structural
- elements of the document, such as the footnotes, headers, title, and so on.
- All other characters in RTF text constitute "plain text." Since the
- characters \, {, and } have specific uses in RTF, the control symbols \\,
- \{, and \} are provided to express the corresponding plain characters.
-
-
- WHAT RTF TEXT MEANS
-
- The reader of an RTF stream will be concerned with separating control
- information from plain text and acting on control information. This is
- designed to be a relatively simple process, as described below. Some control
- information just contributes special characters to the plain-text stream.
- Other control information changes the "program state," which includes
- properties of the document as a whole and also a stack of "group states"
- that apply to parts of the document.
-
- The file is structured as groups and subgroups. Each group state is defined
- by a text group (text enclosed in braces). The group state specifies the
- following:
-
- a. The "destination" or the part of the document that the plain text is
- building up.
-
- b. The character formatting properties, such as bold or italic.
-
- c. The paragraph formatting properties, such as justification.
-
- d. The section formatting properties, such as number of columns.
-
-
- GROUPS AND SUBGROUPS
-
- The overall grouping is the document file as a whole, since the entire
- document is enclosed in braces, beginning with the control word
- \rtf<parameter> with the parameter being the version number of the writer.
- This control word must begin every RTF file, and the entire file must end
- with the corresponding closing brace.
-
- Before any text in the RTF file is entered, the character set for the entire
- destination may be declared using one of these control words:
-
- \ansi Text is the ansi character used by Microsoft Windows
- (default).
- \mac Text is the Macintosh character set.
- \pc Text is the IBM PC character set.
-
-
- COLOR TABLE
-
- The control word \colortbl is used to define the color table, with numeric
- indexes (starting at 0) for red, green, and blue. These indexes are the same
- as those used in Windows (the italicized portion represents the variable
- numeric parameter):
-
- \red000 Red index
- \green000 Green index
- \blue000 Blue index
- \cf000 Foreground color
- \cb000 Background color
-
- Each set of color definitions delimited by semicolons defines the next
- sequential color number. The following example defines color 0 and color 2:
-
- {\colortbl\red128\green0\blue64;;\red64\green128\blue0;}
-
-
- FONT TABLE
-
- The control word \fonttbl designates the group that is the font table, which
- assigns the font name and family to the font numbers used. The text is the
- font name delimited by semicolons. Default is used if no font was assigned
- and the recipient should use whatever font is considered the default for
- that particular output device. The font table, if it exists, must occur
- before the style sheet defintion and any text in the file. Possible font
- families are:
-
- \fnil Family unknown (default)
- \froman Roman family. Proportionally spaced, serif (Times Roman,
- Century, Schoolbook, etc.)
- \fswiss Swiss family. Proportionally spaced, sans serif
- (Helvetica, Swiss, etc.)
- \fmodern Fixed pitch, serif or sans serif (Pica, Elite, Courier,
- etc.)
- \fscript Script family (Cursive, etc.)
- \fdecor Decorative fonts (Old English, etc.)
- \ftech Technical or symbol fonts
-
- Example:
-
- {\fonttbl\f0\froman Tms Rmn;\f1\fswiss Helv;\f2\fnil default;}
-
-
- STYLE SHEETS
-
- The style sheet for the document is defined by a group beginning with the
- control word \stylesheet.
-
- More precisely, plain text in the group between semicolons is interpreted as
- style names, which will be defined to stand for formatting properties that
- are in effect.
-
- Example:
-
- {\stylesheet{\s0\f3\fs20\qj Normal;}
- {\s1\f3\fs24\b\qc Heading Level 3;}}
-
- This defines Style 0 (given the name "Normal") as 10-point justified type in
- font 3 (the font is defined in the font table). Style 1 (Heading Level 3),
- is 12-point font 3, boldface and centered.
-
- The following fields may be present if the destination is \stylesheet.
-
- \sbasedon000 This defines the style number which the current style is
- based on. If this control word is omitted, the style is
- not based on any style.
-
- \snext000 This defines the next style associated with the current
- style. If this control word is omitted, the next style
- is itself.
-
-
- PICTURES
-
- If the group begins with the control word \pict the plain text within the
- group is a hex dump of a picture. The following optional parameters may also
- exist if the group is a picture. If they are not present, the default frame
- size equals the picture size. A picture must be preceded by the \chpict
- special character that serves as the anchor point for the picture that
- follows.
-
- \pich000 Defines picture frame height in pixels. The picture
- frame is the area set aside for the image. The picture
- itself does not necessarily fill the frame (see
- \picscaled).
-
- \picw000 Picture frame width in pixels
-
- \picscaled Scales the picture up or down to fit within the
- specified size of the frame
-
- \wmetafile The picture is a Windows metafile
-
- \macpict The picture is in Mac Quick Draw format
-
- \bin000 Special field used to include binary information within
- the file (in lieu of hex as expected). The parameter
- defines the number of bytes of binary information that
- follow. This is useful for the "clipboard" data
- exchange where the file is not intended to be
- transferred over a communications line.
-
-
- FOOTNOTES
-
- If the group begins with the control word \footnote the group contains
- footnote text. The footnote is anchored to the character that immediately
- preceds the footnote group. The group may be preceded by the footnote
- reference characters if automatic footnote numbering is defined. A footnote
- group may also have the following complements:
-
- \ftnsep Text is a footnote separator.
- \ftnsepc Text is a separator for continued footnotes.
- \ftncn Text is continued footnote notice.
-
-
- HEADERS AND FOOTERS
-
- If the group begins with the control words\header or \footer the group is
- the header or footer for the current section. The group must precede the
- first plain text character in the section. The following variant forms may
- also be used:
-
- \headerl Header on left-hand pages only
- \headerr Header on right-hand pages only
- \headerf Header on first page only
- \footerl Footer on left-hand pages only
- \footerr Footer on right-hand pages only
- \footerf Footer on first page only
-
-
- INFORMATION
-
- The file may also contain an information group, specified by the control
- word \info. This group is used to store such information as the title. The
- name of the author, and the subject of the document. The appropriate
- information is entered as plain text following these control words:
-
- \title
- \subject
- \author
- \operator
- \keywords
- \comment Text will be ignored.
- \version
- \doccomm This last control word is for storing comments. It
- should not be confused with \comment which specifies
- that the text is to be ignored. Here is an example of
- an information block:
-
- {\info{\title Unified Field Theory Analysis}{\author A. Finestyne}
-
- Other control words allow values to be automatically entered into the
- information block:
-
- \vern000 Internal version number
- \creatim Creation time
- \yr000 Year
- \mo000 Month
- \dy000 Day
- \hr000 Hour
- \revtim Revision time
- \printim Last print time
- \buptim Backup time
- \edmins Minutes of editing
- \nofpages000 Number of pages
- \nofwords000 Number of words
- \nofchars000 Number of characters
- \id000 Internal id number
-
-
- TEXT PROPERTIES
-
- Note that a change of destination will reset all text properties to their
- default values and that changes are only legal at the beginning of a text
- group.
-
-
- DOCUMENT FORMATTING PROPERTIES
-
- The default value (given in square brackets) will occur if the control word
- is omitted. When the control words are used,000 is replaced by numeric
- parameters. All measurements are in twips which are twentieths of a point or
- 1/1440th of an inch.
-
- Examples:
- \paperw000 Paper width in twips [12240]
- \paperh000 Paper height [15840]
- \margl000 Left margin [1800]
- \margr000 Right margin [1800]
- \margt000 Top margin [1440]
- \margb000 Bottom margin [1440]
- \facingp Facing pages (enables gutters and odd/even headers)
- \gutter000 Gutter width (inside of facing pages) [0]
- \ogutter000 Outside gutter width [0]
- \deftab000 Default tab width [720]
- \widowctrl Enable widow control
- \headery000 Header y position from top of page [1080]
- \footery000 Footer y position from bottom of page [1080]
- \ftnbj Footnotes at bottom of page (default)
- \ftnsep Text is a footnote separator
- \ftnsepc Text is a separator for continued footnotes
- \endnotes Footnotes at end of section
- \ftntj Footnotes beneath text (top justified)
- \ftnstart000 Starting footnote number [1]
- \ftnrestart Restart footnote numbers each page
- \pgnstart000 Starting page number [1]
- \linstart000 Starting line number [1]
- \landscape000 Printed in landscape format
-
-
- SECTION FORMATTING PROPERTIES
-
- Examples:
- \sectd Reset to default section properties
- \sbknone Section break continuous (no break)
- \sbkcol Section break starts a new column
- \sbkpage Section break starts a new page (default)
- \sbkeven Section break starts an even page
- \sbkodd Section break starts odd page
- \pgnrestart Restart page numbers at 1
- \pgndec Page number format decimal
- \pgnucrm Page number format upper case roman numeral
- \pgnlcrm Page number lower case roman numeral
- \pgnucltr Page number format upper case letter
- \pgnlcltr Page number format lower case letter
- \pgnx000 Page number x pos [720]
- \pgny000 Page number y pos [720]
- \linemod000 Line number modulus [1]
- \linex000 Line number text distance [360]
- \linerestart Line number restart at 1 (default)
- \lineppage Line number restart on each page
- \linecont Line number continued from previous section
- \vertalt Vertically align starting at top of page (default)
- \vertalc Vertically align in center of page
- \vertalj Vertically justify top and bottom
- \vertal Vertically align starting at the bottom
- \cols000 Number of columns (snaking) [1]
- \colsx000 Space between columns [720]
- \endhere Include endnotes in this section
- \titlepg Title page is special
-
-
- PARAGRAPH FORMATTING PROPERTIES
-
- Examples:
- \pard Reset to default paragraph properties
- \s000 Style; if a style is specified, the paragraph formatting
- implied by that style must still be specified with the
- paragraph
- \ql Quad left (default)
- \qr Quad right
- \qj Justified
- \qc Centered
- \fi000 First line indent [0]
- \li000 Left indent [0]
- \ri000 Right indent [0]
- \sb000 Space before [0]
- \sa000 Space after [0]
- \sl000 Space between lines (If no \sl is specified, default is
- 12 points. If \sl000 is specified, line spacing is
- automatically determined by the tallest font on the
- line).
- \keep Keep
- \keepn Keep with next paragraph
- \sbys Side by side
- \pagebb Page break before
- \noline No line numbering
- \tx000 Tab position (this places a vertical bar at the
- specified position for the height of the paragraph)
- \tqr Flush right tab (last specified position)
- \tqc Centered tab
- \tqdec Decimal aligned tab
- \brdrt Border top
- \brdrb Border bottom
- \brdrl Border left
- \brdrr Border right
- \box Border all around
- \brdrs Single thickness border
- \brdrth Thick border
- \brdrsh Shadow border
- \brdrdb Double border
- \tldot Leader dots
- \tlhyph Leader hyphens
- \tlul Leader underscore
- \tlth Leader thick line
-
-
- CHARACTER FORMATTING PROPERTIES
-
- Examples:
- \plain Reset to default text properties
- \b Boldface
- \i Italic
- \strike Strikethrough
- \outl Outline
- \shad Shadow
- \scaps Small caps
- \caps All caps
- \v Invisible text
- \f000 Font number
- \fs000 Font size in half points [24]
- \expnd000 Expansion or compression of space between characters in
- quarter points (negative value indicates compression).
- \ul Continuous Underline
- \ulw Word underline
- \uld Dotted underline
- \uldb Double underline
- \ulnone Cancels all underlining
- \up000 Superscript in half points [6]
- \dn000 Subscript in half points [6]
-
- A zero following the character formatting instruction turns off the format.
-
- For example:
- \b0 Turns off the boldface
- \i0 Turns off the italic
-
-
- SPECIAL CHARACTERS
-
- If a character name is not recognized by a reader, it will simply be
- ignored. Other characters may be added for interchange with other programs.
-
- These are examples of special characters:
- \chpgn Current page number (as in headers)
- \chftn Auto footnote references (footnote to follow in a
- group.)
- \chdate Current date (as in headers)
- \chtime Current time (as in headers)
- \| Formula character
- \~ Nonbreaking space
- \- Discretionary, or soft, hyphen
- \_ Nonbreaking hyphen
- \'hh Any hex value (can be used to identify 8-bit value)
- \page Required page break
- \line Required line break (no paragraph break)
- \par End of paragraph
- \sect End of section and end of paragraph
- \tab Same as ASCII 9
-
- For simplicity of operation, ASCII 9 will be accepted as tab. The control
- code \<10> (backslash followed by ASCII 10) and \<13> will both be accepted
- as \par. ASCII 10 and ASCII 13 without the backslashes will be ignored. They
- may be used to include carriage returns for easier readability but will have
- no effect on the interpretation as long as they do not occur within a
- control word. It's a good idea to insert carriage returns every 255
- characters or less to facilitate transmission via E-mail systems.
-
- ████████████████████████████████████████████████████████████████████████████
-
- Ask Dr. Bob!
-
- DOS Path
-
- Dear Dr. Bob,
-
- I need to use the MS-DOS path to search for the overlays, initialization
- files, and data files my program needs. I can't figure out how to get this
- from MS-DOS. Can you give me a clue about where to get this information?
- ──Searching
-
- Dear Searching,
-
- It's true that MS-DOS has no usable support for path information. But you
- can use the C library getenv ("PATH") function. It returns a pointer to the
- current path string. At that point it's up to your program to decode and use
- that information, which is in the same format you see when you type "path"
- after the MS-DOS prompt. You need to parse the string and then scan
- individual directories for the files that you will need.
-
-
- Redundan-C
-
- Dear Dr. Bob,
-
- My programs (like most everyone's these days) use some C modules and some
- MASM modules. It seems so redundant to type the same declarations in C
- format and then in MASM format. Often, I make a change in one and then
- forget to incorporate it into the other. Is there a way to get around this?
- ──Forgetful
-
- Dear Forgetful,
-
- Actually, there is. The trick is to use C's preprocessor phase. What you can
- do is make a header file that produces the definitions required for both C
- and MASM. Then you write one declaration file using generic rather than C-
- or MASM-specific declarations. When you make a change, all you need to do is
- change the generic header file, and it will automatically change both the C
- and MASM declarations.
-
- Here's how it works. First you make a header file like the one in Figure 1,
- called C_OR_ASM.HDR. It takes information from a generic program declaration
- file and turns it into the format required by either the C compiler or MASM.
-
- Next, put the declarations for your program in a file like the one shown in
- Figure 2. In this case, we have called our file SHARED.HDR. As you can see,
- instead of using either C or MASM's declaration formats, what you use are
- generic formats. C_OR_ASM.HDR then takes this file and turns it into the
- required format.
-
- The last piece you need to finish the puzzle is a way of telling the
- compiler or assembler about these files. Here's where you use C's
- preprocessor. You use both the /D and /EP switches. The /D switch tells the
- preprocessor which set of definitions to use in C_OR_ASM. /EP tells the
- compiler to stop after this preprocessor stage. If you're assembling a MASM
- module, use this line:
-
- msc shared.hdr
- /DMASM_STYLE /EP;
- >shared.inc
-
- It takes SHARED.HDR and produces the declarations required in MASM format in
- a file called SHARED.INC.
-
- For C declarations use the following line:
-
- msc shared.hdr
- /DC_STYLE /EP;
- >shared.h
-
- It produces the declarations in C format in a file called SHARED.H.
-
- Now you have declaration files in both formats. All you need to do is
- include them with your .C or .ASM files when you compile. If you make any
- changes, the only file you need to change is SHARED.HDR.
-
-
- Taking Out Mouse Garbage
-
- Dear Dr. Bob,
-
- I am currently writing a QuickBASIC program that uses some of the new hi-res
- EGA modes──SCREEN 8, 9, and 10. But whenever I move the mouse around while
- my program is writing to the screen, I get garbage. Is there a fix for this?
- ──Mystified
-
-
- Dear Mystified,
-
- You're in trouble. Because the EGA has write-only registers, in order for
- two programs to write to the screen at the same time, they need to
- cooperate. Microsoft QuickBASIC doesn't cooperate with anyone. So you can
- just avoid using the mouse, or, if the mouse is critical, disable it while
- you're drawing on the screen. For additional information you can call
- Microsoft Technical Support at (206) 882-8089 and ask for "Writing EGA
- Software for the IBM PC."
-
-
- Hexing Windows' Calculator
-
- Dear Dr. Bob,
-
- I used to have a terminate-and-stay-resident calculator I used all the time.
- When I started using Microsoft Windows, I found I really needed all my
- memory for Windows and the applications I run with it. I've tried using the
- Windows calculator. Although it has the basics, what I need is the ability
- to display the hex equivalent of decimal numbers. Is there any way I can get
- the Windows calculator to do this?──Refiguring
-
- Dear Refiguring,
-
- You're in luck. It's quick and easy. Just hold down the H key and the number
- currently displayed will temporarily change to its hexidecimal equivalent.
-
-
- Standard Memory Error
-
- Dear Dr. Bob,
-
- I'm running Microsoft Windows and I'm using standard applications. When I
- quit one of the standard applications, Windows doesn't always seem to
- relinquish all of its memory. Occasionally I get a "Not enough memory..."
- error when I think I shouldn't.──Puzzled
-
- Dear Puzzled,
-
- This can happen because of the way Windows manages memory for standard
- applications. You need to follow a couple of basic guidelines when working
- with several standard applications and Windows. First, load the largest
- standard application. When Windows starts the second standard application,
- it swaps the first out and the second in. Loading the largest first ensures
- that there will be enough room. Then if you quit the largest application the
- second will still have as much memory as was allotted for the largest one.
- You can fix this by choosing the About... command in the MS-DOS Executive
- System menu. In addition to displaying the amount of memory, it also does
- garbage collection and will reclaim and reorganize the unused memory.
-
-
- BitBlt Limits
-
- Dear Dr. Bob,
-
- I'm working on a Windows app. I tried to use BitBlt to copy a large portion
- of the screen, but it didn't work. What's wrong?──Frustrated
-
- Dear Frustrated,
-
- An undocumented limitation of existing versions of Windows is that bitmaps
- can't be larger than 64K. One way you can get around this is to use
- "banding"──splitting the block that you want to move into rectangles and
- then calling BitBlt on each of the smaller rectangles.
-
-
- Stock Bitmaps
-
- Dear Dr. Bob,
-
- The Windows Software Development Kit documentation for CreateCompatible
- display context says that "GDI automatically selects a monochrome stock
- bitmap" for the new memory display context. What does this mean? Is there
- something special about a "stock bitmap"?──Wondering
-
- Dear Wondering,
-
- There is nothing special about a monochrome stock bitmap. It is simply a
- bitmap with one plane and one bit per pixel. By using the CreateCompatibleDC
- function a display context is set up whose "display surface" is simply a
- bitmap allocated from system memory──it is not actually displayed anywhere.
- Any GDI function may be used to draw into this bitmap. BitBlt can quickly
- move images between itself and another DC. When a memory DC is created,
- Windows allocates a monochrome bitmap for it as a default, even if your
- original had color. If you have all color information, you can switch to a
- color bitmap by using CreateBitmap and SelectObject.
-
-
- Scaling Text
-
- Dear Dr. Bob,
-
- I'm confused by Windows' mapping modes. My graphics get scaled because I
- specify anisotropic mode, but my text doesn't. Is there a way to scale text?
- ──Confused
-
- Dear Confused,
-
- Maybe. You can tell whether a given device supports character scaling with
- GetDeviceCaps (hDC,TEXTCAPS). Certain bits of the return value tell what
- sorts of text display tricks are supported. If the device does support some
- text scaling, be sure to use DrawText, instead of TextOut, because TextOut
- doesn't scale.
-
-
- Interrupt Routines
-
- Dear Dr. Bob,
-
- I'm writing a Windows app that uses a special interrupt-driven I/O board.
- Obviously, it's not supported by Windows. Ideally, I would like my interrupt
- service routine to send messages to the Windows app. It would be notified by
- GetMessage, just as when you press a key or move the mouse. Am I dreaming?
- ──Dreamer
-
- Dear Dreamer,
-
- Yes. First of all, you can't call any Windows functions from within your
- interrupt service routine because right now Windows is not re-entrant. You
- might have better luck when DOS and Windows are truly multitasking. Windows
- handles the keyboard and mouse in a special way and does not allow you to
- define your own messages that would work like the keyboard and mouse. The
- best you can do is use SetTimer to generate WM_TIMER messages for your
- Windows application periodically. You can have your interrupt routine set
- flags, which are polled in the WM_TIMER code. Obviously, this means that you
- can't count on a quick response since you'll be at the mercy of the Windows
- scheduler and the other applications to return your program control
- occasionally.
-
-
- Windows Color Support
-
- Dear Dr. Bob,
-
- The EGA hardware supports 16 colors. To my surprise, I discovered Windows
- displays only eight. Say it ain't so, Bob.──Surprised
-
- Dear Surprised,
-
- When Microsoft wrote the EGA driver, it traded colors for speed. The EGA
- card has four planes, allowing a possibility of 16 colors. Microsoft chose
- to use only three planes for color and reserved the fourth plane for the
- mouse cursor and the other cursor. If Microsoft had given this plane to
- color rather than the cursor, each time the mouse moved you would need to
- restore the screen in the old cursor position, save the screen in the new
- cursor position, and draw the cursor. All three operations would have to
- process all four planes. This would be too slow, especially on an 8088 PC.
-
- By dedicating one plane to the cursor, you don't have to save a copy of the
- screen under the cursor. Moving the cursor is simply erasing it from the old
- position and drawing it at the new position. This tradeoff seems reasonable.
- After all, Windows can dither the dots and get 64 shades for each of the
- eight colors that are supported.
-
-
- Using Windows and SYMDEB Together
-
- Dear Dr. Bob,
-
- I do a lot of traveling. I like to use my time on the airplane to work on my
- hobby──debugging Windows apps. How can I have my Windows app and SYMDEB
- running on the screen at the same time?──Jetsetter
-
- Dear Jetsetter,
-
- First, fasten your seatbelt, and then relax. It has been reported that you
- can run Windows and SYMDEB on the same screen some of the time. But this
- doesn't work reliably, and in fact, Dr. Bob hasn't been able to get it to
- work at all.
-
- In any event you'll most likely find it easier between landings and takeoffs
- to use a second monitor or a serial terminal. That way you can see your
- Windows app running on one screen and SYMDEB on the other.
-
-
- Getting the Same Image
-
- Dear Dr. Bob,
-
- I'm using the same series of steps for output to the screen and the printer,
- but the images are different. Is there a way to guarantee that the output
- will be the same on both devices?──Curious
-
- Dear Curious,
-
- You can't always guarantee the images will be the same because the images
- that are produced depend on the capability of the output device. For
- example, monitors differ in number of colors and resolution as well as
- support from the device driver. To get the same image on different devices,
- you must use SelectObject to give each device the same pen, brush, and font.
- Then it's up to Windows to match what you send to the capabilities of the
- device.
-
-
- Self-Modifying Code
-
- Dear Dr. Bob,
-
- Let's say that you have a program that manipulates its own code. How then
- can it function with only one code segment? Also, is there an objective
- reality?──Philosophical
-
-
- Dear Philosophical,
-
- Well, if you must write self-modifying code... be sure that the modified
- code can be relocated, and specify that it is not discardable. If Windows
- discards and reloads, then some can be lost. To prevent this, state that it
- isn't moveable in the definition file. Windows uses the same code segment
- when you load multiple instances of the same program. To force Windows to
- load a fresh copy of the code specify:
-
- CODE MULTIPLE MOVEABLE
-
- in the Module Definition File, passed to the linker.
-
- As for objective reality, Dr. Bob defers to his colleague Dr. Science of San
- Francisco, Calif.
-
-
- Figure 1:
-
- #ifdef MASM_STYLE
- #define CONSTANT(name,value) name = value
- #define INTVAR(name) name DW ?
- #define INTARRAY(name,size) name DW size dup(?)
- #define STRUCVAR(name,strc) name DB (size strc)dup(?)
- #define DEFSTRUC(name) name STRUC
- #define ENDSTRUC(name) name ENDS
- #endif
-
- #ifdef C_STYLE
- #define CONSTANT(name,value) #define name (value)
- #define INTVAR(name) int name;
- #define INTARRAY(name,size) int name[size];
- #define STRUCVAR(name,strc) struct strc name;
- #define DEFSTRUC(name) struct name {
- #define ENDSTRUC(name) };
- #endif
-
-
- Figure 2:
-
- #include "c_or_asm.hdr"
-
- CONSTANT (ScreenHt, 200)
- CONSTANT (ScreenWidth, 640)
- CONSTANT (MaxNumPts, 1024)
-
- DEFSTRUC (Point)
- INTVAR (x)
- INTVAR (y)
- ENDSTRUC (Point)
-
- STRUCVAR (startPt,Point)
- INTVAR (counter)
- INTARRAY (data,MaxNumPts)
-
-
- Figure 3:
-
- $cursor dd 0b8000000h ;for MONO adapter use 0b0000000h
- $hexdig db '0123456789ABCDEF'
-
- ;------ Display character in AL and advance cursor:
- $showc: push es,di
- les di,cs:$cursor ; Get current cursor
- ; (segment & offset)
- mov ah,07h ; Set attribute
- cld
- stosw ; Write char to screen
- mov word ptr cs:$cursor,di ; Save csr (offset only)
- pop di,es
- ret
-
- ;------ Display value in DX as a hex number:
- $showx: push f,ax,bx,cx
- mov cx,12 ; Shift by 12,8,4, and 0 bits
- $sx1: mov bx,dx ; Get number from DX
- shr bx,cl ; Get next nybble to display
- and bx,000fh
- mov al,cs:$hexdig[bx] ; Map nybble to hex digit
- call $showc ; Show hex digit
- sub cl,4
- jns $sx1 ; Keep going until shift count
- ; is neg.
- pop cx,bx,ax,f
- ret
-
- ;------ Display string stored in code segment just after CALL to
- ;------ this routine:
- $shows: push f,ax,bx,bp
- mov bp,sp
- mov bx,[bp+8] ; Get return addr -
- ; it points to string
- $ss1: mov al,cs:[bx] ; Get next char from string
- inc bx ; Advance string pointer
- or al,al ; Reached 0 end of string?
- jz $ss2 ; Exit if so
- call $showc ; Display char
- jmp $ss1 ; Back for more
- $ss2: mov [bp+8],bx ; reset return address
- pop bp,bx,ax,f
- ret
-
- ;------ Macro to display a string.
- ;------ Example: SHOWS 'Booga booga!'
- SHOWS macro str
- call $shows
- db str,0
- endm
- ;------ Macro to display a register. Example: SHOWR BX
- SHOWR macro reg
- SHOWS ' ®='
- push dx
- mov dx,reg
- call $showx
- pop dx
- endm
-
- ████████████████████████████████████████████████████████████████████████████
-
- Carl's Toolbox
-
- Carl Warren
-
- Starting a new column in a new magazine can be a tricky business. Often you
- aren't sure who the reader is or what the interest level might be. Luck and
- a great deal of experience seem to be with MSJ. We have a pretty good idea
- of who you are and what you want.
-
- Even though the column is called "Carl's Toolbox," the fact is, it's your
- toolbox. In amateur radio days we called this our junkbox. That was the old
- wooden or paperboard box under the bench that contained odds and ends, a few
- chassis and boards that we picked up at a junkyard or swap meet and from
- which we all vowed to create a masterpiece of ham radio design.
-
- One old friend of mine used this ploy to smuggle in his brand-new Collins S
- line──old-time hams know this was the good stuff. He had convinced his wife
- that all those old military chassis, tubes, and other collected junk would
- soon be transformed into a work of art──which, some six months and about
- $5,600 (equipment was much cheaper in 1959) later, it was.
-
- Times have changed and so has the equipment. Today, prices are a little
- better, and yes, you may decide that building is far better than buying,
- especially when the tool you need doesn't really exist.
-
- I'm going to tell you not only about the tools you can buy, but about those
- that you can build yourself. Since I'll be taking a hands-on approach, I'll
- tell you how well various pieces of equipment work, and perhaps some
- interesting ways to use them.
-
- Of course, not all the tools and equipment that I'll be talking about come
- from manufacturers. Every now and then a few friends and I sit down and try
- to noodle our way through a problem, and occasionally we develop something
- useful. For example, not too long ago my friend Keith dropped by my office
- for a chat. I was complaining about all the problems you run into when you
- add a new board to the system or try to get a new device driver to work.
- "It's like a continual series of reboots," I said.
-
- Keith, who spends most of his time writing specialized code for various
- vendors, asked, "Why don't you do it in software?"
-
- He explained that by using the "A" mini-assembler command in DEBUG, you can
- enter the following four instructions that will reboot the system:
-
- MOV AX,40
- MOV DS,AX
- MOV WORD PTR [0072],1234
- JMP FFFF:0000
-
- The first two instructions (MOV AX,40 and MOV DS,AX) put the hex value 0040
- into the 8086 DS register. This points the 8086 at the area of memory in
- which the IBM PC's ROM I/O, reset, and power-on routines are located.
-
- The next instruction (MOV WORD PTR [0072],1234) stores the hex value 1234 to
- the IBM PC ROM's Reset flag data field. The ROM's power-on routines use this
- flag to determine when a Ctrl-Alt-Del reset is in progress or whether a
- power-on reset is occurring. The 1234 value transfers control directly to
- the power-on routines, and the memory and I/O checks are skipped. If the
- Reset flag contains garbage, as it would during power-up, all the checks are
- performed. Of course, if you want the checks, change the 1234 to 0000. When
- the program is executed, a full I/O and memory check takes place.
-
- That's not a bad little piece of utility software for those of you who
- collect routines. I have a few others that will be published in future
- issues.
-
- Keeping Track of Changes
-
- IBM Personally Developed Software: SuperC
- Donald M. Ludlow and Randy Black, IBM Personally Developed Software; P.O.
- Box 3280; Wallingford, CT 06494; (800) 426-7279
- Price: $29.95
-
- Whenever I review a product from IBM, I'm always afraid that if I don't like
- it, someone will accuse me of being biased against IBM. So, when I was asked
- to test-drive IBM's Personally Developed Software package, SuperC, I heard
- an anxious voice in the back of my mind say, "What if it's junk?" After all,
- the software in this series has ranged from excellent to mediocre, so I
- wasn't exactly sure what to expect.
-
- First of all, SuperC is neither a C-language development package nor a
- tutor. It is, however, a powerful and useful utility that allows
- programmers, writers, editors, and anyone who deals with stored information
- that changes to keep track of those changes.
-
- In operation, you can find changes in source code, binary code, or just
- plain ASCII words. For example, suppose in updating a document you
- accidentally insert a large space or new word in a sentence, something that
- just shouldn't be there or that changes your meaning. SuperC uses its word-
- compare feature to notify you that a change has occurred.
-
- Of course, in order to compare changes, both an old and new file must exist.
- In the case of WordStar users, where a backup copy is kept, the new copy is
- compared with the old through the use of simple syntax:
-
- A>SuperC
- [d:][path]newfile
- [d:][path]oldfile
-
- In some cases, the only thing you would be interested in is whether or not
- there are changes in files. When a change is detected by using the file-
- compare feature, the program reports that there are indeed differences. At
- this time, you can determine whether to search for word, line, or byte
- differences.
-
- Within SuperC is the tagged-as-is delta-file (Tasdf) file format, which is
- intended for experienced programmers. It generates a special output file
- similar to a delta listing with a prefix tagged onto each line, including T)
- for title record, I) for inserted line, D) for deleted line, RN for
- reformatted line, S) for summary record, and E) for end record.
-
- Although it appears to be just a simple compare program, SuperC includes
- capabilities such as bypassing BASIC line numbers, processing assembler
- comments and blank lines, and even substitution for nondisplayable
- characters. Also, you can specify what line(s) should be ignored in the
- process.
-
- Don't be concerned about space, either; SuperC manages just about any length
- of file. Recently, I compared a long ASCII file in a standard document of
- over 70K in length with no difficulty.
-
- SuperC offers on-line help at the touch of a function key (F1). All
- documentation is on-line and can be printed out.
-
- Since the next several columns will address various programming methods,
- I'll be using the listing function in SuperC, which prints up to 132 columns
- wide.
-
- Getting All the Fax
-
- GammaLink
- GammaFAX; 2452 Embarcadero Way; Palo Alto, CA 94303; (415) 856-7421
- Price: $995
- Includes a synchronous modem board that fits in one slot on an IBM PC, XT,
- or AT, or a similar machine, and the GammaFAX software package. The GammaFAX
- board provides bidirectional facsimile transmission CCITT group III to and
- from PCs, as well as PC-to-PC communications with full data compression and
- error checking up to 9,600 bits per second.
-
- As more and more technology comes into the office, the desktop of the future
- is getting quite cluttered. The stacks of paper and in-and-out baskets have
- been pushed to one side to make room for IBM PCs, printers and facsimile
- machines, mice, and whatever else that manufacturers can dream up. At least
- one of these desktop items, the facsimile machine, can be moved off the desk
- and back to the store room. GammaLink's GammaFAX board and software can take
- over the chores once relegated to the standalone facsimile.
-
- Besides fitting into an open slot on the PC backplane, the GammaFAX board
- can also be plugged into any available phone jack with an RJ-11 receptacle.
- By doing this you can probably reclaim at least 2 square feet of your desk.
-
- The GammaFAX system is capable of taking existing text files or even saved
- picture files and shooting them out to a facsimile machine anywhere in the
- world. And since facsimile is a two-way street, so is GammaFAX: the machine
- can be set to work in an answer facsimile mode or to communicate with other
- PCs for very rapid file transfer.
-
- The GammaFAX software is flexible and smart. By entering the DEBUG option,
- you can have the program report the status of the operation, thus ensuring a
- great deal of confidence in the operation of the line as well as the modem.
-
- A series of menus facilitates use of the program. For example, on start-up
- you are greeted with a menu that asks what you want to do: send, receive,
- convert programs for facsimile transmission, or set up a string of files for
- sending or receiving. You make your choices with the function keys, which
- direct you to another level of menus. F10 always takes you back to the
- previous level.
-
- The GammaFAX requires the undivided attention of the machine, which is fine
- unless you have to get some other work done while sending. The store and
- forward function does allow you to set times for a transfer to begin, but
- does not let you use the machine.
-
- I set up a Windows definition file for GammaFAX, so it could be called from
- Microsoft Windows, and there were no ill effects on either application. It
- would be nice, though, if the GammaFAX people would do some Windows
- compatibility rewriting so that it can be called as a windowed task and work
- in the background.
-
- You can use GammaFAX with a PC, XT, or AT. I strongly recommend the AT
- because it lets you use all the speed that the board has. The manual,
- although poorly written and in need of a good layout job, contains lots of
- information──you just have to search hard for it. Clever programmers will be
- able to build a better software mousetrap for communications by using the
- board and the information contained in the manual.
-
- The GammaFAX board is essentially a facsimile machine, so software
- developers will find it ideal for creating desktop publishing images.
- GammaFAX makes it possible to obtain pictures from sources other than
- scanners. Scanners are becoming an important tool for desktop publishing, as
- they allow photographs or other complex pictures to be scanned in.
-
- There is a problem, though. GammaFAX stores pictures in CCITT group III
- format, which is good for facsimile, but not so good for most of the
- formats used with bit-image file formats. GammaFAX does provide the
- utilities necessary to scan images in with a Canon scanner and convert
- them for facsimile transmission.
-
- In the group III format, the least significant bit of the first byte in the
- file is the first bit transmitted during the group III phase of the in
- message of a facsimile call. The most significant bit of the last byte of
- the file is the last bit transmitted. A standard-resolution facsimile page
- has 1,728 dots across and 1,075 lines in the vertical dimension. A high-
- resolution picture, such as a half-tone, is 1,728 by 2,150. The FAX virtual
- page program, supplied with the board, lets you convert the binary image
- format into a bit-image format for use with such programs as Media
- Cybernetics's Dr. Halo II.
-
- Since the image does have specific characteristics, it can be manipulated
- into other forms. The Tag Image File Format (TIFF), a newly proposed format
- developed by Aldus Corp., Dest Corp., and Microsoft Corp., provides a
- structured format for digital data. TIFF is based on CCITT group III and IV
- formats, but it adds additional information for the digital data, including
- color and detail about individual pixels, so that a full picture can be
- saved.
-
-
- ════════════════════════════════════════════════════════════════════════════
-
-
- Vol. 2 No. 2 Table of Contents
-
-
- Microsoft Operating System/2: A Foundation for the Next Generation
-
- Microsoft's joint effort with IBM to develop an operating system for Intel
- 80286- and 80386-based computers resulted in Operating System/2. Using the
- 286's protected mode, OS/2 not only breaks the 640K memory barrier, it also
- provides multitasking capability and inter-process communication.
-
-
- OS/2 Windows Presentation Manager: Microsoft Windows on the Future
-
- Included as a standard part of the new Operating System/2, the Windows
- presentation manager will provide OS/2 with the same advantages that
- Microsoft Windows brings to MS-DOS, such as a standard user interface and a
- rich independent graphic application environment.
-
-
- OS/2 DOS Environment: Compatibility And Transition for MS-DOS Programs
-
- An important OS/2 feature is its MS-DOS compatibility environment, a mode
- that emulates DOS 3.x and runs most existing programs. Discussed are the
- restrictions of the compatibility mode and the considerations in the
- construction of programs designed to run with both MS-DOS and OS/2.
-
-
- OS/2 Multitasking: Exploiting the Protected Mode of the 286
-
- The Microsoft OS/2 scheduler, the dispatcher, and the protected mode of the
- 286 are the keys to OS/2's multitasking operation. This article considers
- the system developers view of the fundamental elements of multitasking:
- processes, threads, and screen groups.
-
-
- OS/2 Memory Management
-
-
- OS/2 Inter-Process Communication: Semaphores, Pipes, and Queues
-
- Multitasking operations need fast and reliable communication between
- multiple processes and threads. To offer the system developer a wide range
- of facilities to choose from, OS/2 depends on shared memory as well as
- system semaphores, pipes, and queues for rapid exchange of information.
-
-
- The MS OS/2 LAN Manager
-
-
- A Complete Guide to Writing Your First OS/2 Program
-
- Discussions of program development for OS/2 introduce a new vocabulary.
- Dynamic linking or module definition files may at first seem forbidding,
- but they, like OS/2, are variants of familiar concepts. We offer a sample
- program complete with multitasking and inter-process communication.
-
-
- Turning Off the Car to Change Gears: An Interview with Gordon Letwin
-
- With more than 350,000 lines of code, OS/2 is unquestionably the most
- complex software that Microsoft has designed. Microsoft Systems Journal
- spoke with Gordon Letwin, the chief architect of OS/2, to learn more about
- the challenges of designing Operating System/2.
-
-
- A Simple Windows Application For Custom Color Mixing
-
- Our regular programming article presents COLORSCR, a deceptively simple
- program. On the surface, it provides three scroll bars to adjust the red,
- green, and blue components of GDI color specification. The elegance of the
- design is its use of child windows to manage the screen area.
-
-
- Ask Dr. Bob
-
-
- EDITOR'S NOTE
-
- The recently announced IBM Personal Series/2 computers and the first product
- of Microsoft's joint development agreement with IBM, Microsoft Operating
- System/2, advance personal computing to new levels of sophistication and
- improved performance. For the end user, this translates into greater
- ease of use. For the software developer, however, improvements in
- technology often mean increasingly complex procedures that bring new
- challenges and probably a few frustrations as well.
-
- To help the software developer unravel the intricacies of OS/2, this issue
- of Microsoft Systems Journal provides an overview of the new operating
- system as well as a detailed look at OS/2's basic features──multitasking,
- inter-process communication, and memory management. And MSJ doesn't stop
- there──the issue continues to explore OS/2 with articles on the MS-DOS
- compatibility environment, the Windows presentation manager, and an
- interview with Microsoft's chief architect of OS/2, Gordon Letwin.
-
- What will OS/2 mean to the PC world? Although no one disagrees that the
- impact will be deep, standards are not made overnight. Because MSJ is
- committed to the concerns and interests of the software developer, we are
- planning to bring you a great deal more on OS/2, but certainly not to the
- exclusion of MS-DOS. MS-DOS will continue to be important for a long time
- to come.
-
- A reminder to DIAL subscribers: Code samples in this and any previous issue
- are available on-line. Keep an eye on the DIAL Bulletin Board for more
- information on this.
-
- We've said it before, but it's worth repeating. We want MSJ to be your
- journal, your forum for exchanging ideas. Write to us with your suggestions
- for articles, your reactions to this and previous issues.──Ed.
-
-
- Masthead
-
- JONATHAN D. LAZARUS
- Editor and Publisher
-
- EDITORIAL
-
- TONY RIZZO
- Technical Editor
-
- BARRY OWEN
- Managing Editor
-
- CHRISTINA G. DYAR
- Associate Editor
-
- JOANNE STEINHART
- Production Editor
-
- GERALD CARNEY
- Staff Editor
-
- DIANA E. PERKEL
- Editorial Assistant
-
- ART
-
- MICHAEL LONGACRE
- Art Director
-
- VALERIE MYERS
- Associate Art Director
-
- CIRCULATION
-
- WILLIAM B. GRANBERG
- Circulation Manager
-
- L. PERRIN TOMICH
- Assistant to the Publisher
-
- BETSY KAUFER
- Administrative Assistant
-
- Copyright(C) 1987 Microsoft Corporation. All rights reserved; reproduction
- in part or in whole without permission is prohibited.
-
- Microsoft Systems Journal is a publication of Microsoft Corporation, 16011
- NE 36th Way, Box 97017, Redmond, WA 98073-9717. Officers: William H.
- Gates, III, Chairman of the Board and Chief Executive Officer; Jon Shirley,
- President and Chief Operating Officer; Francis J. Gaudette, Treasurer;
- William Neukom, Secretary.
-
- Microsoft Corporation assumes no liability for any damages resulting from
- the use of the information contained herein.
-
- Microsoft, the Microsoft logo, MS-DOS, and XENIX are registered trademarks
- and CodeView is a trademark of Microsoft Corporation. Macintosh is a
- trademark of Apple Computer, Inc. IBM is a registered trademark and
- Personal System/2 is a trademark of the International Business Machines
- Corporation. PageMaker is a registered trademark of Aldus Corporation.
- UNIX is a registered trademark of AT&T. Compaq is a registered trademark
- of Compaq Computer Corporation. Intel is a registered trademark of Intel
- Corporation. Paintbrush is a registered trademark of ZSoft Corporation.
-
- ████████████████████████████████████████████████████████████████████████████
-
- Microsoft Operating System/2: A Foundation For the Next Generation
-
- ───────────────────────────────────────────────────────────────────────────
- Also see the related articles:
- Evolution and History of MS-DOS
- CMD.EXE-OS/2 Command Processor
- System Configuration (CONFIG.SYS)
- ───────────────────────────────────────────────────────────────────────────
-
- Tony Rizzo
-
- After much waiting, speculating, and hoping, the word from Microsoft and
- IBM is out, and that word is Operating System/2 (OS/2), their new single-
- user, multitasking operating environment. OS/2 is the successor to MS-DOS
- (and PC-DOS) and is the first product of Microsoft and IBM's Joint
- Development Agreement, which is especially relevant to MSJ's readers.
-
- At the same time that the software announcements were made, IBM launched
- its next generation of personal computers, the Personal System/2 series.
- On April 2, 1987, a host of new IBM hardware and software offerings were
- introduced, but the focus was decidedly on the new IBM machines: an entry-
- level 8086 PC and new 80286- and 80386-based machines, built mainly around
- in-house VLSI technology and offering substantial price/performance
- improvements over the old hardware.
-
- Very briefly, the new systems boast switchless planar (or self-configuring
- system) boards with on-board video and disk controllers, greatly expanded
- RAM capabilities, 3-1/2" floppies, and new IBM hard files with a 1-to-1
- interleave factor. The new 286 and 386 machines also usher in IBM's 16/32-
- bit Micro Channel architecture, a new bus structure that greatly improves
- data throughput, and a new Video Graphics Array (VGA) display subsystem.
- VGA offers built-in, 640-by-480 resolution with 16 colors, which is
- necessary for advanced graphics─based software.
-
- For software developers, however, the big news is the new Microsoft OS/2
- environment, which will run on the new 286 and 386 machines, as well as on
- the older PC AT and compatibles.
-
- ───────────────────────────────────────────────────────────────────────────
- The Parallel Growth of Hardware and Software. As hardware continues to
- provide enhanced and more sophisticated services, the MS-DOS world will
- slowly give way to OS/2 and new multi-applications systems.
-
- ┌───────────┬─────────────┬────────────────┬───────────────┬──────────────┐
- │ │ 8088 │ 8086 │ 80286 │ 80386 │
- ├───────────┼─────────────┼────────────────┼───────────────┼──────────────┤
- │MS-DOS 1.x │≡≡≡≡≡≡≡≡≡≡≡≡≡│≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡│═══════════════│──────────────│
- │ │ │ │ │ │
- │ │ │ │ │ │
- │MS-DOS 2.x │≡≡≡≡≡≡≡≡≡≡≡≡≡│≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡│═══════════════│──────────────│
- │ │ │ │ │ │
- │ │ │ │ │
- │MS-DOS 3.x │≡≡≡≡≡≡≡≡≡≡≡≡≡│≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡│════════REAL MODE ONLY═══════>│
- │ │ │ │ │
- │ │ │ │ │ │
- │MS-OS/2 │ │ │≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡│≡≡≡≡≡≡≡≡≡≡≡≡≡>│
- │ │ │ │ │ │
- │ │ │ │ │ │
- │MS-OS/2 386│ │ │ │≡≡≡≡≡≡≡≡≡≡≡≡≡>│
- └───────────┴─────────────┴────────────────┴───────────────┴──────────────┘
- ───────────────────────────────────────────────────────────────────────────
-
- The Connection
-
- In contrast to the current situation in which IBM offers PC-DOS and other
- manufacturers offer the slightly different MS-DOS, what IBM calls
- "Operating System/2 Standard Edition" and what Microsoft calls "Operating
- System/2" are in fact one and the same operating system.
-
- Perhaps more important is that the Windows presentation manager, the MS
- OS/2 version of Microsoft Windows, is an integral part of the new
- operating system. Both MS OS/2 and the Windows presentation manager are in
- compliance with IBM's recently announced Systems Application Architecture
- (SAA), a set of common specifications created to promote "consistent"
- applications development across the entire line of IBM System/370,
- System/3x, and personal computer systems.
-
- To date, every version of MS-DOS, including the newly announced Version
- 3.3, has been a single-process operating system. That is, they can only
- execute one application at a time. This restriction is a consequence of
- the 8086/8088 architecture, which is limited to 1Mb of memory and offers
- no hardware support for protecting memory──any application can access at
- will any memory location at any time.
-
- Unrestricted memory and device access by software may very well be a boon
- to those creative programmers who look to squeeze every bit of performance
- out of the MS-DOS, 8086/8088 environment, but it poses serious problems
- for those trying to run more than one application at the same time.
- Furthermore, the memory map of the MS-DOS, 8086/8088 environment leaves
- only 640Kb of main memory available for both applications and the
- operating system.
-
- Though MS-DOS has been continually refined─especially in its file
- management, device support, background processing (e.g., print spooling),
- network support, and very rudimentary attempts at multitasking──it has
- remained fundamentally a single-user, single-application design.
-
-
- Almost the Promised Land
-
- Without hardware-based protection mechanisms, an operating system would
- have a very difficult time managing more than one application; preventing
- applications from accessing conflicting memory locations becomes almost
- impossible. Intel addressed this problem by including a "protected mode"
- of operation in the design of its 80286 processor. On account of this,
- expectations for an enhanced operating system were high when IBM
- introduced the 286-based PC. The 286 processor's protected mode of
- operation, its ability to address up to 16Mb of memory, and the fact that
- the 286 also provided a "real" mode fully compatible with the 8086/8088
- suggested the potential for a more powerful operating system.
-
- Unfortunately, the 286 was designed before the importance of the IBM PC
- (8088) standard was established. As a result, the 286 cannot
- simultaneously support both real and protected modes─a major drawback.
- Adding to the problem, the 286 real mode has every deficiency of the 8086
- environment: the 640Kb barrier, no memory protection, and, as a result, no
- multiple-application operation.
-
- Because of these 286 shortcomings, it was not feasible to build a
- protected-mode operating system with MS-DOS compatibility before the
- release of the PC AT. So, rather than take a half-step, Microsoft and IBM
- decided to invest in a new, extensive development effort that would focus
- on a protected-mode operating system (see "Turning Off the Car to Change
- Gears: An Interview with Gordon Letwin,"). The new operating
- system would take advantage of the hardware, break the 640Kb memory
- barrier, allow a multi-application, multitasking environment, and still
- permit total compatibility with MS-DOS applications.
-
- The result of this effort is MS OS/2.
-
- ───────────────────────────────────────────────────────────────────────────
- Block Diagram of the MS OS/2 Environment. MS OS/2 is a complicated set of
- interrelated modules working together to provide a complete multitasking,
- virtual memory environment.
-
- Application
- Application (or Library Routine)
- │ │ │ Dynamic Link │ │ │
- │ │ │ Function │ │ │
- │ │ │ Call Interface │ │ │
- │ │ │ │
- │ │ ┌─System Session Manager─┐ │ │
- │ │ │ │ │ │
- │ │ │Console Device Interface│ │ │
-
- ╔══════════════════════════════════════════════╗
- ║ Kernel ║
- ║ ┌───────────┐ Process Management ║
- ║ │File system│ Memory Management ║
- ║ └───────────┘ Program Management ║
- ║ Time Management ║
- ║ I/O Management ║
- ╚══════════════════════════════════════════════╝
-
- ░░░░░░░░░░░░░░░░ Device Drivers ░░░░░░░░░░░░░░░░
-
- ░░░░░░░░░░░░░░░░░░░ Hardware ░░░░░░░░░░░░░░░░░░░
-
- ───────────────────────────────────────────────────────────────────────────
-
- The Real Thing
-
- MS OS/2 is a priority-based, pre-emptive, multitasking environment which
- includes a new application interface that isolates applications from low-
- level hardware differences between machines running MS OS/2. OS/2 utilizes
- the protected mode of the 286 chip to allocate an exclusive address space
- to each application. OS/2 controls access to memory, so that different
- applications cannot corrupt each other's data and program areas.
-
- In protected mode, the 286 uses the contents of the segment register to
- access a local descriptor table (LDT) containing information about each
- application. The contents of the segment register serve as an index into
- the descriptor table. The operating system also accesses a global
- descriptor table (GDT), which contains information on code and data that
- is shared by all executing applications (see Figure 1). Both of these
- tables are exclusively managed by the operating system, and together they
- allow up to the entire 16Mb of real memory to be mapped to up to 1
- gigabyte of virtual memory.
-
- Also, application programs are normally prevented from writing directly to
- the underlying hardware. The 286 architecture provides for discrete I/O
- privilege levels (IOPL) to be assigned to applications; this capability
- enables the operating system to control access to hardware. Although it is
- possible for an application to obtain hardware control if it can gain
- access to an appropriate privilege level, the operating system generally
- does not explicitly make that privilege available to an application.
- Protected mode and IOPL are the basic, 286 hardware──based underpinnings of
- MS OS/2 (for a detailed discussion of privilege levels and how OS/2
- performs multitasking, see "OS/2 Multitasking: Exploiting the Protected
- Mode of the 286,").
-
-
- Memory Management
-
- In protected mode OS/2 can simultaneously run many applications, any one
- of which may cause the amount of physical memory available to the system
- to be exceeded. When an application's memory requirements exceed real
- memory (referred to as "memory overcommit"), OS/2 will swap memory
- segments to disk as required (see Figure 3). OS/2 dynamically expands and
- contracts allocated memory and collects fragmented memory allocations
- (known as "memory compaction"), which provides efficient control of
- available memory.
-
- Microsoft has gone to great lengths to protect the enormous investment in
- pre-MS OS/2 software. To achieve this goal, OS/2 provides a compatibility
- mode in which real-mode MS-DOS applications can run (see Figure 3). The
- compatibility mode offers an 8086 environment in which most MS-DOS
- programs will run. However, a small number of programs, especially those
- that directly access hardware or with timing dependence, won't run in
- compatibility mode.
-
- Compatibility mode allows for one MS-DOS program to execute in the
- foreground; background, light protected-mode applications are suspended
- when in this mode. A program executing in the compatibility mode can only
- run in the foreground─such a program is suspended if one of the background
- protected-mode applications is selected for foreground operation.
-
-
- Command Processors
-
- Microsoft OS/2 comes with two command processors, COMMAND.COM and CMD.EXE.
- The former is the real-mode command processor, similar to the MS-DOS
- command processor, with its familiar initialization, resident, and
- transient parts. As in MS-DOS, COMMAND.COM is used to start programs when
- in compatibility mode.
-
- CMD.EXE is the MS OS/2 command processor, which offers an extended command
- syntax. Most important, however, is that CMD.EXE is both shareable and
- swappable. This means that multiple copies of CMD.EXE can be executed, but
- only one copy of CMD.EXE is actually kept in memory, and that copy is
- shared by all protected-mode applications. The only additional memory used
- is that which is unique to any given application.
-
-
- Presentation Manager
-
- OS/2 consists of two fundamental, interrelated parts. The first is the
- kernel, which provides for the major internal workings of the operating
- system. Integrated into the MS OS/2 system is the Windows presentation
- manager. Like Microsoft Windows for MS-DOS, it is a consistent, graphic
- user interface and a device-independent applications environment.
-
- The MS OS/2 Windows presentation manager is derived from the current
- Microsoft Windows, Version 1.03, software. The MS OS/2 Windows
- presentation manager, however, has been significantly enhanced; it has
- been designed to use multiple overlapping windows and to conform to the
- Common User Access component of IBM's SAA.
-
- The key word is consistency. Microsoft will soon release Windows, Version
- 2.0, for the MS-DOS environment with the same user interface as the
- Windows presentation manager. The visual interface will be consistent in
- both MS OS/2 and MS-DOS. Also, because the Windows presentation manager is
- consistent with SAA, this new standard is expected to eventually find its
- way into a large number of computer systems, from micros to mainframes.
-
- For the end user, the Windows presentation manager offers
-
- ■ the ability to run graphics-oriented programs
- ■ a simple and consistent user interface
- ■ access to the session manager
- ■ enhanced mouse and keyboard control
-
- And, for the software developer, the Windows presentation manager
- provides
-
- ■ access to the MS OS/2 multitasking and memory management kernel
- functions
-
- ■ access to intertask communication and data interchange facilities
-
- ■ a device-independent output architecture
-
- ■ a rich and sophisticated graphics library
-
-
- The Session Manager
-
- The new Windows environment gives the user full control over the operating
- environment. The windows environment accomplishes this by offering users a
- session manager, which allows users to control the status of concurrently
- running applications, as well as that of any application running in
- compatibility mode. The user requests access to the session manager with
- the SysReq key, and the session manager responds with a list of active
- applications (see Figure 4). The user can then bring any process to the
- foreground, start any number of new processes, or terminate any number of
- existing processes. There is a lot more to the session manager, however,
- than meets the eye.
-
-
- The New Frontier
-
- When the session manager is active, it switches between different screen
- groups. To understand screen groups we need to take a close, if somewhat
- brief, look at the activity underlying executing applications. MS OS/2
- applications are made up of one or more processes, which are in turn made
- up of one or more threads. A thread is an "atomic unit," a unique entity
- that the MS OS/2 scheduler can allocate CPU cycles to, that is, it is
- actually executable by the CPU. A process can have any number of
- executable threads, each of which can be in one of three states: blocked,
- ready to execute, or executing.
-
- Processes are, in turn, assigned to screen groups. Screen groups are
- collections of processes that write to a common virtual display and
- receive input from a virtual keyboard. As an example, a screen group might
- be a collection of related windows, each of which is running its own
- process and each of which was started inside a parent window (screen).
-
- The screen group writes to a virtual display and reads from a virtual
- keyboard because it is possible to execute any number of screen groups,
- each with its own screen and keyboard needs. Since there is only one
- physical screen and keyboard, there must be some system to prevent
- applications from conflicting with each other. MS OS/2's solution maps
- each screen group to its own unique virtual screen, eliminating any
- possible conflicts.
-
- When a user requests access to an active application, the session manager
- maps that application's screen group to the physical screen─more commonly
- known as the foreground. MS OS/2 manages the mapping of virtual screens to
- the physical screen with the session manager (see Figure 5). The user has
- complete control over which applications MS OS/2 will bring to the
- foreground, and over initiation or termination of new applications (and
- their associated screen groups).
-
- There is a lot going on here. Obviously, OS/2 has provided the user with a
- great deal of flexibility. But what has the software developer gained?
-
-
- API and FAPI
-
- MS OS/2 offers a rich application program interface that allows software
- developers to fully exploit the resources of the operating system. For
- example, the Windows presentation manager provides a large set of
- sophisticated graphics functions, giving the software developer the
- building blocks necessary to create very sophisticated screen images.
-
- MS OS/2 provides a substantial set of CALL-based functions──189 of them to
- be exact──to assist in the development of programs that will run in a
- multitasking environment. These functions, which handle file and device
- management, memory management, and task management, are known as the
- kernel, or operating system Application Programming Interface (API).
-
- The API includes facilities to write device drivers, known as the Device
- Driver Interface. Device drivers handle device initialization, transfer of
- data to and from devices, and device error control, and they provide
- access to low-level BIOS functions. High-level languages such as C can
- call these functions directly. These callable functions completely
- eliminate the need to depend on interrupts (for example, Interrupt 21
- function calls) and the need to write directly to hardware.
-
- API also provides functions to deal with the complexities of inter-
- process communication (IPC). Processes and threads communicate through
- semaphores, queues, pipes, and shared memory (see "OS/2 Inter-Process
- Communication: Semaphores, Pipes, and Queues,"). API's functions
- make such complex programming significantly easier to write.
-
- Microsoft will support all of these functions as a standard. There will be
- a substantial improvement in the ability to port software to future
- releases of MS OS/2, as well as an enhanced ability for the products of
- different software developers to interact with each other. Microsoft
- suggests that applications conforming to the API standard will run within
- the MS OS/2 environment and future releases of OS/2. This creates an
- opportunity to standardize coding practices throughout the entire PC
- environment.
-
- Realistically, OS/2 won't become as pervasive as MS-DOS for some time yet.
- It will most likely be at least 3 years before 286- and 386-based machines
- take over the major share of the PC market. MS-DOS, Version 3.3, will
- remain with us for the foreseeable future, so developers will want the
- ability to write software that can move between MS-DOS and MS OS/2.
-
- This need, however, has already been anticipated and resolved; Microsoft
- guarantees to support a subset of the API function set that will execute
- in every available mode of the two operating environments. Dubbed the
- Family Application Programming Interface (FAPI), this set of 91
- functions──25 of them are somewhat restricted──will permit programs to
- execute under MS-DOS 3.3, within the compatibility mode of MS OS/2, and as
- an actual, although limited, MS OS/2 application (see Figure 6).
-
- API/FAPI represents a unique opportunity for software developers to create
- applications that can deal with current limitations while incorporating
- future advances, thus providing users to run state-of-the-art applications
- on older machines.
-
-
- Software Assistance
-
- By August, Microsoft plans to release a Software Development Kit (SDK)
- that will contain the full specifications of MS OS/2─as well as a beta-
- release version of the operating system kernel. The toolkit will come with
- C Version 4.5, MASM Version 4.5, and the CodeView debugger.
-
- The new compiler and assembler will be able to generate code to be used
- with the new Microsoft Dynamic Linker. The linker will allow code to be
- linked with FAR calls, creating external references to various libraries.
- These external references eliminate the need to link library routines into
- each object (or load) module. The Dynamic Link Editor services program
- needs by loading library routines only when necessary.
-
-
- The LAN Manager
-
- The LAN Manager will play an important role in the MS OS/2 environment and
- can be fully integrated into MS OS/2. It promises an exciting new
- applications environment in which different applications, such as
- spreadsheets, word processors, and database systems, will be able to
- communicate with each other not only application-to-application (e.g.,
- spreadsheet to word processor) within one processor, but also among
- processors connected to a network. The LAN API component allows developers
- to take full advantage of the capabilities of the LAN Manager so as to
- build truly distributed applications. It will also include site-to-site
- electronic-mail capabilities and resource sharing. (For more information
- on the LAN API, see "The MS OS/2 Lan Manager,").
-
-
- Help from the 286
-
- The 386 chip provides much faster speed, 1 gigabyte of addressable real
- memory, and a huge virtual address space─64 terabytes! It is also capable
- of running "virtual" 8086 machines, obviating the need for a compatibility
- mode. Users can run multiple MS-DOS sessions within the multitasking
- environment.
-
- Microsoft estimates that only 10 percent of the MS OS/2 code will have to
- be modified or rewritten, primarily in the memory management component, to
- utilize the 386. This code modification will be completely transparent to
- applications written under the MS OS/2 API on the 286 chip; MS OS/2 has
- been standardized in this respect. With the next release of MS OS/2,
- software developers may gain access to additional tools, but they won't
- have to rewrite any code because of changes made to the existing API. This
- means, of course, that software developers can begin now to create on the
- 286 applications that can easily be ported to the 386──a substantial
- development advantage.
-
-
- A New Philosophy
-
- MS OS/2 is obviously a greatly improved and sophisticated piece of
- software. Software development under this new environment will require a
- considerable effort to understand just how all of the pieces fit together.
- It will take some time before developers can claim mastery of all the
- tools currently available.
-
- There will also be a shift in emphasis from programming around the
- operating system to programming with it. Instead of hunting for
- undocumented interrupts that perform unsupported services and bypassing
- the operating system to get to the hardware, developers will learn to
- integrate all of the tools now at their disposal. With new hardware that
- virtually eliminates performance bottlenecks and a new operating system
- that takes full advantage of that hardware, developers can focus directly
- on their applications.
-
-
- Conclusion
-
- Although they are not exactly right around the corner, it won't be long
- before we will have applications "systems" that interact with each other,
- work together across networks, and share resources efficiently in
- environments spanning micros, minis, and mainframes.
-
- What will become of the "under-the-hood" programmer? What about all of
- those who still long to hack, to get their fingers dirty by directly
- driving hardware, manipulating code and data segments, and fooling with
- undocumented system calls? Well, MS OS/2 is undoubtedly full of mysterious
- code to decipher, but the real challenge will be fully utilizing the many
- tools and facilities that are now available to create state-of-the-art
- applications. For the serious microcomputer software developer the future
- is MS OS/2, and the future is now.
-
-
- ───────────────────────────────────────────────────────────────────────────
- Evolution and History of MS-DOS
- ───────────────────────────────────────────────────────────────────────────
-
- IN AUGUST 1981 Microsoft introduced MS-DOS, Version 1.0, a simple
- operating system providing support for a new IBM machine and a fledgling
- industry. Over the last six years the machine and the industry have grown
- up, and MS-DOS has evolved into MS OS/2, a very sophisticated,
- multitasking operating environment.
-
- Presented here is a brief but complete history of MS-DOS. It is hoped that
- the reader will find it entertaining, nostalgic, and perhaps revealing.
- How many readers know, for example, that the original design of the File
- Allocation Table (FAT) can be traced to Mr. Gates himself?
-
- 1974: Intel introduces the 8 bit 8080 processor.
-
- January 1975: MITS introduces the $400 Altair computer; it has no
- keyboard, no monitor, no disk, and no operating system.
-
- February 1975: Paul Allen and Bill Gates develop and sell their own
- version of BASIC to MITS for the Altair.
-
- February 1976: Paul Allen, now working for MITS, asks Bill Gates to write
- a disk-based version of BASIC for the Altair.
-
- Bill Gates creates a working model of his disk BASIC in 10 days. He
- designs a disk layout and file structure based on a centralized File
- Directory and File Allocation Table (FAT). He also includes a rudimentary
- set of file services in the disk BASIC he is developing.
-
- 1976-1978: Microsoft Disk BASIC is ported to all major 8 bit personal
- computers. An assembler and linker are developed for 8080- and Z80- based
- systems.
-
- April 1978: Intel announces the 8086, a 16 bit processor.
-
- January 1979: Tim Paterson of Seattle Computer Products begins work on a
- plug-in 8086 processor card to bring the power of the 8086 to the S-100
- bus.
-
- June 1979: Microsoft and Tim Paterson show Microsoft's BASIC running on
- Paterson's 8086 card at the National Computer Conference in New York.
-
- April 1980: Delays hold up the delivery of CP/M 86. Tim Paterson decides
- to write his own "Quick and Dirty" OS, which becomes known as 86-DOS. He
- incorporates the FAT structure first designed by Bill Gates for Disk-
- BASIC, and some features and techniques underlying MS-DOS.
-
- August 1980: IBM takes its first step towards producing the IBM PC,
- planning to use readily available, off-the-shelf 8 bit hardware. IBM
- visits Microsoft, asking if Microsoft can write a ROM-based BASIC for the
- computer IBM is developing.
-
- Microsoft suggests that IBM consider the 16 bit architecture. IBM's
- "Project Chess" goes on to become the 8088 (8086-based) IBM PC.
-
- The first working version of 86-DOS runs on Tim Paterson's 8086 card. This
- is essentially the birth of what will become known as MS-DOS.
-
- September 1980: IBM asks Microsoft to provide COBOL, FORTRAN and Pascal
- for their personal computer. Microsoft suggests to IBM that an operating
- system would be necessary to develop the additional languages.
-
- October 1980: Microsoft submits a proposal to IBM that includes MS-DOS.
-
- November 1980: The proposal is accepted by IBM. A prototype machine
- arrives at Microsoft and a small DOS team begins a concentrated period of
- work.
-
- February 1981: 86-DOS runs on the prototype for the first time. Over the
- next half year the OS is refined and becomes MS-DOS, Version 1.0.
-
- August 1981: IBM introduces the IBM PC, and announces three operating
- systems: MS-DOS, CP/M 86, and the P System. For several months MS-DOS is
- the only OS available. It is also priced substantially lower than CP/M.
-
- MS-DOS incorporates a number of advanced features:
-
- ■ Greater device independence.
- ■ Greater data integrity.
- ■ Simplified handling of peripheral devices.
- ■ A replaceable COMMAND.COM processor.
-
- February-April 1982: Microsoft introduces for the MS-DOS environment:
-
- ■ The Macro Assembler.
- ■ The FORTRAN Compiler.
- ■ The COBOL Compiler.
-
- June 1982: MS-DOS, Version 1.1, is announced, providing support for
- double-sided, eight sector diskettes on the IBM PC.
-
- March 1983: IBM announces the PC XT.
-
- Microsoft announces the release of MS-DOS, Version 2.0, which includes:
-
- ■ Supports for hard disks including a XENIX(c)-like hierarchical
- directory structure.
-
- ■ File Handles, allowing programs to reference files anywhere they
- have been loaded.
-
- ■ The ability to redirect the I/O of a program to any file or device,
- and implements pipes and filters.
-
- ■ Support for nine-sectored diskettes, increasing storage to 360 Kb.
-
- ■ Provision for installable device drivers, promoting logical device
- independence.
-
- ■ Background printing through PRINT.COM.
-
- ■ ANSI.SYS device driver, providing support for serial "stream"
- processing to give cursor positioning and color control information to
- the monitor.
-
- March 1984: IBM introduces the PCjr with half-height disk drives, and MS-
- DOS, Version 2.11, is introduced, providing support for it.
-
- August 1984: IBM announces the 80286 based PC AT, with a 20 Mb hard disk
- and 1.2 megabyte high density diskette drive.
-
- Microsoft introduces MS-DOS, Version 3.0, which includes:
-
- ■ A rewritten MS-DOS kernel and a new standard set of I/O calls.
-
- ■ ISO Open System Interconnect (ISO) based model for networking.
-
- ■ Network redirector and file sharing support for the IBM PC Network
- Adapter Card.
-
- November 1984: Microsoft introduces MS-DOS, Version 3.1, and Microsoft
- Networks (MS-Net) including:
-
- ■ Redirector and file sharing services for non-IBM network cards.
- ■ Transport and server functions to all MS-DOS systems.
- ■ Network spooled printing.
- ■ The basic structure for Installable File Systems.
-
- June 1985: Microsoft, Intel, and Lotus establish the Lotus-Intel-Microsoft
- Extended Memory Specification, allowing programs to access memory beyond
- the MS-DOS limit of 640 Kb of RAM.
-
- August 1985: IBM and Microsoft enter into a Joint Development Agreement.
-
- January 1986: Microsoft introduces MS-DOS, Version 3.2, which includes
- support for 3.5" diskettes.
-
- April 1987: IBM announces the Personal System/2 series of computers.
-
- Microsoft announces MS OS/2, with an integrated Windows presentation
- manager, MS-DOS, Version 3.3, Windows 2.0, and a new LAN Manager.
-
-
- ───────────────────────────────────────────────────────────────────────────
- CMD.EXE-OS/2 Command Processor
- ───────────────────────────────────────────────────────────────────────────
-
- OS/2 provides the user with a new command processor that extends the
- functionality of the command language and provides enhanced batch file
- support.
-
- Command Line Operators
-
- The following shows both the old operators as well as the new, from
- highest to lowest precedence.
-
- command.com cmd.exe Function
-
- ^ Lexical Escape Charactor
- () Command Grouper
- < < I/O Redirector -+ Same
- > > I/O Redirector |->Precedence
- >> >> I/O Redirector -+ Level
- | | Pipe Operator
- && And Operator
- || Or Operator
- & Command Separator
-
- The operators common to both environments work the same way they
- previously did.
-
- The Lexical Escape Character allows the precedence operators to be used as
- regular charactors. Refer to the following,
-
- 1. echo go home > kid
- 2. echo go home ^> kid
-
- In the first case a file called kid is created with the line "go home" as
- its contents. In the second, "go home > kid" is echoed to the screen.
-
- The Command Grouper works in exactly the same way parentheses work in
- equations, to establish explicit precedence. Refer to the following,
-
- directory a: contains the files temp1, temp2 and temp3
-
- temp1 contains "a"
- temp2 contains "b"
- temp3 contains "c"
-
- 1. dir a:*.* >> temp1 & (temp1 >> temp2 & temp2 >> temp3)
- 2. (dir a:*.* >> temp1 & temp1 >> temp2) & temp2 >> temp3
-
- After execution of the first command line temp1, temp2 and temp3 will look
- as follows
-
- temp1 contains a,temp1,temp2,temp3
- temp2 contains b,a
- temp3 contains c,b,a
-
- If the second line had been executed instead, temp1, temp2 and temp3 would
- look as follows
-
- temp1 contains a,temp1,temp2,temp3
- temp2 contains b,a,temp1,temp2,temp3
- temp3 contains c,b,a,temp1,temp2,temp3
-
- The parentheses determine the order of execution. Additionally, lower
- precedence operators can be executed prior to those of higher precedence.
-
- The Pipe and Redirection operators work in the same way they previously
- did.
-
- The And operator performs the command to its right if and only if the
- command to its left successfully executes. For example, in
-
- dir exist.txt && copy exist.txt lpt1
-
- if the DIR command is successful, then exist.txt is printed. If DIR was
- not successful the command on the right will not be executed.
-
- The Or operator performs one of two commands depending on the success or
- failure of the first command given. For example, in the following
-
- more < document.txt || more < document.doc
-
- more will either output the contents of document.txt and then exit, or, if
- document.txt doesn't exist, will output the contents of document.doc.
-
- The Command Separator allows more than one command to be typed on a
- command line. For example,
-
- dir a:\*.* > temp & dir b:\*.* >> temp
-
- places the directory listing of the root directory of a: in a file called
- temp (which is either overwritten if it exists or is created if it doesn't
- exist) and then immediately executes the next command, which appends the
- contents of the root directory of b: to temp.
-
- BATCH FILE ENHANCEMENTS
-
- OS/2 supports all of the old batch file commands and adds four new ones.
- They are
-
- CALL
- EXTPROC
- SETLOCAL
- ENDLOCAL
-
- The Call command finally provides a means for users to chain together
- batch files so that when a called batch file ends it returns control to
- the calling batch file.
-
- Extproc allows a user to run batch files designed for command processors
- other than CMD.EXE. The command is issued as the first line of the batch
- file and defines the alternate command processor.
-
- Setlocal allows the user to define local drive, directory and environment
- variables for the current batch file. When the command is issued it saves
- the existing drive, directory and environment information and replaces it
- with the user specified information.
-
- In conjunction with the setlocal command, the endlocal command restores
- the drive, directory and environment information previously saved through
- setlocal, and allows additional setlocal calls.
-
- If a setlocal call is issued and the batch file issuing the setlocal call
- terminates without issuing an endlocal call, the operating system restores
- the original drive, directory and environment information, insuring that
- the state previously known to CMD.EXE still exists.
-
-
- ───────────────────────────────────────────────────────────────────────────
- System Configuration (CONFIG.SYS)
- ───────────────────────────────────────────────────────────────────────────
-
- CONFIG.SYS is the file that contains the commands used to configure the
- system. Only a single CONFIG.SYS file is needed to configure the system
- for both real-mode and protected-mode operations.
-
- During system initialization, OS/2 opens and reads the CONFIG.SYS file in
- the root directory of the drive from which it was started and interprets
- the commands within the file. If the file is not found, OS/2 assigns
- default values for the configuration commands.
-
- The following list summarizes the configuration commands for OS/2:
-
- buffers Determine the number of buffers to allocate for disk I/O.
-
- country Select the format for country-dependent information.
-
- device Specify path and filename of a device driver to be
- installed.
-
- iopl Specify whether I/O privilege is to be granted or not.
-
- maxwait Set the time limit for calculating CPU starvation.
-
- memman Select memory management options.
-
- priority Disable dynamic priority variation in scheduling regular
- class processes.
-
- protectonly Select the modes of operation.
-
- protshell Specify path and filename of the OS/2 top-level command
- processor.
-
- run Start a system or daemon process during system
- initialization.
-
- swappath Specify the location of the swap file.
-
- timeslice Set the time slice values for process scheduling.
-
- threads Set the maximum number of threads in the system.
-
- trace Select system trace.
-
- tracebuf Set the system trace buffer size.
-
- The following commands are ignored:
-
- files
- lastdrive
-
- The following commands apply only to the configuration for real-mode and
- only if the MODE command specifies that old applications will be run.
- These real-mode-only commands are documented after the OS/2 configuration
- commands.
-
- break Check for Ctrl-Break.
- fcbs Determine file control block management information.
- rmsize Select the amount of memory for real-mode applications.
- shell Load and start the top-level command processor.
-
-
- Figure 1: Virtual/Real Memory Mapping. The global descriptor table (GDT)
- and every local descriptor table (LDT) can each map 8191 64K
- segments or .5 Gb (8191 x 64K bytes= .5 Gb). There is one GDT for
- the entire MS OS/2 system, shared by every application. Each
- specific application has its own private LDT. Together, the
- shared GDT and the private LDT provide a virtual address space of
- up to one Gb for each application. The virtual addresses are
- managed by the operating system, and are mapped to a real memory
- space of up to 16Mb.
-
- Application Address Spaces
-
- Application n-┌──────────┐
- ┌─┴─────────┐│
- ┌─┴──────────┐││
- ┌┴──────────┐ │││ 1Gb Real
- Application 1-│ ─ ─ ─ ─ ─ │ │││ | Memory
- ┌───────── │_ _Code_ _ │ │││ | ┌───────┐16M Secondary
- │ │ Data │ │││ | ≈ ≈ Storage
- Unique ├ ─ ─ ─ ─ ─ ┤ │││ | ├ ─ ─ ─ ┤ ┌────────┐
- to Each ≈ ≈ │││ LDTs │ Code │╔═══════╗│ .. │
- Application │ │ │├┘ | ├ ─ ─ ─ ┤║Virtual║├─ ─ ─ ─ ┤
- │ ├─ ─ ─ ─ ─ ─┤ ├┘ | │ Code │╣Memory ╠│ Code │
- │ │ Data ├─┘ | ├ ─ ─ ─ ┤║ Mgt ║├ ─ ─ ─ ─┤
- ╞════════ ├─ ─ ─ ─ ─ ─┤ ─ ─ ─ ─5Gb─ ─ │ Data │╚═══════╝│ Data │
- │ ≈ ≈ | ├ ─ ─ ─ ┤ ├─ ─ ─ ─ ┤
- Shared ├─ ─ ─ ─ ─ ─┤ | ├ ─ ─ ─ ┤ │ .. │
- │System Code│ GDT │ System│ └────────┘
- │ │ and Data │ | └───────┘
- └──────────┴───────────┘ |
-
-
- Figure 2: Protected Mode Memory Map
-
- ╔═════════════╗
- ║ Real Memory ║
- ╚═════════════╝
- 16M╔═══════════════════╗ ───┐
- ║ New Application ║ │ Moveable
- ≈ code and data ≈ ├─Swappable or
- ║ segments ║ │ Non-swappable
- 1M╟───────────────────╢ ───┘
- ║ ║
- ║ BIOS and video ║ Fixed
- ║ buffers ║
- 640K╟───────────────────╢ ───┐
- ║ ║ │
- ║ New Application ║ │ Moveable
- ≈ code and data ≈ ├─Swappable or
- ║ segments ║ │ Non-swappable
- ║ ║ │
- ╟─ ─ ─ ─ ─ ─ ─ ─ ─ ─╢ ───┘
- ║ OS/2 ║ Fixed
- ║ ║
- 0╚═══════════════════╝
-
-
- Figure 3: Compatibility Mode Memory Map
-
- ╔═════════════╗
- ║ Real Memory ║
- ╚═════════════╝
- 16M╔═══════════════════╗ ───┐
- ║ New Application ║ │ Moveable
- ≈ code and data ≈ ├─Swappable or
- ║ segments ║ │ Non-swappable
- ╟─ ─ ─ ─ ─ ─ ─ ─ ─ ─╢ ───┘
- ║ OS/2 ║ Fixed
- 1M╟───────────────────╢ ───
- ║ BIOS and video ║ Fixed
- ║ buffers ║
- 640K╟───────────────────╢
- ╟─ ─ ─ ─ ─ ─ ─ ─ ─ ─╢ ───┐
- ║ Single ║ │
- ≈ "old" Application ≈ │
- ║ ║ ├─Variable size
- ╟───────────────────╢ │
- ║ "MS-DOS 3.x" ║ │
- ╟───────────────────╢ ───┘
- ║ OS/2 ║ Fixed
- ║ ║
- 0╚═══════════════════╝
-
-
- Figure 5: OS/2 maps multiple virtual I/O to the
- physical screen and keyboard.
-
- Screen Groups
- ┌─────────┐ ┌─────────┐ ┌─────────┐
- │ │ │▓▓▓ ┌──┐ │ │ │
- │ C> │ │▓▓ │/\│ │ │ ? │
- │ │ │ └──┘ │ │ │
- └─────────┘ └─────────┘ └─────────┘
- Character User Presentation Manager Special Purpose
- Interface Graphics Desktop Publishing │
- Light WP │ │
- Database ┌─────────────────────────────┐ │
- Spreadsheet ──│ Visual Console I/O Management│───────┘
- └──────────────────────────────┘
- ╔════════════════╗ ┌────────────────┐
- ║ ░░░░░░░░░░░░░░ ║█ │ Physical Video │
- ║ ░░░░░░░░░░░░░░ ║█ │ Manager │
- ║ ░░░░░░░░░░░░░░ ║█ └────┬───────────┘
- ║ ░░░░░░░░░░░░░░ ║█───────┘
- ╔═╝────────────────╚═╗
- ║ ║█
- ╚════════════════════╝█
- ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
-
-
- Figure 6: FAPI assures the software developer that an application will
- run under both MS-DOS 3.x and MS OS/2.
-
- 16M ┌──────────────────────────┐
- │ ┌────────────┐ │
- │ │ OS/2 │ │
- │ │ ┌──────────┴───┐ │
- │ │ │ OS/2 │ │
- │ │ │ ┌───────────┴────┐ │
- │ │ │ │ OS/2 │ │
- │ └─┤ │ Protect │ │
- │ │ │ Mode │ │
- │ └──┤ Full New │ │
- │ │ API │ │
- │ └────────────────┘ │
- MS-DOS 3.n │ ┌────────────────┐ │
- ┌──────────────────────┐ │ │ "Family API" │ │
- │ Reserved │───┼────│ Applications │ │
- ├──────────────────────┤ 1M├ ─ ─ └────────────────┘ ─ ┤
- │ DOS 3.n │ │ Reserved │
- │ Compatibility │640K├ ─ ─ ┌────────────────┐ ─ ┤
- │ Environment │────┼────│ DOS 3.n │ │
- └──────────────────────┘ │ │ Compatibility │ │
- │ │ Environment │ │
- │ └────────────────┘ │
- 0 └──────────────────────────┘
-
- ████████████████████████████████████████████████████████████████████████████
-
- The OS/2 Windows Presentation Manager: Microsoft Windows On the Future
-
- Manny Vellon
-
- Last April, Microsoft announced a series of new products, including a new
- operating system, Microsoft Operating System/2 (OS/2). An important part
- of this announcement was the inclusion of Microsoft Windows as a standard
- part of the OS/2 operating system. Currently, Microsoft Windows must be
- purchased separately and installed under MS-DOS.
-
- The Windows presentation manager will be tightly integrated with OS/2 and
- will provide the same benefits that Microsoft Windows provides to MS-DOS:
- a windowed, graphical user interface and support for a variety of input
- and output devices. Through the presentation manager, OS/2 will replace
- the well-known A> prompt of MS-DOS with window-based screens.
-
- This union of Windows with OS/2 strengthens the role of Windows in future
- system and application products and also addresses the ease-of-use issues
- that have been associated with MS-DOS and IBM PC software. Windows
- provides a more intuitive interface that allows novice users to learn
- products more quickly.
-
- Also important is IBM's support of Microsoft Windows in its new products.
- This support ensures that Windows will become a standard part of the
- operating environment and encourages other hardware manufacturers to
- support it as well.
-
-
- Protected Mode
-
- The new IBM PS/2 series, the IBM PC AT, the Compaq 386, and other Intel
- 286- and 386-based computers are capable of running in real mode or
- protected mode. In real mode, they operate much like the Intel 8088-based
- IBM PC: they are limited to using 1 megabyte (Mb) of memory, they can
- usually run only one program at a time, and they are vulnerable to
- crashing when a program goes awry. In protected mode, the 286- and 386-
- based computers don't have those limits.
-
- Under OS/2's protected-mode operating system, programs are no longer
- limited to 640K; applications can take advantage of up to 16 Mb of real
- memory and up to 1 gigabyte of virtual memory. OS/2 is able to perform
- multitasking──it can run several programs simultaneously by quickly
- switching among them. OS/2 is more robust than real-mode operating
- systems; programs run independently of each other, and if one crashes
- it is less likely to affect the others.
-
- Currently, Microsoft Windows can also do many of these functions. By
- performing a variety of sophisticated functions, Windows can take
- advantage of extended memory if available and can provide rudimentary
- multitasking. With the OS/2 Windows presentation manager, however, these
- functions are much easier to implement since the underlying operating
- system kernel includes these capabilities.
-
-
- User Interface
-
- In addition to this symbiosis "under the hood," OS/2 Windows is integral
- to the operating system at the user level. The Windows presentation
- manager will be the standard user interface for the new operating system.
- Users won't have to learn about disk directories, filenames, and cryptic
- commands; executing programs and managing the OS/2 file system with the
- OS/2 Windows presentation manager will be intuitive and fast.
-
- Although the new OS/2 Windows bears a strong resemblance to today's
- Microsoft Windows, there are some substantial differences between the two.
- Most significant are the differences in user interfaces. To eliminate
- these differences so as to produce a common user interface, Microsoft has
- announced a new version of Microsoft Windows──Version 2.0 (see Figure 2).
- This version, while still a real-mode version for MS-DOS, employs the same
- user interface to be used by the OS/2 Windows presentation manager.
-
- When Microsoft Windows was first developed, it used tiled windows (see
- Figure 1).
- Overlapped windows were considered too slow and unusable with
- low-resolution displays. In order to respond to customer requests for
- overlapping windows, and because optimized graphics algorithms and
- improved processing speeds have eliminated performance bottlenecks that
- are found in older technology, the new Windows uses overlapped windows
- instead of tiled windows.
-
- New Windows products will also have improved keyboard interfaces. Although
- Windows is best used with a mouse, it is possible, and sometimes
- preferable, to use it solely through the keyboard. Changes in the user
- interface will make this easier.
-
- First, the keyboard interface has been improved in order to allow direct
- access to items in dialog boxes. In Figure 4, for example, the user can
- type Alt-F to quickly position the cursor in the filename field of the
- dialog box. Access to menus from the keyboard has also been enhanced by
- allowing the developer to select any letter from a menu command to execute
- the item rather than being limited to the first letter, as in the case of
- Windows Version 1.03 and Version 1.04. This helps to make it easier for
- software developers to provide meaningful command names while still
- providing fast keyboard access to commands.
-
- The second significant change affects mouse operation. Currently, Windows
- employs pull-down menus. You click down on the menu bar to make visible a
- pop-up menu; then, while holding down the mouse button, you drag down to
- the desired command in the pop-up menu and let go of the button. This
- technique is fast, but it is prone to accidental selections and requires
- considerable manual dexterity. The new interface allows you to click and
- let go on the menu bar to make the pop-up menu visible and then click down
- on the desired command within the given menu.
-
- Finally, the new interface employs some new terminology (for example,
- minimize and maximize, instead of icon and zoom) and some additional
- keyboard operations.
-
- Users who are accustomed to the old keyboard interface won't be forced to
- learn the new one. Microsoft Windows, Version 2.0, will work in both the
- old and new styles to help users get accustomed to the new interface.
- However, the OS/2 Windows presentation manager will use this new interface
- only.
-
- ───────────────────────────────────────────────────────────────────────────
- Progression for Windows-based Applications
-
- Windows 1.03
-
-
- Optional Changes
- Edit Resource Files Enhanced Printer Interfaces
- Implement Mult Doc Facil Error Condition Support
-
-
- Windows 2.0
- Overlapped Applications EMS/EEMS Support
- New User Interface Display Performance
- Multiple Document Facilities
-
-
- Mechanical API Changes Mode Rules
- GDI-to-GPI Access to OS/2 Facilities
- Conform to Protected
-
-
- OS/2 Windows
- Overlapped Applications Preemptive Multitasking
- New User Interface Large Memory (16Mb)
- GPI and New Device Drivers
-
- ───────────────────────────────────────────────────────────────────────────
-
- API
-
- Although Microsoft Windows, Version 2.0, and the OS/2 Windows presentation
- manager appear to be similar and have many common functions, programs
- written for Microsoft Windows will have to be modified to work with the
- OS/2 Windows presentation manager. OS/2 restricts several operations in
- programs; for example, software interrupts are not allowed. Programs
- written for MS-DOS will also have to be modified to work in OS/2.
-
- Besides the changes required by the new operating system, such as
- replacing software interrupts with operating system calls, other changes
- have been made to the OS/2 Windows Application Program Interface (API) in
- order to standardize coding practices, improve error handling, and exploit
- new graphics capabilities. Fortunately, many of these changes are
- "mechanical" in nature. To follow the new coding practices, for example,
- all window manager calls are preceded with "Win": "WinCreateWindow",
- "WinShowWindow", etc. Other changes entail reordering parameters.
- Applications that make heavy use of graphics will require the most
- changes; the OS/2 Windows presentation manager contains a new graphics
- library that requires a variety of changes to be made to the API.
-
-
- The Future
-
- OS/2 is not intended to replace MS-DOS. Microsoft expects that real-mode
- (MS-DOS) and protected-mode (OS/2) products will continue to co-exist
- until Intel 286- and 386-based computers predominate. MS-DOS and Microsoft
- Windows will continue to be developed and marketed for low-end machines,
- while OS/2 addresses newer, more-powerful computers. The Windows user
- interface will serve as a bridge for users, allowing them to operate each
- of these classes of machines regardless of the underlying hardware.
-
- In time, Microsoft expects Intel 386-based machines to become the standard
- hardware environment for powerful new business and engineering
- applications. Microsoft will extend OS/2 to exploit the additional
- capabilities of the Intel 386 and will upgrade OS/2 in the future. Once
- more, the Windows presentation manager interface will help to keep these
- changes invisible to the user.
-
-
- Figure 2: Comparison of Current and Future Windows Versions
-
- ╓┌───────────────────────────────┌────────────────┌──────────────┌───────────╖
- Microsoft
- Windows Microsoft MS-OS/2
- 1.03 & 1.04 Windows 2.0 Windows
-
- Presentation Spaces Tiled Overlapped Overlapped
-
- More Consistent User --- Yes Yes
- and Keyboard Interface
-
- Processor Environments 8088, 8086, 8088, 8086, 286, 386
- 286, 386 286, 386
-
- Large Memory Support --- EMS/EEMS 16Mb
-
- Multitasking Non Non Fully
- Preemptive Preemptive Preemptive
-
- Enhanced Display Performance --- Yes Yes
- Microsoft
- Windows Microsoft MS-OS/2
- 1.03 & 1.04 Windows 2.0 Windows
- Enhanced Display Performance --- Yes Yes
-
- Runs Existing Windows (1.03) Yes Yes Yes
- Application
-
- Graphics API GDI GDI GDI
-
- Multiple Document Interface --- Yes Yes
-
- Device Drivers --- Enhanced New Model
-
- Old Application Support --- Improved Improved
-
- Integral Part of OS --- --- Yes
-
- Protected Mode Execution --- --- Yes
- Applications Execution
-
- Microsoft
- Windows Microsoft MS-OS/2
- 1.03 & 1.04 Windows 2.0 Windows
- New Development API --- --- Yes
-
- New User "Shell" --- --- Yes
- and Keyboard Interface
-
-
- Figure 3a: Microsoft Windows API. Programs written for MS-DOS will
- have to be modified to work in OS/2. Many of these changes
- are mechanical in nature──for example, all window manager
- calls are preceeded with "Win". Compare the above Windows 2.0
- code with that shown in Figure 3b below.
-
- ∙
- ∙
- ∙
- int PASCAL WinMain( hInstance, hPrevInstance, lpszCmdLine, cmdShow )
- HANDLE hInstance, hPrevInstance;
- LPSTR lpszCmdLine;
- int cmdShow;
- {
- MSG msg;
- HWND hWnd;
- NPWNDCLASS pHelloClass;
- ∙
- ∙
- ∙
- /* Allocate and initialize class data structure pHelloClass */
- ∙
- ∙
- ∙
- /* Create a window class */
- if (!RegisterClass( (LPWNDCLASS)pHelloClass ) )
- return FALSE;
-
- /* Create an application window */
- hWnd = CreateWindow((LPSTR)"Class", (LPSTR) "Title",
- WS_TILEDWINDOW, 0, 0, 0, 0, (HWND)NULL,
- (HMENU)NULL, (HANDLE)hInstance,
- (LPSTR)NULL );
- ∙
- ∙
- ∙
- /* Process messages*/
- while (GetMessage((LPMSG)&msg, NULL, 0, 0)) {
- TranslateMessage((LPMSG)&msg);
- DispatchMessage((LPMSG)&msg);
- }
-
- return (int)msg.wParam;
- }
-
- CAPTION: OS/2 Windows Presentation Manager Program
- ∙
- ∙
- ∙
-
-
- Figure 3b: OS/2 Windows presentation manager API (see figure 3a)
-
- int cdecl main(argc, argv)
- int argc;
- char *argv[];
- {
- QMSG qmsg;
- HAB hab;
- HMQ hmq;
- HWND hwnd, hwndFrame;
-
- /* get an anchor block */
- hab = WinInitialize();
-
- /* create a message queue for the application */
- hmq = WinCreateMsgQueue(hab, 0);
-
- /* create a window class */
- if (!WinRegisterClass( (LPCH) "Class",
- WndProc,
- WS_SYNCPAINT | WS_CLIPSIBLINGS |
- WS_SIZEREDRAW,
- 0,
- NULL)) return FALSE;
-
- /* create an application window */
- hwndFrame =
- WinCreateStdWindow(NULL,
- FS_MENU | FS_TITLEBAR | FS_MINMAX |
- FS_SIZEBORDER | FS_SYSMENU,
- (LPSTR) "Class",
- (LPSTR) "Title", 0L, NULL,
- IDM_APPMENU,
- (HWND far *)&hwnd);
- ∙
- ∙
- ∙
- /* process messages */
- while (WinGetMsg(hab, (LPQMSG)&qmsg, NULL, 0, 0))
- WinDispatchMsg(hab, (LPQMSG)&qmsg);
-
- /* destroy the resources used by the application */
- WinDestroyWindow(hwndFrame);
- WinDestroyMsgQueue(hmq);
- WinFinalize(hab);
- }
-
- ████████████████████████████████████████████████████████████████████████████
-
- The OS/2 MS-DOS Environment:Compatibility and Transition for MS_DOS Programs
-
- ───────────────────────────────────────────────────────────────────────────
- Also see the related article:
- Some Programming Don'ts for OS/2
- ───────────────────────────────────────────────────────────────────────────
-
- Joel Gillman
-
- Microsoft's multitasking, virtual memory operating system has finally been
- unveiled. Now that MS OS/2 is a reality, programmers may be wondering with
- some trepidation whether they'll have to rewrite all their MS-DOS
- programs.
-
- The good news is that nearly all of your existing code, both commercial
- products and custom-made utilities, will run just fine in OS/2's MS-DOS
- compatibility environment, an operating mode that emulates MS-DOS 3.3.
- Programs that need low-level access will not run in the compatibility
- mode, but these are exceptions. Most spreadsheets, word processors, and
- many other applications programs will work just fine.
-
- However, once you start writing code to run in the multitasking protected
- mode of OS/2, you will have to do some things differently. There's a
- silver lining in this, because you will end up with better code once
- you've unlearned some of the bad habits you picked up writing in MS-DOS.
- Multitasking implies resource sharing, so you can't be overly greedy with
- system resources anymore. But what you may lose in resource access you
- gain in the power of multitasking.
-
- After booting OS/2, the user is greeted by the session manager, from which
- the user can start any number of protected-mode applications or enter
- compatibility mode. For each protected-mode program, the session manager
- creates a new protected mode execution environment, called a screen group.
- Each screen group has a command processor (CMD.EXE, which corresponds to
- the MS-DOS 3.x COMMAND.COM), a virtual screen buffer, a virtual keyboard,
- and a virtual mouse, along with a virtual memory space of up to 16
- megabytes (Mb) depending on the total number of screen groups.
-
-
- Compatibility Mode
-
- The MS-DOS compatibility mode in OS/2 uses the 80286 chip's real mode,
- which is engaged by a technique called mode switching (see "OS/2: Turning
- Off the Car to Change Gears,"). Mode switching emulates a full
- system reset without disrupting operation, allowing the processor to
- switch from protected to real mode. The compatibility mode gives the
- system an 8086 interface with 1 Mb of address space and emulates MS-DOS
- 3.x with the MS-DOS SHARE utility installed. OS/2 needs up to 500 Kb of
- system memory in a typical configuration──substantially more than the 50 Kb
- required by MS-DOS 3.x.
-
- The compatibility mode recognizes all of the documented MS-DOS services. A
- number of undocumented interrupt 21H services also exist under MS-DOS 3.x,
- but since OS/2 does not recognize most of the undocumented MS-DOS
- services, programs that use them won't run in the compatibility mode.
-
- The compatibility mode supports a ROM data area and accepts service
- interrupts, but you cannot call ROM services directly by address. You must
- use the interrupt 10 through interrupt 1A services instead (see Figure 1).
- Applications can call any hardware interrupt except the CMOS
- clock/calendar interrupt or an interrupt already taken over by any OS/2
- device driver other than the keyboard. The compatibility mode will issue
- interrupt 28h (spooler interrupt)──so you can still run SideKick(R)──and
- interrupt 24h (critical error handler).
-
- A program running in the MS-DOS compatibility mode freezes up if the
- system places it in the background by switching to the protected mode.
- Programs frozen in the background receive no CPU service or interrupts. A
- program that determines the time of day by counting clock ticks, for
- example, will generate inaccurate times if it goes into the background.
-
- Version-specific applications won't run in the compatibility mode, because
- the MS-DOS version number of OS/2 is 10. A way around this is to modify
- the code so that it calls DOSGETVERSION and, if it gets back 10,
- determines which mode it's executing by calling DOSGETMACHINEMODE. The
- program can then take the appropriate action.
-
-
- Handling Devices
-
- Most of your old device drivers will not run in compatibility mode. OS/2
- doesn't support any of the MS-DOS block device drivers, such as those used
- with disk or tape drives. The only character device drivers it supports
- are VDI (video display interface) and CON (console) drivers, since
- character device drivers will work only if they are polled rather than
- interrupt-driven. The system supports all of the device driver command
- packets shown in Figure 2.
-
- When you run a driver, only programs running in the compatibility mode can
- use its device. The device isn't available to protected-mode applications.
- Device drivers cannot call user code, because they operate at a higher
- privilege level than the user program.
-
- Drivers are installed the same way as in MS-DOS(R), using the configuration
- command
-
- device = driver filename
-
- MS-DOS device drivers are loaded and initialized in compatibility mode.
- Initialization is in most respects the same as in MS-DOS, except that no
- interrupt 21h (hardware-independent) functions can be performed from the
- initialization code.
-
- Compatibility mode restricts which devices programs can manipulate. Sound-
- generating programs that need a higher frequency time base for more
- precise pitch control can remap the 8253 clock/timer chip, that is, assign
- different numbers to its system interrupts. Remapping the 8259 interrupt
- controller is not allowed. Such programs, which must remap the 8259 in
- order to trap keystrokes, will not run. Applications can still hook
- keystrokes after OS/2 gets them, however.
-
- Programs that need low-level disk I/O access for copy protection purposes
- cannot remap the disk controller. Programs do have direct access to the
- disk controller via interrupt 13h (floppy disk services), interrupt 25h
- (absolute disk read), and interrupt 26h (absolute disk write). Note that
- interrupt 13h and interrupt 26h are not allowed for fixed media such as
- hard disks.
-
- High-speed communications applications that must remap the DMA controller
- won't run because the operating system remaps the controller. Applications
- can remap the COM, AUX, and parallel ports, although using one of these
- ports in the compatibility mode makes it unavailable to protected mode
- programs and vice versa.
-
-
- 80286 Restrictions
-
- Programmers writing applications for MS-DOS developed some programming
- techniques and coding shortcuts to enhance performance, even though the
- books and manuals tended to discourage using them. Many of these
- techniques won't work in OS/2 because of differences between the 8086 or
- 8088 chip and the 80286. You'll have to observe several restrictions if
- you want to run your applications in the compatibility environment or in
- protected mode.
-
- First, don't use wrap-around segment offsets. The 8086 and 8088
- microprocessors translate an out-of-range address value into something
- recognizable, but the 80286 doesn't. You cannot address beyond the
- allocated size of a segment; the system aborts the program if an offset
- larger than the segment descriptor's limit value is used to reference that
- segment.
-
- The 80286 doesn't allow writable code segments. One of the bits in the
- segment descriptor identifies the segment as either code or data. A code
- segment's descriptor doesn't have a read/write bit, so only valid code
- segments can be placed in the CS (code segment selector) register, and a
- program may not write into valid code segments. However, an alias can be
- used to make a data segment into a code segment to be executed.
-
- Since different machines use different timing speeds, don't count on the
- CPU clock as a timing constant. This is a typical problem in copy-
- protected programs.
-
- Don't allow a division-trap handler to resume execution in the original
- code stream unless it is able to detect and understand differences between
- the 8086 or 8088 and the 80286. After a division-error trap, the 80286
- points to the division instruction (including prefixes) and doesn't change
- the register values. The 8086 and 8088 point to the instruction following
- the division instruction and may change the DX:AX or AH:AL register sets
- as well.
-
- The 80286 CL (low-order loop/shift/repeat count) registers won't permit
- shift counts greater than 31. On the 80286, the shift and rotate counts
- are masked to 5 bits.
-
- When executing the PUSH SP (push stack pointer onto stack) instruction,
- the 80286 pushes the SP value onto the stack before incrementing the
- value, while the 8086 and 8088 push the SP value after incrementing it.
- Few programs use this particular code sequence, but for those that do,
- Microsoft offers this way around the problem:
-
- MOV AX,SP
- PUSH AX
-
- these two lines of code can be implemented by a macro.
-
- The PUSHF instruction followed by a POPF may change the contents of the
- flag register in the 80286, since more flag bits are defined in the 80286
- flag word than in those of the 8086 or 8088. Also, because of a bug in the
- 80286, the POPF may change the contents of the flag register in the
- 80286. You should use flag-specific instructions to set or test flag
- register values instead of setting or testing for explicit bit patterns.
-
-
- FAPI
-
- In order to permit you to write new programs guaranteed to run in MS-DOS
- and in OS/2's protected mode (both current and future versions), Microsoft
- has defined a set of system calls that are guaranteed to support both
- environments. This set of system calls, the Family Application Program
- Interface (FAPI), is a subset of the full OS/2 Application Program
- Interface (API).
-
- Five types of code will run on OS/2: old programs designed for MS-DOS 3.x
- that run on OS/2 in the compatibility mode; presentation manager programs,
- FAPI programs that run on OS/2 in the compatibility mode; FAPI programs
- that run on OS/2 in protected mode; and new programs that run on OS/2 only
- in the protected mode. Figure 3 summarizes the characteristics valid for
- each of these program types.
-
- FAPI allows a program to be linked to run in both modes and includes
- system calls in all categories except those specific to protected mode,
- such as multitasking, run-time linking, and device monitors. Some
- restrictions apply to using the FAPI calls in the compatibility mode,
- which are discussed in detail in the OS/2 Programmer's Guide.
-
- The protected mode does not give you direct access to the screen, as you
- would have with a MS-DOS 3.x BIOS call because of its memory protection.
- Instead, you have access to a virtual screen buffer within each screen
- group. The screen buffer paints the screen only when that screen group is
- active. For bimodal compatibility, FAPI provides a subset of the video
- input/output (VIO) calls for creating and writing to a virtual screen
- buffer.
-
- Just using FAPI calls, however, won't guarantee protected mode
- compatibility. Basically, you want a well-behaved program; it shouldn't
- sneak past the operating system to the hardware or get too clever with the
- segment registers. To write code that operates in protected mode, you give
- up sovereignty over the hardware. Multitasking requires that the operating
- system, rather than your program, allocate hardware and system resources.
-
- If you've done any programming in XENIX or UNIX, you're used to this.
- However, if you've only worked in MS-DOS, you'll want to adjust your
- thinking a little.
-
- OS/2 uses an indirect segment addressing scheme: a segment number points
- to a table entry, called a segment decriptor, which in turn points to the
- memory space. OS/2's memory management service will change the memory
- pointer in the descriptor as the total system memory allocation changes,
- so there is no way of knowing where a given segment number actually puts
- you in physical memory. Thus, a well-behaved program doesn't try to
- interpret segment numbers or calculate other segment locations from a
- given segment number.
-
- You can't assume that any given segments overlap or don't overlap, nor can
- you assume any particular relationship between segment:offset combinations
- and physical memory. The segment number is nothing more than a segment ID,
- with no particular significance apart from that.
-
- The segment registers are intended for the storage of valid segment
- numbers. If you store invalid numbers there──for example, by using the
- segment registers as scratch-pad memory (the 8086 doesn't seem to have
- enough registers to suit some people)──your program will crash.
-
- Your program cannot issue a CLI (clear interrupt) instruction in protected
- mode because this causes a protection trap. When in compatibility mode,
- IRET (interrupt return) restores the previous value of IFLG (interrupt
- flag), but IRET has no effect in protected mode. Protected mode programs
- can interact directly with hardware only by linking to a special I/O
- Privilege Level Segment. This allows access to the 80286 processor's Ring
- 2 security level.
-
-
- Bimodal Device Drivers
-
- OS/2 supports bimodal device drivers that run in either mode, obviating
- the need for the system to switch modes to process interrupts.
-
- Unlike a MS-DOS 3.x device driver, a bimodal OS/2 driver has to support
- multiple synchronous and asynchronous requests. However, the basic
- structure remains pretty much the same: the driver contains a strategy
- routine and an interrupt routine. In addition, some device drivers may
- have to include a routine to trap ROM BIOS software interrupts from
- compatibility mode.
-
- An application program uses a request packet to call the strategy routine,
- just as with a MS-DOS 3.x driver. The strategy routine determines whether
- a request is valid and places valid requests in a queue to the device,
- using the DevHlp functions to manage the queue. If the device is idle, the
- strategy routine starts it and returns to the operating system, which
- suspends the thread until the request has been executed.
-
- When the device completion interrupt occurs, the interrupt routine sets
- the return status in the request packet and removes that packet from the
- queue. It then calls a DevHlp routine, DevDone, to tell OS/2 that the
- request is complete.
-
- The strategy routine should disable interrupts when checking to see if the
- device is active and when examining the queue. This protects the interrupt
- routine from other driver request interrupts. When interrupts are
- reenabled, the interrupt routine will receive only ones of higher
- priority.
-
- This is only a sample of some design considerations in writing OS/2
- bimodal device drivers. The subject is treated at greater length in the
- OS/2 Device Driver Guide.
-
-
- New Tools
-
- A new C compiler that runs in both modes is included in the OS/2 Software
- Development Kit. However, executable files written in Microsoft C
- Compiler, Version 4.0, should have no trouble running in the compatibility
- mode, as long as they meet the general criteria outlined earlier for
- operation in OS/2. You don't need to recompile or relink them with the new
- OS/2 C run-time library.
-
- The new C library is nearly identical to the Version 4.0 library, which is
- designed for single-thread execution only. Most of the functions are not
- re-entrant and therefore will not work in a multithread process. Figure 4
- lists the re-entrant functions that may be used in multithread programs.
-
- All the routines in Figure 4 will work properly in programs that use a far
- data model (compact and large model). However, in near-data-model-memory
- (small- and medium-model) programs, only some of these routines are
- guaranteed to function properly. The others have model-dependent pointers
- in their interfaces and have the potential to allocate stack space outside
- the default segment (SS!=DS).
-
- The kit also contains a new macro assembler that will run in protected
- mode. Again, existing MASM executable files will run in the compatibility
- mode, subject to the general restrictions described earlier.
-
- The MS-DOS 3.2 network function calls aren't supported in the
- compatibility mode, but the new Microsoft OS/2 LAN Manager will allow
- networking in protected mode.
-
-
- Device Monitors
-
- One problem that has plagued MS-DOS programmers is that when several
- terminate-and-stay-resident (TSR) programs are loaded into memory,
- they tend to step on each other in order to catch a reactivate keystroke
- from the keyboard. When you put a TSR like Borland's SideKick, Rosesoft's
- ProKey, or North Edge Software's TimeSlips into the background, the
- program calls interrupt 21h function 31h (Terminate and Stay Resident) and
- remains in memory even though you return to the system prompt. The program
- is still watching the keyboard, trapping keystrokes before the system gets
- them. When you hit a hot key combination, such as Ctrl-Alt, the program
- pops back onto the screen. The problem is that when two or more pop-up
- programs reside in the background at the same time, each wants to trap the
- keyboard interrupt before any other program does (see "Moving Toward an
- Industry Standard for Developing TSRs," MSJ, Vol. 1, No. 2).
-
-
- OS/2 solves this problem in protected mode by giving each TSR its own
- keyboard device monitor for reading keystrokes. Suppose you want to have
- several TSRs written by different vendors available within a given screen
- group. Each one will have its own keyboard device monitor. The monitors
- receive keystrokes in the order of monitor registration, which is set when
- the programs are first run. Keystrokes are passed on to the first monitor
- registered, which can trap the keystroke and generate a response or pass
- the keystroke on the next monitor registered (see Figure 5).
-
- Obviously, in this scheme, no two TSRs can use the same reactivate-key
- sequence. You can't reactivate a pop-up program in one screen group from a
- different screen group. By definition, a screen group is made up of a
- virtual screen, virtual mouse, virtual keyboard, and virtual memory
- space, and screen groups can't talk to each other.
-
-
- The Tradeoff
-
- The constraints that OS/2 imposes on the programmer may seem at first
- rather strict. With MS-DOS, you are pretty much free to use the system
- services or to leave them alone and go directly to the CPU instead. But in
- a multitasking system, you just can't do that. OS/2 takes over the CPU and
- the hardware, granting access only in certain instances.
-
- On the other hand, a lot of old problems go away. Applications will no
- longer have to fight each other for system resources, because OS/2
- allocates resources among them on a priority basis. Applications will have
- a common program interface (API) to work with, ensuring future
- compatibility. What you lose in resource access you gain in the new
- opportunities of multitasking, such as the potential for real-time
- multithread applications.
-
- Similarly, the memory access restrictions are compensated for in protected
- mode by the greatly increased size of the memory space that you can work
- with and the addition of memory management capabilities. The only
- limitation is that you must use system calls for access rather than
- stuffing numbers directly into the segment registers. And the 80286 chip
- is fast enough that efficiency issues aren't as critical as they were on
- the original PCs and XTs, so programming shortcuts such as segment games
- just aren't necessary anymore.
-
-
- Figure 1: Hardware/Operating Environment Compatibility Chart. Hardware
- compatibility and support is different for DOS 3.3 and OS/2.
- OS/2 seeks to insulate applications software from the hardware.
-
- ┌──────────OS/2─────────┐
- DOS 3.3 Compatibility New
- Environment Programs
- Supported Hardware 8088 -- --
- 8086 -- --
- 286 286
- 386 386
-
- Available Memory 640K 840K 16MB
- Can overcommit Memory -- -- YES
- True Multitasking -- -- YES
- Use Software Interrupts YES YES NO
- Use Hardware Interrupts YES YES NO
- Use undocumented DOS YES Some NO
- Interfaces
- Have direct access to Hardware YES YES YES
- Can run in the Background NO NO YES
- Obey 286 Segment Rules NO NO YES
-
-
- Figure 2: Device Driver Commands. Shown here are the device driver commands
- supported under OS/2.
-
- Code Command
-
- 0 Init
- 3 IOCtl Input
- 4 Input (Read)
- 5 Non-Destructive Input No Wait
- 6 Input Status
- 7 Input Flush
- 8 Output
- 9 Output with Verify
- 10 Output Status
- 11 Output Flush
- 12 IOCtl Output (Write)
- 13 Device Open
- 14 Device Close
- 15 Generic IOCtl
-
-
- Figure 3: Software Compatibility Chart. Programs may run in one or more
- modes depending on how they behave.
-
- ╓┌──────────────────────┌─────────────┌─────────────┌────────────┌───────────╖
- ┌─ DOS ──┐ ┌────────────────── OS/2 ───────────┐
- Old Programs ┌─────FAPI Programs─────┐ New Programs
-
- Start With COMMAND.COM COMMAND.COM COMMAND.EXE CMD.EXE
-
- Can Run in OS/2
- Compatibility Box Yes Yes No No
-
- Can Run in Background No No Yes Yes
-
- ┌─ DOS ──┐ ┌────────────────── OS/2 ───────────┐
- Old Programs ┌─────FAPI Programs─────┐ New Programs
- Permit Old-Style INT
- 21H Dos 3.x
- Interrupts Yes No No No
-
- Permit Undocumented
- Dos Interfaces No No No No
-
- Have IOPL Yes Via FAPI Via FAPI Via OS/2
-
- Obey 286 Segment
- Rules No Yes Yes Yes
-
- Can Overcommit
- Memory No No Yes Yes
-
- Addressable Memory 640K 640K 16M 16M
-
- Permit Software
- ┌─ DOS ──┐ ┌────────────────── OS/2 ───────────┐
- Old Programs ┌─────FAPI Programs─────┐ New Programs
- Permit Software
- Interrupts Yes Via FAPI No No
-
- Permit Hardware
- Interrupts Yes No No No
-
- Program Residence Below Below Above Above
- Boundary Boundary Boundary Boundary
-
- Permit Multitasking No Yes Yes Yes
-
-
- Figure 4: OS/2 API Calls
-
- Family API (FAPI) functions are highlighted
-
- * Indicates that FAPI support is limited (certain restrictions are
- imposed)
-
- ╓┌─────────────────────┌─────────────────────────────────────────────────────╖
- API Function Name Description
-
- BadDynLink Bad Dynamic Link
- DosAllocHuge* Allocate Huge Memory
- DosAllocShrSeg Allocate Shared Segment
- DosBeep Generate Sound From Speaker
- DosBufReset Commit File Cache Buffers
- DosCaseMap* Perform Case Mapping on String of
- Binary Characters
- DosChdir Change Current Directory
- DosChgFilePtr Change File Read/ Write Pointer
- DosClose Close File Handle
- DosCloseQueue Close Queue
- DosCloseSem Close System Semaphore
- DosCreateCSAlias Create CS Alias
- DosCreateSem Create System Semaphore
- DosCreateThread Create another thread of execution
- DosCreateQueue Create Queue
- DosCWait* Wait for child termination
- DosDelete Delete File
- API Function Name Description
- DosDelete Delete File
- DosDevConfig Get Device Configuration
- DosDevIOCtl* I/O Control for Devices
- DosDupHandle Duplicate File Handle
- DosEnterCritSec Enter Critical Section of Execution
- DosError* Enable Hard Error Processing
- DosExecPgm* Execute Program
- DosExit* Exit Program
- DosExitCritSec Exit Critical Section of Execution
- DosExitList Routine List for Process Termination
- DosFileLocks* File Lock Manager
- DosFindClose* Close Find Handle
- DosFindFirst* Find First Matching File
- DosFindNext* Find Next Matching File
- DosFlagProcess Set Process External Event Flag
- DosFreeModule Free Dynamic-Link Module
- DosFreeSeg Free Segment
- DosGetCtryInfo* Get Country-Dependent Formatting Information
- DosGetDateTime Get Current Date and Time
- DosGetDBCSev Get DBCS Environmental Vector
- API Function Name Description
- DosGetDBCSev Get DBCS Environmental Vector
- DosGetEnv Get Address of Process
- DosGetHugeShift Get Shift Count
- DosGetInfoSeg Get Address of System VariablesSegment
- DosGetMachineMode Return Current Mode of Processor
- DosGetMessage Get System Message and Insert Text Strings
- DosGetModHandle Get Dynamic-Link Module Handle
- DosGetModName Get Dynamic-Link Module Name
- DosGetProcAddr Get Dynamic-Link Procedure Address
- DosGetPrtyGet Process Priority
- DosGetShrSeg Access Shared Segment
- DosGetVersion Get Dos Version Number
- DosGiveSeg Give Access to Segment
- DosHoldSignal* Disable/Enable Signal
- DosInsMessage Insert Variable Text Strings In Message
- DosIOAccess Request I/O Access to Device
- DosKillProcess Terminate Process
- DosLoadModule Load Dynamic-Link Module
- DosMakePipe Create Pipe
- DosMkdir Make Subdirectory
- API Function Name Description
- DosMkdir Make Subdirectory
- DosMonClose Close Connection to OS/2 Device Driver
- DosMonOpen Open Connection to OS/2 Device Driver
- DosMonRead Read Input from Monitor Structure
- DosMonReg Register Set of Buffers as Monitor
- DosMonWrite Write Output to Monitor Structure
- DosMove Move File or Subdirectory
- DosMuxSemWait Wait for one of n semaphores to be cleared
- DosNewSize Change File Size
- DosOpen* Open File
- DosOpenQueue Open Queue
- DosOpenSem Open Exisiting Semaphore
- DosPeekQueue Peek Queue
- DosPurgeQueue Purge Queue
- DosPutMessage Output Message Text To Handle
- DosQCurDir Query Current Directory
- DosQCurDisk Query Current Disk
- DosQFHandState Query File Handle State
- DosQFileInfo* Query File Information
- DosQFileMode Query File Mode
- API Function Name Description
- DosQFileMode Query File Mode
- DosQFSInfo Query File System Information
- DosQHandType Query Handle Type
- DosQueryQueue Query Queue
- DosQVerify Query Verify Setting
- DosRead Read from File
- DosReadAsync Asynchronous Read from File
- DosReadQueue Read from Queue
- DosReAllocHuge* Change Huge Memory Size
- DosReAllocateSeg* Change segment Size
- DosResumeThread Restart Thread
- DosRmdir Remove Subdirectory
- DosSelectDisk Select Default Drive
- DosSemClear Clear (release) Semaphore
- DosSemRequest Request Semaphore
- DosSemSet Set Semaphore Owned
- DosSemSetWait Set Semaphore and wait for next Clear
- DosSemWait Wait for Semaphore to be Cleared
- DosSetDateTime Set Current Date and Time
- DosSetFHandState* Set File Handle State
- API Function Name Description
- DosSetFHandState* Set File Handle State
- DosSetFileInfo Set File Information
- DosSetFileMode Set File Mode
- DosSetMaxFH Set Maximum File Handles
- DosSetPrty Set Process Priority
- DosSetSigHandler* Handle Signal
- DosSetVec Establish Handler For Exception Vector
- DosSetVerify Set/Reset Verify Switch Delay ProcessExecution
- DosSubAlloc Suballocate Memory within Segment
- DosSubFree Free Memory Suballocated within Space
- DosSubSet Initialize or Set Allocated Memory
- DosSuspendThread Suspend Thread Execution
- DosSystemService Dos System Process Services
- DosTimerAsync Start Asynchronous Timer
- DosTimerStart Start Periodic Interval Timer
- DosTimerStop Stop Asynchronous or Interval Timer
- DosPTrace Interface for Program Debugging
- DosWrite Synchronous Write to File
- DosWriteAsync Asynchronous Write to File
- DosWriteQueue Write to Queue
- API Function Name Description
- DosWriteQueue Write to Queue
- KbdCharIn Read Character Scan Code
- KbdFlushBuffer Flush Keyboard Buffer
- KbdGetStatus Get Keyboard Status
- KbdPeek* Peek at Character-Scan Code
- KbdRegister Register keyboard Subsystem
- KbdSetStatus Set Keyboard Status
- KbdStringIn Read Character String
- MouClose Close Mouse Device For Current Screen
- GroupMouDrawPtr Release Screen Area For Device Driver Use
- MouGetDevStatus Get Current Pointing Device Driver Status Flags
- MouGetEventMask Get Current Pointing Device One- Word Event Mask
- MouGetNumButtons Get Number of Buttons
- MouGetNumMickeys Get Number of Mickeys-Per-Centimeter
- MouGetNumQueel Get Current Status for Pointing Device Event Queue
- MouGetScaleFact Sets Scale Factors for Current Pointing Device
- MouOpen Open Mouse Device For Current Screen Group
- MouReadEventQue Read Pointing Device Event Queue
- MouRegister Register Mouse Subsystem
- MouRemovePtr Reserve Screen Area For Application Use
- API Function Name Description
- MouRemovePtr Reserve Screen Area For Application Use
- MouSetEventMask Assign New Event Mask To Current Pointing Device
- MouSetHotKey Set System Hot Key
- MouSetPtrShape Set Pointer Shape and Size
- MouSetScaleFact Set Scale Factors for Current Positioning Device
- VioEndPopUp Deallocate a Pop-up Display Screen
- VioGetAnsi Get ANSI State
- VioGetBuf Get Logical Video Buffer
- VioGetCurPos Get Cursor Position
- VioGetCurType Get Cursor Type
- VioGetPhysBuf Get Physical Video Buffer
- VioPopUp Allocate Pop-up DisplayScreen
- VioPrtScreen Print Screen
- VioReadCellStr Read Character-Attribute String
- VioReadCharStr Read Character String
- VioRegister Register Video Subsystem
- VioSavReDrawWait Screen Save RedrawWait
- VioScrLock* Lock Screen
- VioScrollDn Scroll Screen Down
- VioScrollLf Scroll Screen Left
- API Function Name Description
- VioScrollLf Scroll Screen Left
- VioScrollRt Scroll Screen Right
- VioScrollUp Scroll Screen Up
- VioScrUnLock Unlock Screen
- VioSetAnsi Set ANSI On or Off
- VioSetCurPos Set Cursor Position
- VioSetCurType Set Cursor Type
- VioSetMode Set Display Mode
- VioShowBuf Display Logical Buffer
- VioWrtCellStr Write Character-Attribute String
- VioWrtCharStr Write Character String
- VioWrtCharStrAttr Write Character String With Attribute
- VioWrtNAttr Write N Attributes
- VioWrtNCell Write N Character-Attributes
- VioWrtNChar Write N Characters
- VioWrtTty Write TTY String
-
-
- Figure 5: When TSR programs are run in protected mode, OS/2 assigns each a
- keyboard device monitor, which reads keystrokes from the keyboard.
- This keeps the TSRs from fighting each other to be the first to
- trap keystrokes.
-
- ╔═══════════╗ ╔═══════════╗ ╔═══════════╗ ╔═══════════╗ ╔═══════════╗
- ║ 1 ║ ║ 2 ║ ║ 3 ║ ║ 4 ║ ║ 5 ║
- ║ ║ ║ Pop-Up Applications ║ ║ ║
- ╚═══════════╝ ╚═══════════╝ ╚═══════════╝ ╚═══════════╝ ╚═══════════╝
-
- ┌─────╨─────┐ ┌─────╨─────┐ ┌─────╨─────┐ ┌─────╨─────┐ ┌─────╨─────┐
- │ 1 │ │ 2 │ │ 3 │ │ 4 │ │ 5 │
- │ │ │ Keyboard Device Monitors │ │ │
- └────────╥──┘ └────────╥──┘ └────────╥──┘ └────────╥──┘ └───────────┘
- ║ ║ ║ ║
- ┌────────║────────║─────║─────────║────║─────────║────║────────║────────┐
- │ ╚════════╝ ╚═════════╝ ╚═════════╝ ╚════════╝ │
- ├ ─ ─ ─ ─ ┐ │
- │Interrupt│ Keystroke Distribution │
- └────────┴────────────────────────────────────────────────────────╥────┘
- ╚═══════╗ ╔═══════╝
- ┌───────╨────────────────────────────────────────────────────┐
- │ Keyboard Device Driver │
- └────────────────────────────────────────────────────────────┘
- ║
-
-
- ───────────────────────────────────────────────────────────────────────────
- Some Programming Don'ts for OS/2
- ───────────────────────────────────────────────────────────────────────────
-
- When writing programs for either the compatibility mode or protected mode
- in OS/2, you must observe a number of restraints that weren't necessary in
- writing for MS-DOS. Many of these restrictions arise from differences
- between the 8086 or 8088 and the 80286 microprocessors.
-
- ■ Don't depend on segment overlap or lack of it.
-
- ■ Don't depend on any relationship between segment:offset combinations and
- physical memory.
-
- ■ Don't use wrap-around address offsets.
-
- ■ Don't use the segment registers for anything but valid segment numbers.
-
- ■ Never address beyond the allocated size of a segment.
-
- ■ If you have to play with the I/O ports, use only the appropriate dynamic
- link routines (FAPI).
-
- ■ Don't mix code and data in the same segment.
-
- ■ Don't use undefined opcodes.
-
- ■ Don't use the PUSH SP instruction.
-
- ■ Don't use the POPF instruction.
-
- ■ Don't use shift counts greater than 31.
-
- ■ Don't use IDIV operands to produce a most-negative number.
-
- ■ Don't resume execution in the original code stream after a division
- trap.
-
- ■ Don't use redundant prefix bytes.
-
- ■ Don't use CLI instructions (in protected mode).
-
- ■ Don't use CPU speed as a timing constant.
-
- ■ Don't examine or set explicit flag register values.
-
- ■ Don't single-step interrupt instructions in debuggers.
-
- ■ Don't write self-modifying code.
-
- ████████████████████████████████████████████████████████████████████████████
-
- OS/2 Multitasking: Exploiting the Protected Mode of the 80286
-
- ───────────────────────────────────────────────────────────────────────────
- Also see the related article:
- Configuring the OS/2 Multitasker
- ───────────────────────────────────────────────────────────────────────────
-
- Ray Duncan
-
- Multitasking is the technique of dividing CPU time between multiple tasks
- so that they appear to be running simultaneously. Of course, the
- microprocessor is only executing one sequence of machine instructions
- within one task at any given time, but the switching from one task to
- another is invisible to both the user and the programs themselves. The
- operating system is responsible for allocating system resources such as
- memory to the various executing tasks and for resolving contending
- requests to such peripherals as video displays and disk drives.
-
- The part of the operating system that allocates CPU time between tasks is
- called the scheduler or dispatcher, and the rotation from one task to
- another, or from a task to a module of the operating system, is called a
- context switch. When a context switch occurs, the dispatcher must save the
- current state of the task that was executing, including its registers and
- program counter, load the registers and program counter belonging to the
- next task to run, and transfer control to that task at the point where it
- was previously suspended.
-
- There are two basic strategies for task scheduling that are used by modern
- multitasking operating systems: event-driven and pre-emptive.
-
- Event-driven schedulers rely on each task to be "well-behaved" and yield
- control at frequent enough intervals so that every program has acceptable
- throughput and none will be "starved" for CPU cycles. This yielding may be
- explicit (the program calls a specific operating system function to give
- up control) or implicit (the program is suspended when it requests the
- operating system to perform I/O on its behalf and regains control only
- after the I/O is completed and other tasks have in turn yielded control).
- This strategy is quite efficient in transaction-oriented systems where
- there is a great deal of I/O and not much computation, but the system can
- be brought to its knees by a single compute-bound task, such as an in-
- memory sort.
-
- A pre-emptive scheduler relies on the presence of an external signal
- generated at regular intervals, typically a hardware interrupt triggered
- by a real-time clock. When the interrupt occurs, the operating system
- gains control from whatever task was executing, saves its context,
- evaluates the list of programs that are ready to run, and gives control to
- (dispatches) the next program. This approach to multitasking is often
- called "time-slicing"; this term is derived from the mental image of
- dividing the sweep of a second hand around a clock face into little wedges
- and doling out the pieces to all the eligible programs.
-
- Modern mainframe and minicomputers, and the more powerful microcomputers,
- such as the Intel 80286 and Motorola 68020, include hardware features
- specifically designed to make multitasking operating systems more
- efficient and robust. These include privilege levels and memory
- protection.
-
- In the simplest use of privilege levels, the CPU is either in kernel mode
- or user mode at any given time. In kernel mode, which is reserved for the
- operating system, any machine instruction can be executed, and any
- location in memory can be accessed. As part of the mechanism of
- transferring control from the operating system to an application program,
- the CPU is switched into user mode. In this CPU state, any attempt to
- execute certain reserved instructions, such as writing to an I/O port,
- causes a hardware interrupt and returns control to the operating system.
- (Although the Intel 80286 microprocessor actually supports four privilege
- levels, OS/2 ordinarily uses only the highest and lowest of these.)
- Similarly, the hardware memory protection mechanisms detect any attempt by
- an application program to access memory that does not belong to it, and
- generate a hardware interrupt that allows the operating system to abort
- the offending task.
-
- Microsoft OS/2 is designed around a pre-emptive, priority-based
- multitasking scheduler. Understanding OS/2 multitasking requires a grasp
- of three distinct, but related, concepts or system objects: processes,
- threads, and screen groups.
-
- ───────────────────────────────────────────────────────────────────────────
- Three types of systems objects are involved in OS/2 multitasking: processes,
- threads, and screen groups. A process represents an application program
- accessing system resources such as files, memory, and inter-process
- communication facilities. A process can contain multiple concurrent points
- of execution called threads; each thread has its own priority and stack.
- Processes are collected into screen groups that read and write a virtual
- display and keyboard; the Session Manager is used to select a screen group.
-
- ┌──────────────────────────────────┐
- │ │█
- │ │█
- │ │█
- │ Physical Screen │█
- │ │█
- │ │█
- │ │█
- │ │█
- └──────────────────────────────────┘█
- ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
-
- Session manager maps
- virtual screen for a group to the
- physical display.
- ┌ ─ ─ ─ ─ ─ ─ ─ ─ ┴──────────────────┐
- │ │
- ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
- █ █
- │ │█ │ │█
- Virtual screen for █ Virtual screen for █
- │ screen group 1 │█ │ screen group 2 │█
- █ █
- └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘█ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘█
- ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
-
- │ │ │ │
- ┌───────┴───────┐ ┌──────┴───────────────┐ ┌───┴────┐ ┌──────┴────────┐
- │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
- │ Thread Thread │ │ Thread Thread Thread │ │ Thread │ │ Thread Thread │
- │ A1 A2 │ │ B1 B2 B3 │ │ C1 │ │ D1 D2 │
- └───────────────┘ └──────────────────────┘ └────────┘ └───────────────┘
- PROCESS A PROCESS B PROCESS C PROCESS D
- ───────────────────────────────────────────────────────────────────────────
-
- Processes
-
- The simplest case of an OS/2 process is very similar to an application
- program loaded for execution under MS-DOS 2.x and 3.x. A process is
- started (whether by a shell or command processor or by another
- application) by a call to the OS/2 service DOSEXECPGM. OS/2 initiates a
- new process by allocating memory segments to hold that process's code,
- data, and stack, and then initializing the memory segments from the
- contents of the program's .EXE disk file.
-
- Once it is running, a process can obtain access to additional system
- resources such as files, pipes, semaphores, queues, and additional memory
- with various OS/2 function calls (see Figure 1). A process terminates
- itself with a call to the OS/2 function DOSEXIT and can also be aborted by
- its parent process, by an unrecoverable hardware error, or by a memory
- protection fault. In any case, OS/2 will automatically release all
- resources belonging to the process when it terminates.
-
- Ordinarily, processes are only aware of themselves, OS/2 services, and any
- child processes they start directly. They cannot directly access memory
- space or resources belonging to another process, including a child
- process, without the cooperation of that process.
-
-
- Threads
-
- The MS OS/2 scheduler, however, knows nothing about processes; it
- partitions the available CPU cycles among dispatchable entities known as
- threads. A thread has a point of execution, a priority, a stack pointer,
- and general register contents (see Figure 1). At any given time, a thread
- is either blocked (waiting for I/O or some other event), ready to execute,
- or actively executing (it has control of the CPU).
-
- Each process has a primary thread that receives control from OS/2 when the
- process is started; it begins executing at the program's designated entry
- point. However, that primary thread can start up additional threads within
- the same process. Multiple threads within a process execute asynchronously
- to one another, can have different priorities, and can manipulate one
- another's priorities.
-
- Although the threads within a process have separate stacks, they share the
- same near data segment (DGROUP) and thus the same local heap. Carefully
- designing code to use the stack for local variables allows procedures to
- be shared between threads──this happens naturally in C programs. Access to
- static variables or other data structures must be coordinated between
- threads through the use of RAM semaphores or similar mechanisms. The
- threads also share all the other system resources owned by the process as
- a whole──open files, system semaphores, queues, and pipes──but OS/2
- generally provides automatic serialization of operations on these
- resources.
-
- When a thread within an application program is executing, the system is in
- user mode; the thread can only access the resources owned by the process
- that contains it and cannot execute certain machine instructions. A clock
- tick, another hardware interrupt, or a call by the application for an OS/2
- function causes a transition back into kernel mode, so that OS/2 can
- service the interrupt or provide the requested operation.
-
- When OS/2 is ready to exit kernel mode, the scheduler receives control and
- examines its list of active threads. The thread with the highest priority
- that is ready to execute gains control of the machine. If the thread that
- was just interrupted is one of several eligible threads and has not used
- up its time-slice, it receives preference. If a thread becomes starved for
- CPU cycles because other threads with higher priorities are getting all
- the attention, OS/2 will temporarily bump that thread's priority to a
- higher value.
-
-
- Screen Groups
-
- Processes are in turn members of screen groups, which is what the average
- user perceives as being OS/2 multitasking. When the user presses the
- SysReq key, he exits from the current session, or screen group, to a menu
- displayed by the Session Manager. He can then select a command processor
- or other program already executing in another screen group or establish a
- new screen group by loading a new copy of the protected-mode command
- processor.
-
- OS/2 maintains a separate virtual screen for each screen group, which
- receives the output of all the processes in that group. The virtual screen
- is mapped to the physical display whenever that screen group is selected
- by the user with the Session Manager. New processes are added to a screen
- group by being "spawned" by the command processor or another process
- already executing within the group.
-
- By convention, only one of the processes within a screen group should be
- in the foreground at any given time, that is, writing to the screen and
- reading from the keyboard. The OS/2 DETACH command allows programs to be
- placed in the background from the command processor level. However, since
- OS/2 does not place any restrictions on access to the virtual display by
- the various processes within a screen group, DETACHing normal programs is
- not usually too useful since it just results in chaotic displays. Programs
- intended to be used as background tasks must be specially designed to use
- the OS/2 services for pop-up windows and keyboard monitors, so that they
- do not disrupt the displays or otherwise interfere with the proper
- operation of the foreground task within their group.
-
- A screen group, or session, is removed from the system by first
- terminating any active application programs within that group and
- returning to the command processor prompt. Then, entry of the EXIT
- command terminates the command processor itself. The Session Manager
- regains control and displays a menu of the remaining screen groups.
-
-
- OS/2 Programming
-
- OS/2 offers a diverse set of services to application programs that allow
- the creation of complex and powerful multitasking applications (see
- Figure 2). These services include
-
- ■ starting and stopping child processes
-
- ■ obtaining the return code of a child process
-
- ■ starting, suspending, and destroying threads
-
- ■ altering the priorities of threads
-
- ■ inter-process communication (see "OS/2 Inter-Process Communication:
- Semaphores, Pipes, and Queues,")
-
- OS/2 DOSEXECPGM is used by the parent process to load and execute the
- child process. This function is analogous to, but considerably more
- powerful than, the EXEC function (Int 21h Function 4Bh) that was available
- in MS-DOS 2.x and 3.x. The child process can, in turn, load other
- processes and so on until system limits on threads or open files are
- exhausted or the system runs out of swap space on the disk.
-
- The OS/2 DOSEXECPGM function is called with
-
- ■ the address of the filename of the child process to be executed
-
- ■ a flag controlling whether the child process is synchronous or
- asynchronous
-
- ■ the addresses of an argument string block and an environment block to
- be passed to the child process (Each of these blocks consists of a
- series of null-terminated (ASCIIZ) strings; the block is terminated
- by an extra null byte. The environment block corresponds exactly to
- the environment block you are familiar with in MS-DOS 2.x and 3.x,
- while the simplest case of the argument block is simply a copy of the
- command line that invoked a process.)
-
- ■ addresses of buffers to receive the process ID of the child and other
- information
-
- When a child process executes synchronously, execution of the thread in
- the parent process that made the DOSEXECPGM call is suspended until the
- child process terminates, either intentionally or owing to an error
- condition. When the thread in the parent resumes execution, it is supplied
- with the return code of the child and a termination code that indicates
- whether the child terminated normally or was aborted by the operating
- system.
-
- If the child process is asynchronous, all threads in the parent process
- continue to execute while the child is running. Any thread in the parent
- process can later use the DOSCWAIT call to resynchronize with the child
- process by suspending itself until the child terminates and then obtaining
- its return code. As an alternative, the parent can use the process ID of
- the child, which is supplied by OS/2 on return from the original
- DOSEXECPGM call, to unilaterally abort execution of the child process with
- the DOSKILLPROCESS call at any time. (A special usage of the asynchronous
- option of DOSEXECPGM, together with the DOSPTRACE function, allows the
- child program to be traced, inspected, and modified under the parent
- program's control. This combination of OS/2 services allows the creation
- of program debuggers that are compatible with 80286 memory protection.)
-
- The child process automatically inherits access to certain resources of
- the parent process. These resources include the handles for any open files
- (unless the parent process explicitly opened the files with the
- noninheritance flag), handles to any open pipes, handles to any open
- system semaphores (but not ownership of the semaphores), and a copy of the
- parent's environment block (unless the parent goes to the trouble of
- creating a new block and passes a pointer to it).
-
- Figure 3 is a demonstration of the DOSEXECPGM function──a synchronous
- execution of CHKDSK as a child process. The various options for
- asynchronous execution, coupled with several options that are available
- with the DOSCWAIT function and the DOSKILLPROCESS function, allow for very
- flexible execution-time relationships between parent and child processes.
-
-
- Managing Threads
-
- OS/2 has a rich repertoire of function calls for control of multiple
- threads of execution within a process. The use of multiple threads is
- particularly appropriate for cases in which a process must manage several
- I/O devices with vastly different I/O rates (for example, the keyboard,
- disk, and video display) and remain rapidly responsive to the needs of
- each. Multiple threads are also appropriate for cases in which a process
- needs to run in multiple instances, but the instances do not require
- separate data segments or resources, since multiple threads are started
- much more quickly than multiple processes and have less system overhead.
-
- As mentioned previously, each process has a single primary thread that is
- known to the OS/2 dispatcher when it is started up, and this thread's
- initial point of execution is the program's designated entry point. The
- primary thread can use the MS OS/2 function DOSCREATETHREAD to start up
- additional threads within the process, and those new threads can also
- start up threads and so on. Each thread is initially entered through a far
- call from OS/2 and can terminate through a far return or by issuing the
- OS/2 function call DOSEXIT. (A process is terminated when the sole
- remaining active thread in a process issues DOSEXIT, or when any thread
- issues DOSEXIT with a special parameter that indicates that all threads
- should be immediately terminated.)
-
- A thread can use the function DOSSLEEP to suspend itself for a programmed
- period of time, or it can block on a semaphore to await reactivation by
- another thread or process triggering that same semaphore. Alternatively, a
- thread can use the functions DOSSUSPENDTHREAD or DOSRESUMETHREAD to
- suspend or reactivate other threads within the same process without their
- cooperation. Similarly, a thread can use the functions DOSGETPRTY or
- DOSSETPRTY to inspect or modify the execution priority of itself or other
- threads in accordance with execution-time requirements.
-
- Because a thread can be unilaterally suspended by another thread without
- its knowledge or cooperation, the functions DOSENTERCRITSEC and
- DOSEXITCRITSEC are provided in order to protect a thread from interruption
- while it is executing a critical section of its code.
-
- Figure 4 demonstrates the use of DOSCREATETHREAD by one thread to start up
- another thread that emits ten beeps at one-second intervals and then
- terminates. Although this is a trivial use of multiple threads, it gives
- an inkling of the enormous power of this concept and the ease with which
- asynchronous processing can be incorporated into an OS/2 application.
-
-
- Summary
-
- OS/2 uses a time-slicing, pre-emptive, priority-based multitasking
- strategy. The Intel 80286 microprocessor's support for privilege levels
- and memory protection are fully exploited by OS/2 in order to run multiple
- concurrent tasks and to protect those tasks from damaging each other or
- the operating system.
-
- A process represents the execution of an application and the ownership of
- any resources──files, memory, etc.──associated with that execution.
- Processes can spawn other processes and can exert certain control over
- those child processes, but sharing of data between two processes is
- possible only with the cooperation of both processes. OS/2 has a wealth of
- facilities for inter-process communication, which are discussed in "OS/2
- Inter-Process Communication: Semaphores, Pipes, and Queues."
-
- The thread is the dispatchable element used by OS/2 to track execution
- cycles of the processor. Threads can start, stop, and influence the
- execution of other threads within the same process. Sharing of data
- between threads is natural and, in fact, difficult to avoid, since all
- threads in a process share access to the same memory, files, pipes,
- queues, and semaphores. In essence, the OS/2 loader knows about processes,
- but the OS/2 scheduler knows about threads.
-
-
- Figure 1: Thread- and Process-specific information maintained by OS/2.
-
- Per-Process Information
-
- Process ID (PID)
- Disk-swapping information
- Local Descriptor Table (LDT) pointer
- System resources owned or opened:
- Files
- Pipes
- Queues
- System Semaphores
- Device monitors
- Memory
- Child processes
-
- Per-Thread Information
-
- Thread ID
- Thread priority
- Thread state: blocked, ready to execute, active
- Time-slice
- Instruction pointer
- Processor state (registers and flags)
- Stack pointer
-
-
- Figure 2: OS/2 multitasking services at a glance.
-
- Process Control
-
- DOSEXECPGM Load and execute a child process
- DOSCWAIT Wait for child process to terminate
- DOSKILLPROCESS Unconditionally terminate another process.
- DOSPTRACE Inspect/modify/trace a child process
-
- Threads Controlling Threads
-
- DOSCREATETHREAD Create another execution thread within the sameprocess
- DOSSUSPENDTHREAD Suspend the execution of a thread
- DOSRESUMETHREAD Reactivate a thread
- DOSEXIT Terminate current thread or all threads in process
-
- Read/Alter Thread Priorities
-
- DOSGETPRTY Get the priority of specified thread
- DOSSETPRTY Set the priority of specified thread
-
- Inter-Thread Protection
-
- DOSENTERCRITSEC Disable other threads in same process
- DOSEXITCRITSEC Re-enable other threads in same process
-
-
- Figure 3: This sample code fragment demonstrates the use of the OS/2 system
- DOSEXECPGM to run CHKDSK as a synchronous child process
-
- ∙
- ∙
- ∙
- push ds ; address of object buffer
- push offset DGROUP:ObjName
- push ObjNameLen ; length of object buffer
- push 0 ; execute synchronously
- push ds ; address of argument block
- push offset DGROUP;ArgBlk
- push 0 ; address of envir.block
- push 0 ; (OL=inherit parent's)
- push ds ; address to receive return
- push offset DGROUP:RetCode ; and termination codes
- push ds ; address of name of child
- push offset DGROUP :PgmName
- call DOSEXECPGM ; transfer to 286DOS
- or ax,ax ; was EXEC successful?
- jnz error ; jump if EXEC failed...
- ∙
- ∙
- ∙
- ObjName db 64 dup (0) ; receives name of dynamic link
- ObjNameLen equ $-ObjName ; causing EXEC failre
-
- ArgBlk db 'chkdsk *.*',0 ; block of argument strings for child...
- db 0 ; extra null byte terminates block
-
- ; receives return codes from child...
- RetCode dw 0 ; termination code for child
- dw 0 ; result code from child's DOSEXIT
-
- PgmName db 'chkdsk.eve',0 ; name of child program to run
-
-
- Figure 4: An example of the use of multiple threads for asynchronus
- execution of tasks within a single process. The main line of
- execution allocates memory for a new stack and then starts up
- another thread called beeper. The new thread uses the OS/2 service
- DOSBEEP to emit ten short tones at one second intervals and then
- terminates.
-
- stksiz equ 1024 ; size of new thread's stack
- ∙
- ∙
- ∙
- Selector dw ? ; selector from DOSALLOCSEG
-
- BeeperlD dw ? ; Thread ID for new thread
- ; named 'beeper'
- ∙
- ∙
- ∙
- push stksiz ; size of new segment
- push ds ; address of variable
- push offset DGROUP:Selector ; to receive new selector
- push 0 ; non-shared segment
- call DOSALLOCSEG ; TRANSFER TO 286DOS
- or ax,ax
- jnz error ; jump if alloc failed
-
- push cs ; execution address of
- push offset_TEXT:Beeper ; new thread
- push ds ; address to receive new
- push offsetDGROUP;BeeperID ; Thread ID
- push Selector ; address of base of
- push stksiz ; new thread's Stack
- call DOSCREATETHREAD ; transfer to 286DOS
- or ax,ax
- jnz error ; jump if create failed
- ∙
- ∙
- ∙
- beeper proc far ; entry point for thread
-
- mov cx,10 ; emit ten beeps...
-
- beep1: push 440 ; sound a 440 Hz tone
- push 100 ; for 100 milliseconds
- call DOSBEEP ; transfer to 286DOS
-
- push 0 ; now pause for one sec.
- push 1000
- call DOSSLEEP ; transfer to 286DOS
-
- loop beep1 ; loop ten times
- ret
-
- beeper endp
- ∙
- ∙
- ∙
-
- ───────────────────────────────────────────────────────────────────────────
- Configuring the OS/2 Multitasker
- ───────────────────────────────────────────────────────────────────────────
-
- There are four different directives that can be placed in the system
- CONFIG.SYS file to influence the operation of OS/2 multitasking. These
- are
-
- THREADS=n
- MAXWAIT=seconds
- PRIORITY=ABSOLUTE|DYNAMIC
- MAXWAIT=x[,y]
-
- The THREADS directive controls how many threads can be simultaneously
- created in the system. The parameter n may be 16-255, with a default of
- 16, which is sufficient for OS/2 and a few simple processes. The upper
- limit of 255 cannot be expanded.
-
- When a thread is denied the CPU for the number of seconds specified by
- MAXWAIT because of other higher-priority threads using up all the time-
- slices, the starved thread receives a temporary increase in its priority
- for one time-slice.
-
- The PRIORITY directive activates OS/2's mechanisms for dynamically
- altering the priority of each thread based on the activity of other
- threads within the system. When PRIORITY=ABSOLUTE, the MAXWAIT directive
- has no effect whatsoever.
-
- The TIMESLICE directive controls the length of the time-slices used by the
- OS/2 scheduler. The x parameter represents the normal length of a thread's
- time-slice in milliseconds. If a thread uses up its entire time-slice and
- must be pre-empted, its next time-slice will be one tick longer, up to the
- limit given by the y parameter. This strategy is used by OS/2 to reduce
- the amount of context-switching overhead when several compute bound
- threads are running.
-
- ████████████████████████████████████████████████████████████████████████████
-
- OS/2 Memory Management
-
- In a multitasking system, random access memory (RAM) must be administered
- as a critical, finite system resource akin to disk storage or a line
- printer. The well-bred application program uses the host operating
- system's facilities to request additional memory politely when necessary,
- releases unneeded memory, and abstains from meddling with memory that
- doesn't belong to it.
-
- In the MS-DOS environment, such genteel programs are few and far between.
- Most popular application programs place the goal of high performance above
- all else, and a disposition to grab all available memory, capture
- interrupt vectors, and directly access the video controller's refresh
- buffer for fast screen displays is the rule rather than the exception.
- This behavior causes many headaches for those system software vendors who
- seek to graft multitasking abilities onto MS-DOS with such new user
- interfaces as Windows, TopView, and DESQview.
-
- The 80286 microprocessor, which is the heart of the IBM PC AT and
- compatible computers, can execute in either of two modes: real mode and
- protected mode. When the 80286 is running in real mode, as it does under
- MS-DOS, it essentially functions as a fast 8086/88 processor with a few
- added machine instructions. The segment registers contain physical
- addresses, which can be manipulated directly, and the total amount of
- memory that can be addressed is 1 megabyte (Mb) (see Figure 1). On the
- 8086, and the 80286 running in real mode, there is no way for the
- operating system to intervene when a program does not use memory properly
- or to prevent one program from writing into another's memory space.
-
- Under OS/2, however, the rules are changed. OS/2 runs the 80286 processor
- in protected mode, providing hardware support for memory protection and
- memory access privilege levels, and presenting a very different hardware
- architecture to the programmer. The contents of segment registers are now
- logical selectors rather than physical addresses (see Figure 2). Selectors
- are indexes into descriptor tables that contain the actual physical
- address and length of the corresponding chunk of memory, and the
- descriptor tables themselves are typically not accessible to application
- programs at their privilege level. Each segment has an attribute that
- determines how it can be accessed: executable, read-only data, or read-
- write data.
-
- Memory protection between programs is enforced by each process's local
- descriptor table (LDT), a map of the memory segments that can be accessed
- by that process. If a task tries to load a segment register with a
- selector that is not valid for its own descriptor table or for the global
- descriptor table (GDT), a hardware interrupt is generated (see Figure 3).
- The operating system then recovers control, terminates the offending
- process, and displays diagnostic information on the system console.
-
- Thus, applications running under OS/2 must be civil to survive: there is
- no alternative to using the host system's services for managing memory.
- Only the operating system is entrusted with the privilege level and the
- knowledge of physical memory layout, which is necessary to create and
- destroy memory segments through manipulation of descriptor tables.
- Fortunately, OS/2 provides applications with a complete repertoire of
- memory management services, in three major groups: local heap management,
- conventional allocation and release of global memory segments, and "huge"
- global block management (logically contiguous memory spaces that are
- larger than 64Kb).
-
- In exchange for good behavior, the 80286's protected mode provides a
- reward──virtual memory (see Figure 4). A task can request, and own, more
- bytes of RAM memory than physically exist in the system. The combination
- of hardware support for paging and a specialized module of the operating
- system called the swapper present to the task the illusion that all of its
- segments are simultaneously present and accessible.
-
- When all of physical memory is in use and a process attempts to allocate
- additional memory, the least recently used segment is copied to a swap
- file on the disk, and the memory it occupied is freed up. A special bit in
- the descriptor table entry for the swapped-out segment is set to indicate
- that it is nonresident. When a selector for a swapped-out segment is used
- in a memory access, a hardware interrupt called a page fault occurs. The
- virtual memory manager (swapper) gains control, reads the needed segment
- from the disk into physical memory (possibly writing another segment out
- first to make room), updates the descriptor table entry for the selector
- to correspond to the new physical address, and restarts the process's
- instruction that was trying to access the segment.
-
- Thus, virtual memory means that the presence or absence in physical memory
- of any particular segment is completely transparent to the process that
- owns it. If an address that is not present is accessed, it will be
- invisibly loaded on demand. Correspondingly, the amount of memory that can
- be committed by all the processes in the system combined is limited only
- by the amount of swapping space on the disk (although limits on the number
- of available segment selectors do exist, they are of no practical
- significance owing to the sizes of today's fixed disks).
-
- The 80286's support for protected, virtual memory facilitates two other
- OS/2 features: shared text (code) segments and dynamic linking. Segments
- containing machine instructions are marked with the executable attribute
- and cannot be modified. Thus, when the same program is loaded for
- execution in two or more sessions, each instantiation can share the same
- memory-resident piece of code, instead of requiring separate, distinct
- copies. The protected-mode command processor called CMD.EXE is a good
- example: the overhead of multiple screen groups is small, since only one
- copy of the machine code in CMD.EXE is needed. Adding another screen group
- with its own command processor requires only the creation of another data
- segment and virtual screen buffer.
-
- Dynamic link libraries are simple extensions of the concepts of virtual
- memory and shared text segments. Under MS-DOS, when a program was linked
- to a run-time library, the actual machine code for the library routines
- became a permanent part of the application. Under OS/2, the linker and the
- .EXE file header have been extended to allow run-time binding (Figure 5)
- of library routines. When a program containing dynamic links is loaded for
- execution, OS/2 examines the file header and loads any modules from
- dynamic link libraries that are required, then updates the FAR CALL
- addresses within the application. Naturally, these dynamically linked
- routines can be shared between multiple processes and will be discarded
- and reloaded as needed by the memory manager. Much of OS/2 itself is
- implemented in the form of dynamic link libraries.
-
-
- Figure 1: Real Address Mode Segment Selector Interpretation. The selector
- is used to identify segments in real memory.
-
- ┌─────────────────────────────────────────────────────────────────┐
- │ │█
- │ ╔══════════════╤══════╗ ╔══════════╗ ───┐ │█
- │ ║ Selector │ 0000 ║ ║ ║ │ │█
- │ ╚══════════╤═══╧══════╝ ║ ║ │ │█
- │ │ ┌─ ╟──────────╢ 1M │█
- │ Segment and Base │ ║ ║ Physical │█
- │ Address 64K ║ Seg 1 ║ Address │█
- │ │ │ ║ ║ Space │█
- │ └──────────────────────┴─╟──────────╢ │ │█
- │ ║ ║ │ │█
- │ ║ ║ │ │█
- │ ╚══════════╝ ───┘ │█
- │ │█
- └─────────────────────────────────────────────────────────────────┘█
- ██████████████████████████████████████████████████████████████████
-
-
- Figure 2: In protected mode the selector does not specify a segment's
- location in physical memory. Instead, it uniquely identifies
- (or names) any one of 16K possible segments──the selector uses
- 14 bits to address the segments in a given task's virtual address
- space. Because each segment can address 64K, the total virtual
- space available is Gb.
-
- ╔════════════╗ ──┐
- ╠═ Seg 3fff ═╣ │
- ╠═ Seg 3ffe ═╣ │
- ╠═ Seg 3ffd ═╣ │
- ┌──────╠═ Seg 3ffc ═╣ │
- │ ╠═ Seg 3ffb ═╣ │
- ┌────┴───┐ ║ ║ │
- │Selector│ < < 1 G Virtual
- └────────┘ > > Address Space
- ║ ║ │
- ╠══ Seg 4 ══╣ │
- 1 to 64K───╠══ Seg 3 ══╣ │
- ╠══ Seg 2 ══╣ │
- ╠══ Seg 1 ══╣ │
- ╠══ Seg 0 ══╣ │
- ╚════════════╝ ──┘
-
-
- Figure 3: The GDT maintains system-wide information while the LDT maintains
- task-specific information. There is one GDT for any given system
- that is shared by every task or process. Each task's virtual local
- space, managed bu the LDT, is isolated from that of any other.
- OS/2 itself resolves virtual addresses to map tasks to real
- memory.
-
- CPU │ Memory │
- ┌─────────────────────────┐ ├────────────┤──┐
- │ │ ┌─┬──├────────────┤ │
- │ ┌15─────────0┐ │ │ ├────────────┤ │
- │ │ GDT Limit ├─────────┘ │ │ ∙ │ │
- GDT│ ┌23────┴────────────┤ │ │ ∙ │ GDT
- │ │ GDT Base ├─────────┐ │ │ ∙ │ │
- │ └───────────────────┘ │ │ ├────────────┤ │
- ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ │ │ ├────────────┤ │
- │ ┌15─────────0┐ │ │ ├────────────┤ │
- │ │LDT Selector│ │ └─┴──├────────────┤──┘
- │ └────────────┘ │ │ │
- │ │ │ LDT{1} │
- │ ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐ │ ┌─┬──├────────────┤──┐
- LDT│ ┌15─────────0┐ │ │ ├────────────┤ │
- │ │ │ LDT Limit ├┼────────┘ │ ├────────────┤ │
- │ ┌23────┴────────────┤ │ │ ∙ │ │
- │ ││ LDT Base ├┼────────┐ │ │ ∙ │ Current
- │ └───────────────────┘ │ │ │ ∙ │ LDT
- │ │ │ │ │ │ ├────────────┤ │
- │ Program Invisible │ │ ├────────────┤ │
- │ └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘ │ │ │ ├────────────┤ │
- └─────────────────────────┘ └────├────────────┤──┘
- │ │
- TASK 1 │ LDT{n} │
- VIRTUAL ADDRESS─┐ ├────────────┤
- SPACE │ ├────────────┤
- ┌──────────────┐ │ ├────────────┤
- │ ╔══════════╗ │ │ │ ∙ │
- │ ║TASK 1 ║ │ │ │ ∙ │
- TASK 3 │ ║LOCAL ║ │ │ │ ∙ │
- VIRTUAL ADDRESS │ ║ADDRESS ║ │ │ ├────────────┤
- SPACE ┐ │ ║SPACE ║ │──┘ ├────────────┤
- │ ╚══════════╝ │ ├────────────┤
- ┌─────────────│──────────────│┐ ├────────────┤
- │ ╔══════════╗│┌─────────────│───────────────┐
- │ ║TASK 3 ║││ ╔════════╗ ││ ╔══════════╗ │
- │ ║LOCAL ║││ ║GLOBAL ║ ││ ║TASK 2 ║ │
- │ ║ADDRESS ║││ ║ADDRESS ║ ││ ║LOCAL ║ │
- │ ║SPACE ║││ ║SPACE ║ ││ ║ADDRESS ║ │
- │ ╚══════════╝││ ╚════════╝ ││ ║SPACE ║ │
- └─────────────└──────────────┘┘ ╚══════════╝ │
- └─────────────────────────────┘
-
- └TASK 2
- VIRTUAL ADDRESS
- SPACE
-
-
- Figure 4: 80826 Virtual Address Space. OS/2 provides one GDT that is shared
- by each task or process. Hardware support for paging and the OS/2
- swapper together provide the illusion that all segments are
- simultaneously present and accessible.
-
- Task B Address Space
- Task A Private Address Space Task B Private Address Space
-
- ┌───────┐ 65535 ┌───────┐ 65535
- │ │ │ │
- │ SEG. │ Offset │ SEG. │ Offset
- │ │ │ │ │ │
- ┌───────┬───────└───────┘ 0 ┌───────┬───────└───────┘ 0
- │ │ ∙ │ │ ∙
- │ LDT A │ ∙ │ LDT B │ ∙
- │ │ ∙ │ │ ∙
- └───────┴──┐ ┌───────┐ 65535 └───────┴──┐ ┌───────┐ 65535
- │ │ │ │ │ │
- │ │ SEG. │ Offset │ │ SEG. │ Offset
- │ │ │ │ │ │ │ │
- └────└───────┘ 0 └────└───────┘ 0
-
- Task C Private Address Space Shared Address Space
-
- ┌───────┐ 65535 ┌───────┐ 65535
- │ │ │ │
- │ SEG. │ Offset │ SEG. │ Offset
- │ │ │ │ │ │
- ┌───────┬───────└───────┘ 0 ┌───────┬───────└───────┘ 0
- │ │ ∙ │ │ ∙
- │ LDT C │ ∙ │ GDT │ ∙
- │ │ ∙ │ │ ∙
- └───────┴──┐ ┌───────┐ 65535 └───────┴──┐ ┌───────┐ 65535
- │ │ │ │ │ │
- │ │ SEG. │ Offset │ │ SEG. │ Offset
- │ │ │ │ │ │ │ │
- └────└───────┘ 0 └────└───────┘ 0
-
-
- Figure 5: To invoke run-time dynamic linking, the application uses the
- following system calls.
-
- DOSLOADMODULE Load Dynamic Link Module
- DOSFREEMODULE Free Dynamic Link Module
- DOSGETPROCADDR Get Dynamic Link Procedure Address
- DOSGETMODHANDLE Get Dynamic Link Module Handle
- DOSGETMODNAME Get Dynamic Link Module Name
-
- ████████████████████████████████████████████████████████████████████████████
-
- OS/2 Inter-Process Communication: Semaphores, Pipes, and Queues
-
- Ray Duncan
-
- Operating System/2 multitasking services allow applications to create
- multiple concurrent threads within a process or to create child processes.
- However, as we have seen earlier (see "OS/2 Multitasking: Exploiting the
- Protected Mode of the 80286,"), the multitasking functions provide
- a parent process with only a very limited ability to influence and
- communicate with a child process. The parent process can pass information
- to the child at load time in the form of command strings and the
- environment block, it can obtain the child process's exit (return) code
- and a code describing the child process's method of termination, and
- unilaterally abort a child process.
-
- To fill the need for rapid exchange of all kinds of information between
- concurrent threads and related or unrelated processes, OS/2 includes a
- rich set of system services called inter-process communication (IPC)
- functions. Computer literature is teeming with different mechanisms for
- inter-process communication, and more are being added every day. Still,
- there is broad agreement on three basic methods of IPC, all of which are
- supported by OS/2: semaphores, pipes, and queues.
-
- In this article, we'll briefly discuss each of these three IPC mechanisms,
- summarize the relevant OS/2 functions, and supply a brief coding example.
- Since there are many options available in the IPC services, we can't
- provide a comprehensive description of their capabilities here; the
- program fragments are only intended to demonstrate the most common usage.
- More detailed information can be found in the OS/2 Programmer's Reference
- manual.
-
- While reading about the IPC functions, bear in mind the distinctions
- between processes and threads. When a process opens or creates a
- semaphore, pipe, or queue, a handle is returned that can be used by any
- thread within that process and is inherited by any child processes. On the
- other hand, when a thread issues an OS/2 function call that waits on a
- semaphore or performs a synchronous read or write to a pipe or queue, only
- the calling thread is suspended──other threads in the process continue to
- run as they did before. When multiple threads in the same process issue
- requests against the same IPC object, any necessary serialization of these
- requests is taken care of within the operating system.
-
-
- Semaphores
-
- A semaphore can be viewed as a simple object with two states: set (owned)
- or cleared (not-owned). OS/2 provides a complete battery of system
- services to test, set, and clear semaphores; a summary of these functions
- is found in Figure 1. Semaphores are a high-performance mechanism of
- inter-process communication, because they are always resident in memory
- and the OS/2 services that manipulate them are relatively fast.
-
- The classical use of semaphores, and a usage fully supported by OS/2, is
- to control access to a nonreentrant routine or a serially reusable
- resource (SRR). An SRR is a file, peripheral device, or a memory object
- that is damaged or produces unpredictable results if it is accessed by
- more than one thread or process at a time. With semaphores, mutual
- exclusion on such a resource is easy to arrange between cooperating
- processes. A semaphore is established that represents the resource, and a
- thread or process refrains from accessing the resource unless it "owns"
- the corresponding semaphore.
-
- An alternative use of semaphores in OS/2 is to provide for synchronization
- or signaling between threads or processes. In this usage, one or more
- threads can suspend themselves by using an OS/2 function to "block" or
- wait on a semaphore. When the semaphore is cleared by another thread in
- response to some event, all of the threads that were blocking on that
- semaphore will wake up and run. When a semaphore is used for signaling and
- no resource that can be corrupted is involved, any thread that knows about
- the semaphore can set, clear, or test the semaphore at any time.
-
- To provide for the slightly different requirements of inter-thread and
- inter-process communication, OS/2 supports two types of semaphores: RAM
- semaphores and system semaphores.
-
- RAM semaphores are used for signaling or resource control between multiple
- threads in a single process. Each RAM semaphore requires the allocation of
- a double word of data storage; the double word should be initialized to
- zero when the process is started. The handle for a RAM semaphore is simply
- its 32-bit address (selector and offset).
-
- System semaphores are named objects that are used for signaling or
- synchronization between processes; the name always takes the form:
-
- \SEM\name.ext
-
- The extension (.ext) is optional. The storage for a system semaphore is
- allocated by the operating system somewhere outside the creating
- processes' memory space.
-
- Access to a system semaphore is obtained by creating or opening it by
- name; OS/2 returns a 32-bit handle that can be used for subsequent access
- to the semaphore. When the process no longer requires access to the system
- semaphore, it can use the handle to close it as it would close a file. The
- three functions that control access to a system semaphore, DOSCREATESEM,
- DOSOPENSEM, and DOSCLOSESEM, are described in more detail below.
-
- Creating a system semaphore. The function DOSCREATESEM is called with the
- address of a null-terminated (ASCIIZ) semaphore name, the address of a
- double-word variable that will receive the semaphore handle, and a flag
- that indicates whether ownership of the semaphore will be "exclusive"──in
- which case the state of the semaphore cannot be altered by a process that
- does not own it (see Figure 2). If the create operation is successful, the
- initial state of the semaphore will be not-owned (cleared).
-
- Opening a system semaphore. The function DOSOPENSEM is called with the
- address of a null-terminated (ASCIIZ) semaphore name of an existing
- semaphore and the address of a double word of storage that will receive a
- handle. Opening a semaphore does not establish ownership, test, or change
- the value of the semaphore.
-
- When a process creates or opens system semaphores, the handles to those
- semaphores are inherited by any child processes that are started with
- DOSEXECPGM. However, the child process does not own the semaphores even if
- the parent owned them; only one process at a time can own a given
- semaphore.
-
- Closing a system semaphore. The function DOSCLOSESEM is called with a
- handle and terminates access to a system semaphore. If a process
- terminates with open semaphores, they are closed automatically.
-
- However, if any of these semaphores were owned by the process, any threads
- in other processes that were waiting on the semaphore are awakened and
- receive an error code that indicates that the owning process may have
- terminated abnormally and the resource controlled by the semaphore may be
- in an indeterminate state. The semaphore itself ceases to exist when
- processes that use the semaphore have terminated or called DOSCLOSESEM.
-
- ───────────────────────────────────────────────────────────────────────────
- RAM Semaphores: A thread (Tx) issues the DosSemRequest call to claim access
- to the semaphore controlling a given SRR. If the semaphore is currently
- unused, DosSemRequest gives the calling thread ownership of the semaphore.
- All other threads are locked out until the owning thread clears the semaphore
- through the DosSemClear and relinquishes ownership of both the SRR and the
- semaphore.
-
-
- T1 D Tx enters and owns
- Multiple o the SRR (semaphore D
- Concurrent s not available) o
- Threads S │ s
- (T1...Tn) T2 e │ S Tx exits and
- desire m e relinquishes
- access to R Tx Tx Tx m Tx ownership of
- a Serially e C the SRR and
- Reusable ... q │ l the semaphore
- Resource u │ e
- (SRR) e Serially Reuseable a
- s Resource r
- Tn t
-
-
- SEMAPHORE AVAILABLE
-
- ───────────────────────────────────────────────────────────────────────────
-
- Mutual Exclusion
-
- The two OS/2 functions that are associated with the classical use of a
- semaphore are DOSSEMREQUEST and DOSSEMCLEAR.
-
- Claiming a semaphore. The OS/2 function DOSSEMREQUEST is used by a thread
- to establish ownership of a semaphore and, by inference, ownership of the
- resource associated with the semaphore. DOSSEMREQUEST is called with the
- handle of a system or RAM semaphore and a time-out parameter. If the
- specified semaphore is currently unowned, the function sets it as owned
- and returns a success code. If the semaphore is already owned by another
- thread, the system either suspends the thread indefinitely until the
- semaphore becomes available, waits for a specified interval and then
- returns with an error code, or returns immediately with an error code,
- depending on the time-out parameter.
-
- Recursive requests for system semaphores are supported by means of a use
- count, maintained by OS/2, of the number of times the semaphore owner has
- issued DOSSEMREQUEST without a corresponding DOSSEMCLEAR. However,
- recursive use of RAM semaphores is not supported.
-
- Releasing a semaphore. The function DOSSEMCLEAR is called with a semaphore
- handle and releases the thread's ownership of that semaphore. An error
- code is returned if the handle is invalid or if the semaphore was created
- with the exclusive option and is not currently owned by the calling
- thread. If the semaphore is already cleared, then no error is returned.
-
- When multiple threads are waiting on the same semaphore with
- DOSSEMREQUEST, it can be difficult to predict which thread will be
- awakened and acquire the semaphore when it is released by the current
- owner. The thread selected depends on an interaction of the priorities of
- the waiting threads, the position of the threads in the scheduler's list,
- and the activity of other threads in the system.
-
-
- Signaling
-
- Semaphores are used as signals when only one thread is in a position to
- detect an event but other threads may wish to take action based on this
- event. For example, a thread in a detached process utility that is
- monitoring the keyboard data stream for a hot key might wish to trigger
- other threads in the same process to display a pop-up screen or write to a
- file. The MS OS/2 functions that support signaling include DOSSEMSET,
- DOSSEMCLEAR, DOSSEMWAIT, DOSSEMSETWAIT, and DOSMUXSEMWAIT.
-
- Setting a semaphore. The OS/2 function DOSSEMSET is called with the handle
- of a system or RAM semaphore and unconditionally sets that semaphore (see
- Figures 3 and 4). The function fails if the handle is invalid or if a
- system semaphore was created with the exclusive option and is currently
- owned by another process (a semaphore used for signaling would not
- ordinarily be created with the exclusive option).
-
- Clearing a semaphore. The function DOSSEMCLEAR is called with the handle
- of a RAM or system semaphore and unconditionally clears that semaphore. As
- with DOSSEMSET, the function fails if the semaphore handle is invalid or
- if the semaphore was created with the exclusive option and is currently
- owned by another process. If any threads were blocked on the semaphore,
- they are restarted.
-
- Waiting on a semaphore. OS/2 contains several functions that allow a
- thread to suspend itself until one or more semaphores are cleared. The
- function DOSSEMWAIT is called with a semaphore handle and a time-out
- value. The calling thread is suspended until the indicated semaphore is
- cleared by another thread or process with DOSSEMCLEAR; it regains control
- immediately if the semaphore is not set. The function returns an error if
- the handle is invalid, no-wait was specified, and the semaphore is
- currently set; if a finite wait was specified and timed out; or if the
- semaphore was created with the exclusive option and is currently owned by
- another process.
-
- The function DOSSEMSETWAIT works just like DOSSEMWAIT, except that it sets
- the semaphore if it was not already set and then suspends the current
- thread until the semaphore is cleared by another thread or process or the
- indicated time-out expires.
-
- DOSSEMWAIT and DOSSEMSETWAIT are level-triggered, not edge-triggered. This
- means that it is possible for a thread that is blocking on a semaphore to
- miss a quick clearing and resetting of the semaphore, depending on the
- thread's priority and position in the scheduler's list and the activity of
- the other threads in the system.
-
- The function DOSMUXSEMWAIT is called with a list of up to 16 semaphores
- and an optional time-out interval. The calling thread is suspended until
- any of the indicated semaphores are cleared or the indicated interval has
- elapsed. Unlike DOSSEMWAIT and DOSSEMSETWAIT, the function DOSMUXSEMWAIT
- is edge-triggered; the waiting thread is awakened when one of the
- semaphores in the list changes state even if that semaphore gets set
- (armed) again right away by another process or another thread.
-
-
- Pipes
-
- Pipes are a means of inter-process communication midway in power between
- semaphores and queues. Like semaphores, the level of performance of pipes
- is relatively high, because the information is always kept resident in
- memory; like queues, pipes can be used to pass any type of data between
- processes. Physically, a pipe is simply a chunk of memory that is used as
- a ring buffer, with In and Out pointers maintained by the system. From a
- process's point of view, reading and writing a pipe are equivalent to
- reading and writing a file, except that a pipe is much faster.
-
- Pipes are not named entities. A process creates a pipe by calling the OS/2
- function DOSMAKEPIPE with the addresses of two variables to receive the
- read and write handles for the pipe, and a maximum pipe size (up to 65,504
- bytes). The function fails if there is not enough memory to create the
- pipe or if no handles are available.
-
- The two handles for reading and writing the pipe are assigned out of the
- same numeric sequence as those used for access to files. Any child
- processes automatically inherit the handles and have access to the pipe.
- We have already encountered the two major restrictions on the use of
- pipes: they can only be used for communication between closely related
- processes, and the maximum amount of data that a pipe can contain at one
- time is relatively small.
-
- Threads read and write a pipe with the usual file DOSREAD and DOSWRITE
- calls, supplying a handle, buffer address, and record length (see
- Figure 5). If a thread attempts to write to a pipe and the pipe is full,
- that thread is suspended until another thread or process removes enough
- data from the pipe so that the write can be completed. If a thread
- requests a synchronous read from a pipe and the pipe is empty, that thread
- is suspended until some other thread writes enough data to the pipe to
- satisfy the read request. There are no control or permission mechanisms or
- checks performed on operations to pipes.
-
- A pipe vanishes from the system when all the processes using the pipe
- either close the pipe handles or terminate. If two processes are
- communicating with pipes and the process reading the pipe ends, the
- process writing the pipe receives an error code.
-
- The most common usage of pipes by a process──and the way that the system's
- command interpreter uses them──is to arrange for the pipe's handles to be
- substituted for the standard input and standard output handles. Any child
- processes that are started then automatically communicate with the parent
- process for their input and output instead of the keyboard and screen,
- without any special knowledge or action on the child's part.
-
- ───────────────────────────────────────────────────────────────────────────
- Communicating with a Pipe: Pipes are a high performance mechanism for
- communication between closely related processes (such as a parent process and
- multiple child processes). Pipes are read and written like a file, and may
- contain variable-length messages with any content.
-
- ╔═════════╗ PIPES ╔══════════╗
- ║ NEXT IN ║ ╔═══════════╗ ║ NEXT OUT ║
- ╚═════════╝ ║ aaaaaa ║ ╚═════════╝
- █ ║ aaaaaa ║ │ │
- ╔═════════════╗ █ █ ╟───────────╢ │ │ ╔═════════════╗
- ║ PROCESS A ╟─ █ ║ eeeeee ║ │ └────║ PROCESS X ║
- ╚═════════════╝ █ █ ║ eeeeee ║ │ ╚═════════════╝
- █ █ ╟───────────╢ │
- ╔═════════════╗ █ █ ║ bbbbb ║─────┘
- ║ PROCESS B ╟─ █ ╟───────────╢
- ╚═════════════╝ █ █ ║ ggggg ║
- █ █ ║ ggggg ║
- ╔═════════════╗ █ █ ║ ggggg ║
- ║ PROCESS C ╟─ █ ╟───────────╢
- ╚═════════════╝ █ █ ║ ccccc ║
- █ ╠═══════════╣
- █─ ╠═══════════╣
- █ ╠═══════════╣
- ╚═══════════╝
- ───────────────────────────────────────────────────────────────────────────
-
- Queues
-
- Queues are the most powerful inter-process communication service provided
- by OS/2, and consequently the most complex to use. Queues are slower than
- pipes, and much slower than semaphores, but they are also far more
- flexible.
-
- ■ Queues are named objects and can be opened and written by any
- process.
-
- ■ The size of a queue is limited only by the amount of free memory plus
- swap space on the disk.
-
- ■ Each record in a queue is a separately allocated block of memory
- storage and can be as large as 65,536 bytes.
-
- ■ The records in a queue may be ordered by the first-in-first-out
- (FIFO), last-in-first-out (LIFO), or priority methods.
-
- ■ The queue owner can examine records in the queue and remove them
- selectively, in any order.
-
- ■ Data in the queue is not copied from place to place by the operating
- system; instead, the data is passed in shared memory segments.
-
- The name of a queue will always take the form of \QUEUES\name.ext where
- the extension (.ext) is optional. When a queue is created or opened, OS/2
- returns a handle that is used for subsequent access to the queue. A
- summary of OS/2 queue services can be found in Figure 6.
-
- Creating a queue. The function DOSCREATEQUEUE is used to originate a new
- queue in the system. The function is called with a null-terminated
- (ASCIIZ) string that names the queue, a parameter that specifies the queue
- ordering (FIFO, LIFO, or priority), and the address of a variable to
- receive the queue handle (see Figure 7). An error code is returned if a
- queue already exists with the same name, the given name is invalid, or
- there is not enough free memory in the system to establish the queue's
- supporting data structure. Only the process that creates the queue, called
- the queue owner, can remove records from the queue, but all threads in the
- owning process can access the queue with equal authority.
-
- Opening a queue. Any process can open an existing queue by calling the
- OS/2 function DOSOPENQUEUE with the name of the queue and the address of
- two variables to receive the queue handle and the PID of the queue owner.
- The handle is used for subsequent writes of records to the queue.
-
- Writing a queue. Adding records to a queue is much more involved than
- writing a record to a pipe. The writer must first allocate a chunk of
- global shared memory of appropriate size with the function DOSALLOCSEG and
- copy the data for the queue record into it. Next, the writer must use the
- function DOSGIVESEG to obtain a new selector for the memory segment that
- can be passed to another process. Finally, the writer calls DOSWRITEQUEUE
- with a queue handle, a priority, the address (from DOSGIVESEG) and length
- of the data to be added to the queue (see Figure 8). The queue write
- proper fails if the queue handle is invalid or if there is insufficient
- system memory to expand the supporting structure of the queue. Obviously,
- the sequence can also fail at an earlier point (in DOSALLOCSEG or
- DOSGIVESEG) if the system runs out of memory (the physical memory plus
- swap space on the disk is exhausted) or selectors.
-
- Reading a queue. The function DOSREADQUEUE is used by the queue owner to
- remove a record from a queue; it is called with a queue handle and several
- other selection parameters and returns the address of a queue element
- (selector and offset) and its length. The owner can choose a record from
- the queue based on its position or priority or simply take the next record
- in line. The read operation can also be synchronous (the calling thread is
- suspended until a record is available) or asynchronous (the calling thread
- gets control back immediately, and a semaphore is set when a record is
- available).
-
- The function DOSPEEKQUEUE works similarly to DOSREADQUEUE, but the record
- is not removed from the queue when it is retrieved. This allows the queue
- owner to scan through the data waiting in the queue and decide on a
- processing strategy, without disturbing the data structure or being forced
- to copy the records to its own memory.
-
- Miscellaneous operations. The function DOSQUERYQUEUE allows any process
- that has a valid handle for a queue to obtain the current number of
- elements in that queue. DOSPURGEQUEUE discards all records that are
- currently in the queue; this function can only be called by the queue
- owner.
-
- Closing a queue. The function DOSCLOSEQUEUE is called with a queue handle
- and informs OS/2 that the calling process will not require further access
- to the queue. If the closing process is also the queue owner, the queue is
- purged and destroyed──any further attempts to write records to the queue
- by other processes will fail.
-
- ───────────────────────────────────────────────────────────────────────────
- Communicating with a Queue: Queues are the most flexible and powerful
- mechanism of inter-process communication supported by OS/2. A queue is
- basically an ordered list of shared memory segments; each segment contains
- a separate message and may be as large as 64K. The messages may be ordered
- in the queue by First-In-First-Out, Last-In-Last-Out, or priority, and may
- be inspected or removed from the queue by the server process in any order
- whatsoever.
-
- ╔═════════════╗
- ║ Queues Data ╠════════════╗
- █ ║ Structure ║ ║
- ╔═══════════╗ █ ╚═══╦═════════╝ ║ ╔═══════════╗
- ║ Process D ║ ╚═══║ Process Y ║
- ╚═══════════╝ █ ╔════════╦════════════╗ ╚═══════════╝
- ╔═══════════╗ █ ║ MSG 'H'║
- ║ Process E ║ ╚════════╝ ╔════════╦═════════════╗
- ╚═══════════╝ █ ║ MSG 'K'║
- ╔═══════════╗ █ ╚════════╝ ╔════════╗
- ║ Process F ║ ╔═════════════════╣ MSG 'J'║
- ╚═══════════╝ █ ╚════════╝
- █ ╔════════╗
- ╔══════╣ MSG 'N'║
- ╚════════╝
- ╔════════╗
- ║ MSG 'M'║
- ╚════════╝
- ───────────────────────────────────────────────────────────────────────────
-
- Summary
-
- OS/2 supports three methods of inter-process communication that are used
- to pass messages between threads or processes: semaphores, pipes, and
- queues.
-
- Semaphores are used to symbolize ownership of a resource or signal
- occurrence of an event; they can be thought of as simple flags that can be
- set, cleared, or tested.
-
- Pipes are a high-performance means of passing variable-length, variable-
- content data between two closely related processes; however, pipes have
- the relative disadvantage that their maximum size is fixed at the time
- that they are created and they can never hold more than 64K.
-
- Queues allow a vast amount of prioritized data to be passed from multiple
- client processes to a server process; each message in the queue can be as
- large as 64K, and the total amount of data in the queue is limited only by
- the system's virtual memory. The selection of a particular IPC technique
- for a given application must be made carefully on the basis of performance
- requirements and the character of the messages to be exchanged between the
- interested threads or processes.
-
-
- Figure 1: A summary of OS/2 semaphore support functions. Semaphores can be
- used to coordinate access to a resource or for signaling between
- threads or processes.
-
- Access to system semaphores
-
- DOSCREATESEM Create a system semaphore
- DOSOPENSEM Open an existing system semaphore
- DOSCLOSESEM Close a system semaphore
-
- Semaphore functions for resource control
-
- DOSSEMREQUEST Establish ownership of a semaphore
- DOSSEMCLEAR Release ownership of a semaphore
-
- Semaphore functions for signaling
-
- DOSSEMSET Unconditionally set a semaphore
- DOSSEMCLEAR Unconditionally clear a semaphore
- DOSSEMWAIT Wait until semaphore is cleared
- DOSSEMSETWAIT Unconditionally set semaphore and wait
- until it is cleared by another thread
- DOSMUXSEMWAIT Wait for any of several semaphores to be cleared
-
-
- Figure 2: A code fragment that illustrates the creation or opening of a
- system semaphore.
-
- push 1 ; exclusive ownership not desired
- push ds ; variable to receive semaphore handle
- push offset DGROUP:shandle
- push ds ; address of semaphore name
- push offset DGROUP:sname
- call DOSCREATESEM ; transfer to OS/2
- or ax,ax ; create successful?
- jz continue ; jump if it was...
- cmp ax,err_sem_exists ; semaphore already exists?
- jnz error ; jump if some other error
-
- ; semaphore exists, open it instead
- push ds ; variable to receive handle
- push offset DGROUP:shandle
- push ds ; address of semaphore name
- push offset DGROUP:sname
- call DOSOPENSEM ; transfer to OS/2
- or ax,ax ; was open successful?
- jnz error ; if can't open and can't create,
- ; something is terribly wrong
- ∙
- ∙
- ∙
-
- sname db '\SEM\RESOURCE.LCK',0
-
- shandle dd 0
-
-
- Figure 3: A code sample that demonstrates setting a system semaphore.
- System semaphores are best suited for "between" processes.
-
- ∙
- ∙
- ∙
- ; open the semaphore
-
- push ds ; variable to receive handle
- push offset DGROUP:shandle
- push ds ; address of semaphore name
- push offset DGROUP:sname
- call DOSOPENSEM ; transfer to O/S2
- or ax,ax ; open successful?
- jnz open_err ; jump if open failed
- ∙
- ∙
- ∙
- ; set the semaphore:
- ; push semaphore handle...
- push word ptr shandle+2
- push word ptr shandle
- call DOSSEMSET ; transfer to O/S2
- or ax,ax ; was operation successful?
- jnz set_err ; jump if set failed
- ∙
- ∙
- ∙
- sname db '\SEM\RESOURCE.LCK',0
-
- shandle dd 0 ; handle for system semaphore
-
-
- Figure 4: A code fragment that demonstrates setting a RAM semaphore. RAM
- semaphores are best suited for use "within" processes.
-
- ∙
- ∙
- ∙
- ; set the RAM semaphore
- push ds ; push its address
- push offset DGROUP:my_sem
- call DOSSETSEM ; transfer to O/S2
- or ax,ax ; was set successful?
- jnz set_err ; jump if set failed
- ∙
- ∙
- ∙
- mysem dd 0 ; storage for RAM semaphore
-
-
- Figure 5: Establishing Pipes
- ∙
- ∙
- ∙
- push ds ; address to receive handle
- push offset DGROUP:prdh ; for reading pipe
- push ds ; address to receive handle
- push offset DGROUP:pwriteh ; for writing pipe
- push 0 ; max pipe size = default
- call DOSMAKEPIPE ; transfer to O/S2
- or ax,ax ; was pipe created?
- jnz error ; jump if create failed.
- ∙
- ∙
- ∙
- ; in here we spawn a "child"
- ; process which inherits handles...
-
- ; now send a message to child
- ; process through the pipe...
-
- push pwriteh ; pipe write handle
- push ds ; address of message
- push offset DGROUP:message
- push message_length ; length of message
- push ds ; address of variable to
- push offset DGROUP:status ; receive bytes written
- call DOSWRITE ; transfer to O/S2
- or ax,ax ; did write succeed?
- jnz error ; jump if write failed.
- ∙
- ∙
- ∙
- preadh dw ? : handle to read pipe
-
- pwriteh dw ? ; handle to write pipe
-
- status dw ? ; receives length from DOSWRITE
-
-
- Figure 6: Substantial control over queues is provided by the OS/2 queue
- management functions.
-
- Queue access
-
- DOSCREATEQUEUE Create a queue (process is owner, can read or write
- messages to queue)
- DOSOPENQUEUE Open a previously existing queue (can only write
- messages to queue)
- DOSCLOSEQUEUE Close queue (queue is destroyed when closed by owner)
-
- Queue input/output
-
- DOSWRITEQUEUE Write message to queue (either queue owner or any process
- that has opened queue)
- DOSREADQUEUE Read message from queue (queue owner only)
- DOSPEEKQUEUE Nondestructive read of queue message (queue owner only)
-
- Queue information
-
- DOSQUERYQUEUE Find number of messages currently in queue (queue owner or
- any process that has opened queue)
- DOSPURGEQUEUE Discard all messages currently in queue (queue owner only)
-
-
- Figure 7: Creating a queue, waiting for a message to be written into it, and
- then closing the queue (destroying it).
-
- ∙
- ∙
- ∙
- ; first create queue...
- push ds ; address to receive handle
- push offset DGROUP:qhandle
- push 0 ; queue ordering = FIFO
- push ds ; address of queue name
- push offset DGROUP:qname
- call DOSCREATEQUEUE ; transfer to OS/2
- or ax,ax ; was create successful?
- jnz error ; jump if create failed
-
- ; now read from queue
- push qhandle ; queue handle
- push ds ; address to receive PID
- push offset DGROUP:qident ; and event code
- push ds ; receives message length
- push offset DGROUP:qmsglen
- push ds ; receives message pointer
- push offset DGROUP:qmsgptr
- push 0 ; 0= read first element
- push 0 ; 0= synchronous read
- push ds ; receives message priority
- push offset DGROUP:qmsgpri
- push ds ; handle for RAM semaphore
- push offset DGROUP:qsem ; (not used for synch reads)
- call DOSREADQUEUE ; transfer to OS/2
- or ax,ax ; was read successful?
- jnz error ; jump if read failed
-
- les bx,qmsgptr ; let ES:BX point to queue msg
-
- push es ; now release queue message's
- call DOSFREESEG ; shared memory segment
- or ax,ax ; was release successful?
- jnz error ; jump if released failed
-
- push qhandle ; now close the queue
- call DOSCLOSEQUEUE ; also destroying it
- or ax,ax ; was close successful?
- jnz error ; jump if close failed
- ∙
- ∙
- ∙
- qhandle dw ? ; receives handle from DosCreateQueue
-
- qname db '\QUEUES\MYQUEUE',0 ; ASCIIZ queue name
-
- qident dw 0,0 ; writer's PID, event code
-
- qmsglen dw ? ; length of queue msg received
-
- qmsgptr dd 0 ; address of queue msg received
-
- qmsgpri dw ? ; priority of queue msg
-
-
- Figure 8: Opening a queue, writing a message into it, and then closing it.
- ∙
- ∙
- ∙
- push ds ; address to receive PID of
- push offset DGROUP:qowner ; queue's owner
- push ds ; address to receive handle
- push offset DGROUP:qhandle
- push ds ; address of queue's name
- push offset DGROUP:qname
- call DOSOPENQUEUE ; transfer to OS/2
- or ax,ax ; was open successful?
- jnz error ; jump if open failed
-
-
- push qmsglen ; length of shared memory segment
- push ds ; receives selector for segment
- push offset DGROUP:qselc
- push 1 ; 1 = segment will be shared
- call DOSALLOCSEG ; transfer to OS/2
- or ax,ax ; was allocation successful?
- jnz error ; jump if memory allocate failed
-
-
- mov si,offset qmsg ; copy our queue message
- mov es,qselc ; to shared segment
- xor di,di
- mov cx,qmsglen
- cld
- rep movsb
-
-
- push qselc ; get a new selector for the
- push qowner ; shared segment that can
- push ds ; be used by receiver of
- push qselr ; the queue message
- call DOSGIVESEG ; transfer to OS/2
- or ax,ax ; did function succeed?
- jnz error ; jump if can't give it away
-
-
- push qhandle ; handle for queue
- push 0 ; 0= event data
- push qmsglen ; length of queue message
- push qselr ; selector for queue data
- push 0 ; (offset)
- push 0 ; queue element priority
- call DOSWRITEQUEUE ; transfer to OS/2
- or ax,ax ; did write succeed?
- jnz error ; jump if queue write failed
-
-
- push qselc ; release shared memory that
- call DOSFREESEG ; contains the queue message
- or ax,ax ; did release succeed?
- jnz error ; jump if release failed
-
-
- push qhandle ; now close the queue
- call DOSCLOSEQUEUE ; transfer to OS/2
- or ax,ax ; did close succeed?
- jnz error ; jump if close failed
- ∙
- ∙
- ∙
- qhandle dw ? ; receives handle from DosOpenQueue
-
- qowner dw ? ; receives PID of queue owner
-
- qname db '\QUEUES\MYQUEUE',0 ; ASCIIZ queue name
-
- qselc dw ? ; shared memory selector for caller
-
- qselr dw ? ; shared memory selector for receiver
-
- qmsg db 'This is my queue message'
- qmsglen equ $-qmsg
-
- ████████████████████████████████████████████████████████████████████████████
-
- The MS OS/2 LAN Manager
-
- System connectivity has become an increasingly significant issue in the
- development of today's personal computing systems. As a result, it is
- becoming substantially more important for operating systems to provide
- expanded support for Local Area Networking.
-
- The design of Microsoft OS/2 recognizes this fact by providing substantial
- networking support in the form of the Microsoft OS/2 LAN Manager.
-
- Basically, the MS OS/2 LAN Manager is a protected mode version of
- Microsoft Networks, and it is compatible with existing versions of MS-Net,
- XENIX Net and the IBM PC Local Area Network Program.
-
- The LAN Manager is a separately "installable" component of MS OS/2; once
- installed, it works with the operating system to seamlessly solve
- networking requirements.
-
- The LAN Manager is designed to facilitate the traditional network file-
- and printer-sharing services. But it goes far beyond this by providing for
- a new class of communication facilities, which will allow software
- developers to increase the sophistication of network services
- substantially and will make distributed processing practical.
-
- The LAN Manager uses the protected mode, multitasking facilities of MS
- OS/2 to implement non-dedicated servers with extensive network
- administration and security services, as well as transparent file- and
- printer-sharing capabilities.
-
- The LAN Manager allows an OS/2 based system to be added to existing MS-
- Net-based networks. The new system can function within the network as a
- workstation or a server or both.
-
-
- Protected Mode NetBios
-
- The multitasking environment in which the LAN Manager must function
- requires a new, protected-mode version of the MS-Net NetBios. The NetBios
- is the equivalent of the transport layer of the OSI model, and provides
- for virtual circuit and datagram services.
-
- The standardized NetBios API allows network drivers to access low-level
- hardware (for such tasks as checking the status of a network card). The
- NetBios is designed not only to understand the hardware at one end
- (network hardware comes with a customized Hardware/NetBios layer), but
- also to understand API calls from network drivers at the other end.
- Applications access NetBios services via the network drivers.
-
- The LAN Manager must deal with several multitasking issues. First,
- applications will most likely need to share access to more than one
- network driver. Second, there must be a way for network drivers to share
- access to the NetBios services.
-
- Central to the LAN Manager is the Microsoft Net Kernel, which provides
- applications with the support necessary to simultaneously access one or
- more network drivers. Very briefly, the kernel serves as a dispatcher for
- applications' requests to access network drivers, and controls access to
- those network drivers by treating each driver as a named, installable
- device driver that can be opened or closed.
-
- To insure that network drivers can interface to the net kernel and
- therefore share access to the NetBios, developers must adhere to the
- system interface specifications (and the protected mode NetBios API)
- published as part of the Microsoft OS/2 LAN Manager. These specifications
- will ease the effort that is necessary for OEMs and third-party network
- software developers to design their hardware and software applications to
- support the new multitasking, distributed processing environment.
-
-
- Distributed Processing
-
- From a software developer's point of view, the LAN Manager can be seen as
- providing a set of extensions to the MS OS/2 Inter-process Communications
- API. Just as MS OS/2 provides semaphores, pipes, and queues for
- communication between different processes running on the same machine, the
- LAN Manager provides named pipes and mailslots for communication between
- processes running on different machines on the network.
-
- Named pipes and mailslots are the building blocks of distributed
- processing. These remote inter-process communication facilities provide
- the ability to establish communications channels between tasks, allowing
- for sharing of data──not only between a server machine and its clients,
- but also among peer machines residing on a given network.
-
- Data-base applications provide a clear example of the distinction between
- file service and distributed proessing. Using current LAN technology, it
- is typical to search a file resident on another machine by reading a
- virtual file that is mapped to the real file by the MS-Net redirector.
-
- In the distributed processing model, the requesting workstation passes a
- request to the server, which then conducts the search and passes back the
- results. The only information that travels on the network is the original
- request and the reply. This requires a level of inter-machine
- communication not possible with file- or record-oriented facilities.
-
- The LAN Manager API is analogous at the network level to the MS OS/2
- Inter-process Communication API that allows thread-to-thread and process-
- to-process communication at the operating system level. At any given
- moment, LAN API functions can block, be blocked, or can be in a state of
- execution (i.e., reading, writing or querying either named pipes or
- mailslots).
-
- At the applications level, the LAN API provides CALL-based functions that
- can be incorporated into high-level language code. Programs written to the
- specifications of the API will be able to communicate across a network.
-
-
- Virtual Circuits
-
- The LAN API supports communications at the "Network and Transport Levels"
- of the Open System Interconnect (OSI) model. Stream-oriented and
- optionally, message-oriented information is communicated through a
- mechanism called the named pipe. Message-oriented information can also be
- communicated via the high-speed mechanism called a named mailslot.
-
- To understand the difference between named pipes and mailslots, one needs
- to understand two concepts basic to communications: those of virtual
- circuits and datagrams.
-
- Virtual circuits are network-level data channels that guarantee an error-
- free and continuous communication channel, where each packet of routed
- information is delivered in order. Regardless of how a channel is
- established between two points (it could, for example, be a very complex
- switching network in a phone system), the two communicating ends perceive
- the channel as a direct communications link. This is, in essence, how
- named pipes work.
-
- Datagrams are network-level channels that accept isolated messages
- (specific packets of information) from the transport layer and attempt to
- deliver them without regard for order or timeliness, much the same way as
- a postal system, in which the sender mails letters without knowing exactly
- when they will arrive, or in what order (message B, sent after message A,
- might arrive before message A). Named mailslots work in this fashion.
-
- Across the network, either of these mechanisms lets processes
- transparently establish remote or local communications through symbolic
- names that follow MS OS/2 file naming conventions. Additionally, it is
- possible to impose access restrictions on named pipes and mailslots, just
- as can be done with files.
-
-
- Named Pipes
-
- Every reader of MSJ should be familiar with MS-DOS pipes. These pipes
- function as a transparent holding area for the output of one program to
- become the input of another. For example, DIR | SORT routes the output
- from DIR to a temp file that is then read by SORT.
-
- Named pipes essentially extend this facility to provide direct
- communication between processes running on a network. Named pipes are two-
- way (or full duplex) channels that can be written at either end and read
- at the opposite end (that is, what is written at one end cannot be read
- from that same end).
-
- To create a named pipe a process executes the DosMakeNmPipe function call
- (see Figure 1), which then opens a pipe as either a byte stream or a
- message stream. DosMakeNmPipe returns an identifying handle for that end
- of the pipe, which is called the serving end of the pipe. DosOpen (a
- regular OS/2 API call) opens the other end of the pipe, known as the
- client end, and similarly returns a handle.
-
- Once a named pipe has been established it is controlled through the
- facilities provided by the LAN Manager. It can be read, written to and
- queried for activity or information. Named pipes can, at this point be
- utilized across a network, and can be serially closed and opened by other
- processes.
-
- Named pipes are therefore extremely flexible. Once created they can be
- accessed just like any sequential file, from beginning to end. Because
- rewinding of pipes is not possible (information comes and goes), the
- contents of a pipe can only be accessed once, unless the original
- information is again sent through the pipe.
-
- Local and remote procedure call dialogues between processes can be
- efficiently implemeted because named pipes support transaction I/O calls.
-
- Named pipes can also be accessed by applications running on MS DOS 3.x,
- machines serving as workstations on a given network, allowing those
- programs to communicate with server-based applications.
-
- Thus, DosMakeNmPipe can allow multiple instances of a pipe, each with the
- same name. This insures that multiple clients DosOpening to that name will
- obtain separate and distinct pipes to the serving process.
-
-
- Named Mailslots
-
- Named pipes must insure that a full duplex communication channel exists
- between processes. There is, of necessity, a considerable amount of system
- overhead necessary to maintain such a connection.
-
- Certain types of application-specific transactional messages to be
- exchanged between processes do not require a full duplex, error-free
- channel of communication. A named mailslot provides the optimal means for
- handling messages under these conditions. Note that byte streams cannot
- take advantage of named mailslots. Byte streams need the support
- mechanisms built into named pipes to ensure error-free transmission.
-
- Mailslots are inherently fast because they need not be opened or closed by
- processes. Once a named mailslot has been established through the
- DosMakeMailslot function call processes running on different machines
- across a network simply write to it by name.
-
- The function call used to write to a named mailslot, DosWriteMailslot,
- supports both "class" and "priority" settings for messages. Priority
- controls which messages DosReadMailslot reads first.
-
- Class is usually used with remote mailslots. Messages can be assigned
- either a first- or second-class status. First-class mail is reliable in
- that DosWriteMailslot blocks until a message is delivered or an error
- occurs, insuring that the state of the message will be known. Second-class
- mail is sent strictly without error checking. Although delivery will
- probably succeed, if it fails an error will not be reported.
-
- Between high-speed named mailslots and highly reliable named pipes, the
- LAN Manager provides the necessary communications facilities necessary to
- meet most sophisticated network communications needs. Software developers
- now have a base on which to develop complete, distributed applications.
- The opportunity to take advantage of a programming environment in which
- applications can access remote sites, and in which workstations can
- offload work to a server, will drive the next generation of software.──T.R.
-
-
- Figure 1: DosMakeNmPipe Function
-
- int FAR PASCAL
-
- DosMakeNmPipe(name, handle, omode, pmode, size1, size2, timeout)
- char far * name; asciz pipe name
- int far * handle; place for handle to be returned
- unsigned int omode; DOS open mode
- unsigned int pmode; pipe open mode
- unsigned int size1; hint of outgoing buffer size
- unsigned int size2; hint of incoming buffer size
- long timeout; time out for DosWaitNmPipe
- Name: Asciz name of pipe. Pipes are named
- \PIPE\NAME.
-
- Handle: Handle of the named pipe that is created.
-
- Omode: Open mode mask as for DosOpen call. The following bits are
- defined:
-
- Open Mode Bits
- 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
- O W * * * * * * I O O O * A A A
-
- I Inheritance: 0 = Spawned processes inherit the pipe handle
- 1 = Spawned processes don't inherit the pipe
- W Write-through: 0 = Write-behind to remote pipes is allowed
- 1 = Write-behind to remote pipes is not allowed
- AAA Access mode: 000 = Inbound pipe (client to server)
- 001 = Outbound pipe (server to client)
- 010 = Full-duplex pipe (server to/from client)
- Other values invalid
-
- Pmode: Pipe-specific mode parameters
-
- 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
- B * * * T T R R |... I count..|
-
- B Blocking: 0 = Reads/writes block if no data available
- 1 = Reads/writes return immediately if no data
-
- Reads normally block until at least partial data can be returned.
- Writes by default block until all bytes requested have been written.
- Nonblocking mode (B=1) changes this behavior as follows:
-
- 1) Reads will return immediately with BytesRead = 0 if no data is
- available.
-
- 2) Writes will return immediately with BytesWritten = 0 if the data
- transfer cannot be started. Otherwise, the entire data area will be
- transferred.
-
- TT Type of a pipe: 00 = Pipe is a byte stream pipe
- 01 = Pipe is a message stream pipe
-
- All writes to message stream pipes record the length of the write
- along with the written data.
-
- RR Read mode 00 = Read pipe as a byte stream pipe
- 01 = Read pipe as a message stream
-
- Message pipes can be read as byte or message streams, depending
- on the setting of RR. Byte pipes can only be read as pipe streams
-
- Icount: 8-bit count to control pipe instancing. When making the
- first instances of a named pipe, Icount specifies how many
- instances can be created: 1 means that this can be the only
- instance (pipe is unique) and -1 means the number of
- instances is unlimited; 0 is a reserved value. Subsequent
- attempts to make a pipe will fail if the maximum number of
- allowed instances already exists. The Icount parameter is
- ignored when making other than the first instance of a pipe.
- When multiple instances are allowed multiple clients can
- simultaneously DosOpen to the same pipe name and get handles
- to distinct pipe instances.
-
- Size1: Hint to system, number of bytes to allocate for outgoing buffer.
-
- Size2: Hint to system, number of bytes to allocate for incoming buffer.
-
- Timeout: Default value for timeout parameter to PipeWait. This value
- may be set only at the creation time of the first instance of the
- pipe name. If at that time the value is zero, a systemwide default
- value (50 ms) will be chosen.
-
- ████████████████████████████████████████████████████████████████████████████
-
- A Complete Guide to Writing Your First OS/2 Program
-
- Charles Petzold
-
- For the program developer, OS/2 opens up a whole new world of capabilities
- and challenges, and along with them, new concepts to learn. We'll explore
- some of these new concepts by walking through the creation of a typical OS/2
- application that demonstrates inter-process communication (IPC) and
- multitasking.
-
- FINDDEMO and FINDER work in conjunction to locate and list all files on a
- disk that fit a particular file specification, for instance, *.EXE. You can
- enter up to nine different file specifications at any one time, and all nine
- run simultaneously. Figure 1 shows FINDDEMO in action.
-
- FINDDEMO.EXE is the program that you execute from the OS/2 command line.
- FINDER.EXE must be in the current subdirectory or a subdirectory in your
- PATH. FINDDEMO manages the screen display and keyboard input and loads as
- many as nine instances of FINDER.EXE as child processes. FINDER.EXE does the
- actual file searching and funnels data back to FINDDEMO via a queue.
- Separate threads within FINDDEMO read the queue and monitor termination of
- the FINDER instances.
-
- In other words, there's a real party going on here. I'll have more to say
- about the workings of this program after we look at what's involved in
- coding, compiling, and linking it. If you've already done some Windows
- programming, don't be surprised to see some familiar things along the
- way──you're several steps ahead of everybody else.
-
-
- Protected Mode
-
- Your first OS/2 programs will probably be existing MS-DOS programs that you
- will port to OS/2. Most programs written in straight C with standard library
- functions can simply be recompiled by using the new .LIB files.
-
- However, programs with assembly language routines that make software
- interrupt calls, programs that use the Microsoft C int86 or intdos library
- functions, and programs that access memory outside the program or directly
- manipulate I/O ports will have to be modified somewhat.
-
- OS/2 supports nearly 200 function calls that you'll use for communicating
- with the operating system and the PC's hardware. Obviously, these new
- function calls encompass virtually everything you can do now under MS-DOS
- 3.x using Int 21H. However, they also include a much faster and more
- versatile set of character-mode video I/O calls than the calls that have
- previously been available from the IBM PC BIOS services. You will no longer
- have to directly access video display memory in order to get good character-
- mode performance. OS/2 also includes a set of mouse function calls and many
- new calls that are related to tasking and inter-process communication.
-
-
- DOS Function Calls
-
- In C programs, OS/2 function calls look like ordinary function or subroutine
- calls. The names of all the functions are capitalized, and they begin with
- the letters DOS, KBD (keyboard), VIO (video), or MOU (mouse).
-
- Virtually all OS/2 function calls return a zero if the function is
- successful and an error code otherwise. If the function must return
- information back to your program, one or more of the arguments will be a far
- pointer to your program's data segment. OS/2 uses this pointer to store the
- result of the function. Some OS/2 function calls also require far pointers
- to structures for returning more extensive information.
-
- For instance, if you want to obtain the current cursor position, the OS/2
- function that you must use is VIOGETCURPOS. The first argument is a far
- pointer to a variable where OS/2 stores the cursor row position, the second
- argument is a far pointer to a variable to store the column, and the third
- parameter is a "video I/O handle" that currently must be set to zero.
-
- Within a C program you would define two variables for the row and column
- cursor positions like this:
-
- unsigned row, col ;
-
- and then call VIOGETCURPOS by using pointers to these variables:
-
- VIOGETCURPOS(&row, &col, 0) ;
-
- The C source code files for FINDDEMO, shown in Figure 2, and FINDER, shown
- in Figure 3 begin with the statement
-
- #include <doscall.h>
-
- This DOSCALL.H header file contains just two lines:
-
- #include <doscalls.h>
- #include <subcalls.h>
-
- In the DOSCALLS.H and SUBCALLS.H header files are the declarations for all
- OS/2 functions. The DOS functions are declared in DOSCALLS.H, and the KBD,
- VIO, and MOU functions are declared in SUBCALLS.H. For instance, the
- VIOGETCURPOS function is declared in SUBCALLS.H as
-
- extern unsigned
- far pascal
- VIOGETCURPOS(unsigned far *, unsigned far *, unsigned) ;
-
- All OS/2 functions are declared far, which directs the compiler to generate
- a far inter-segment call, and pascal. The pascal keyword indicates a Pascal
- calling sequence──the arguments are pushed onto the stack from left to
- right, and the called function then adjusts the stack before returning
- control to the caller.
-
- Normally, C compilers generate code that pushes arguments on the stack from
- right to left, and the caller fixes the stack. This calling sequence permits
- C to support functions with a variable number of arguments. Moreover, you
- can usually call a C function with an incorrect number of parameters, and
- while the function may not work correctly, in many cases the program will
- not crash.
-
- A function that uses the Pascal calling sequence, however, expects to
- receive a fixed number of arguments. Therefore, if you get the compiler
- warning message "too few actual parameters" for an OS/2 function call, you
- should fix the code before you try to run the program.
-
- Note that the function declaration shows the first two parameters to be far
- pointers to unsigned integers, but the VIOGETCURPOS call used simply &row
- and &col. The C compiler generates far addresses without casting based on
- the function declaration.
-
- The DOSCALLS.H and SUBCALLS.H header files also contain structure
- declarations used in some of the other OS/2 function calls. For instance,
- FINDER.EXE uses the FileFindBuf structure in conjunction with the
- DOSFINDFIRST and DOSFINDNEXT function calls. FINDDEMO.C uses the ResultCodes
- structure for returning values from the DOSEXECPGM and DOSCWAIT functions,
- and the KeyData structure for returning keyboard input from KBDCHARIN.
- Here's what the KeyData structure declaration looks like:
-
- struct KeyData
- {unsigned char char_code ;
- unsigned char scan_code ;
- unsigned char status ;
- unsigned char nls_shift ;
- unsigned shift_state ;
- unsigned long time} ;
-
- That's a little more information than you currently get from Int 16H, isn't
- it?
-
-
- Assembly Language
-
- If you are programming in assembly language, OS/2 function calls are made by
- pushing arguments on the stack and calling external far functions. Near the
- top of your source code you specify that the OS/2 function is in a different
- segment:
-
- extrn
- VIOGETCURPOS:far
-
- The two variables to receive the row and column positions would go in your
- data segment:
-
- row dw ?
- col dw ?
-
- When you call VIOGETCURPOS, you push the arguments on the stack and make the
- call:
-
- push ds
- push offset row
- push ds
- push offset col
- push 0
- call VIOGETCURPOS
-
- When you return from the VIOGETCURPOS call, the AX register contains the
- return value of the function call, and the stack pointer is the value prior
- to pushing the arguments on the stack.
-
- This code sample pushes immediate values on the stack. Because these
- instructions are supported by the 80286 but not by the 8086, you have to
- include the assembler directive
-
- .286c
-
- in your source code file. Or you can simply transfer the values to registers
- before pushing them on the stack.
-
-
- Linking
-
- The new LINK generates a "new-format executable." The extension is still
- .EXE, but the format of the file is not the same as that used in MS-DOS
- versions through 3.2. However, it is the same as the new-format executable
- currently in use for Windows applications.
-
- Among other things, the new-format executable contains an "import table"
- that OS/2 uses to match the far calls in your program code to the
- appropriate OS/2 function calls. The actual entry address of the function
- call routine is inserted into the code when the program is loaded into
- memory. (This is dynamic linking, which has appeared previously in Windows
- programming.)
-
- LINK requires that two libraries be explicitly listed in the command line.
- These are SLIBC5.LIB, which contains normal C library functions that make
- OS/2 functions calls, and DOSCALLS.LIB.
-
- The DOSCALLS.LIB library is an "import library." It merely provides LINK
- with the module names and ordinal numbers associated with the OS/2 function
- calls. LINK uses this information to set up the import table contained in
- the new-format executable. DOSCALLS.LIB performs the same function in OS/2
- programs that the SLIBW.LIB library performs in Windows applications.
-
-
- Module Definition File
-
- The new LINK also requires that a module definition file be specified as the
- fifth argument on the LINK command line. The module definition file (yet
- another concept that Windows programmers are already familiar with) provides
- information about your program's segments, stack size, and local heap
- size. The two module definition files that describe FINDDEMO.DEF and
- FINDER.DEF are shown in Figures 4 and 5, respectively.
-
- The NAME line indicates the name of the module, which is usually the same as
- that of the program. The DESCRIPTION is generally a copyright notice
- imbedded in the .EXE file. The keyword PROTMODE indicates that the program
- runs only in protected mode.
-
- The CODE and DATA lines specify characteristics of the code and data
- segments in your program. The options that are shown here are normal for
- protected mode.
-
- Both the code and data segments are MOVABLE. This is no sweat in protected
- mode because a segment can be physically moved but still retain the same
- selector address, previously known as the segment address. Specifying the
- CODE segment as PURE means that the same code segment loaded in memory can
- be used for multiple instances of the program. The automatic data segment of
- a program cannot be PURE because it contains the stack and most likely
- contains read/write data as well. However, data segments with read-only data
- without relocation information can also be flagged as PURE and can be shared
- among multiple instances of the program.
-
- For large applications, you might divide the program into many different
- segments and specify characteristics of each in the module definition file.
- These characteristics would include the keywords PRELOAD, which means the
- segment is loaded into memory when the program is run, and LOADONCALL, which
- means the segment is loaded into memory only when a routine within it is
- needed. Based on a least-recently-used algorithm, OS/2 can free up memory
- that is occupied by code segments and later reload them when needed from the
- .EXE file. This facility is essentially a built-in, hassle-free overlay
- manager.
-
- The automatic data segment of your program contains static variables, the
- stack, and a local heap organized as in Figure 6.
-
- The stack is fixed to the size specified in the STACKSIZE line of the module
- definition file. The HEAPSIZE value is a minimum local heap size, which is
- the size that OS/2 sets aside when your program is first loaded into memory.
- If you use C memory allocation functions such as malloc to allocate more
- memory from your local heap than is specified by HEAPSIZE, OS/2 can expand
- the local heap──by physically moving the data segment in memory if
- necessary.
-
-
- MAKEing the Program
-
- A make-file for the FINDDEMO application is shown in Figure 7. You create
- the FINDDEMO.EXE and FINDER.EXE executables by running
-
- MAKE FINDDEMO
-
- The compile step uses several compiler switches:
-
- cl -c -G2 -Zp
- finddemo.c
-
- The -c switch specifies that the program is to be compiled but not linked;
- this is necessary because the link step requires nondefault arguments. The
- -G2 switch compiles code by using 80286 instructions. This is optional, but
- it results in a slightly smaller .EXE size and faster execution.
-
- The -Zp switch indicates that structures are "packed." Normally the C
- compiler aligns each element of a structure on an even address. However,
- some of the structures declared in DOSCALLS.H and SUBCALLS.H, such as the
- KeyData structure shown earlier, contain consecutive char variables.
-
-
- New Executable
-
- The new-format executable created by LINK is really a superset of the old-
- format executable. It begins with an old-format header. This makes it appear
- to old DOS versions (and the OS/2 compatibility box) as a normal .EXE
- program. However, when you link the FINDDEMO and FINDER programs, the old-
- format header is constructed so that the program appears to require more
- memory than is available. So, if you attempt to run FINDDEMO.EXE under real
- mode (the OS/2 compatibility box or MS-DOS 2.x or 3.x), you get the message
- "Program too big to fit in memory."
-
- You have some alternatives to this message. The first alternative involves
- the STUB option in the module definition file, which Windows programmers
- already know about:
-
- STUB 'oldprog.EXE'
-
- The file oldprog.EXE is an old-format executable that runs under MS-DOS 2.x
- or 3.x. The STUB option directs LINK to insert this old-format executable in
- the top of the new-format executable. Now the .EXE file appears to MS-DOS
- 2.x or 3.x as a regular program. When MS-DOS 2.x or 3.x loads the program,
- this old program is executed instead.
-
- Windows programmers generally include the line
-
- STUB 'WINSTUB.EXE'
-
- in their module definition files. The standard WINSTUB.EXE program simply
- displays the message "This program requires Microsoft Windows." You might
- want to create a similar program that would display a message such as "This
- program must be run in OS/2 protected mode."
-
- The second alternative is to write a real-mode version and include that in
- the STUB line if your program is short. If you're writing a program using
- straight C without any explicit OS/2 function calls, you could simply
- compile and link it under a different name by using the old real-mode
- libraries. This process creates one .EXE file that actually contains two
- versions of the same program: the protected-mode version runs under
- protected mode, and the real-mode version runs under real mode.
-
- For longer programs, or programs that use explicit OS/2 function calls, you
- should consider the third alternative: going family style.
-
-
- Family Style
-
- The OS/2 developer's kit includes a library file called API.LIB that
- translates many OS/2 function calls into equivalent MS-DOS Int 21H function
- calls. Video I/O calls are translated into equivalent BIOS calls and direct
- screen accesses. When you run
-
- BIND program
-
- after linking, parts of this API.LIB library are tacked onto the end of the
- .EXE file, and the old-format header is modified to run a loader program
- that is also inserted into the .EXE file. When you run the resulting .EXE
- file under a real-mode DOS version, the loader patches addresses into your
- program that call the routines within this library rather than OS/2
- functions. If you run the program under OS/2 protected mode, the loader and
- library routines are simply discarded.
-
- Thus, you have one executable file and one program that runs under
- protected-mode and real-mode MS-DOS. This is called the "family API" model.
-
- Not all OS/2 functions can be converted into old MS-DOS Int 21H calls. For
- instance, the FINDDEMO and FINDER programs shown here use several OS/2
- facilities that have no old MS-DOS equivalents. Some options of other OS/2
- functions are not fully supported. However, you can determine within your
- program whether it is running in real mode or protected mode and have
- separate logic that is appropriate for each.
-
-
- The Workings
-
- Now that we know how to create FINDDEMO.EXE and FINDER.EXE, let's look at
- how they work. FINDER is a child process run by FINDDEMO. FINDER does all
- the work while FINDDEMO sits back and waits for the messages from FINDER
- that report what it has found.
-
- FINDDEMO starts off by creating a queue named \QUEUES\FINDDEMO. A queue is a
- linked list maintained by OS/2 that you can use to transfer data from one
- program to another. One program creates the queue; another program opens the
- same queue. The program that creates the queue──in this case, FINDDEMO──has
- read and write privileges. The program that then opens the existing
- queue──in this case, FINDER──has write privileges. Queues may be first-in-
- first-out (FIFO), last-in-first-out (LIFO), or based on priority; the
- FINDDEMO queue is FIFO.
-
- FINDDEMO also creates two additional threads of execution. These appear in
- the program as functions called dispthread and waitthread. These two threads
- run simultaneously with the main thread, sometimes called the "parent"
- thread. I'll explain what they do a little later.
-
- FINDDEMO then prompts for a number and a file specification from the user.
- For each file specification you enter, FINDDEMO executes FINDER by using the
- OS/2 function call DOSEXECPGM. FINDER is executed in an asynchronous
- mode──the DOSEXECPGM call returns immediately, and FINDDEMO can prompt for
- the next file specification. As many as nine instances of FINDER can be
- executed. Although these multiple instances use different data segments,
- they share the same code segment in memory.
-
- When FINDDEMO executes FINDER it passes to it the file specification, the
- name of the queue, and an index number from 1 to 9 that indicates the
- display line on which the file specification appears.
-
- Now let's look at FINDER. FINDER opens the queue and searches for files
- meeting the file specification over the entire disk by using a recursive
- function called find. You'll note that FINDER changes the subdirectory
- during this search. Under OS/2, each process maintains its own current
- subdirectory. So each instance of FINDER can change the subdirectory and
- not affect other instances. Nice, huh?
-
- When FINDER finds a file that fits the file specification, it allocates a
- small shared segment of memory using DOSALLOCSEG and copies the pathname
- into it. The DOSGIVESEG function obtains a new selector (segment address)
- appropriate for its parent FINDDEMO. (FINDER obtained the process ID of its
- parent when it opened the queue.) FINDER can then write this segment to the
- queue.
-
- Now back to FINDDEMO. During initialization, FINDDEMO created a second
- execution thread, the subroutine called dispthread. This thread sits in an
- infinite loop that starts off with a call to DOSREADQUEUE. When DOSREADQUEUE
- returns control to the program, it reads the contents of the shared memory
- segment created by FINDER and writes it to the display.
-
- The other thread created during FINDDEMO initialization is waitthread. This
- is also an infinite loop that waits, using DOSCWAIT, for instances of FINDER
- to terminate. Because DOSCWAIT returns with an error if no child processes
- are running, FINDDEMO first sets nine semaphores and clears one only when
- executing FINDER. The waitthread thread is suspended, through the use of the
- function DOSMUXSEMWAIT, until one of these semaphores is cleared.
-
- Another set of semaphores, which are in the array runsem, are normally
- cleared. They are set when an instance of FINDER is executed and cleared
- when the instance terminates. These semaphores are used when you terminate
- FINDDEMO by using the Esc key. FINDDEMO uses DOSKILLPROCESS to terminate any
- instances of FINDER that are still running and then waits for all of the
- runsem semaphores to be cleared.
-
-
- The New Generation
-
- I claimed earlier that FINDDEMO's use of inter-process communication and
- multitasking represented a "typical" OS/2 application. Obviously, you can
- take advantage of the larger protected mode addressing space and more
- versatile OS/2 API without getting into the fancy stuff. Whether you do get
- fancy or not, I'm sure you'll find some interesting applications for these
- new OS/2 capabilities.
-
-
- Figure 2: FINDDEMO.C
-
- /* finddemo.c -- program to demonstrate IPC and multitasking */
-
- #include <doscall.h>
-
- #define WORD unsigned int
- #define DWORD unsigned long
-
- #define NUMPROC 9 /* number of process */
- #define CHILDPROG "FINDER.EXE"
- #define QUEUENAME "\\QUEUES\\FINDDEMO"
- #define THREADSTACK 1024
-
- void far dispthread (void) ;
- void far waitthread (void) ;
-
- WORD queuehandle, count [NUMPROC] ;
- DWORD runsem [NUMPROC], waitsem [NUMPROC] ;
- struct ResultCodes rc [NUMPROC] ;
- struct {
- WORD count ;
- struct {
- WORD reserved ;
- DWORD far *sem ;
- } index [NUMPROC] ;
- } semtab ;
-
- main ()
- {
- static char prompt[] = "Line number or Esc to end --> \b" ;
- struct KeyData keydata ;
- WORD index, len, i, dispID, waitID, sigaction ;
- DWORD sigaddr ;
- char dirmask [15], dispstack [THREADSTACK],
- waitstack [THREADSTACK] ;
-
- /*
- Initialize: Set up "semtab" structure for DOSMUXSEMWAIT.
- ---------- Disable Ctrl-Break and Ctrl-C exits.
- Create queue for IPC with FINDER.EXE.
- Create threads for messages from FINDER
- and waiting for FINDER terminations.
- Display text.
- */
-
- semtab.count = NUMPROC ;
- for (index = 0 ; index < NUMPROC ; index++) {
- DOSSEMSET ((DWORD) &waitsem[index]) ;
- semtab.index[index].sem = &waitsem[index] ;
- }
- DOSSETSIGHANDLER (0L, &sigaddr, &sigaction, 1, 1) ;
- DOSSETSIGHANDLER (0L, &sigaddr, &sigaction, 1, 4) ;
-
- if (DOSCREATEQUEUE (&queuehandle, 0, QUEUENAME)) {
- puts ("FINDDEMO: Cannot create new queue") ;
- DOSEXIT (1, 1) ;
- }
- if (DOSCREATETHREAD (dispthread, &dispID, dispstack + THREADSTACK) ||
- DOSCREATETHREAD (waitthread, &waitID, waitstack + THREADSTACK)) {
- puts ("FINDDEMO: Cannot create threads") ;
- DOSEXIT (1, 1) ;
- }
- displayheadings () ;
-
- /*
- Main Loop: Display prompt and read keyboard.
- --------- Execute FINDER.EXE.
- */
- do {
- VIOSETCURPOS (18, 0, 0) ;
- VIOWRTTTY (prompt, sizeof prompt - 1, 0) ;
- KBDCHARIN (&keydata, 0, 0) ;
-
- index = keydata.char_code - '1' ;
-
- if (index <= NUMPROC && rc[index].TermCode_PID == 0) {
- VIOWRTTTY (&keydata.char_code, 1, 0) ;
- VIOWRTNCHAR (" ", 77, 7 + index, 3, 0) ;
- do {
- VIOSETCURPOS (7 + index, 3, 0) ;
- len = 13 ;
- KBDSTRINGIN (dirmask, &len, 0, 0) ;
- }
- while (len == 0) ;
-
- dirmask [len] = '\0' ;
- executeprogram (index, dirmask) ;
- }
- }
- while (keydata.char_code != 27) ;
-
- /*
- Clean-up: Kill all existing FINDER.EXE processes.
- -------- Wait for processes to terminate.
- Close the queue and exit.
- */
-
- for (index = 0 ; index < NUMPROC ; index++)
- if (rc[index].TermCode_PID)
- DOSKILLPROCESS (0, rc[index].TermCode_PID) ;
-
- for (index = 0 ; index < NUMPROC ; index++)
- DOSSEMWAIT ((DWORD) &runsem [index], -1L) ;
-
- DOSCLOSEQUEUE (queuehandle) ;
- DOSEXIT (1, 0) ;
- }
-
- displayheadings ()
- {
- static char heading [] = "286DOS File Finder Demo Program",
- colheads [] = "Dir Mask Status Files",
- colunder [] = "-------- ------ -----" ;
- char buffer [5] ;
- WORD row, col, i, len ;
-
- VIOGETCURPOS (&row, &col, 0) ; /* get current attr */
- VIOWRTTTY (" ", 1, 0) ;
- len = 2 ;
- VIOREADCELLSTR (buffer, &len, row, col, 0) ;
- VIOSCROLLUP (0, 0, -1, -1, -1, buffer, 0) ; /* clear screen */
-
- len = sizeof heading - 1 ;
- col = (80 - len) / 2 ;
- VIOWRTCHARSTR (heading, len, 1, col, 0) ; /* heading */
- VIOWRTNCHAR ("\xC6", 1, 2, col - 1, 0) ; /* underline */
- VIOWRTNCHAR ("\xCD", len, 2, col, 0) ;
- VIOWRTNCHAR ("\xB5", 1, 2, col + len, 0) ;
- VIOWRTCHARSTR (colheads, sizeof colheads - 1, 5, 3, 0) ;
- VIOWRTCHARSTR (colunder, sizeof colunder - 1, 6, 3, 0) ;
-
- for (i = 0 ; i < NUMPROC ; i++) { /* numbers */
- sprintf (buffer, "%d.", i + 1) ;
- VIOWRTCHARSTR (buffer, 2, 7 + i, 0, 0) ;
- }
- }
-
- executeprogram (index, dirmask)
- WORD index ;
- char *dirmask ;
- {
- char objbuf [32] ;
- char args [128] ;
-
- strcat (strcpy (args, CHILDPROG), " ") ; /* construct args */
- strcat (strcat (args, dirmask), " ") ;
- strcat (strcat (args, QUEUENAME), " ") ;
- itoa (index, args + strlen (args), 10) ;
-
- count [index] = 0 ; /* initialize count */
-
- if (DOSEXECPGM (objbuf, 32, 2, args, 0, &rc[index], CHILDPROG))
- {
- puts ("FINDDEMO: Can't run FINDER.EXE") ;
- DOSEXIT (1, 1) ;
- }
- VIOWRTCHARSTR ("Running", 7, index + 7, 16, 0) ;/* now executing */
- DOSSEMSET ((DWORD) &runsem [index]) ;
- DOSSEMCLEAR ((DWORD) &waitsem[index]) ;
- }
-
- void far dispthread () /* thread to read messages from FINDER */
- { /* and display filenames. */
- DWORD request ;
- WORD len, index, i ;
- char far *farptr ;
- char priority, pathname [80], buffer [64] ;
-
- while (1) {
- DOSREADQUEUE (queuehandle, &request, &len,
- &(DWORD)farptr, 0, 0, &priority, 0L) ;
- i = 0 ;
- while (pathname [i++] = *farptr++) ;
- index = (WORD) (request >> 16) ;
- count [index] += len > 0 ;
- sprintf (buffer, "%5d %-48.48s", count [index], pathname) ;
- VIOWRTCHARSTR (buffer, 56, 7 + index, 24, 0) ;
- DOSFREESEG ((WORD) ((DWORD) farptr >> 16)) ;
- }
- }
-
- void far waitthread () /* thread to wait for FINDER terminations */
- {
- WORD index, PID ;
- struct ResultCodes rescode ;
-
- while (1) {
- DOSMUXSEMWAIT (&index, (WORD far *) &semtab, -1L) ;
- DOSCWAIT (0, 0, &rescode, &PID, 0) ;
-
- for (index = 0 ; index < NUMPROC ; index++) /* find index */
- if (PID == rc[index].TermCode_PID)
- break ;
-
- VIOWRTCHARSTR (rescode.TermCode_PID ? "Halted " : "Done ",
- 7, index + 7, 16, 0) ;
- rc[index].TermCode_PID = 0 ;
- DOSSEMCLEAR ((DWORD) &runsem [index]) ;
- DOSSEMSET ((DWORD) &waitsem[index]) ;
- }
- }
-
-
- Figure 3: FINDER.C
-
- /* finder.c -- child process of finddemo */
-
- #include <doscall.h>
-
- unsigned index ;
- unsigned queuehandle ;
- unsigned parentPID ;
-
- main (argc, argv)
- int argc ;
- char *argv [] ;
- {
- if (argc < 4) {
- puts ("Run FINDDEMO rather than FINDER") ;
- DOSEXIT (1, 1) ;
- }
- if (DOSOPENQUEUE (&parentPID, &queuehandle, argv [2])) {
- puts ("FINDER: Cannot open queue") ;
- DOSEXIT (1, 1) ;
- }
- index = atoi (argv [3]) ;
-
- writequeue ("") ;
- chdir ("\\") ;
- find (argv [1]) ;
- writequeue ("") ;
-
- DOSCLOSEQUEUE (queuehandle) ;
- DOSEXIT (1, 0) ;
- }
-
- find (searchstr)
- char *searchstr ;
- {
- struct FileFindBuf ffb ;
- char cwd [81], pathname [100] ;
- unsigned handle = 0xFFFF, num = 1 ;
-
- if (cwd [strlen (getcwd (cwd, 80)) - 1] != '\\')
- strcat (cwd, "\\") ;
-
- DOSFINDFIRST (searchstr, &handle, 7, &ffb, sizeof ffb, &num, 0L) ;
- while (num) {
- writequeue (strcat (strcpy (pathname, cwd), ffb.file_name)) ;
- DOSFINDNEXT (handle, &ffb, sizeof ffb, &num) ;
- }
- DOSFINDCLOSE (handle) ;
-
- handle = 0xFFFF ;
- num = 1 ;
- DOSFINDFIRST ("*.*", &handle, 0x17, &ffb, sizeof ffb, &num, 0L) ;
- while (num) {
- if (ffb.attributes & 0x10 && ffb.file_name [0] != '.') {
- chdir (ffb.file_name) ;
- find (searchstr) ;
- chdir ("..") ;
- }
- DOSFINDNEXT (handle, &ffb, sizeof ffb, &num) ;
- }
- DOSFINDCLOSE (handle) ;
- }
-
- writequeue (str)
- char *str ;
- {
- unsigned selector, parentselector ;
- char far *farptr ;
- int len = strlen (str) ;
-
- DOSALLOCSEG (len + 1, &selector, 1) ;
- farptr = (char far *) (((unsigned long) selector) << 16) ;
-
- while (*farptr++ = *str++) ;
-
- DOSGIVESEG (selector, parentPID, &parentselector) ;
- DOSFREESEG (selector) ;
- farptr = (char far *) (((unsigned long) parentselector) << 16) ;
-
- DOSWRITEQUEUE (queuehandle, index, len, farptr, 0) ;
- }
-
-
- Figure 4: FINDDEMO.DEF
-
- NAME FINDDEMO
- DESCRIPTION 'File Finder Demonstration Program'
- PROTMODE
- DATA MOVABLE
- CODE MOVABLE PURE
- HEAPSIZE 2048
- STACKSIZE 4096
-
-
- Figure 5: FINDER.DEF
-
- NAME FINDER
- DESCRIPTION 'File Finder Module for FINDDEMO'
- PROTMODE
- DATA MOVABLE
- CODE MOVABLE PURE
- HEAPSIZE 2048
- STACKSIZE 8192
-
-
- Figure 6: Automatic data segment containing static variables, the stack,
- and a local heap.
-
- ┌───────────────────────────┐▄
- │ ╔═══════════════╗
- │ Local Heap ║ High Memory ║
- │ ╚═══════════════╝
- └───────────────────────────┘█
- ████████████████████████████
-
- ┌───────────────────────────┐
- │ │█
- │ Stack │█
- │ │█
- └───────────────────────────┘█
- ████████████████████████████
-
- ┌───────────────────────────┐▄
- │ ╔═══════════════╗
- │ Static Variables ║ Low Memory ║
- │ ╚═══════════════╝
- └───────────────────────────┘█
- ████████████████████████████
-
-
- Figure 7: Make-file for FINDDEMO.EXE and FINDER.EXE
-
- finddemo.obj : finddemo.
- cl -c -G2 -Zp finddemo.c
-
- finddemo.exe : finddemo.obj finddemo.def
- link finddemo, /align:16, /map, doscalls slibc5, finddemo.def
-
- finder.obj : findef.c
- cl -c -G2 -Zp finder.c
-
- finder.exe : finder.obj finder.def
- link finder, /align:16, /map, doscalls slibc5, finder.def
-
- ████████████████████████████████████████████████████████████████████████████
-
- An Interview with Gordon Letwin: OS/2: Turning Off the Car to Change Gears
-
- ───────────────────────────────────────────────────────────────────────────
- Also see the related article:
- Gordon Letwin: The Challenge of the 286 Speaks His Language
- ───────────────────────────────────────────────────────────────────────────
-
- Lori Valigra
-
- The development of Microsoft's multitasking operating system has taken many
- twists and turns since Microsoft began working on it three years ago. The
- many stages it has gone through so far have resulted from working with the
- schizophrenic Intel 80286 chip, which can run in either of two incompatible
- modes: protected (286) mode, and unprotected (8086 or "real") mode. The main
- problem has been to get an operating system which runs both protected- and
- real-mode programs. Microsoft solved the problem with mode switching──a
- feature which essentially works by mimicking the act of turning off the
- computer and restarting it, similar to turning off a car in order to switch
- gears.
-
- The new operating system began as an ambitious project to produce an
- operating system for Intel 80286-based computers running a mixture of office
- automation applications. This new operating system was to be capable of
- multitasking and compatible with the protected 286 mode. But as the project
- got too unwieldly, Microsoft split it up in early 1985, announcing a version
- called DOS 4. This version incorporates multitasking but does not include
- protected mode features. The DOS 5 project, as it was called, is intended to
- support the protected mode and add other features.
-
- OS/2, as the project has finally been named, was easily the most difficult
- and challenging program written at Microsoft to date, with more than 350,000
- lines of code. While Microsoft generally feels that the most efficient
- projects are those staffed by one person, the complexity and time
- constraints of the OS/2 project required that the company assign more than
- 35 programmers to it. Managing the project──dividing the programmers into
- teams, dividing the project among them, and coordinating the various teams'
- modifications to the program's code──became an enormous challenge. Adding to
- the challenge was the joint development of the product with IBM, a company
- whose culture and methodology are quite different from Microsoft's.
-
- Microsoft Systems Journal talked with the project's chief architect, Gordon
- Letwin, to learn more about the planning, development, and cooperative
- effort that went into the development of OS/2.
-
- MSJ: What was the goal of the OS/2 project?
-
- Letwin: We tried to create the underpinnings for the ideal office automation
- operating system. In our opinion, that operating system would allow
- applications direct access to high-bandwidth peripherals, offer device-
- independent graphics drivers, provide multitasking without performance
- degradation (compared with a single-tasking system), run programs in both
- the protected 286 and real 8086 modes of the 286 processor, and provide a
- protected environment to assure system stability. I think we've done a
- pretty good job.
-
- MSJ: Microsoft has been developing multitasking operating systems for three
- years now, beginning with DOS 4. What was the purpose of that operating
- system, and where does it stand now that OS/2 is out?
-
- Letwin: DOS 4 was the first product to result from Microsoft's multitasking
- DOS effort. We began it even before IBM introduced the PC AT. It was an
- ambitious product that was originally to include a protected mode with mode
- switching abilities so it could run on the 8086 or the 286.
-
- A general-purpose multitasking system needs to run in both modes: the
- unprotected 8086 mode so that we can run existing DOS applications, and the
- protected 286 mode so that we can multitask arbitrary, unrelated
- applications. But the architecture of the 286 caused some delays. Although
- we knew the project would be difficult, it was only after we'd gotten deeply
- into it that we realized just how difficult it would be.
-
- As a result, DOS 4 became too complicated for our schedules. Because of the
- pressure of customer demand as well as that of previous commitments, we
- broke the project into two parts. DOS 4 runs only in real mode and provides
- multitasking only for specialized applications. DOS 5, which has now been
- released as OS/2, includes the protected mode and other features. DOS 4 was
- delivered in the last half of 1986 and is being sold in special application
- environments, primarily in Europe. It is a specialized product that can
- share the market with OS/2, because it runs on 8086 hardware, while OS/2
- requires a 286. The move from DOS 4 to OS/2 was a gradual evolutionary
- process. OS/2 is by far the most complex and sophisticated operating system
- project we've worked on. To offer multitasking in a protected environment,
- we developed it jointly with IBM to get around the constraints of the 286.
-
- MSJ: How does OS/2 compare with earlier multitasking operating systems like
- Concurrent CP/M?
-
- Letwin: Unlike OS/2, Concurrent CP/M has a major problem. It serializes
- access to the file system. When multitasking you need to make the operating
- system features such as the file system accessible to multiple programs at a
- time. But Concurrent CP/M didn't do this. It only allowed one program to
- call the file system at a time. So in a real life situation with lots of
- I/O, Concurrent CP/M was too slow.
-
- MSJ: What are some of the features of OS/2 that you consider particularly
- neat?
-
- Letwin: One is the seamless system service interface, which means that
- system services don't need to be in the kernel. Some can be provided by
- subroutine libraries and others by system processes. Many services can be
- upgraded piecemeal, in the field, without changing the whole operating
- system. This is accomplished by a technique called "dynamic linking," which
- allows external procedure calls to be linked up at execution time, rather
- than link time. This same mechanism is used as the standard system interface
- between applications, utility packages, etc.
-
- Another exceptional feature is direct hardware access, which allows an
- application to write directly to the hardware. For example, it will do
- special graphics on a screen.
-
- MSJ: How did you get around the limitations of the 286 chip?
-
- Letwin: The 286 is designed to run in only one mode, either real mode or
- protected mode. Since we knew we needed to support programs written using
- protected mode, a group of us, including Bill Gates and some project
- engineers, brainstormed to try to figure out how to run 8086 mode
- applications from within protected 286 mode. One option was "real mode
- emulation," where we could emulate the operation of 8086 real mode by doing
- a lot of special things to the protected mode segment tables. IBM was
- examining this technique independently. We thought emulation would be too
- slow, since the 286 would run at one-third the speed of an 8086. IBM's
- research later showed this to be the case. Soon thereafter, Intel began
- shipping a chip with a bug in it that would stop the technique anyway, so
- we tried another approach: mode switching.
-
- The 286 chip switches from real mode to protected mode very easily, but it
- was not designed to switch back from protected mode to real mode at all. You
- could only switch back to real mode by causing a full system reset. The idea
- of doing a full system reset while running a multitasking system was very
- radical. IBM was aware of the mode switching technique itself and had the
- capability for it in the AT BIOS for use during power-up diagnostics and for
- special block copies from high memory. However, this feature was never
- intended for use during full system operation. What we were thinking of was
- similar to switching gears in a car by turning the motor off. The idea was
- to master-clear the processor and restart it. The question was, would this
- interfere with DMA transfers that might be happening at the same time, lose
- pending interrupts, or fail in some other way? The IBM ROM code was designed
- to "switch gears by turning off the car" with the car standing still. We
- wanted to switch gears with the car traveling at highway speeds, and without
- causing a jolt. Working together with IBM, we set up an experiment to see if
- it would work, and it did. However, it took us months to convince everyone
- involved that the technique was feasible. The first time we presented the
- idea to IBM Boca, they gave us a funny look and suggested that perhaps we'd
- had a bit too much sun.
-
- There is one difficulty with mode switching, however. Because the designers
- thought that it would be an infrequent operation, done primarily during the
- power-on diagnostics, they implemented it in a fashion that saved some
- components but produced a very slow mode switch: typically 1,000
- microseconds, round trip. During this 1 millisecond interval the system
- cannot respond to interrupts. Any interrupts that arrive then are processed
- after the mode switch. This delay would normally be a problem only for high-
- speed communications at 96K bps or faster while you are simultaneously
- interacting with a real-mode program. You may experience data overruns while
- the real-mode screen group is on the display. Mode switching will affect any
- program that requires a very high-speed interrupt service rate faster than
- 500 Hz. If the communications protocols on your system can handle missing
- characters by asking for a retransmit, and most can, then communications
- will be okay.
-
- MSJ: Why is protection so important in OS/2?
-
- Letwin: We knew we needed a multitasking system to accomplish our goals, and
- we knew we needed to have it protected. If you run a multiprocessor system,
- software can accidentally read or change other programs or data if the
- memory is unprotected. You can't stop it and you can't tell it happened. The
- system can crash or you could corrupt data. Even in an unprotected DOS 3
- system you can have problems with running programs simultaneously. SideKick,
- for example, loads into memory and terminates itself. It keeps memory in
- use, but the system doesn't know what is in that memory location. If you
- load several such programs at the same time and they conflict on how they
- use memory or interrupt vectors, the software will not work correctly, or
- the system will crash. So in an unprotected environment the system is always
- vulnerable to flaky software or conflicts between software products from
- different vendors.
-
- Of even greater importance than this is the fact that we needed to use
- protected mode to allow programs to access more than 640K bytes of memory.
- The 640K limit in earlier versions of DOS is a result of the addressing
- limitations of real mode. Protected mode programs are released from this
- restriction, and can use many megabytes of memory. As memory costs continue
- to drop and memory consumption by applications rises, it has become
- unacceptable to be restricted to the 640K that real mode provides.
-
- MSJ: How did you plan such an ambitious project, both conceptually and in
- getting the needed resources?
-
- Letwin: The first step was to decide what we wanted the system to do. Then,
- when we discovered how many of those capabilities we could implement, and by
- when, we could revise our goals. As a result, the initial planning process
- was iterative. We needed to have multitasking and to execute existing DOS 3
- programs. While there were still only a few engineers on the project, we
- thought about techniques such as mode switching and when necessary,
- prototyped some code. As we got a better handle on what we could do, and how
- long it would take, we met again with Bill Gates and revised our goals. The
- tone of the first phase was as ambitious as possible, and we listed all the
- things we thought we could do. The second phase involved determining which
- of those features were feasible in the required time frame. As soon as the
- general scope of the project was determined, the engineers met in groups of
- two and three to lay out further details of the architecture of the system.
-
- After the initial meetings came the joint design work. Engineering then
- finalized the goals. Because of the scope of the project, this design phase
- took a few months.
-
- Next was the sign-up phase, a tradition in the computer industry, where we
- explained the excitement, the importance, and the tight schedule of the
- project, then let everyone "sign up" for a long stretch of hard, focused
- work. You can't force someone to excel; you've got to provide the
- opportunity and the encouragement and then let them excel.
-
- Then the actual work began. We decided on the key systems and the key
- characteristics of the product. Next came the design sessions for each of
- the key systems. About a half dozen people were involved, typically three
- from Microsoft and three from IBM.
-
- During the coding months, my job was to understand fully all of the parts of
- the system and to make sure that everthing worked toward achieving our
- overall system goals. In a project this large, with this many people working
- on it, it's easy for design elements to diverge. One project member might
- think that speed is more important than code size, and write his code that
- way. Another might think the opposite and write his code the other way. The
- result would be that neither goal is achieved. I participated in the design
- of each component to insure that the goals and design philosophies of the
- system's architecture were consistent throughout the code. On most projects,
- this role can be fulfilled on a "part time" basis, leaving time for the
- chief designer to also get to write some code. Unfortunately, on the OS/2
- project this position required a full time effort, leaving me little
- opportunity to write any real code.
-
- MSJ: How did you divide up tasks on the project?
-
- Letwin: The most efficient programming is done by one person. It's a classic
- saying in the industry that "if one person can do it in one year, two people
- can do it in two years." But in a project like OS/2, although one person
- might be able to do it in 8 man years, you can't afford to wait 8 years, so
- you have to make it a multi-person project and have it take 30 man years. In
- order to reduce the inefficiency introduced by having multiple people on a
- project, it's important to break the work up properly, and to have each
- piece of work be a one man project. This project had about 35 people on it.
-
- The programmers worked on individual parts of the code, but they were
- organized into teams with a maximum of eight people each to facilitate
- coordinating their work. There was a test team, two teams of developers each
- headed by a senior engineer, a build team with four people, a test team with
- five people, and myself as the chief architect. This project was so
- complicated, especially since development work was simultaneously taking
- place at IBM in Boca Raton, Florida, that we needed a build team just to
- coordinate the sources, build the executable binaries, and otherwise bring
- organization to the physical aspects of software development. The test team,
- as its name implies, was responsible for the development of test cases, bug
- tracking, and performance measurement.
-
- MSJ: What was the most difficult part of managing the project?
-
- Letwin: This project's very large staff added to the complexity of
- integrating changes made at the IBM locations and timing the development
- between the two sites. We had to be especially careful that any changes made
- didn't affect other parts of the project. In the source code control system,
- for example, we split the source into about 200 modules, with modification
- tracking on each. We had a rule that you couldn't add a change to a module
- until you'd proven that the change wouldn't break anything. That way we
- retained a totally stable version. Without this rule, each engineer could
- have spent days looking for problems that were, in fact, introduced by
- another engineer.
-
- MSJ: IBM and Microsoft, to put it mildly, have very different corporate
- cultures and philosophies. How did you mesh the two environments during the
- development of OS/2?
-
- Letwin: First, whenever we arrived in Boca Raton, we rushed into the
- bathroom and changed into suits.
-
- But seriously, there were some times when differences of opinions were
- frustrating. Normally each company builds its own products and calls all the
- shots. But in a joint development, compromises were necessary. IBM engineers
- were slowed down by a set of rules which are intended to bring stability to
- large IBM projects. This naturally caused some conflicts. IBM-ers
- undoubtedly felt that Microsoft engineers were "wild and crazy guys."
-
- MSJ: What parts of OS/2 would you do differently if you had the choice?
-
- Letwin: A year ago it became clear we weren't going to meet our schedules,
- and we had to go through a simplification procedure. For example, we took
- out named pipes, a form of inter-process communication to send information
- to another program (now part of the OS/2 LAN Manager). We'll put that in
- another release of the product. Installable file systems were another
- victim. As a result, the hard disk file system still doesn't perform as well
- as we'd all like. These features will be added later. Also, we now know the
- parts of the system which turned out to be bigger or slower than they
- should, or which in some other way were a bit disappointing. Naturally we'd
- give them additional attention. At the end of any project, there are always
- a few things you'd like to improve. And we will, in the next release.
-
- MSJ: How does it feel to give up your product when it's ready to go to
- market?
-
- Letwin: At first, I felt relief when we got the product through testing and
- it went out the door. The last 20 percent is hard work. You just get tired
- of the product. The exciting stuff takes place in the earlier stages of the
- project. The last days are dealing with problem after problem──fixing bugs,
- making it smaller, making it faster, etc. You've worked on it so much there
- dosen't seem to be much change over the last few months. You're tired and
- all you do is deal with problems. In my opinion, it's the ability to keep
- working hard during the last part of a long project that separates the great
- people from the average. In any case, when you finally move on to something
- else, you feel a mixture of relief that it's completed, excitement about
- your new project, and anxiety about how well your efforts will be received.
-
- MSJ: What's your philosophy of programming?
-
- Letwin: Software is special at our level. Computer programming is 20 percent
- science, 60 percent engineering, and 20 percent art. The "art" element is
- the hardest to achieve, and it's the hardest to explain. When engineers talk
- about the "elegance" of their work, people usually think that such elegance
- is non-functional: the engineers are just indulging themselves. This might
- be the case in mechanical or civil engineering since an elegant bridge
- carrys no more load than an inelegant one. But it isn't so in advanced
- software development because mechanical engineering deals with physical
- objects and software engineering deals with information. Physical objects
- have properties independent of human perception, but information doesn't. In
- other words, although a bridge is equally functional whether or not it's
- elegant, information is much more useful when it's organized in a clear,
- flexible and useable form──a form a programmer would call "elegant." Bridge
- design is organized to meet just two goals──buildability and final function.
- Software is designed to meet those goals plus one more. It has to have
- minimal intellectual complexity and maximum flexibility so that it can be
- understood by the programmers who will maintain and upgrade it in the
- future.
-
-
- ───────────────────────────────────────────────────────────────────────────
- GORDON LETWIN: THE CHALLENGE OF THE 286 SPEAKS HIS LANGUAGE
- ───────────────────────────────────────────────────────────────────────────
-
- What attracted 34-year-old Gordon Letwin to computers in the late 1960s has
- also kept him in the forefront of programming today: an insatiable drive to
- overcome technical obstacles.
-
- "I'd read about computers in the late 1960s, that they were supposed to be
- super complicated. Computers held an abstract mystique. And I wanted to
- understand how that stuff worked," he says. That was a tough goal at a time
- when the cheapest computers cost $100,000 and were locked behind glass.
- Letwin taught himself FORTRAN from a manual, without access to a computer to
- try it on. While in high school he took some courses at a small private
- technical college to get free time on the school's computer.
-
- Letwin studied physics at Purdue, but found he was more interested in
- getting computer time than in studying physics. Whenever he could, he'd
- spend his day breaking through the computer's protective environments
- instead of studying physics. "Saying that there's something that I can't
- understand, or can't access because I'm not initiated into some mystery is
- like waving a flag in front of a bull," he says. He finally solved this
- conflict by changing his major to computer science.
-
- A large part of the enjoyment in computing for Letwin is getting immediate
- results. He likens computers to pinball machines. "You get positive and
- negative reinforcement right away." And, being "innately lazy," he likes the
- idea of computers doing work for him.
-
- Upon getting his Bachelor's and Master's degrees in computer science from
- Purdue, Letwin went to work for Wintek, a company run by one of his
- professors. He ended up bidding on a contract to supply Heathkit with a
- BASIC interpreter, assembler, and editor.
-
- He later moved to Michigan and went to work at Heath but found he didn't
- agree with its management's direction. "I wasn't happy with the direction of
- the company, which was run by `suits' who didn't know computers well but
- insisted on making the technical decisions anyway." When his concerns with
- Heath's approach at that time weren't improving, he signed on with
- Microsoft, then in Albuquerque, New Mexico. "I came here because of Bill
- Gates," says Letwin, who met Microsoft's chairman during sales meetings at
- Heath.
-
- Letwin became the first new employee when the firm relocated to Seattle
- about eight years ago. He started working on a BASIC compiler, then a Pascal
- compiler.
-
- When he became tired of being a "compiler compiler," he took advantage of
- his operating system experience to switch to the newly formed operating
- systems group, starting work on the IBM PC. "We thought the 8086 chip was
- the first processor powerful enough for general purpose desktop use. We had
- a vision of an office of the future. We knew that to realize this vision,
- we'd need a powerful operating system, much more sophisticated then those
- which were available at that time. Since the only way to insure its
- existence was to write it ourselves, we got into the operating system
- business." The widespread acceptance of MS-DOS makes that statement
- Microsoft's platform for its development of the single-user office
- automation operating system
-
- ████████████████████████████████████████████████████████████████████████████
-
- A Simple Windows Application for Custom Color Mixing
-
- Charles Petzold
-
- COLORSCR ("Color Scroll"), is a program for Microsoft Windows that displays
- three scroll bars in the left half of the client area, which are labeled
- "Red," "Green," and "Blue," as shown in Figure 1. As you scroll the scroll
- bars, the right half of the client area will change to the composite color
- that is produced by the mix of the three primary colors. The numeric values
- of the three primary colors are displayed under the three scroll bars.
-
- You can scroll the scroll bars by using either the mouse or the keyboard.
- COLORSCR works best when Windows is installed to use a color display, such
- as an Enhanced Graphics Adapter connected to a color monitor, but you can
- also run the program on a monochrome display.
-
- You can use COLORSCR as a development tool in experimenting with color and
- choosing attractive colors for your own Windows programs. But COLORSCR is
- most interesting for what it doesn't do.
-
- For instance, if you've done some Windows programming, you'll probably
- assume that the Red, Green, and Blue labels on top of the scroll bars and
- the color values at the bottom are displayed to the client area by using the
- TextOut function.
-
- However, COLORSCR does not use TextOut.
-
- Also, you'll probably assume that the colored rectangle at the right half of
- the client area is drawn by using the Rectangle or FillRect function.
-
- No, COLORSCR doesn't use these functions either, or any of the other GDI
- drawing functions.
-
- When COLORSCR is made into an icon, the entire surface of the icon is
- painted with the selected color. You might think that COLORSCR traps WM_SIZE
- messages to check if it's being iconed and then paints the client area
- differently if it is.
-
- Wrong again. COLORSCR does not know when it's an icon and doesn't have
- separate icon logic.
-
- In fact, COLORSCR doesn't process WM_PAINT messages and doesn't directly
- display anything to its client area. At some point the COLORSCR program does
- obtain a handle to a display context, but only for the purpose of
- determining the height of a character in the default system font.
-
- In short, COLORSCR is one of the laziest Windows programs ever. How can it
- afford to do so little? Because COLORSCR puts its children to work.
-
-
- Child Windows
-
- Child windows are separate windows that can be overlaid on the client area
- of a tiled or pop-up window. They usually have their own window functions
- separate from the parent window function. In their simplest form, child
- windows can simply divide the client area of a window into smaller
- rectangular windows, each with its own window function.
-
- For instance, the Windows MS-DOS Executive uses three child windows to
- divide its client area into sections for the drive icons, the current
- subdirectory path, and the file list. Three different window functions
- process messages from the three child windows. This simplifies painting and
- mouse processing in the MS-DOS Executive because each child window has its
- own client area coordinates.
-
- One type of child window is called a child window control, which usually
- sends messages back to its parent window (the tiled or pop-up window on
- which the child control appears) based on mouse or keyboard input to the
- control.
-
- Windows contains several predefined child window controls, which take the
- form of buttons, check boxes, edit boxes, list boxes, text strings, and
- scroll bars. Child window controls are most often used in dialog boxes; in
- such instances, the placement and size of the child window controls are
- defined in a dialog box template contained in the program's resource script.
- When you create a dialog box template, you can either calculate the position
- and sizes manually or you can use the DIALOG program included with the
- Windows Software Development Kit 1.03 (see "Latest Dialog Editor Speeds
- Windows Applications Development," MSJ, Vol. 1 No. 1).
-
- However, you can also use predefined child window controls on the surface of
- a tiled or pop-up window's client area. You create each child window with a
- CreateWindow call and adjust the position and size of the child windows with
- calls to MoveWindow.
-
- COLORSCR uses ten predefined child window controls. The color that appears
- on the right half of the window is actually the background color of the
- tiled window, as shown in Figure 2.
-
- On the left half of the client area there is a "static" child window called
- SS_WHITERECT (white rectangle) that blocks out half the client area (see
- Figure 3). The three scroll bars are child window controls with the style
- SBS_VERT (see Figure 4) placed on top of the SS_WHITERECT child. Six more
- static child windows of style SS_CENTER (centered text) provide the labels
- and the color values (see Figure 5).
-
-
- Raising the Children
-
- The COLORSCR.C source code, the COLORSCR.DEF module definition file, and the
- COLORSCR make-file are shown in Figures 6, 7, and 8, respectively. If you
- have the Microsoft C Compiler 4.0 and the Windows Software Development Kit
- installed on your hard disk, you will be able to create COLORSCR.EXE by
- executing:
-
- MAKE COLORSCR
-
- COLORSCR creates its normal tiled window and the ten child windows within
- the WinMain function through the use of the Windows CreateWindow call.
-
- The first parameter to the CreateWindow call indicates the window class of
- the window. The tiled window uses the window class called "ColorScr," which
- is defined and registered during WinMain initialization; this is perfectly
- normal. The child windows use classes called "static" (for the white
- rectangle and the text) and "scrollbar," which are the predefined child
- window classes. The window functions for these child windows are contained
- within Windows.
-
- For a tiled window, the second parameter to the CreateWindow call is the
- text that appears in the window's caption bar. For the six child windows
- that display text, this parameter is the text displayed in the child window.
- For the rectangle and scroll bars, the text is ignored.
-
- The window style parameter for all ten child windows includes the WS_CHILD
- indentifier. The window style for the three scroll bars includes the
- SBS_VERT identifier, indicating a vertical scroll bar. For the static child
- windows, the SS_CENTER identifier indicates centered text, and the
- SS_WHITERECT identifier indicates a white rectangle.
-
- The fourth through seventh parameters to the CreateWindow call normally
- indicate a position and size of the child window. The position is relative
- to the upper left-hand corner of the client area of the parent window. These
- values are all set to 0 initially because the placement and sizing depend on
- the size of the client area, which is not yet known.
-
- For child windows, the eighth parameter is the window handle of the parent,
- which is simply the hWnd value returned from the first CreateWindow call.
- The following parameter is a child ID that uniquely identifies the child
- window. We'll use this child ID later on.
-
- COLORSCR's window function (WndProc) resizes all ten child windows when it
- receives a WM_SIZE message. The formulas are based upon xClient, the width
- of the client area, yClient, the height of the client area, and yChar, the
- height of a character.
-
- Whenever you resize the COLORSCR window, the sizes of the child windows
- change proportionally. This is particularly interesting for the scroll bars.
- Depending on the dimensions of COLORSCR's window, the scroll bars can be
- long and thin or short and stubby (see Figure 9). They may look a little
- peculiar, but they work just fine.
-
-
- Scroll Bar Messages
-
- Child window controls that take the form of buttons, edit boxes, list boxes,
- and scroll bars pass messages back to the window function of the parent
- window. Buttons, edit boxes, and list boxes will post WM_COMMAND messages to
- the parent window. Scroll bar controls, however, will use the messages
- WM_VSCROLL (to scroll vertically) and WM_HSCROLL (for scrolling
- horizontally) to indicate that the scroll bar has been clicked with the
- mouse or the scroll bar thumb is being dragged.
-
- The first step in using the scroll bars is to set a range for the scroll
- bar, which is done in WinMain. Since the primary color values range from 0
- to 255, the range is set accordingly:
-
- SetScrollRange (hChScrol[n],SB_CTL,0, 255, FALSE) ;
-
- When the WndProc window function receives a WM_VSCROLL message, the high
- word of the lParam parameter is the child window ID number, which is the
- number specified in the ninth parameter to the CreateWindow call. For the
- three scroll bars, we have conveniently set that ID to a 0, 1, and 2. That
- tells WndProc which scroll bar is generating the message:
-
- n = GetWindowWord(HIWORD(lParam),GWW_ID) ;
-
- Because the handles to the child windows had been saved in arrays when the
- windows were created, WndProc can set the new value of the appropriate
- scroll bar by using the SetScrollPos call:
-
- SetScrollPos(hChScrol[n],SB_CTL,color[n],TRUE) ;
-
- and it can change the text of the child window at the bottom of the scroll
- bar:
-
- SetWindowText(hChValue[n],itoa(color[n],szbuffer,10)) ;
-
-
- Keyboard Interface
-
- Scroll bar controls can also process keystrokes, but only if they have the
- input focus. (Because only one window can receive keyboard input at any
- time, the window that receives the keyboard input is referred to as the
- window with the input focus.) The keyboard cursor keys are translated into
- the scroll bar messages:
-
- Scroll Bar
- Message
- Cursor Key wParam Value
-
- Home SB_TOP
- End SB_BOTTOM
- PgUp SB_PAGEUP
- PgDown SB_PAGEDOWN
- Left-arrow SB_LINEUP
- Up-arrow SB_LINEUP
- Right-arrow SB_LINEDOWN
- Down-arrow SB_LINEDOWN
-
- In fact, the SB_TOP and SB_BOTTOM scroll messages are never generated when
- using the mouse on the scroll bar, only when using the keyboard.
-
-
- The Input Focus
-
- If you want a scroll bar control to obtain the input focus when the scroll
- bar is clicked with the mouse, you must include the WS_TABSTOP identifier in
- the window class parameter of the CreateWindow call. When a scroll bar has
- the input focus, a blinking gray block is displayed on the scroll bar
- thumb.
-
- However, to provide a full keyboard interface to the scroll bars, some more
- work is necessary. First, the WndProc window function must specifically
- give a scroll bar the input focus. It does this by processing the WM_FOCUS
- message, which the parent tiled window receives when the parent window
- obtains the input focus. WndProc simply sets the input focus to one of the
- scroll bars:
-
- SetFocus(hChScrol[nFocus]);
-
- But you also need some way to get from one scroll bar to another by using
- the keyboard, preferably with the Tab key. This is more difficult, because
- once a scroll bar has the input focus it processes all keystrokes. The
- scroll bar only cares about the cursor keys and ignores the Tab key.
-
-
- Window Subclassing
-
- Adding a facility to COLORSCR in order to jump from one scroll bar to
- another using the Tab key requires window subclassing.
-
- The window function for the scroll bar controls is somewhere inside Windows,
- but the address of this window function can be obtained by a call to
- GetWindowLong using the GWL_WNDPROC identifier as a parameter. Moreover, you
- can set a new window function for the scroll bars by calling SetWindowLong.
-
- This facility allows you to hook into the existing scroll bar window
- function, process some messages within your own program, and pass all the
- other messages to the old window function.
-
- The window function that does preliminary scroll bar message processing in
- COLORSCR is called ScrollProc and is found toward the end of the COLORSCR.C
- listing. Since ScrollProc is a function within COLORSCR that is called by
- Microsoft Windows, it must be defined as FAR PASCAL and it must be listed in
- the EXPORTS in the COLORSCR.DEF module definition file.
-
- First, to ensure that ScrollProc accesses the proper data segment, you must
- obtain a far address for the function with MakeProcInstance:
-
- lpfnScrollProc = MakeProcInstance((FARPROC)ScrollProc,hInstance);
-
- For each of the three scroll bars, COLORSCR uses GetWindowLong to obtain and
- save the address of the existing scroll bar window function:
-
- lpfnOldScr[n] =(FARPROC)GetWindowLong(hChScrol[n],GWL_WNDPROC) ;
-
- Next, it sets the new scroll bar window function:
-
- SetWindowLong(hChScrol[n],GWL_WNDPROC,(LONG)lpfnScrolProc) ;
-
- Now, the function ScrollProc gets first dibs on all messages that Windows
- sends to the scroll bar window function for the three scroll bars in
- COLORSCR (but not, of course, scroll bars in other programs). The ScrollProc
- window function simply changes the input focus to the next, or previous,
- scroll bar when it receives a Tab or Shift-Tab keystroke. It calls the old
- scroll bar window function with CallWindowProc.
-
-
- Use of Color
-
- The Windows functions that require (or return) a color value use an unsigned
- long (32-bit) integer, where the lowest three bytes specify red, green, and
- blue values ranging from 0 through 255 (see Figure 10).
-
- This results in a potential 224 (about 16 million) possible colors.
-
- This unsigned long is often referred to in the Windows documentation as
- rgbColor. The WINDOWS.H header file provides several macros for working with
- rgbColor values. The RGB macro in WINDOWS.H takes three arguments
- representing red, green, and blue values and then sticks the bytes together
- in order to form an unsigned long:
-
- #define RGB(r,g,b)(((DWORD)(b << 8 | g) = << 8)| r)
-
- Thus, the value
-
- RGB (255, 0, 255)
-
- is really 0x00FF00FF, an rgbColor value for magenta. When all three
- arguments are set to 0, the rgbColor is black; all three arguments set to
- 255 yields white.
-
- C mavens will justifiably recoil upon seeing this RGB macro. The b, g, and r
- identifiers within the definition should be surrounded by parentheses to
- avoid errors when one of the parameters is an expression involving operators
- of higher precedence than << and |. You should watch out when you use
- expressions for the b, g, and r values.
-
- The GetRValue, GetGValue, and GetBValue macros extract the unsigned
- character primary color values from an unsigned long rgbColor value. These
- macros are not used in COLORSCR, but they sometimes come in handy when you
- are using Windows functions that will return rgbColor values to your
- program.
-
- The Windows display driver for the Enhanced Graphics Adapter attached to a
- color display uses only three of the four color planes of the EGA board, so
- only eight pure colors are possible. (In COLORSCR, the eight pure colors
- result when all scroll bars are either at the top or bottom positions.)
-
- Windows can display additional colors on the EGA by dithering, the creation
- of a pixel pattern that combines pixels of different pure colors. You'll
- find from experimenting on an EGA that the dithering pattern remains the
- same for every increment of four units for the red, green, and blue values.
- So, for an EGA, you have 218 or 262,144 dithered colors.
-
- On a monochrome display──such as a CGA, an EGA connected to a monochrome
- display, or a Hercules board──Windows uses 64 different patterns of white
- and black. The density of white is approximately equal to
-
- red + green + blue
- ──────────────────
- 255 * 3
-
- where the red, green, and blue values range from 0 to 255.
-
- Watch out for this──if you're developing using a color display, you might
- see nothing wrong with putting a pure red object against a pure blue back-
- ground. However, on a monochrome display the pure red and pure blue
- "colors" appear to be the same.
-
-
- Background Color
-
- When COLORSCR is first executed, it sets up a window class structure, called
- wndclass, that defines certain characteristics of its window. One member of
- this structure is a handle to a brush that Windows uses to color the
- background of the client area. This starts off as a solid black brush:
-
- wndclass.hbrBackground=CreateSolidBrush(0L);
-
- CreateSolidBrush requires an rgbValue and returns a handle to a brush.
- Although the hbrBackground member of the structure refers to a background,
- in more technical terms it is the brush that Windows uses to erase the
- window's client area in preparation for repainting by the program's window
- function.
-
- When you change the settings of COLORSCR's scroll bars, the program must
- create a new brush and put the new brush handle in the window class
- structure. Just as we were able to get and set the scroll bar window
- function by using GetWindowLong and SetWindowLong, we can get and set the
- handle to this brush by using the calls GetClassWord and SetClassWord.
-
- First, it's necessary to delete the existing brush:
-
- DeleteObject(GetClassWord(hWnd,GCW_HBRBACKGROUND)) ;
-
- Then the new brush can be created and the handle inserted in the window
- class structure:
-
- SetClassWord (hWnd,GCW_HBRBACKGROUND,
- CreateSolidBrush(RGB(color[0],color[1],color[2]))) ;
-
- Now, the next time Windows recolors the background of the window, Windows
- will use this new brush. To force Windows to erase the background, we
- invalidate the entire client area:
-
- InvalidateRect(hWnd, NULL, TRUE);
-
- The TRUE (nonzero) value as the third parameter indicates that we want the
- background erased before repainting.
-
- InvalidateRect causes Windows to put a WM_PAINT message in the message queue
- of the window function. Because WM_PAINT messages are low priority, this
- message will not be processed immediately if you are still moving the scroll
- bar with the mouse or the cursor keys. Alternatively, if you want the window
- to be updated immediately after the color is changed, you could add the
- statement
-
- UpdateWindow(hWnd) ;
-
- after the InvalidateRect call. However, this slows down keyboard and mouse
- processing.
-
- COLORSCR's WndProc function doesn't process the WM_PAINT message and just
- passes it on to DefWindowProc. Windows's default processing of WM_PAINT
- messages simply involves calling BeginPaint and EndPaint to validate the
- window. Because we specified in the InvalidateRect call that the background
- should be erased first, the BeginPaint call causes Windows to generate the
- message WM_ERASEBKGND (erase background). WndProc ignores this message also.
- Windows processes it by erasing the background of the client area with the
- brush specified in the window class.
-
- Normally Windows would erase the entire client area using the window class
- brush. This would cover the 10 child windows. Windows would then have to
- send WM_PAINT messages to all 10 child windows so that they could repaint
- themselves, which would be very annoying. However, you can avoid this
- problem by using the WS_CLIPCHILDREN style value when you are first creating
- the parent window with the CreateWindow call. The WS_CLIPCHILDREN style
- prevents the parent window from painting over its children. If you then take
- the WS_CLIPCHILDREN style out of CreateWindow, you'll see a big difference
- in how COLORSCR works.
-
- Like all GDI objects, the brushes created by a program that uses
- CreateSolidBrush are not automatically deleted by Windows when the program
- terminates. So far, we've been good about deleting each brush before
- creating a new one, but when the program is about to terminate, there is
- still one final brush in the window class that we should discard. Thus,
- during the processing of the WM_DESTROY message, DeleteObject is called once
- more:
-
- DeleteObject(GetClassWord(hWnd,GCW_HBRBACKGROUND));
-
-
- Multiple Instances
-
- Normally, most Windows programs will reuse the same window class when you
- load multiple instances of the program. The window class is registered only
- if the previous instance is NULL:
-
- if (!hPrevInstance){wndclass.style = CS_HREDRAW | CS_VREDRAW;
-
- .
- .
- .
-
- }
-
- But COLORSCR must take a different approach because the background color is
- specified in the window class. If all instances of COLORSCR used the same
- window class, then each instance would alter and use the same background
- color.
-
- This problem is easy to solve: each instance simply registers its own window
- class. COLORSCR doesn't check if it's the first instance or the twentieth.
- Contrary to Windows documentation, a window class registered with the same
- name as a previously registered window class does not replace the earlier
- window class. A program simply has the option of using its own window class
- or a previously registered window class. COLORSCR takes the former
- approach.
-
-
- COLORSCR as an Icon
-
- One final mystery remains. When you make COLORSCR into an icon, the entire
- surface of the icon appears as the selected color rather than just the right
- half. Yet COLORSCR doesn't seem to have any separate icon logic.
-
- Notice that COLORSCR specifies a NULL icon in the window class:
-
- wndclass.hIcon=NULL;
-
- This indicates that COLORSCR is responsible for painting its icon. But why
- does the entire icon appear as the selected color?
-
- This is the simplest part of COLORSCR──Windows hides child windows when a
- program is iconed. The colored background is then completely uncovered. So,
- when the parent is sleeping, the children are neither seen nor heard.
-
-
- Figure 6: COLORSCR.C
-
- /* COLORSCR.C -- Color Scroll (using child window controls) */
-
- #include <windows.h>
- #include <stdlib.h>
-
- long FAR PASCAL WndProc (HWND, unsigned, WORD, LONG) ;
- long FAR PASCAL ScrollProc (HWND, unsigned, WORD, LONG) ;
-
- FARPROC lpfnOldScr[3] ;
- HWND hChScrol[3], hChLabel[3], hChValue[3], hChRect ;
- short color[3], nFocus = 0;
-
- int PASCAL WinMain (hInstance, hPrevInstance, lpszCmdLine, nCmdShow)
- HANDLE hInstance, hPrevInstance;
- LPSTR lpszCmdLine;
- int nCmdShow;
- {
- MSG msg;
- HWND hWnd ;
- WNDCLASS wndclass ;
- FARPROC lpfnScrollProc ;
- short n ;
- static char *szColorLabel[] = { "Red", "Green", "Blue" } ;
- static char szAppName[] = "ColorScr" ;-
-
- wndclass.style = CS_HREDRAW | CS_VREDRAW ;
- wndclass.lpfnWndProc = WndProc ;
- wndclass.cbClsExtra = 0 ;
- wndclass.cbWndExtra = 0 ;
- wndclass.hInstance = hInstance ;
- wndclass.hIcon = NULL ;
- wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
- wndclass.hbrBackground = CreateSolidBrush (0L) ;
- wndclass.lpszMenuName = NULL ;
- wndclass.lpszClassName = szAppName ;
-
- if (!RegisterClass (&wndclass)) return FALSE;
-
- lpfnScrollProc = MakeProcInstance ((FARPROC) ScrollProc, hInstance) ;
-
- hWnd = CreateWindow (szAppName, " Color Scroll ",
- WS_TILEDWINDOW | WS_CLIPCHILDREN,
- 0, 0, 0, 0, NULL, NULL, hInstance, NULL) ;
-
- hChRect = CreateWindow ("static", NULL,
- WS_CHILD | WS_VISIBLE | SS_WHITERECT,
- 0, 0, 0, 0, hWnd, 9, hInstance, NULL) ;
-
- for (n = 0 ; n < 3 ; n++)
- {
- hChScrol[n] = CreateWindow ("scrollbar", NULL,
- WS_CHILD | WS_VISIBLE | WS_TABSTOP | SBS_VERT,
- 0, 0, 0, 0, hWnd, n, hInstance, NULL) ;
-
- hChLabel[n] = CreateWindow ("static", szColorLabel[n],
- WS_CHILD | WS_VISIBLE | SS_CENTER,
- 0, 0, 0, 0, hWnd, n + 3, hInstance, NULL) ;
-
- hChValue[n] = CreateWindow ("static", "0",
- WS_CHILD | WS_VISIBLE | SS_CENTER,
- 0, 0, 0, 0, hWnd, n + 6, hInstance, NULL) ;
-
- lpfnOldScr[n] = (FARPROC) GetWindowLong (hChScrol[n],
- GWL_WNDPROC) ;
- SetWindowLong (hChScrol[n], GWL_WNDPROC,(LONG) lpfnScrollProc) ;
-
- SetScrollRange (hChScrol[n], SB_CTL, 0, 255, FALSE) ;
- SetScrollPos (hChScrol[n], SB_CTL, 0, FALSE) ;
- }
-
- ShowWindow (hWnd, nCmdShow) ;
- UpdateWindow (hWnd);
-
- while (GetMessage (&msg, NULL, 0, 0))
- {
- TranslateMessage (&msg) ;
- DispatchMessage (&msg) ;
- }
- return msg.wParam ;
- }
-
- long FAR PASCAL WndProc (hWnd, iMessage, wParam, lParam)
- HWND hWnd;
- unsigned iMessage;
- WORD wParam;
- LONG lParam;
- {
- HDC hDC ;
- TEXTMETRIC tm ;
- char szbuffer[10] ;
- short n, xClient, yClient, yChar ;
-
- switch (iMessage)
- {
- case WM_SIZE :
- xClient = LOWORD (lParam) ;
- yClient = HIWORD (lParam) ;
-
- hDC = GetDC (hWnd) ;
- GetTextMetrics (hDC, &tm) ;
- yChar = tm.tmHeight ;
- ReleaseDC (hWnd, hDC) ;
-
- MoveWindow (hChRect, 0, 0, xClient / 2, yClient, TRUE);
-
- for (n = 0 ; n < 3 ; n++)
- {
- MoveWindow (hChScrol[n],
- (2 * n + 1) * xClient / 14, 2 * yChar,
- xClient / 14, yClient - 4 * yChar, TRUE) ;
-
- MoveWindow (hChLabel[n],
- (4 * n + 1) * xClient / 28, yChar / 2,
- xClient / 7, yChar, TRUE) ;
-
- MoveWindow (hChValue[n],
- (4 * n + 1) * xClient / 28,
- yClient - 3 * yChar / 2,
- xClient / 7, yChar, TRUE) ;
- }
- SetFocus (hWnd) ;
- break ;
-
- case WM_SETFOCUS:
- SetFocus (hChScrol[nFocus]) ;
- break ;
-
- case WM_VSCROLL :
- n = GetWindowWord (HIWORD (lParam), GWW_ID) ;
-
- switch (wParam)
- {
- case SB_PAGEDOWN :
- color[n] += 15 ; /* fall through */
- case SB_LINEDOWN :
- color[n] = min (255, color[n] + 1) ;
- break ;
- case SB_PAGEUP :
- color[n] ╤= 15 ; /* fall through */
- case SB_LINEUP :
- color[n] = max (0, color[n] - 1) ;
- break ;
- case SB_TOP:
- color[n] = 0 ;
- break ;
- case SB_BOTTOM :
- color[n] = 255 ;
- break ;
- case SB_THUMBPOSITION :
- case SB_THUMBTRACK :
- color[n] = LOWORD (lParam) ;
- break ;
- default :
- break ;
- }
- SetScrollPos (hChScrol[n], SB_CTL, color[n], TRUE) ;
- SetWindowText (hChValue[n], itoa (color[n], szbuffer, 10)) ;
-
- DeleteObject (GetClassWord (hWnd, GCW_HBRBACKGROUND)) ;
- SetClassWord (hWnd, GCW_HBRBACKGROUND, CreateSolidBrush
- (RGB (color[0], color[1], color[2]))) ;
-
- InvalidateRect (hWnd, NULL, TRUE) ;
- break ;
-
- case WM_DESTROY:
- DeleteObject (GetClassWord (hWnd, GCW_HBRBACKGROUND)) ;
- PostQuitMessage (0) ;
- break ;
-
- default :
- return DefWindowProc (hWnd, iMessage, wParam, lParam) ;
- }
- return 0L ;
- }
-
- long FAR PASCAL ScrollProc (hWnd, iMessage, wParam, lParam)
- HWND hWnd ;
- unsigned iMessage ;
- WORD wParam ;
- LONG lParam ;
- {
- short n = GetWindowWord (hWnd, GWW_ID) ;
-
- switch (iMessage)
- {
- case WM_KEYDOWN:
- if (wParam == VK_TAB)
- SetFocus (hChScrol[(n +
- (GetKeyState (VK_SHIFT) < 0 ? 2 : 1)) % 3]) ;
- break ;
-
- case WM_SETFOCUS:
- nFocus = n ;
- break ;
- }
- return CallWindowProc (lpfnOldScr[n], hWnd, iMessage, wParam,
- lParam) ;
- }
-
-
- Figure 7: COLORSCR.DEF
-
- NAME ColorScr
- STUB 'WINSTUB.EXE'
- CODE MOVABLE
- DATA MOVABLE MULTIPLE
- HEAPSIZE 1024
- STACKSIZA 4096
- EXPORTS WndProc
- ScrollProc
-
-
- Figure 8: Make-file for COLORSCR.EXE
-
- colorscr.obj : colorscr.c
- cl -c -d -Gsw -Os -W2 -Zpd colorscr.c
-
- colorscr.exe : colorscr.obj colorscr.def
- link4 colorscr, /align:16, /map /line, slibw, colorscr
- mapsym colorscr
-
-
- Figure 10: An unsigned long (32-bit) integer where the lowest three bytes
- specify red, green, and blue values.
-
- ╔═╤═╤═╤═╤═╤═╤═╤══╤══╤═╤═╤═╤═╤═╤═╤══╤══╤═╤═╤═╤═╤═╤═╤══╤══╤═╤═╤═╤═╤═╤═╤══╗
- ║0│ │ │ │ │ │ │7 │ 8│ │ │ │ │ │ │15│16│ │ │ │ │ │ │23│24│ │ │ │ │ │ │31║
- ╚═╧═╧═╧═╧═╧═╧═╧══╧══╧═╧═╧═╧═╧═╧═╧══╧══╧═╧═╧═╧═╧═╧═╧══╧══╧═╧═╧═╧═╧═╧═╧══╝
-
-
-
-
- ╔═════╗ ╔═════╗ ╔═════╗ ╔═════╗
- ║ 0 ║ ║BLUE ║ ║GREEN║ ║ RED ║
- ╚═════╝ ╚═════╝ ╚═════╝ ╚═════╝
-
- ████████████████████████████████████████████████████████████████████████████
-
- Ask Dr. Bob
-
- The Driverless Printer
-
- Dear Dr. Bob,
- Could you tell me if it is possible in a Windows application to send text
- and control characters to a printer for which there is no driver?──Wondering
-
- Dear Wondering,
- If you're using a commercial Windows app, you're in trouble; you can't print
- without a driver. If you're writing your own app, you can't set up a DC
- (display context) and use standard GDI functions like Rectangle(), BitBlt(),
- and TextOut(). But you can use DOS file I/O to open, write, and close the
- DOS PRN: device. If you do this, it's up to you to understand the language
- your printer talks──its escape codes and special control characters. This,
- of course, ties your program to that specific printer and loses the device
- independence normally provided by Windows.
-
- Math Library Functions
-
- Dear Dr. Bob,
- How can I use Microsoft C math library functions in my assembler
- program?──Math Maven
-
- Dear Maven,
- Microsoft C, Version 4.0, has several floating-point options: in-line 8087
- code, calls to an 8087 emulator, or calls to an alternative library. Before
- we tell you how to go about this task, we'll make a few disclaimers: C
- floating-point functions are meant for use by the compiler and handle
- arguments differently from standard functions, they're undocumented, and the
- fact that they work with Version 4.0 is no guarantee that they'll work with
- future versions.
-
- To figure out how the compiler uses floating point, we used the /Fc compiler
- switch to examine the compiler's assembly language output and /FPa or /FPc
- so that the compiler would produce calls to the floating-point functions
- instead of 8087 code.
-
- First, you have to initialize the library. If possible, let the regular C
- startup code handle this. If this won't work for you, look at the __CINIT
- function found in CRT0DAT.ASM from the C startup code disk. You should be
- able to pull the floating-point initialization code from this file.
-
- To find the names of the floating-point functions and the arguments that
- each function requires, write small C programs that demonstrate the
- floating-point functions you need and compile them with the /Fc switch.
- Study the assembler code and model yours after it. There are library
- functions for putting arguments onto a stack, doing math functions, and
- popping them off. Figure 1 is a sample C program and the assembler code it
- generates.
-
- You can explore other areas of compiler code this way, too. Another one you
- might want to look at is long int arithmetic.
-
- You don't have to worry about formatting your numbers because MASM's DD,
- DQ, and DT directives use the IEEE formats for the real number constants
- that the library expects.
-
- The floating-point functions documented in the C library manual, such as
- sin() and exp(), use standard C calling conventions, but before you use
- them, look at how the compiler converts all operands to double precision.
-
- Using COM2 Without COM1
-
- Dear Dr. Bob,
- I just recently ran into a problem with QuickBASIC. I opened COM2 for I/O
- and kept getting a bad filename error. The problem turned out to be that I
- did not have anything addressed as COM1 in my system. Everything worked fine
- after I installed a modem or serial card addressed as COM1.
-
- All that Microsoft product support would say is that you should not use
- COM2 if you do not have a COM1 already installed. I have designed IBM-
- compatible serial interfaces, so I know how the hardware works. To me they
- are two completely separate entities with two unique I/O addresses. Why am I
- having this problem, and why doesn't Microsoft consider it a problem? They
- told me they would not try to fix it.──Constrained
-
- Dear Constrained,
- We're on your side. The hardware for COM2 can work without COM1. It seems
- that QuickBASIC looks in the BIOS data area at RS232_BASE. This area stores
- the addresses of up to four serial communications ports. When you start your
- system, the BIOS initialization code checks for serial ports, puts in
- addresses of any it finds, and sets the rest of the locations to zero. When
- both COM1 and COM2 are present, the address of COM1 (3F8h), is in the first
- word of RS232_BASE and the address of COM2 (2F8h) is in the second word. But
- when COM2 is the only port installed, its address, 2F8h, is in the first
- word, and the second word is zero. When QuickBASIC is loaded, it looks at
- RS232_BASE to see what serial ports are present, but it only recognizes COM2
- when its address is in the second word.
-
- Here's a way to work around this. The program shown in Figure 2 will patch
- the second word of RS232_BASE with the address of COM2. You must run it
- before you run your QuickBASIC application that uses COM2. If you put this
- code at the beginning of your program, it will be executed too late to do
- any good because QuickBASIC checks for serial ports before it starts running
- your first BASIC statement.
-
- C Queries and Suggestions
-
- Dear Dr. Bob,
- I'd like to offer some suggestions and ask some questions regarding Version
- 4.0 of Microsoft C. In CodeView, in order to trace through a C source
- program, one must link with the object module. This is fine, but it would
- also be nice to have CodeView trace through the C source programs that have
- modules within a library. Is this possible?
-
- A related question: does the EXEPACK linker option remove the extra
- debugging information from specified libraries? If so, this would at least
- eliminate the need to compile a file twice──once with the CodeView options
- and once without. This way I would compile everything with the CodeView
- options before creating the libraries. Then if I needed to debug a
- particular program from the library, I could link in the appropriate object
- file.
-
- While using the watch option in CodeView, I tried to enter the following
- expression:
-
- strptr[ (idx==0)
- ? 0
- : idx-1 ],s
-
- where strptr was declared as char *strptr[] and idx was an int. CodeView did
- not allow this and returned a message that ']' was missing. Am I missing
- something?
-
- Also, I often find myself setting several debugging options in CodeView
- (tracepoints, watchpoints, breakpoints, etc.), finding a bug, exiting
- CodeView, editing the source, compiling, linking, and then starting CodeView
- and having to reset the options I had before. It would be nice to have a
- save/reset option for CodeView that will save and later reset the
- tracepoints, watchpoints, and breakpoints. Here's what I suggest. I usually
- enter CodeView with the same options. There could be an environment variable
- (let's say CV) that would look something like "set CV= -w -f" so that these
- options will now be the default for entering CodeView.
-
- One more question, about malloc, free, and _memavl. If I have a program
- like the one shown in Figure 3, the last _memavl () does not reflect the
- amount of space now available from the prior free () call. This was done by
- using the small compiler model with the two options /Zi and /Od.
-
- Finally, I would like to share some tips about the MAKE facility. I now set
- up MAKE files that look like Figure 4.
-
- There might be standard include files in subdirectories \include,\gf\s,
- \gf\m, and gf\l, and standard libraries in subdirectories \lib, \gf\s,
- \gf\m, and \gf\l.──C Buff
-
- Dear C Buff,
- Yes, you are missing something: the CodeView manual, Section 4.4, which says
- CodeView supports a subset of C operators. The conditional operator ?: is
- not in the subset. That's the reason for the error.
-
- One thing we found interesting about the complex operations CodeView does
- support is that it reevaluates the entire expression each time it displays
- the watch window. This means that if you're watching an array such as
- strptr[idx], CodeView may not only watch a fixed location; when idx changes,
- you'll be watching a different element of the array. This is not what we
- expected, but we like what CodeView does.
-
- Your idea for the CodeView save/reset options is a good one. We'll pass it
- on. We, too, get tired of turning case sense off each time.
-
- Your malloc(), free(), and _memavl() question is interesting. By marking the
- block as no longer in use, free() releases the memory. But as you
- discovered, this free space does not show up immediately in _memavl().
- According to the C manual, _memavl() returns "approximately" how much memory
- is available. Because it doesn't add up the free blocks until they're
- needed, what it in fact returns is the largest size you can be sure you can
- allocate. You may, however, be able to allocate a much larger block, because
- if malloc() gets a request for a block larger than _memavl(), it goes down
- the list of free blocks joining adjacent ones, trying to make a large enough
- block. There is no way to force garbage collection analogous to the BASIC
- FRE($) function.
-
- Figure 5 is a simple function, _maxavl(), that returns the size of the
- largest possible block. We suggest using it instead of _memavl().
-
-
- Figure 1: A sample C program and the assembler code it generates.
-
- float a,b,c,d,e;
- main()
- {a = (b+c)*(d+e) ; }
-
- lea bx,WORD PTR _b ; put address of b into the bx register
- call __flds ; push b onto fp stack
- lea bx,WORD PTR _c
- call __fadds ; add c to top of stack
- lea bx,WORD PTR _d
- call __flds ; push d onto fp stack
- lea bx,WORD PTR _e
- call __fadds ; add e to top of stack
- call __fmul ; multiply top two elements of stack
- ; product replaces previous two
- values on stack
- lea bx,WORD PTR _a
- call __fstsp ; pop stack putting result in variable a
-
-
- Figure 2: This program demonstrates how to patch the second word of
- RS232_BASE with COM2. You must run it before you run your
- QuickBASIC application that uses COM2.
-
- def seg _ &h0040
- if (peek(0) + 256*peek (1))= &h02f8 then _
- poke 2,&hf8: poke 3,&h02:_
- print "COM2 patch made"else_
- print "COM2 patch NOT made"
-
-
- Figure 3: The last _memavl () does not reflect the amount of space now
- available from the prior free () call. This was done by using the
- small compiler model with the two options /Zi and /Od.
-
- printf ("starting available memory = %u\n",_memavl () );
- if (NULL == (ptr=malloc ((unsigned) 5000)) )
- printf("Out of memory.");
- printf("Available memory after malloc() = %u\n",_memavl () );
- free(ptr);
- printf("Available memory after free() = %u\n",_memavl () );
-
-
- Figure 4: MAKE file template
-
- # MAKE file template - ###############################
- # Assume the application program is called DOIT and the source
- # directory is \CUSTOMER\SOURCE. The target directory for all
- # obj and exe files is \CUSTOMER\S (which can easily be changed
- # to \CUSTOMER\M or CUSTOMER\L by setting M=specific model
- # type. The switches are:
- #
- # M= model type S, M, or L
- #
- # O= Compiler options (set for CodeView)
- #
- # L= LINT_ARGS switch (currently set on)
- # To set this off, use L=
- #
- # I= Switch to set environment variables
- #
- # To set this off, use I=null
- #
- # S= Switch to force a compile
- #
- # To force a compile, use S=null
- #
- ######################################################
- M=S
- O=/Zi/Od
- L=/DLINT_ARGS
- D=\CUSTOMER\$(M)
- I=null
- S=\include\stdio.h
-
- COMP=\bin\msc $*,$@ /A$(M) $(L) $(0);
- LINK=\bin\link $(D)\doit.obj,$(D)\doit.exe,$(D)\doit.map,/M/E
- LINKCV=$(LINK)/CO
-
- # Make sure the include and library environ vars are correctly set
- $(I): \bin\inclib.bat
- \bin\inclib $(M)
-
- $(D)\doit.obj: doit.c $(S)
- $(COMP)
-
- $(D)\doitcv.exe: $(D)\doit.obj
- $(LINKCV)
-
- $(D)\doit.exe: $(D)\doitcv.exe
- $(LINK)
- dir $(D)\doit*.*
- # End of MAKE file#####################################
-
- The \BIN\INCLIB.BAT file looks something like this:
-
- if "" == "%1" goto :default
- set INCLUDE=\include;\gf\%1
- set LIB=\gf\%1;\lib
- goto :end
- :default
- set INCLUDE=\include;\gf\s
- set LIB=\gf\s;\lib
- goto :end
- :end
-
-
- Figure 5: A simple function, _maxavl (), that returns the size of the
- largest block that malloc () can allocate.
-
- /*
- * _maxavl() - Return the size of the largest block that
- * malloc() can allocate
- */
- int _maxavl() {
- register unsigned size;
- register unsigned incr;
- char *p;
-
- /*
- * Do binary search for largest block malloc() can return.
- * Start search with a size of 32K. Keep going until the
- * increment value is reduced to 0, with size homing in on
- * its largest possible value.
- */
- for (size=incr=0x8000; incr; ) {
- incr >>= 1; /* cut increment in half */
- if ( (p=malloc(size))==NULL )
- size -= incr; /* malloc() failed, so decrease size */
- else {
- free (p); /* malloc() succeeded, so free block */
- size += incr; /* and increase size */
- }
- }
- /* If last malloc() failed, return size-1 instead of size: */
- return( (==NULL) ? size-1 : size );
- }
-
-
- ════════════════════════════════════════════════════════════════════════════
-
-
- Vol. 2 No. 3 Table of Contents
-
-
- PLEXUS Introduces Windows-based Tools for Building Image Databases
-
- The Plexus Extended Data Processing (XDP) system integrates a UNIX-based
- file server with Microsoft Windows-based workstation software. XDP provides
- a high-level DBMS environment for creating sophisticated, graphics-oriented
- database applications.
-
-
- Porting MS-DOS Assembly Language Programs to the OS/2 Environment
-
- OS/2's protected mode precludes programs and routines written in assembly
- language from taking certain liberties allowed under real mode. This article
- offers a guide that can minimize the conversion effort necessary to insure
- programs will work under both MS-DOS and MS OS/2.
-
-
- Microsoft Windows 2.0: Enhancements Offer Developers More Control
-
- Windows 2.0 gives developers over 60 new functions, a new user interface
- with overlapping windows, and a substantially improved development
- environment. Our author uses some of the new facilities to create TILER,
- which supplies one feature missing in Windows 2.0──tiled windows.
-
-
- Keeping Up With The Real World: Speedy Serial I/O Processing
-
- Serial I/O processing under MS-DOS benefits dramatically when the BIOS
- serial communications routines available through INT 14H are replaced.
- Strategies for handling the necessary interrupts, XON/XOFF processing,
- and reliable communications up to 38,400 baud are suggested.
-
-
- BLOWUP: A Windows Utility for Viewing and Manipulating Bitmaps
-
- Using bitmaps in the Windows environment is not trivial. The programmer
- must understand memory display contexts, how bitmaps are scaled, and how
- to transfer bitmaps via the clipboard. BLOWUP, a useful Windows tool, can
- capture and manipulate any part of the windows display.
-
-
- Increase the Performance of Your Programs with a Math Coprocessor
-
- The performance of calculations, especially those that make extensive
- use of floating-point operations, increases significantly when the CPU is
- supplemented with an 8087/80287/80387 coprocessor. Learn how your programs
- can take advantage of these coprocessors.
-
-
- TIFF: An Emerging Standard for Exchanging Digitized Graphics Images
-
- The growing popularity of desktop publishing has created a need for the
- different vendors' software to be able to exchange digitized graphics
- images. The Tag Image File Format (TIFF) is gaining momentum as the
- possible industry standard for scanner-created digital images.
-
-
- Ask Dr. Bob
-
-
- EDITOR'S NOTE
-
- The shipment of the Microsoft OS/2 Software Development Toolkit allows
- developers to begin programming for the new operating system. In this
- issue, and in those to come, MSJ focuses on specific OS/2 programming
- topics. Article 2, "Porting MS-DOS(R) Assembly Language Programs to the
- Microsoft OS/2 Environment", offers a step by step guide to converting
- assembler code so that it will work under OS/2.
-
- The importance of the Windows environment continues to grow. More and
- more companies, such as PLEXUS (spotlighted in article 1) rely on the
- Windows environment for their workstation software environment. Our article
- on the about-to-be-released Windows 2.0 highlights why the new and enhanced
- features are of particular interest to developers. And, Charles Petzold
- continues his Windows programming series with Blowup, another small but
- interesting Windows tool.
-
- One of the dangers a publication encounters when working with prerelease
- software is the inevitable changes products go through before they
- are released. We do our best to ensure the sample code works when the
- magazine is printed. You can find the most current versions of all our
- source code, as well as full code listings that we don't have room to
- print, on the DIAL system. We will soon be posting the complete code
- listings on other popular bulletin boards. Check in upcoming issues for
- more information.
-
- Some readers of our last issue commented that our discussion of the OS/2
- DOS Environment implied that bypassing MS-DOS, writing directly to the
- hardware, and playing games with segment reisters should be punishable
- by death. Not at all! We recognize that MS-DOS has certain limitations.
- The article merely meant that OS/2 eliminates a good deal of those
- limitations and that it will no longer be necessary to bypass the
- operating system. If you have any comments, suggestions, or critiques,
- please write us and let us know.──Ed.
-
-
- Masthead
-
- JONATHAN D. LAZARUS
- Editor and Publisher
-
- EDITORIAL
-
- TONY RIZZO
- Technical Editor
-
- CHRISTINA G. DYAR
- Associate Editor
-
- JOANNE STEINHART
- Production Editor
-
- GERALD CARNEY
- Staff Editor
-
- DIANA E. PERKEL
- Editorial Assistant
-
- ART
-
- MICHAEL LONGACRE
- Art Director
-
- VALERIE MYERS
- Associate Art Director
-
- CIRCULATION
-
- WILLIAM B. GRANBERG
- Circulation Manager
-
- L. PERRIN TOMICH
- Assistant to the Publisher
-
- BETSY KAUFER
- Administrative Assistant
-
- Copyright(C) 1987 Microsoft Corporation. All rights reserved; reproduction
- in part or in whole without permission is prohibited.
-
- Microsoft Systems Journal is a publication of Microsoft Corporation, 16011
- NE 36th Way, Box 97017, Redmond, WA 98073-9717. Officers: William H.
- Gates, III, Chairman of the Board and Chief Executive Officer; Jon Shirley,
- President and Chief Operating Officer; Francis J. Gaudette, Treasurer;
- William Neukom, Secretary.
-
- Microsoft Corporation assumes no liability for any damages resulting from
- the use of the information contained herein.
-
- Microsoft, the Microsoft logo, CodeView, MS, MS-DOS, and XENIX are
- registered trademarks of Microsoft Corporation. IBM is a registered
- trademard of International Busness Machines Corporation. PageMaker is a
- registered trademark of Aldus Corporation. dBase is a registered trademark
- of Ashton-Tate. UNIX is a registered trademark of AT&T. Lotus and 1-2-3
- are registered trademarks of Lotus Development Corporation. Intel is a
- registered trademark of Intel Corporation. Macintosh is a trademark of
- of Apple Computer, Inc. Paintbrush is a registered trademark of ZSoft
- Corporation. Motorola is a registered trademark of Motorola, Inc.
-
- ████████████████████████████████████████████████████████████████████████████
-
- Plexus Introduces Windows-based Tools for Building Image Databases
-
- Kevin Strehlo
-
- When personal computers first began to make inroads into the business
- environment, database managers such as dBASE(R) were fine for what was then
- a text-only world. They gave people who weren't crack programmers the
- ability to build custom applications with reasonable, if limited,
- interfaces for the management and manipulation of textual data. But
- Microsoft(R) Windows, scanners, and laser printers have taken us from that
- text-only world into one in which we can generate and manipulate images
- intermingled with text. OCR devices even allow us to translate images of
- documents into computer graphics and text. Where are the visual database
- managers with which we can easily build custom applications in order to
- manage huge volumes of this image data?
-
- Plexus(R) thinks it has the answer: its Extended Development Environment
- (XDE), which couples Windows with a fourth-generation database language.
- Still in its beta-test stage, XDE is designed to run on Plexus's Extended
- Data Processing (XDP) System, an elaborate configuration of LAN hardware
- and software for handling an Informix-based graphics database. XDE serves
- as a tool for creating custom applications that manage graphics data with
- a sophisticated visual interface.
-
- The real beauty of XDE is its simplicity for the programmer. Writing a
- picture perfect (or rather, a perfect picture) database with it requires
- no knowledge of the event-driven mysteries of Windows, the intricacies of
- C, or the problem of finding a bitmap in an optical disk stack. It lets
- the programmer create a Windows database application without worry about
- such things as WinMain functions or WINDOWS.H files.
-
-
- Hardware and Software
-
- When it comes to system requirements, XDE and standard database managers
- part ways, however. XDE will run only on a complete XDP system, which
- means a substantial investment in hardware and software. Complete systems
- range from $75,000 to more than $1 million.
-
- At the center of XDP is a 68020-based Plexus supermicro that acts as the
- database server for an Ethernet(TM) LAN. Hooked up to the LAN are a number
- of AT-compatible workstations running Windows on high-resolution, 1,664-
- by 1,200-pixel displays. The cabinets of the system can provide as much as
- 6.7Gb of magnetic disk storage and 8Gb of optical disk storage. If more
- is needed, a jukebox of optical laser disk drives can be attached to
- handle as much as 280Gb with an average access time of under 10 seconds.
- If you think that's an incredible amount of disk capacity, you're right,
- but let's face it──at 300Kb or more per compressed image, a database that
- replaces a roomful of microfiche or shelves full of technical manuals is
- going to require an incredible amount of storage.
-
- To record all of that visual information, you can attach microfiche and
- paper scanners to the workstations. To translate captured document images
- into ASCII text suitable for manipulation, a powerful OCR device handles a
- variety of fonts at a minimum speed of 60 characters per second with
- greater than 99 percent accuracy.
-
- For handling large images with dispatch, each AT workstation contains a
- board with enough memory to hold and manipulate an uncompressed 400-dpi
- image and a graphics coprocessor tailored for pixel manipulation. Having
- an entire 1.5Mb bitmap available in memory speeds up image manipulation.
- The coprocessor board also minimizes transmission times among the database
- server, the workstations, and the OCR subsystem by quickly compressing
- each image before transferring it across the Ethernet.
-
- The software at the heart of XDP is as much a mixed bag as the database
- that it supports. On the AT-compatible workstations, the system runs MS-
- DOS(R) 3.1 because of its networking hooks. The Plexus DataServer runs an
- enhanced version of the Informix relational database under UNIX(R) System V.
- Meanwhile, communication between server and workstations is handled
- according to TCP/IP protocols on an Excelan implementation of Ethernet.
- The Informix database running on the server has been extended with data
- types for handling images. A similarly extended version of the SQL
- standard for database queries (which is dubbed XESQL) allows mixed-mode
- (text and graphics) transactions. The custom applications created by XDE
- and running on the AT workstations generate the XESQL queries.
-
-
- How XDE Works
-
- The Plexus XDE consists of two components: an application called UI-Build
- (UI stands for user interface) and an extended version of the Informix 4GL
- language. UI-Build does for the overall user interface what the Microsoft
- Windows Dialog Editor does for dialog boxes (see "Latest Dialog Editor
- Speeds Windows Application Development," MSJ, Vol. 1 No. 1). It allows a
- sophisticated end-user or relatively inexperienced programmer to define
- the Windows front-end of the application interactively. After defining the
- elements of the Windows interface, the application developer uses the 4GL
- language to write the code behind the drop-down menus, buttons, image
- fields, and text fill-in boxes.
-
- The 4GL language is not strictly declarative. It is a high-level
- programming language with standard procedural capabilities, including
- IF/THEN/ELSE, CASE, FUNCTION, and VARIABLE.
-
- Upon compilation, XDE links that simple 4GL code to the appropriate
- elements of the Windows interface. XDE handles all of the tasks associated
- with building a full-blown Windows application, which are by no means
- trivial.
-
- "We try to cover up all the difficulties that Windows can bring in and
- present to the user," says Maurizio Gianola, software tools manager for
- Plexus. The visual and event-driven nature of Windows throws the unwary
- programmer up against new and strange concepts, such as client area and
- messages, he points out. "You have to know so many things to write a
- Windows application. What is the window handler? What are all those
- messages coming in? What do you do with those messages? Which ones go to
- the menu handler? Which ones go to the system?"
-
- Despite its high-level nature, the 4GL language offers rich functionality,
- including a number of graphics-oriented extensions to SQL, such as the
- capacity to adjust scanner parameters and a "magnifying glass" function
- that automatically rescales the on-screen presentation of an image from
- 100 dpi to 300 dpi. And to accommodate needs that the language's
- developers didn't foresee or thought too specialized to implement,
- specialized routines can be written in C and dropped into the 4GL
- program.
-
-
- Windows and Houses
-
- To illustrate the general form and function of XDE, Gianola whipped up a
- sample real estate application: the user specifies a range of house prices
- and asks to see some listings. The application in turn queries a database
- on the Plexus server and responds by allowing the user to view matching
- listings one at a time. The address, the number of bedrooms, and,
- optionally, a scanned image of each house are displayed (see Figure 1).
-
- When you invoke UI-Build to begin creating a new application, all you see
- above the Windows icon area are the two free-floating menu bars. Figure 2
- shows the UI-Build environment a step into the process of creating the
- sample real estate application. Gianola has already named the title bar
- "Real Estate Database." Had he chosen to do so, he could have specified in
- the main UI-Build menu that the system box appear in the title bar, that
- the window be sizable and movable, and so on. Wishing to keep the
- application simple, Gianola merely chooses Menu_Bar from the Palette, adds
- the Options menu, and names the only command in that menu Show Picture.
-
- In Figure 3, Gianola has filled in the UI-Build dialog box that comes up
- when Menu is chosen from the Palette; Figure 4 shows the result after he
- has added two menu choices.
-
- Gianola next designs the forms that will pop up in the course of running
- the application. When users ask to see listings, they must use the form
- shown in Figure 5 to specify the minimum and maximum prices they wish to
- consider. The form is a Group_Box; it is labeled Price Range on screen and
- priceRange internally. Within this box Gianola has defined an Entry area,
- which he can stretch to the desired size by means of handles. He assigns
- Min as an ID for whatever text a user will input; this lets him reference
- it easily from within his 4GL program.
-
- Figure 6 shows the form that will appear when the user selects Show
- Listing from the Commands menu. Gianola has labeled the rectangle
- Available Houses, and because it is currently selected, it can be resized
- by stretching its handles. Note that he has defined two buttons in the
- lower right corner──Next and Done. To define a large area in which the
- scanned images of the houses can be displayed, he chose Image from the
- Palette. The figure shows Gianola in the process of defining fields in
- which to display the number of rooms, price, and address of the current
- house listing.
-
- Had he desired, Gianola could have included other means of interacting
- with the user. For example, he could have let the user specify the desired
- number of rooms by clicking on a toggle to enable a table of numbers drawn
- from the database and then vertically scrolling to his or her choice──much
- as point sizes are selected in the Type Specifications dialog box of
- PageMaker(R).
-
-
- Writing the Code
-
- After establishing the user interface with UI-Build, the programmer then
- uses a text editor to write the 4GL code that goes with it. Figure 7 shows
- the code that takes care of the preliminaries. The database is named
- "houses," and the predefined table in that database called "house" is used
- to define the records that will appear in windows on the AT's screen.
- Three variables are declared, one of which will serve as a flag.
-
- The first lines of the MAIN section name the help file and initialize the
- showpicture flag. Next, the program diplays the menu defined in Figures 2,
- 3, and 4 and names mainMenuHdl as the routine that will deal with user
- selections of items in that menu. The first instance of the modeless form
- availHouses is brought up as well.
-
- Figure 8 shows the menu-handling routine for the main menu. When the user
- selects Show Picture from the Options menu, that choice (the internal
- IDfor it is showHousePicture) is checked by calling the XDE standard Check
- function and the showpicture flag is changed if necessary. If the user
- chooses Show Listing from Commands, the form defined in Figure 5 pops up.
- The routine creates a cursor to allow user input and selects all records
- in the database that fall within the range specified by the user. It then
- calls the function showData, which we will discuss later. Finally, since
- the user is now looking at listings, the Show Listing choice in the menu
- is disable, and the End Listings choice changes from gray to black to
- indicate it is enabled.
-
- Figure 9 shows the form handling routine availHouseHdl, which uses the 4GL
- construct called Before Input to initialize the blank form. A message
- explaining how to view the picture appears along with the Windows note
- icon and an OK box for the user to check. If the user clicks in the Next
- button, the showData function is called, while a click in Print calls the
- printHouseInfo function. The line ON photo initiates a rather nifty
- feature of XDE: if the user clicks on the empty field in which houses are
- displayed in an effort to bring up an image, a message tells him or her to
- make the appropriate menu selection. Finally, when the user is done, the
- application asks if the current listing should be printed before it
- disappears.
-
- The routine in Figure 10 performs a simple check to ensure that the user
- has filled in minimum and maximum values that make sense. If this is not
- the case, a warning message will appear.
-
- Finally, the two functions called in the earlier routines appear in
- Figure 11. The first displays the proper image and values within their
- respective fields in the current instance (availH) of the form
- availHouses. The second prints the listing at the user's request.
-
-
- Unique Approach
-
- Several unique aspects of the Plexus XDE should appeal to anyone who wants
- to build Windows applications quickly. For one thing, the system
- automatically sets up everything required by Microsoft Windows. When the
- application is compiled, it produces not only a resource file that encodes
- everything done with UI-Build, but also a WINDOW.H file that includes the
- predefined XDE functions. For example, the functions available for
- creating rich menus include gray and ungray──that is, disable and enable
- menu choices──and check and uncheck.
-
- Gianola seems especially proud of another unique feature of XDE. "Usually,
- every time you change something in the user interface, you have to
- recompile a Windows application, but we tried to avoid that." Essentially,
- Plexus bound the user interface to the program at link time rather than
- during compilation. "In other words, when you go back and touch up your
- user interface after you've gotten the application running," Gianola says,
- "you will need to link but not to recompile." This feature is actually
- almost a necessity in XDE since compilation involves two levels of
- translation──first from 4GL to C, and then from C to object code.
- Recompilation would thus be a more cumbersome task than usual.
-
-
- Applications
-
- The Plexus XDP is already in use by some firms even though XDE is not yet
- available. Such was their need for a visual approach to managing large
- volumes of image data that these companies (with the help of Plexus) wrote
- ordinary Windows programs in C to run the system. The applications created
- thus far are fairly complex and demonstrate that there will often be a
- need to enhance an application roughed out with XDE by embedding at least
- a modicum of C code. For example, one customer is using a five-workstation
- system for producing CD ROM discs to replace microfiche. One feature of
- this system enables the user to exclude parts of a page from being
- recorded by the scanner. Such refinements are beyond the capabilities of
- XDE, although Plexus plans to enhance the environment in future releases.
-
- Another Plexus customer, the regional phone company US West, uses an XDP
- system to manage the placement of ads in its yellow pages publishing
- operations. When a customer calls, ad sales representatives can quickly
- find the order through a text-oriented database search that's facilitated
- by an easy-to-use Windows interface. They can view an image of the
- advertisement on-screen.
-
- Desktop publishing ventures involving a large number of technical diagrams
- or other images would seem to be another natural application for the
- system. Since an application built with XDE resides in the Windows
- environment, retrieved images could be transferred directly into a Windows
- desktop publishing program──and rich text could be moved just as easily.
-
- Altogether, XDP seems a big improvement over comma-delimited ASCII export
- files and pasted photocopies.
-
-
- Figure 7: DATABASE houses
-
- DEFINE
- {The record prospect is defined like the database table house }
- prospect RECORD LIKE house.*
- Min, Max INTEGER
- showpicture SMALLINT
-
- MAIN
- HELP FILE "houses.hlp" { F1 is request for help }
- LET showpicture = FALSE
- SHOW MENU mainMenu USING mainMenuHdl
- OPEN FORM availHouses USING availHousesHdl AS availH { modeless form }
- END MAIN
-
-
- Figure 8: mainMenuHdl
-
- MENUHANDLER mainMenuHdl
- ON SHOW MENU
- DISABLE IN MENU endListing
- MENU
- ON showHousePicture
- IF showhouse = FALSE THEN
- CHECK IN MENU showHousePicture
- LET showhouse = TRUE
- ELSE
- UNCHECK IN MENU showHousePicture
- LET showhouse = FALSE
- END IF
- ON showListing
- OPEN FORM priceRange USING priceRangeHdl { modal form }
- DECLARE listingCursor CURSOR FOR
- SELECT * INTO prospect.* FROM house
- WHERE house.price <= Max AND
- house.price > Min
- OPEN listingCursor
- CALL showData()
- DISABLE IN MENU showListing
- ENABLE IN MENU endListing
- ON endListing
- ENABLE IN MENU showListing
- DISABLE IN MENU endListing
- CLOSE listingCursor
- END MENUHANDLER
-
-
- Figure 9: availHouseHdl
-
- FORMHANDLER availHouseHdl()
- DEFINE
- print SMALLINT
-
- ON OPEN FORM
- DISPLAY 0, "", NULL, 0 TO FIELD price, address, photo, numOfRooms
- INPUT
- ON Done
- CLOSE FORM
- ON Next
- CALL showData()
- ON PrintAllInfo
- CALL printHouseInfo()
- ON photo
- MESSAGE
- "To see the picture of the house check the menu selection"
- NOTE_ICON OK_BUTTON
- END INPUT
- ON CLOSE FORM
- MESSAGE "Do you want to print this listing?"
- NOTE_ICON YES_NO_BUTTONS RETURNING print
- IF print = YES THEN
- CALL printHouseInfo(prospect.code)
- END IF
- END FORMHANDLER
-
-
- Figure 10: priceRangeHdl
-
- FORMHANDLER priceRangeHdl()
- INPUT BY NAME Min, Max
- ON idOk
- IF Min <= Max THEN
- CLOSE FORM
- ELSE
- MESSAGE "Incorrect price range!!" WARNING_ICON OK_BUTTON
- END IF
- END INPUT
- END FORMHANDLER
-
-
- Figure 11: Other Functions
-
- FUNCTION showData()
- FETCH listingCursor
- DISPLAY BY NAME prospect.* TO FORM availH
- IF showpicture = TRUE THEN
- DISPLAY prospect.photo TO FORM availH FIELD photo
- END IF
- END FUNCTION
-
- FUNCTION printHouseInfo()
- PRINT prospect.price, prospect.address, prospect numOfRooms
- IF showpicture = TRUE THEN
- PRINT prospect.photo
- END IF
- END FUNCTION
-
- ████████████████████████████████████████████████████████████████████████████
-
- Porting MS-DOS Assembly Language Programs to the OS/2 Environment
-
- Ray Duncan
-
- OS/2 the protected-mode operating system for the 80286 microprocessor,
- offers the applications programmer a rich variety of system services and
- is comparable in many ways to the powerful real-time operating systems
- used on minicomputers and superminicomputers. It supports preemptive
- multitasking, multiple screen groups, a broad spectrum of inter-process
- communication facilities, and virtual memory management. It includes high-
- performance device drivers for the video display, keyboard, mouse, and
- serial port. It even boasts a true print spooler. In short, OS/2 provides
- a sturdy foundation for an entirely new generation of business
- applications with vastly expanded features and capabilities.
-
- Yet at first programmers will be more concerned with porting their
- existing MS-DOS tools and applications to OS/2's protected-mode
- environment than with attempting to exploit the capabilities of the new
- system. Those who write code in high-level languages, such as Microsoft(R)
- C, can simply relink and run the programs when the appropriate protected-
- mode libraries are available. But those of us who still labor in the
- vineyards of bit-banging──writing primarily in assembly language or at
- least making extensive use of assembly language subroutines to obtain
- optimum performance──face a much more extensive revision process.
-
- Here I will outline a useful strategy for porting MS-DOS(R) assembly
- language programs to OS/2. Derived from my own experience in working with
- the alpha- and beta-test versions of OS/2, this conversion process should
- not be construed as an official recommendation from Microsoft, although I
- find that it works well. The procedure consists of five steps: segmentation,
- rationalization, encapsulation, conversion, and optimization. The first
- three should be performed and tested in the MS-DOS environment. It is only
- the last two that require OS/2 and the associated programming tools.
-
-
- Segmentation
-
- The Intel(R) 80286 microprocessor can function in either of two distinct
- modes: real mode or protected virtual address mode. In real mode, the
- 80286 has a 1Mb address space and behaves like an 8086 or 8088 processor,
- except that some machine instructions are more efficiently implemented and
- a few new instructions are available. MS-DOS runs on the 80286 in real mode.
-
- In protected mode, the personality of the 80286 is considerably different.
- Hardware features come into play letting an operating system manage 1Gb of
- virtual address space, isolate the memory used by one process from that
- used by another, perform fast context switching between processes and
- interrupt handlers, intercept certain privileged machine instructions, and
- detect the execution of invalid opcodes. OS/2, of course, runs in the
- 80286's preferred state: protected mode.
-
- Most of the 80286's protected-mode capabilities revolve around a change in
- the way memory is addressed. In real mode, the value in a segment register
- directly corresponds to a physical memory address. In protected mode, this
- correspondence is indirect: a segment register holds a selector, which is
- an index into a table of descriptors. A descriptor defines the physical
- address and length of a memory segment, its characteristics (executable,
- read-only data, or read/write data) and access rights, and whether the
- segment is currently resident in RAM or has been swapped to disk. Each
- time a program loads a segment register or accesses memory, the 80286
- hardware checks the relevant descriptor table entry and the program's
- privilege level, generating a hardware interrupt (called a protection
- fault) if the selector or memory operation is not valid. Needless to say,
- manipulation of the descriptor table itself is reserved for the operating
- system's memory manager.
-
- This scheme of memory addressing in protected mode has two immediate
- consequences for applications programs. First, they can no longer perform
- arithmetic on the contents of segment registers (since selectors are only
- index numbers and have no direct relationship to physical memory
- addresses) or use segment registers for storing temporary values. A
- program must not load a segment register with anything but a legitimate
- selector that it received from the OS/2 loader or as a result of an OS/2
- memory allocation function call. Second, machine code ("text") and data
- must be strictly segregated from each other by placing them in separate
- segments with distinct selectors, since an executable selector is not
- writable, and vice versa.
-
- Accordingly, the first step in converting a program for OS/2 is to impose
- upon it a segmented structure compatible with the protected-mode
- environment. The program must have at least one code and one data segment,
- and it should use the special name DGROUP to declare a group containing
- the "near data" segment, stack, and local heap (if there is one). Figure 1
- shows this skeletal structure. It's best to follow the segment naming and
- ordering conventions used by the Microsoft compilers (see Figures 2 and
- 3). At this stage, you should also remove or rewrite any code that
- directly manipulates segment values.
-
- Now reassemble and link your program and make sure it still works as
- expected under MS-DOS. Changing or adding segmentation often uncovers
- hidden addressing assumptions in the code, so it is best to track these
- problems down before making any other substantive changes to the program.
-
-
- Rationalization
-
- Once you've successfully segmented your program so that it can be linked
- and executed as an .EXE file under MS-DOS, the next step is to rationalize
- your code. This means converting your program into a completely well-
- behaved MS-DOS application, ruthlessly eliminating any elements that
- manipulate the peripheral device controllers directly, alter interrupt
- priorities or edit the system interrupt vector table, depend on CPU speed
- or characteristics (such as timing loops), or are incompatible with MS-
- DOS's memory management or handle-based file and record management.
-
- When an MS-DOS application directly accesses the hardware, it is usually
- in connection with display routines and keyboard handling. Caught between
- the user's demand for snappy screen interaction and the regrettably poor
- performance of the MS-DOS and IBM(R) PC ROM BIOS video drivers, most
- programmers have felt it necessary to go around the operating system and
- give the applications program complete control of the video adapter. This
- allows the screen to be updated at the maximum speed possible under MS-DOS
- but poses obvious difficulties in a multitasking, protected memory
- environment such as OS/2. For porting purposes, all routines in your
- program that write text to the display, control character attributes, or
- affect cursor shape or position should be converted into Write Handle
- (Interrupt 21H Function 40H) calls with ANSI escape sequences or ROM BIOS
- Interrupt 10H calls. Similarly, convert all keyboard operations to Read
- Handle (Interrupt 21H Function 3FH) or ROM BIOS Interrupt 16H calls.
-
- After you've expunged all hardware dependence from your program, your next
- priority is to make sure it uses system memory properly. Whereas MS-DOS
- typically hands an application all of the unoccupied memory in the system,
- this is not true of OS/2. A process is initially allocated only enough
- memory to hold its code segment and stack and to meet its declared data
- storage needs. You can make the MS-DOS loader behave in the same way that
- the OS/2 loader does by linking your application with the /CPARMAXALLOC
- switch. Alternatively, your program can execute a Set Memory Block
- (Interrupt 21H Function 4AH) call early in its initialization routine to
- release any extra memory it may have been allotted by the loader.
-
- When your program has begun its main sequence of operation, it should
- dynamically obtain and release any additional memory it may require for
- buffers and tables with the MS-DOS Interrupt 21H Functions 48H (allocate
- memory block) and 49H (free memory block). The size of any single
- allocated block should not exceed 65,536 bytes, even though MS-DOS allows
- the allocation of larger blocks; this will ensure compatibility with
- protected mode.
-
- Finally, you must turn your attention to file handling. MS-DOS takes a
- rather schizoid approach to mass storage by supporting two completely
- different sets of file and record management calls. The first set relies
- on a data structure known as a file control block (FCB), while the second
- and far more powerful group uses null-terminated (ASCIIZ) filename strings
- and 16-bit file tokens called handles. The FCB calls became obsolete with
- the advent of the hierarchical directory structure in MS-DOS 2.0, but MS-
- DOS still supports them in the name of upward compatibility, and
- unfortunately there are still programs around that use them. If your
- program is one of these, you'll have to replace every FCB file or record
- function call with its handle-based equivalent, because OS/2's protected
- mode contains no support at all for FCBs.
-
-
- Encapsulation
-
- Now that you have a well-behaved, segmented MS-DOS application in hand,
- the most painful part of the porting process is behind you. To prepare
- your program for the actual conversion to protected-mode operation, you
- should next isolate all parts of it that are specific to the host
- operating system and encapsulate them inside individual subroutines. The
- objective here is to localize the program's knowledge of the environment
- into small procedures that you can subsequently modify without affecting
- the remainder of the program.
-
- As an example of a program component that needs encapsulation, consider a
- typical call by an MS-DOS application to write a string to the standard
- output device (ordinarily the video display). MS-DOS services are invoked
- with a software interrupt, which occupies less space than a subroutine
- call, so it is common practice to code MS-DOS calls in-line, as shown in
- Figure 4. To facilitate conversion to OS/2, you should replace every
- instance of such a write to a file or device with a call to a small
- subroutine that hides the mechanics of the actual operating system
- function call, as illustrated in Figure 5.
-
- Another candidate for encapsulation, which does not necessarily involve an
- operating system function call, is the application's code to gain access
- to command line parameters, environment block variables, and the name of
- the file it was loaded from. MS-DOS divides this information between the
- program segment prefix and the environment block. Nearly every assembly
- language programmer has evolved his or her own techniques for obtaining
- these parameters when they're needed. My own solution is to write three
- subroutines that return the same information as C's argc, argv, and
- getenv: the number of command line parameters, a pointer to a specific
- parameter, and the string associated with a specific environment block
- variable, respectively.
-
- When you've finished encapsulating the system services, subject your
- program to thorough testing once more under MS-DOS. Since you are still
- working in a familiar milieu and have access to your favorite debugging
- tools, this is the best time to catch any subtle errors you may have
- introduced during the three steps discussed thus far. You'll have more
- trouble trying to uncover them once you move to the OS/2 environment.
-
-
- Conversion
-
- Now each system-dependent procedure you created during the encapsulation
- stage must be rewritten so that your program can execute under OS/2. In
- contrast to MS-DOS functions, which are actuated through software
- interrupts and use registers for parameter passing, OS/2's Application
- Program Interface functions are requested through a "far call" to a named
- entry point. Parameters are passed on the stack, along with the addresses
- of variables or structures that lie within the calling program's data
- segment and will receive any results returned by the function. The status
- of an operation is returned in register AX (0 if the function succeeded,
- an error code otherwise). All other registers are preserved.
-
- Figures 6, 7, and 8 list OS/2 services that are equivalent to selected
- MS-DOS and ROM BIOS Interrupt 21H, Interrupt 10H, and Interrupt 16H calls.
- These tables don't include MS-DOS functions related to file control blocks
- or program segment prefixes because OS/2 does not support either of these
- structures. MS-DOS TSR functions are also omitted. Since OS/2 is a true
- multitasking system, there is no need for a process to terminate in order
- to stay resident while another process is running.
-
- As you examine each encapsulation subroutine, refer to the entry for the
- appropriate function in the MS(R) OS/2 Programmer's Reference manual. All
- you have to do is write code that takes the parameters passed into the
- procedure and pushes them onto the stack in the correct order, calls the
- appropriate OS/2 function, tests the returned status, and loads any
- returned values into the appropriate registers.
-
- While working your way through the program, you must remember to declare
- each OS/2 function used as a "far external." If the function requires any
- additional storage space in the application's data segment, you must also
- create this. For some OS/2 calls (such as DOSOPEN), it will also be
- necessary to push other parameters that had no counterpart under MS-DOS.
- These extra parameters can usually be given reasonable values that will
- make their existence temporarily invisible to the remainder of the
- application.
-
- Figure 9 illustrates the final form of our sample procedure after its
- conversion for OS/2. Note especially the addition of the EXTRN statement
- and the wlen variable, as well as the simulation of the MS-DOS function
- status. This code may not be elegant, but it is the result of only a
- minimum of changes in the source file.
-
- Once you've converted all the encapsulation subroutines and added the
- necessary variables and external references, you may delete the STACK
- segment declaration from the .ASM source file and change the ASSUME
- statement for the SS register to DGROUP. Next, assemble the source file
- into a relocatable .OBJ module file with the Microsoft Macro Assembler in
- the usual manner. In order to transform the .OBJ file into a protected-
- mode .EXE file, however, you will need to supply the linker with two
- additional files: DOSCALLS.LIB and a module definition (.DEF) file for
- your application.
-
- DOSCALLS.LIB is a special type of object module library, which contains
- stub records, called dynamic link reference records, for each OS/2
- function call. Each time the linker finds a stub record that matches an
- EXTRN declaration within the program, it adds the name of the external
- routine to a table in the .EXE file header. When the program is ultimately
- loaded for execution, the table is examined, the necessary external
- procedures are brought into memory from dynamic link (.DLL) libraries (if
- they are not already resident), and the far calls within the program are
- fixed up appropriately.
-
- The .DEF file is just an ASCII text file that can be created with any line
- or screen editor. It contains directives that control various
- characteristics of a protected-mode executable, such as the module name,
- whether the file is an application or a dynamic link library, the
- attributes of each segment within the program, and the size of the stack
- (and for C programs, the size of the local heap). Figure 10 provides a
- very simple sample .DEF file.
-
- You will be pleasantly surprised to find that testing and debugging a
- newly converted protected-mode application is a relatively straightforward
- endeavor. MASM has been upgraded so that it can include line numbers in
- object modules, and the Microsoft OS/2 Software Development Kit includes a
- protected-mode version of CodeView(R), Microsoft's excellent symbolic
- debugger. In addition, the very nature of protected mode means that
- although your programs are just as prone to crashing as before, it's
- fairly difficult to crash the entire system──you won't need that big red
- switch nearly as often as you did with MS-DOS.
-
-
- Optimization
-
- Now that your program is running properly in protected mode, you will be
- anxious to smooth out some of the alterations you made for purposes of
- conversion and introduce various optimizations. There are three obvious
- categories of optimization to consider: taking advantage of the additional
- functionality of the OS/2 calls already being used, exploiting 80286-
- specific machine instructions where appropriate, and revamping the
- application in order to cash in on OS/2's multitasking, timer, virtual
- memory, and inter-process communication facilities.
-
- To make optimum use of the OS/2 function calls, you can quickly modify and
- enhance the subroutines that encapsulate them. For example, the OS/2
- DOSOPEN function allows the programmer to determine separately what action
- will be taken if the named file already exists (open, fail, or truncate to
- zero length) or does not exist (create or fail). Similarly, the OS/2 video
- driver offers performance and a variety of services far superior to the
- screen support under MS-DOS.
-
- In the case of OS/2 functions that are called only once or twice by the
- application, it may be more efficient to throw away the encapsulating
- subroutine and place the function call in-line, thus eliminating the need
- to load parameters into registers outside the subroutine and push them on
- the stack inside.
-
- If you do not intend to use the Family Application Program Interface so
- that your program will also run on 8086/88-based machines, you can add the
- directive .286c to the beginning of the source file and take advantage of
- 80286-specific machine instructions. The most useful of these are the
- instructions that let you shift or rotate by an immediate count other than
- one, a three-operand multiply where one of the operands is an immediate
- (literal) value, and a push immediate value instruction. The last of these
- is particularly handy for setting up OS/2 function calls. For example, in
- Figure 9, the sequence
-
- mov ax,offset DGROUP:wlen
- push ax
-
- could be replaced by the single instruction
-
- push offset DGROUP:wlen
-
- If you wish to restructure your application to take full advantage of
- OS/2's programmable timers, multitasking, virtual memory, inter-process
- communication, dynamic linking, and device monitors you'll have to make a
- close examination of both your application and the OS/2 Application
- Programming Interface. Such study will often pay off in sizable benefits
- in performance, maintainability, and code sharing.
-
- For instance, in many cases, different elements of an application deal
- with I/O devices of vastly different speeds, such as the keyboard, disk,
- and video display. You can both simplify the application and increase its
- performance by separating these elements into threads (subprocesses) that
- execute asynchronously, communicating through shared data structures and
- synchronizing when necessary via semaphores.
-
- As another example, when several applications are closely related and
- contain many identical or highly similar procedures, it makes sense to
- extract those procedures from the individual applications and place them
- in a private dynamic link library. This reduces the size of each
- application's .EXE file, since the dynamic link library routines are
- brought into memory and bound to the application when it's loaded. It
- allows more efficient use of memory, since the code segments in the
- dynamic library can be shared among all of the related applications when
- they are running concurrently. Best of all, using private dynamic link
- libraries vastly simplifies code maintenance, since you can debug or
- improve the routines in the libraries at any time without relinking the
- calling applications, which will automatically benefit from the new code
- the next time they are executed.
-
-
- Figure 1: Skeleton of a properly segmented MS-DOS assembly language
- application being prepared for conversion to OS/2.
-
- name myprog
- page 55,132
- title MYPROG - segmentation skeleton
- ∙ ; miscellaneous equates
- ∙ ; structures, and other
- ∙ ; declarations go here
- DGROUP group _DATA ; 'automatic data group'
-
- _TEXT segment byte public 'CODE' ; all executable code
- ; goes in this segment
-
- assume cs:_TEXT,ds:DGROUP,ss:STACK
-
- main proc far ; the routine that initially
- ∙ ; receives control can be
- ∙ ; called anything ...
- ∙
- mov ax,4c00h ; main routine terminates,
- int 21h ; passing return code=0
-
- main endp
- ∙ ; other routines needed by
- ∙ ; the program go here
- ∙
- _TEXT ends
-
- _DATA segment word public 'DATA' ; all read/write or static
- ; data items in this segment
- ∙
- ∙
- ∙
- _DATA ends
-
- STACK segment para stack 'STACK'
-
- db 256 dup (?)
-
- STACK ends
-
- end main ; declares end of module
- ; and initial entry point
-
-
- Figure 2: These memory models are commonly used in assembly language and
- C programs. Microsoft FORTRAN programs uniformly use the large
- model. Microsoft C also supports a huge model, which allows
- creation of data structures larger than 64Kb.
-
- Model Code Segments Data Segments
-
- Small One One
- Medium Multiple One
- Compact One Multiple
- Large Multiple Multiple
-
-
- Figure 3: Microsoft assembler programs use these naming conventions for the
- standard memory models. Microsoft C programs use a superset of
- these segments and classes.
-
- ╓┌──────────┌─────────────┌───────┌─────────┌──────────┌─────────────────────╖
- Memory Segment Align Combine Class
- Model Name Type Classes Name Group
-
- Small _TEXT byte public CODE
- _DATA word public DATA DGROUP
- STACK para stack STACK DGROUP
-
- Medium module_TEXT byte public CODE
- ∙
- ∙
- _DATA word public DATA DGROUP
- STACK para stack STACK DGROUP
-
- Compact _TEXT byte public CODE
- data para private FAR_DATA
- Memory Segment Align Combine Class
- Model Name Type Classes Name Group
- data para private FAR_DATA
- ∙
- ∙
- _DATA word public DATA DGROUP
- STACK para stack STACK DGROUP
-
- Large module_TEXT byte public CODE
- ∙
- ∙
- data para private FAR_DATA
- ∙
- ∙
- _DATA word public DATA DGROUP
- STACK para stack STACK DGROUP
-
-
-
- Figure 4: This typical in-line code sequence for an MS-DOS service call
- writes a string to the standard output device. Since the
- standard output might be redirected to a file without the
- program's knowledge, it must also check that all of the
- requested characters were actually written. If the returned
- length is less than the requested length, this usually indicates
- that the standard output has been redirected to a disk file and
- the disk is full.
-
- stdin equ 0 ; handle for standard input
- stdout equ 1 ; handle for standard output
- stderr equ 2 ; handle for standard error
- ∙
- ∙
- ∙
- msg db 'This is a sample message'
- msg_len equ $-msg
- ∙
- ∙
- ∙
- mov dx,seg msg ; DS:DX = address of message
- mov ds,dx
- mov dx,offset DGROUP:msg
- mov cx,msg_len ; CX = length of message
- mov bx,stdout ; BX = file or device handle
- mov ah,40h ; AH = function 40h, write
- int 21h ; transfer to MS-DOS
- jc error ; on return, CY=true if error
- cmp ax,msg_len ; were all characters written?
- jne dev_full ; no, output device is full
- ∙
- ∙
- ∙
-
-
- Figure 5: The code in Figure 4 has here been encapsulated. The portion of
- the code that is operating system-dependent has been isolated
- inside a subroutine that is called from other points within the
- application.
-
- stdin equ 0 ; handle for standard input
- stdout equ 1 ; handle for standard output
- stderr equ 2 ; handle for standard error
- ∙
- ∙
- ∙
- msg db 'This is a sample message'
- msg_len equ $-msg
- ∙
- ∙
- ∙
- mov dx,seg msg ; DS:DX = address of message
- mov ds,dx
- mov dx,offset DGROUP:msg
- mov cx,msg_len ; CX = length of message
- mov bx,stdout ; BX = file or device handle
- call write ; perform the write
- jc error ; on return, CY=true if error
- cmp ax,msg_len ; were all characters written?
- jne dev_full ; no, output device is full
- ∙
- ∙
- ∙
- write proc near ; Write to file or device
- ; Call with:
- ; BX = handle
- ; CX = length of data
- ; DS:DX = address of data
- ; Returns:
- ; If successful, Carry clear
- ; and AX = bytes written
- ; If error, Carry set
- ; and AX = error code
-
- mov ah,40h ; function 40h = write
- int 21h ; transfer to MS-DOS
- ret ; return status in CY and AX
-
- write endp
- ∙
- ∙
- ∙
-
-
- Figure 6: Selected MS-DOS function calls and their OS/2 counterparts. Note
- that OS/2 functions are generally much more powerful and flexible
- than their MS-DOS ancestors.
-
- ╓┌───────────┌────────────────────────────────────┌──────────────────────────╖
- MS-DOS
- Interrupt
- 21H
- Function Description OS/2 Function
-
- 00H Terminate process DosExit
- 01H Keyboard input with exho KbdCharIn
- 02H Output character to screen VioWrtTTY
- 03H Auxiliary input DosRead
- 04H Auxiliary output DosWrite
- 05H Printer output DosWrite
- 06H Direct console I/O KbdCharIn,
- VioWrtTTY
- 07H Unfiltered input without echo KbdCharIn
- 08H Keyboard input without echo KbdCharIn
- 09H Output string to screen VioWrtTTY
- 0AH Buffered keyboard input KbdStringIn
- 0BH Get keyboard Status KbdPeek
- MS-DOS
- Interrupt
- 21H
- Function Description OS/2 Function
- 0BH Get keyboard Status KbdPeek
- 0CH Reset buffer and input KbdFlushBuffer,
- KbdCharIn
- 0DH Disk reset DosBufReset
- 0EH Select default disk drive DosSelectDisk
- 19H Get default disk drive DosQCurDisk
- 1BH Get information for default drive DosQFSInfo
- 1CH Get information for selected drive DosQFSInfo
- 2AH Get system date DosGetDateTime
- 2BH Set system date DosSetDateTime
- 2CH Get system time DosGetDateTime
- 2DH Set system time DosSetDateTime
- 2EH Set Verify Switch DosSetVerify
- 30H Get MS-DOS version DosGetVersion
- 36H Get free disk space DosQFSInfo
- 38H Get or select country DosGetCtryInfo
- 39H Create directory DosMkdir
- MS-DOS
- Interrupt
- 21H
- Function Description OS/2 Function
- 39H Create directory DosMkdir
- 3AH Delete directory DosRmdir
- 3BH Select directory DosChdir
- 3CH Create or truncate file DosOpen
- 3DH Open file DosOpen
- 3EH Close file DosClose
- 3FH Read file or device DosRead
- 40H Write file or device DosWrite
- 41H Delete file DosDelete
- 42H Move file pointer DosChgFilePtr
- 43H Get or set file attributes DosQFileMode
- 44H Device driver control (IOCTL) DocDevIOCtl
- 45H Duplicate handle DosDupHandle
- 46H Force duplicate of handle DosDupHandle
- 47H Get current subdirectory DosQCurDir
- 48H Allocate memory block DosAllocSeg
- 49H Release memory block DosFreeSeg
- MS-DOS
- Interrupt
- 21H
- Function Description OS/2 Function
- 49H Release memory block DosFreeSeg
- 4AH Resize memory block DosReAllocSeg
- 4BH Load and execute chils process DosExecPgm
- 4CH Terminate process with return code DosExit
- 4DH Get return code of child process DosCWait
- 4EH Search for first match DosFindFirst
- 4FH Search for next match DosFindNext
- 54H Get verify flag DosQVerify
- 56H Rename file DosMove
- 57H Get or set file date and time DosQFileInfo,
- DosSetFileInfo
- 59H Get extended error information DosErrClass
- 5BH Create unique file DosOpen
- 5CH Lock or unlock file region DosFileLock
-
-
- Figure 7: OS/2 video services include these equivalents to ROM BIOS
- Interrupt 10H video display driver functions used by MS-DOS
- applications.
-
- ROM BIOS
- Interrupt
- 10H
- Function Description OS/2
-
- 00H Select display mode VioSetMode
- 01H Set cursor type VioSetCurType
- 02H Set Cursor position VioSetCurPos
- 03H Get cursor position VioGetCurPos
- 06H Initialize or scroll window up VioScrollUp
- 07H Initialize or scroll window down VioScrollDn
- 08H Read character and attribute VioReadCellStr
- 09H Write character and attribute VioWrtNCell
- 0AH Write character VioWrtNChar
- 0EH Write character in teletype mode VioWrtTTY
- 0FH Get current display mode VioGetMode
- 13H Write string in teletype mode VioWrtTTY
-
-
- Figure 8: OS/2 keyboard services include these equivalents to ROM BIOS
- Interrupt 16H keyboard driver functions used by MS-DOS
- applications.
-
- ROM BIOS
- Interrupt
- 16H
- Function Description OS/2 Function
-
- 00H Read keyboard character KbdCharIn
- 01H Get keyboard status KbdPeek
- 02H Get keyboard flags KbdGetStatus
-
-
- Figure 9: The code in Figure 5 after conversion. The equivalent OS/2
- function call has replaced the MS-DOS function call. Since
- dependence on the operating system has been confined to the
- subroutine by the previous encapsulation step, the surrounding
- program's requests for write operations should run unchanged.
- Note that the OS/2 function had to be declared as an external
- name with the "far" attribute and that a variable named when
- was added to the data segment of the application to receive the
- actual bytes written.
-
- stdin equ 0 ; handle for standard input
- stdout equ 1 ; handle for standard output
- stderr equ 2 ; handle for standard error
-
- extrn DOSWRITE:far
- ∙
- ∙
- ∙
- msg db 'This is a sample message'
- msg_len equ $-msg
- wlen dw ?
- ∙
- ∙
- ∙
- mov dx,seg msg ; DS:DX = address of message
- mov ds,dx
- mov dx,offset DGROUP:msg
- mov cx,msg_len ; CX = length of message
- mov bx,stdout ; BX = file or device handle
- call write ; perform the write
- jc error ; on return, CY=true if error
- cmp ax,msg_len ; were all characters written?
- jne dev_full ; no, output device is full
- ∙
- ∙
- ∙
- write proc near ; Write to file or device
- ; Call with:
- ; BX = handle
- ; CX = length of data
- ; DS:DX = address of data
- ; Returns:
- ; If successful, Carry clear
- ; and AX = bytes written
- ; If error, Carry set
- ; and AX = error code
-
- push bx ; handle
- push ds ; long address of data
- push dx
- push cx ; length of data
- push ds ; address of variable to
- mov ax,offset DGROUP:wlen ; receive length written
- push ax
- call DOSWRITE ; transfer to OS/2
- or ax,ax ; did write succeed?
- jnz writerr ; jump, error was returned
- mov ax,wlen ; no error, OR cleared CY
- ret ; and AX := bytes written
-
- writerr: stc ; write error, return CY set
- ret ; and AX = error number
-
- write endp
- ∙
- ∙
- ∙
-
-
- Figure 10: This simple module definition (.DEF) file defines MYPROG.EXE
- as a protected-mode application rather than a dynamic link
- library and declares that the code segment is movable and
- discardable (a fresh copy can be loaded when needed, so
- swapping is not necessary), the data segment is movable and
- swappable but not discardable (it contains read/write data
- items), and the stack size is 4,096 bytes.
-
- NAME MYPROG
- PROTMODE
- DATA MOVEABLE
- CODE MOVEABLE PURE
- STACKSIZE 4096
-
- ████████████████████████████████████████████████████████████████████████████
-
- Microsoft Windows 2.0: Major Enhancements Offer More Control
-
- ───────────────────────────────────────────────────────────────────────────
- Also see the related article:
- The Beta Version of Windows 2.0 Update
- ───────────────────────────────────────────────────────────────────────────
-
- Michael Geary
-
- Windows Version 2.0 is Microsoft(R)'s first major revision of its windowed
- operating environment. This updated version features the new overlapping-
- window user interface that will be used in the OS/2 presentation manager.
- Unlike the presentation manager, however, Windows 2.0 uses the same
- programming interface as Windows 1.x, albeit with a number of new
- functions and messages, and retains, for the most part, upward
- compatibility with existing Windows applications. Most .EXE files will run
- with no alterations. Yet source code will require some minor revisions to
- iron out several incompatibilities and bring it in line with changes made
- to the application style.
-
- Once you convert to Windows 2.0, you'll be rewarded with a fine assortment
- of new function calls and messages that make Windows a much more flexible,
- capable product for the applications developer.
-
-
- User Interface
-
- The most obvious change in Windows 2.0 is, of course, the new user
- interface. (For a closer look at the features of this interface, see "OS/2
- Windows Presentation Manager: Microsoft Windows on the Future," MSJ Vol. 2,
- No. 2.) For the most part, this won't affect your programs. If an
- application ran in a tiled window, it will now simply use an overlapping
- window instead──and most likely will not even notice the difference.
- However, you should make several changes in the menu structure and
- keyboard accelerators so that your application will conform to the new
- user interface guidelines.
-
- Applications should no longer add menu items to the System menu. At the
- bottom of the File menu, add a separator line and the menu item Exit,
- which will allow the user to exit the application. Then move the
- "About..." item from the System menu to the end of the File menu,
- appending the name of your application here. Help menu items should now be
- flush right in the menu bar, and there is a new HELP dialog option to
- allow this. The Edit menu items remain unchanged, but the keyboard
- accelerators are different and are also shown in a different manner.
- Therefore, you should change your resource file and any special code you
- may have to handle the editing keys. Figure 1 gives the editing key
- assignments.
-
- You can now specify which letter of each menu item can be used in
- combination with the Alt key as a shortcut for making a menu selection.
- Simply insert an ampersand (&) in the item name preceding the shortcut
- letter, and Windows will underline it. For examples of this, see Figure 2,
- which shows a portion of an .RC file in the style recommended for the File
- and Edit menus.
-
- You can also add keyboard mnemonics to your dialog boxes. As with menu
- items, insert an ampersand at the appropriate point in the text of the
- dialog item. For push buttons, radio buttons, and check boxes, you can put
- a shortcut letter right in the name. You can't do this with an edit
- control, of course, but you can instead put a shortcut letter in an
- adjacent static text control. When the user presses the shortcut key, it's
- interpreted as a tab to the next tab stop after the static control.
-
-
- MDI
-
- A major new style recommendation for both Microsoft Windows 2.0 and the
- OS/2 presentation manager is the Multiple Document Interface (MDI). This
- is a method for using child windows in an application that works on
- multiple documents or multiple views of one document. Instead of creating
- a top-level overlapping window for each document or view, the application
- creates a single main window called the application workspace, which
- contains a child window for each document or view. These child windows can
- overlap and can be moved, sized, minimized, and maximized, just like top-
- level windows. The difference, of course, is that all the child windows
- are clipped at the edge of the workspace window.
-
- The active child window has its title bar highlighted along with the
- parent window's title bar. Each child window has a System menu, but none
- can have its own menu bar. The workspace window contains the menu for the
- entire application. If different menus are needed for the different child
- windows, the application should change or add items in the workspace
- window's menu as appropriate.
-
- Any child window can be maximized, that is, enlarged to fill up the
- workspace window, hiding all the other child windows. The user can still
- flip through the different child windows, but each one in turn becomes
- maximized. The workspace window has a pop-up Window menu, which lists all
- the child windows so that any one can be easily selected, whether it's
- visible or not.
-
- For example, a sequencer program for controlling musical instruments via
- the Musical Instrument Digital Interface (MIDI) might have these child
- windows in its application workspace:
-
- ■ conventional music (staff) notation
- ■ piano-roll notation
- ■ a piano keyboard
- ■ synthesizer voice editing
- ■ miscellaneous windows for tempo control, score formatting, key
- signature, MIDI options, and so on.
-
- If these were all independent top-level windows, it would be a real chore
- for the user to switch from the sequencer program to, say, Windows Write.
- There would be a good half-dozen windows cluttering up the screen. With
- the Multiple Document Interface, these windows would all be contained in a
- single application workspace window, and the user could get them all out
- of the way by simply minimizing the application window (that is, by changing
- it to an icon).
-
- Thus, MDI helps organize applications as independent entities, cleanly
- separating the windows of different programs. This will become more
- important as users really start to make use of the multitasking abilities
- of Windows and OS/2.
-
- Although Windows provides several new features that let developers create
- possible MDI applications, such applications require a fair amount of code
- to support MDI. The Windows 2.0 Application Style Guide details what's
- needed in an MDI application.
-
-
- Scheduling and Input
-
- Windows 2.0 has nearly 60 new functions, and several of the old functions
- have been extended with new options. Many of the changes were made to open
- up the Windows architecture, giving the application designer more control.
- Many things that just couldn't be done in earlier versions of Windows are
- now simple function calls.
-
- One change is relatively invisible to applications but quite welcome to
- the developer: pulling down a menu no longer stops scheduling, so
- applications continue to run when a menu is pulled down. But what of those
- cases in which an application really needs to turn off scheduling? For
- example, a forms-design program may want to let the user drag a selection
- rectangle across the entire screen when creating a pop-up window. The
- program can't yield to other applications, because it is temporarily
- writing into their screen space.
-
- In Windows 1.x, there was no way to turn off scheduling and still be able
- to determine when the mouse button was released. You couldn't use
- PeekMessage or GetMessage, because they would yield to other applications.
- Nor would GetKeyState work, because this gives you the key state at the
- time of the last message, not the real-time key state.
-
- Now, both of these methods are available. A new option in the PeekMessage
- function lets you choose not to yield to other applications. Complementing
- this is a new GetAsyncKeyState function, which resembles GetKeyState but
- gives you the real-time key state that is not synchronized with the
- message queue.
-
- A related function for keyboard control is GetKeyboardState, which gives
- you the current state of all 256 virtual keys in a buffer you provide. Its
- inverse, SetKeyboardState, lets you set the current state of the 256
- virtual keys.
-
- Another useful function is GetInputState, which returns a Boolean value
- indicating whether any mouse, keyboard, or timer events are waiting in the
- system queue. And if the old limit of eight messages in your application's
- queue isn't enough, you can call SetMessageQueue to change your queue
- size.
-
- Rounding out the input functions are a couple of handy Get functions.
- GetCapture tells you which window (if any) has captured the mouse with
- SetCapture. GetCaretPos is the complement of SetCaretPos, returning the
- current position of the blinking caret. (Actually, GetCaretPos has always
- been available in Windows──it just wasn't documented until now.)
-
-
- Creating Windows
-
- The CreateWindow function has some new style bits. Since a window can now
- have Minimize and Maximize icons in its title bar, there are style bits
- for these. Also, some of the other style flags have acquired new names. If
- you feel silly using WS_TILEDWINDOW when your window is no longer tiled,
- you can use the more accurate but functionally identical
- WS_OVERLAPPEDWINDOW.
-
- More important, you can now specify the initial location and size of a
- top-level window. You're no longer at the mercy of the tiling algorithm.
- If you want to create two windows side by side, you can do it. You can
- still let Windows assign a default size and location, though. You just use
- the special value CW_USEDEFAULT (which happens to be 0x8000) for the
- position and size parameters to CreateWindow.
-
- Now we come to a slight anomaly. A Windows application certainly isn't
- passing 0x8000 for its position and size. More likely, it's passing zeros
- for all four values, thinking it's creating a tiled window. Windows
- assigns default values in this case, just as it used to. But, if you try
- passing zeros to CreateWindow in your Windows 2.0 application, you'll get
- a very tiny window instead. What's going on here? There certainly aren't
- two different CreateWindow functions.
-
- The key here is that the Resource Compiler sets a version number flag in
- your .EXE file, and Windows uses that to decide how to interpret some
- function parameters, such as the position and size in CreateWindow. If it
- encounters a Windows 1.x application, it ignores the position and size as
- before. But if it sees the 2.0 flag, it pays attention to those parameters.
-
-
- Window Management
-
- The window management capability of Microsoft Windows 2.0 really made me
- happy, since Windows 1.x is frustratingly limiting in this area at times.
- When using Windows 1.x, I knew it was maintaining a linked list of my
- windows, but I couldn't manipulate the list at all. For one of my own
- programs, this was a definite problem. I really needed a way to change the
- order of windows in the list and step through the list backwards and
- forwards as the dialog manager does. Previous versions of Windows just
- don't allow that. The only functions that come close are BringWindowToTop
- and EnumWindows.
-
- Windows 2.0 gives you all kinds of direct access to the window list. Let's
- start with GetWindow and GetNextWindow. These functions take a window
- handle and return your choice of next sibling, previous sibling, first
- sibling, last sibling, parent, or first child. And you can navigate in the
- same way that the dialog manager does. Next, we have SetWindowPos, an all-
- purpose window rearranger. It duplicates some of the functions of
- MoveWindow and ShowWindow but also has the capability to change the
- ordering of the window list. If you have Windows A and B, for example, you
- can place window A after (underneath) window B and even resize and move
- the window at the same time.
-
- If that's not enough, there's SetParent. This lets you change the parent
- of a child window──you can actually move a child window from one parent to
- another.
-
- Next, we have ShowOwnedPopups, which lets you show or hide all the pop-up
- windows associated with a window. Similarly, with ShowScrollBar you can
- show or hide a scroll bar (or both scroll bars) without having to fiddle
- with the scroll bar range.
-
- To round out window management, there are a few more functions.
- GetTopWindow returns the topmost child of a parent window, GetWindowTask
- returns the task handle for a window, and EnumTaskWindows resembles
- EnumWindows but enumerates only windows belonging to a specified task
- handle.
-
-
- Window Drawing
-
- Windows 2.0 contains several new GDI functions. Most important of these
- are EnumMetaFile and PlayMetaFileRecord, which together are the equivalent
- of PlayMetaFile. EnumMetaFile, however, calls a function you provide
- instead of just playing the file. It passes each metafile record to your
- function, where you can do whatever you want with it. If you just want to
- play it, call PlayMetaFileRecord. The new Microsoft Windows Software
- Development Kit documentation gives all the details of the metafile
- format, another case in which Microsoft has really opened up the Windows
- architecture.
-
- The TextOut function now lets you specify how the text will be aligned
- with the x-y starting point. Previously, that point could correspond only
- to the top left corner of the text, but now you have the choice of top
- left, top right, left center, top center, bottom center, or font baseline.
- You can also specify whether TextOut updates GDI's current position. These
- options form the text alignment flag and are part of the new SetTextAlign
- function. (The calling sequence for TextOut itself isn't changed.) There's
- also a GetTextAlign function to retrieve the current text alignment flag.
-
- DrawText has a couple of new options, too. There's a DT_CALCRECT option,
- which updates the formatting rectangle to show how much space DrawText
- actually used. Also, DrawText now interprets the ampersand as a flag to
- underline the next character; if you want to display an ampersand, you
- need to use two of them (&&). You can defeat this action with the
- DT_NOPREFIX option.
-
- For the font mapper, there's a new SetMapperFlags function. This lets you
- alter the font mapper's algorithm, determining how much priority it places
- on matching the aspect ratios of the font and the output device.
-
- There's one new graphics output function, Chord, which is the same as Arc
- except it draws the line segment as well as the ellipse segment, filling
- in the enclosed area with the current brush. For bitmaps, there's
- CreateDiscardableBitmap, which duplicates CreateCompatibleBitmap but makes
- the bitmap discardable.
-
- A new scrolling function, ScrollDC, is similar to ScrollWindow but allows
- more control over the scrolling. Instead of letting the uncovered area
- accumulate in the window's update region, it passes the uncovered region
- and its bounding rectangle back to the caller.
-
- Finally, there are two new region functions. ExcludeUpdateRgn is related
- to ExcludeClipRect, but it removes a window's update region from a
- clipping region. This could be useful when you're doing some drawing in
- advance of a WM_PAINT message and want to draw only in the region that
- will not be taken care of by the WM_PAINT. SetRectRgn is just like
- CreateRectRgn, but instead of allocating a new region, it takes an
- existing region and sets it to the specified rectangle.
-
-
- Dialog Boxes
-
- Have you ever wished you could create a dialog box on the fly at run time
- instead of having to use a dialog template from the resource file?
- Previously you had to give explicit CreateWindow calls for all the child
- windows in the dialog box. There was no way you could just set up a dialog
- template data structure and let it run.
-
- Now two new functions let you do just that. CreateDialogIndirect and
- DialogBoxIndirect correspond to CreateDialog and DialogBox, but instead of
- taking a resource from your .EXE file, they take a pointer to a dialog
- template in memory. (Actually, DialogBoxIndirect takes a global handle,
- and CreateDialogIndirect takes a far pointer.) You are no longer forced to
- choose between using a canned dialog box or using dozens of CreateWindow
- calls.
-
- Two other functions, GetNextDlgGroupItem and GetNextDlgTabItem, let you
- scan items the way the dialog manager does in response to the arrow and
- tab keys. In fact, I'd wager that these functions were there all
- along──because the dialog manager needed them──but were never made publicly
- available. This seems to be yet another example of the architecture being
- opened up to let you get at things previously hidden.
-
-
- Control Windows
-
- There aren't too many changes in the various types of control windows, but
- radio buttons have been given additional functionality. You can now create
- an automatic radio button. This is just like any other radio button,
- except that when clicked it automatically checks itself and unchecks all
- the other radio buttons in its group. There's always been a
- CheckRadioButton function, of course, but this was for dialog boxes only.
- If you wanted to add a group of radio buttons to some other kind of
- window, you had to write your own code to check and uncheck them. Now the
- buttons are smart enough to take care of themselves.
-
- An enhancement of both radio buttons and check boxes gives you the option
- of having label text appear on the left rather than the right side of the
- graphic.
-
-
- Menus
-
- Menus used to be write only, at least in part. For example, given a menu
- position, you could get the corresponding text string with GetMenuString,
- but there was no way to find out what the menu ID was. This made it
- difficult to write helper utilities that would send menu commands to other
- applications, a process that required a menu ID.
-
- Three new functions have overcome this limitation. GetMenuItemID takes a
- menu position and menu handle and returns the menu item ID. GetMenuItemCount
- tells you how many items are in either a top-level or a pop-up menu.
- GetMenuState allows you to determine what the menu flags (MF_ENABLED and so
- on) are for a menu item.
-
- The new LoadMenuIndirect function replicates LoadMenu, but instead of
- loading the menu resource from your .EXE file, it takes a pointer to a
- menu resource in memory. This makes it easy to build a menu on the fly
- without having to go through a whole series of CreateMenu and ChangeMenu
- calls. Like the new indirect dialog box calls, this function provides a
- nice middle ground between a static menu from the .EXE file and all the
- explicit code it would otherwise take to build a menu.
-
-
- Window Hooks
-
- Window hooks are now almost fully documented and functional. They are no
- longer among the most mysterious aspects of Microsoft Windows. The
- SetWindowsHook function can install any of the hook functions listed in
- Figure 3.
-
- Two new functions help support hook functions. DefHookProc lets a hook
- function chain to the previous hook function, so that there can be more
- than one hook of the same type. UnhookWindowsHook removes a hook function
- from a hook chain──something that before could only be done by exiting from
- Windows.
-
- They haven't taken all the mystery out of Windows, though. Along with the
- documented hooks, WINDOWS.H shows three new ones that are undocumented:
- WH_CBT, WH_SYSMSGFILTER, and WH_WINDOWMGR.
-
-
- Memory Management
-
- Windows now comes equipped with a kind of long-term equivalent of
- GlobalLock, called GlobalWire. When you allocate global memory and declare
- it FIXED, Windows allocates the space at the low end of memory to avoid
- fragmentation problems. If you allocate MOVEABLE memory, Windows doesn't
- worry about where it's located; then if you issue a GlobalLock, Windows
- just locks this memory in its current location for the sake of speed. This
- is fine for a short-term lock, but if you lock down memory for a long
- time, the memory space becomes fragmented, causing all sorts of memory
- management problems. GlobalWire prevents fragmentation by allocating a
- MOVEABLE memory block to low memory, as if it had been declared FIXED in
- the first place. GlobalUnwire unlocks memory that had been moved and
- locked with GlobalWire.
-
- SetSwapAreaSize lets you increase the amount of global memory dedicated to
- code rather than data. Normally, Windows allows nearly all global memory
- to be allocated for data with GlobalAlloc, reserving just enough space for
- the largest single code segment from any application or library. Although
- flexible, this arrangement will cause Windows to slow down severely if
- memory gets extremely tight, because it has to keep swapping code segments
- in. With SetSwapAreaSize, you can increase the amount of memory dedicated
- for code. This reduces the amount of global data you can allocate but
- prevents Windows from doing a great deal of low-memory thrashing.
-
- ValidateFreeSpaces is a new debugging function that helps you catch wild
- pointers. In the debugging version of Windows 2.0, the kernel fills all
- free memory with 0x0C. ValidateFreeSpaces scans all the free memory, and
- if it finds any byte that's not equal to 0x00C, it returns that address.
- If you run into a memory-clobbering problem, frequent calls to this
- function could help track it down.
-
-
- Miscellany
-
- Windows now has global atoms. These are like local atoms, but they are
- allocated from the global heap instead of your local heap and are shared
- among all applications. Four new functions serve as the global equivalents
- of the local atom calls: GlobalAddAtom, GlobalDeleteAtom, GlobalFindAtom,
- and GlobalGetAtomName.
-
- A few more miscellaneous new functions: EqualRect simply compares two
- rectangles and returns a Boolean value indicating whether they're equal.
- GetTickCount returns the number of milliseconds since the system was
- started. EnableHardwareInput enables or disables mouse and keyboard input.
- Figure 4 lists a number of new or enhanced messages.
-
-
- Upward Compatibility
-
- Most existing Windows applications will run under Microsoft Windows 2.0,
- but a change made to CreateWindow will affect nearly every
- application──fortunately, this should be the only real incompatibility for
- most programs. The position and size parameters of CreateWindow now have
- different meanings. The parameters (0,0,0,0) will still work, but the
- window will start out as being very tiny. You must either put in the
- CW_USEDEFAULT parameters or give an explicit position and size. If you do
- the latter, don't assume a particular screen size──use GetSystemMetrics.
- (0,0,0,0) on a Windows 1.x application gives the same result as
- CW_USEDEFAULT.
-
- WM_NCxxxxxx messages can cause problems. Most programs pass the nonclient
- messages through to DefWindowProc. If you process any of these messages to
- do any kind of special nonclient area handling, expect some trouble. You
- may also have problems if you have created a pop-up window that the user
- can iconize or zoom, since you probably had to fiddle with the WM_NCxxxxxx
- messages to get this to work. Since your application will be in an
- overlapping window anyway, you can eliminate the problem by just removing
- code (the easiest way to fix a bug).
-
- Any code that depended on the order of the System menu items or their
- exact functions will probably fail, since these have been changed. It's no
- problem if you just added an "About. . ." box to the System menu, although
- you will want to move it to the File menu in order to stay consistent with
- other applications.
-
- The last parameter to PeekMessage used to be called bRemoveMsg and was
- treated like most Booleans──zero meant false and nonzero meant true. If
- your code uses a value other than 0 or 1 for this parameter, you could run
- into trouble, because the parameter has been extended. It's now called
- wRemoveMsg, and the 0 and 1 values work as they always did, but any other
- values will have different meanings. For example, a value of 2 would be
- interpreted as PM_NOYIELD | PM_NOREMOVE instead of PM_REMOVE (TRUE).
- You're not likely to run into this problem, but if you do, it could be
- hard to track down. It's worth checking all your PeekMessage calls. (Do
- this at the same time you convert your CreateWindow calls.)
-
- Since DrawText now attaches a special meaning to the ampersand, this
- function won't do what you want if you have one in the string. You must
- either change it to a double ampersand or add the DT_NOPREFIX option to
- the call.
-
- A few of the things that Windows 1.x let you get away with aren't
- tolerated by Windows 2.0. Strictly speaking, they were errors all
- along──they just weren't caught before. I've run into a couple of these so
- far, but there are probably more.
-
- The one that gave me a little trouble converting to Windows 2.0 was
- GetParent(NULL). It's not legal to pass a NULL window handle to GetParent,
- but earlier versions of Windows accepted it and returned a NULL. Windows
- 2.0 (at least in the debugging version) will reject it.
-
- The other problem occurred in Microsoft's CUBE demo program. This program
- loads a binary resource into memory with LoadResource and then locks it
- with GlobalLock. That's not kosher at all, but somehow it worked before.
- The correct procedure is to use LockResource instead of GlobalLock.
-
-
- New Documentation
-
- The new programmer's documentation is one of the nicest features of
- Windows 2.0. The manuals have been extensively revised, with excellent
- tutorial discussions of such areas as window creation, painting, and
- scrolling. Unlike the old Windows manuals we've all struggled with, the
- new programmer's manual actually explains things. Newcomers to Windows
- programming will now have a much easier time learning the ropes.
-
- Besides containing new explanatory and overview material, the manual has
- been reorganized to list all the functions and messages in alphabetical
- order. No more scratching your head trying to figure out whether SetRect
- is a GDI or User function.
-
-
- TILER
-
- Despite all the enhancements made to Windows 2.0, I really miss one
- feature of the old version: tiled windows. As nice as the new overlapping
- windows are, tiled windows can be very handy too. So, which will it be,
- friends──New Windows or Windows Classic?
-
- Since we're talking about software, not soft drinks, why not have both? To
- this end, I wrote a program called TILER, which does just what its name
- implies──it moves and resizes your windows into a tiled arrangement. TILER
- runs as an icon and gives you two menu items to choose from: Tile Columns
- and Tile Rows (see Figure 5). When you select either of these menu
- options, TILER searches for windows that it can rearrange and then tiles
- them. According to which option you choose, TILER will first try to place
- your windows either in columns (side by side) or in rows (one above the
- other). Figures 6 and 7 show the different ways TILER will rearrange
- overlapping windows.
-
- This demonstrates the greater versatility of Windows 2.0. Windows 1.x
- allowed only a single tiling arrangement, with columns taking precedence.
- There was no way you could arrange your windows in rows (see Figure 8). A
- program such as TILER, which resizes the windows of other applications,
- could not even be written under the old Windows, which strictly prohibited
- an application from controlling the size of any tiled window, even its
- own. Now an application can resize its own windows or those of other
- applications, and no particular window arrangement is mandatory.
-
- A good tiling algorithm can be fairly complex. Because of space
- limitations, the version of TILER given in Figures 9, 10, 11, and 12
- uses a very simplified algorithm that merely divides the screen into equally
- sized rows or columns. In fact, TILER will handle no more than four
- windows, simply leaving any additional windows where they were, hidden
- underneath the tiled windows. Even with this limitation, TILER is fairly
- useful. But you can download a more sophisticated version of TILER, which
- allows more-flexible window placement, from Microsoft's DIAL service.
-
-
- Initializing TILER
-
- TILER's WinMain resembles the main programs of most Windows applications.
- Yet, while most Windows applications allow several copies of themselves to
- be run concurrently, this wouldn't be useful for TILER, so it just beeps
- and exits if you try to run more than one instance of it.
-
- TILER calls its Initialize function to create and display its icon. Then
- it settles into a GetMessage/DispatchMessage loop, which pulls messages
- from the application queue and routes them to TILER's window function,
- TilerWndProc. As in most Windows applications, the real work consists of
- processing these messages.
-
- You may notice there's no TranslateMessage call in TILER's message loop.
- This function is needed only when your application wants to use WM_CHAR
- messages to get ASCII keyboard input. Since TILER does not require
- WM_CHAR, it doesn't have to call TranslateMessage. Similarly, there's no
- TranslateAccelerator call, since TILER doesn't use any keyboard
- accelerators.
-
- TILER's Initialize function registers its window class and creates its
- window using the RegisterClass and CreateWindow functions found in nearly
- every Windows application. It also adds Tile Columns and Tile Rows to
- TILER's System menu. Although it's generally not a good idea for
- applications to add items to their System menus, this is quite appropriate
- for a program like TILER, which has no other menu. (Indeed, since TILER
- runs as an icon, there would be no place to display another menu if it had
- one.)
-
- Initialize performs a little trick to determine how much of the screen the
- tiled windows should take up. We don't want to use the full screen, since
- we have to leave room at the bottom for icons, just as in the previous
- version of Windows. So TILER first creates a normal (noniconic) but
- invisible window and tells Windows to assign a default window size and
- position. The bottom edge of this default window happens to be just where
- we want the bottom of the tiled windows to be, so TILER saves that value
- as the bottom coordinate of TileRect.
-
- Finally, Initialize makes the window visible with the ShowWindow function
- call. The SW_SHOWMINIMIZED parameter to ShowWindow converts the window
- into an icon. (This parameter is the same as SHOW_ICONWINDOW in Windows
- 1.x.)
-
-
- Take a Message
-
- TILER's window function, TilerWndProc, handles only three messages
- specially. It passes all others along to DefWindowProc, which performs
- Windows' default message processing. This lets TilerWndProc concentrate on
- just the things it needs to do.
-
- The most important message for TILER is WM_SYSCOMMAND, since this is sent
- when the user selects an item from the System menu, where TILER's two menu
- items are located. TilerWndProc checks to see whether the message
- originates from a TILER menu item. If so, it processes the message by
- calling the TileWindows function. All other WM_SYSCOMMAND messages are
- passed on to DefWindowProc so Windows can handle the other System menu
- commands.
-
- Another message useful to TILER is WM_QUERYOPEN. Windows sends this
- message whenever the user tries to open the icon, that is, change it back
- into an open window. Since TILER runs only as an icon, it replies to this
- message by returning a 0, so Windows won't open it.
-
- Finally, the WM_DESTROY message tells TILER that the user has selected the
- Close item from the System menu. TilerWndProc then calls PostQuitMessage
- to tell the main application loop to terminate the next time through.
-
-
- Tiling Algorithm
-
- TILER really gets down to business with the TileWindows function. First,
- TileWindows calls CalcWindowRects, which loops through the active windows,
- calculating new sizes and positions for them. CalcWindowRects is the
- actual tiling algorithm. If there's just one window, it gets the entire
- tiling area, of course. Two windows split the area in half, either
- horizontally or vertically, depending on which menu item was selected. If
- there are three windows, the first one gets half the tiling area, and the
- second and third split the remaining half. Four windows get the four
- corners of the tiling area. With more than four windows, TILER just tiles
- the first four and leaves the remaining windows hidden underneath them.
-
- In the more sophisticated version of TILER available on DIAL,
- CalcWindowRects can handle more than four windows and is sensitive to how
- the user had already sized them. For example, if the user makes the
- topmost window larger, the enhanced TILER will shrink the other windows to
- fit instead of arbitrarily splitting the screen in half (see Figure 13).
-
- To keep things simple, TILER attempts to tile only top-level overlapping
- windows that are resizable and are not minimized (iconic) or maximized
- (zoomed). The IsTileable function determines whether a window fits these
- criteria, and CalcWindowRects ignores the window if IsTileable returns the
- value FALSE.
-
- CalcWindowRects is much simpler than it would have been in Windows 1.x,
- because it uses the slick new GetWindow function. Anyone who has suffered
- through having to use the clumsy EnumWindows will appreciate GetWindow,
- which lets you write an ordinary C loop to scan through the window list
- instead of forcing you to use a "call back" function. Figure 14 makes it
- clear just how much easier it is to use GetWindow instead of EnumWindows.
-
- Once the window rectangles are calculated, the TileWindows function moves
- the windows into place with the new SetWindowPos function. SetWindowPos
- also sends the WM_MOVE and WM_SIZE messages to the windows to notify them
- that they have been moved. The old MoveWindow function would have done the
- job here, but SetWindowPos does two things at once: besides setting the
- window positions, it reorders the window list, so the Alt-Tab and Alt-Esc
- key combinations will scan through the tiled windows in a natural order.
-
- TileWindows performs one other function here. It checks each window to see
- whether the CS_HREDRAW or CS_VREDRAW bits are set. If they are, it
- invalidates the window, causing it to be repainted. This is necessary
- because some Windows applications──notably CLOCK──do not properly respond to
- the WM_SIZE message but depend on those redraw flags to generate a
- WM_PAINT message.
-
- Programs like CLOCK that change their displays to fit the window size
- should really respond to the WM_SIZE message and recalculate their
- displays at that point. But CLOCK doesn't. If you feel like experimenting,
- remove the InvalidateRect call from TileWindows, and watch what happens
- when CLOCK is one of the windows you're tiling. You'll get some strange-
- looking results.
-
-
- Building TILER
-
- To compile and link TILER, you will need the Microsoft C Compiler, Version
- 4.0 or 5.0, and the Windows Software Development Kit, Version 2.0. Put the
- TILER.C source code, TILER.RC resource file, TILER.DEF module definition
- file, and TILER make-file (see Figures 9, 10, 11, and 12) in a directory,
- and make up a TILER.ICO icon file with the Icon Editor. Then issue the
- command MAKE TILER to build TILER.EXE.
-
-
- Figure 1: Editing Key Assignments
-
- Function Assignment
-
- Undo Alt+BkSp
- Cut Shift+Del
- Copy Ctrl+Ins
- Paste Shift+Ins
- Clear Del
-
-
- Figure 2: This .RC file establishes the File and Edit menus and their
- accelerators.
-
- MyApp MENU
- BEGIN
- POPUP "&File"
- BEGIN
- MENUITEM "&New" , CMD_NEW
- MENUITEM "&Open..." , CMD_OPEN
- MENUITEM SEPARATOR
- MENUITEM "&Save" , CMD_SAVE
- MENUITEM "Save &As..." , CMD_SAVEAS
- MENUITEM SEPARATOR
- MENUITEM "E&xit" , CMD_EXIT
- MENUITEM "A&bout My Application..." , CMD_ABOUT
- END
-
- POPUP "&Edit"
- BEGIN
- MENUITEM "&Undo\tAlt+BkSp" , CMD_UNDO
- MENUITEM SEPARATOR
- MENUITEM "Cu&t\tShift+Del" , CMD_CUT
- MENUITEM "&Copy\tCtrl+Ins" , CMD_COPY
- MENUITEM "&Paste\tShift+Ins" , CMD_PASTE
- MENUITEM "C&lear\tDel" , CMD_CLEAR
- END
-
- MENUITEM "\a&Help" , CMD_HELP, HELP
-
- END
-
- MyApp ACCELERATORS
- BEGIN
- VK_DELETE, CMD_CUT, VIRTKEY
- VK_DELETE, CMD_CLEAR, VIRTKEY, SHIFT
- VK_INSERT, CMD_COPY, VIRTKEY, CONTROL
- VK_INSERT, CMD_PASTE, VIRTKEY, SHIFT
- VK_BACK, CMD_UNDO, VIRTKEY, ALT
- END
-
-
- Figure 3: Hook Functions Installed by SetWindowsHook
-
- ╓┌───────────────────┌───────────────────────────────────────────────────────╖
- Hook Function Description
-
- WH_MSGFILTER Message filter. Called when a dialog box, message box,
- or menu has received a message but before it has
- processed the message.
-
- WH_KEYBOARD Keyboard filter. Called when the application calls
- GetMessage or PeekMessage and there is any keyboard
- input pending.
-
- WH_GETMESSAGE GetMessage filter. Called from inside the GetMessage
- function before it returns the message to the
- application. (Debugging version of Windows only.)
-
- WH_CALLWNDPROC Window function filter. Called whenever any message is
- sent to a window function. (Debugging version of
- Windows only.)
-
- Hook Function Description
- WH_JOURNALRECORD Journal recording. Called on every event message
- (keyboard, mouse, or timer message) for any
- application.
-
- WH_JOURNALPLAYBACK Journal playback. Plays back messages recorded by
- WH_JOURNALRECORD. With these two hook functions, you
- could write a "ProMouseKey" program for Windows.
-
-
- Figure 4: New Messages in Windows 2.0
-
- ╓┌───────────────────┌───────────────────────────────────────────────────────╖
- New Messages Description
-
- BM_SETSTYLE Alters the style of a button. With this message, you
- can change a button control from its current style to
- any other style. The main purpose of the message is to
- allow the new user interface trick where any push
- button you tab to becomes the default push button.
- New Messages Description
- button you tab to becomes the default push button.
-
- BN_DOUBLECLICKED Notifies the parent window when the user double-clicks
- a button control.
-
- EM_LINEFROMCHAR Returns the line number for a given character position
- in an edit control.
-
- EM_SETWORDBREAK Lets you specify a word-break function to replace the
- default word-break function (which breaks on a blank).
-
- EN_UPDATE Just like EN_CHANGE, except that it is sent before the
- edit control displays the changed text instead of
- after. The text has already been formatted, so you
- can issue an EM_LINELENGTH or EM_GETLINECOUNT and
- resize the edit control accordingly. This makes it
- easy to create an edit control that expands and
- shrinks to fit its text. With this message, I was
- able to replace about 50 lines of very tricky code in
- one of my programs with three lines of utterly simple
- New Messages Description
- one of my programs with three lines of utterly simple
- code.
-
- WM_CHILDACTIVATE Sent to a child window's parent when the SetWindowPos
- function moves the child.
-
- WM_GETMINMAXINFO By default, when you Maximize a window, it fills the
- full screen. By replying to this message, you can
- specify a location and size that your window will
- take on when it's Maximized. You can also specify the
- minimum and maximum tracking size (for when the user
- sizes the window).
-
- WM_NCHITTEST Returns new values to indicate when the mouse is in
- one of the thick borders or in the Minimize or
- Maximize icons.
-
- WM_SHOWWINDOW Several new options to support the new Minimize,
- Maximize, and Restore commands.
-
- New Messages Description
- WM_SYSCOMMAND New option to support the Restore command.
-
-
- Figure 9: Make-File for TILER.EXE
-
- tiler.obj: tiler.c
- msc -AS -Gcsw -Ox -u -W3 -Zdp $*;
-
- tiler.res: tiler.rc tiler.ico
- rc -r tiler.rc
-
- tiler.exe: tiler.obj tiler.res tiler.def
- link4 tiler, tiler/align:16, tiler/map/line, slibw, tiler.def
- rc tiler.res
- mapsym tiler
-
-
- Figure 10: DEF File for TILER.EXE
-
- TILER.DEF
-
- NAME Tiler
-
- DESCRIPTION 'Windows Tiler by Michael Geary'
-
- STUB 'WINSTUB.EXE'
-
- CODE MOVEABLE
- DATA MOVEABLE MULTIPLE
-
- HEAPSIZE 1024
-
- STACKSIZE 4096
-
- EXPORTS
-
-
- Figure 11: RC File for TILER.EXE
-
- TILER.RC
-
- /* Tiler.RC - resource file for TILER.EXE */
-
- #include <style.h>
-
- Tiler! ICON tiler.ico
-
-
- Figure 12: Code for TILER.C
-
- TILER.C
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - *\
- * Tiler.c *
- * Windows 2.0 Tiling Utility *
- * Written by Michael Geary *
- \* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
- #include <windows.h>
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
- #define MAXINT 32767
-
- /* Menu command definitions */
-
- #define CMD_TILECOLS 1
- #define CMD_TILEROWS 2
-
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
- typedef struct {
- HWND hWnd;
- RECT rect;
- } WINDOW;
-
- WINDOW Window[4]; /* Window info for each tiled window */
- int nWindows; /* How many windows we will tile */
-
- HANDLE hInstance; /* Our instance handle */
- int hWndTiler; /* hWnd of our icon */
- RECT TileRect; /* Overall tiling rectangle */
-
- char szClass[] = "Tiler!"; /* Our window class name */
- char szTitle[] = "Tiler"; /* Our window title */
- char szTileCols[] = "&Tile Columns";
- char szTileRows[] = "Tile &Rows";
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
-
- /* Declare full templates for all our functions. This gives us
- * strong type checking on our functions.
- */
-
- void CalcWindowRects( BOOL );
- BOOL Initialize( void );
- BOOL IsTileable( HWND );
- long FAR PASCAL TilerWndProc( HWND, unsigned, WORD, LONG );
- void TileWindows( BOOL );
- int PASCAL WinMain( HANDLE, HANDLE, LPSTR, int );
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
-
- /* Calculate window rectangles for the four topmost tileable windows
- * and set up the Window array for them. This is a simple-minded
- * tiling algorithm that simply divides the tiling area into equal
- * rows and columns.
- */
-
- void CalcWindowRects( bColumns )
- BOOL bColumns;
- {
- HWND hWnd;
- int n;
-
- n = 0;
- for(
- hWnd = GetWindow( hWndTiler, GW_HWNDFIRST );
- hWnd;
- hWnd = GetWindow( hWnd, GW_HWNDNEXT )
- ) {
- if( ! IsTileable( hWnd ) ) continue;
-
- Window[n].hWnd = hWnd;
- CopyRect( &Window[n].rect, &TileRect );
-
- switch( n ) {
- case 0:
- break;
- case 1:
- if( bColumns ) {
- Window[0].rect.right = TileRect.right / 2;
- Window[1].rect.left = Window[0].rect.right - 1;
- } else {
- Window[0].rect.bottom = TileRect.bottom / 2;
- Window[1].rect.top = Window[0].rect.bottom - 1;
- }
- break;
- case 2:
- if( bColumns ) {
- Window[2].rect.left = Window[1].rect.left;
- Window[1].rect.bottom = TileRect.bottom / 2;
- Window[2].rect.top = Window[1].rect.bottom - 1;
- } else {
- Window[2].rect.top = Window[1].rect.top;
- Window[1].rect.right = TileRect.right / 2;
- Window[2].rect.left = Window[1].rect.right - 1;
- }
- break;
- case 3:
- if( bColumns ) {
- Window[3].rect.right = Window[0].rect.right;
- Window[0].rect.bottom = TileRect.bottom / 2;
- Window[3].rect.top = Window[0].rect.bottom - 1;
- } else {
- Window[3].rect.bottom = Window[0].rect.bottom;
- Window[0].rect.right = TileRect.right / 2;
- Window[3].rect.left = Window[0].rect.right - 1;
- }
- break;
- }
- if( ++n == 4 ) break;
- }
- nWindows = n;
- }
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
-
- /* Initialize TILER. Assumes a single instance.
- * Returns TRUE if initialization succeeded, FALSE if failed.
- */
-
- BOOL Initialize()
- {
- WNDCLASS Class; /* Class structure for RegisterClass */
- HMENU hMenu; /* Menu handle of system menu */
-
- /* Register our window class */
- Class.style = 0;
- Class.cbClsExtra = 0;
- Class.cbWndExtra = 0;
- Class.lpfnWndProc = TilerWndProc;
- Class.hInstance = hInstance;
- Class.hIcon = LoadIcon( hInstance, szClass );
- Class.hCursor = LoadCursor( NULL, IDC_ARROW );
- Class.hbrBackground = COLOR_WINDOW + 1;
- Class.lpszMenuName = NULL;
- Class.lpszClassName = szClass;
-
- if( ! RegisterClass( &Class ) ) return FALSE;
-
- /* Create our window but don't iconize it yet */
- hWndTiler = CreateWindow(
- szClass, szTitle,
- WS_OVERLAPPED | WS_SYSMENU,
- CW_USEDEFAULT, 0,
- CW_USEDEFAULT, 0,
- NULL, NULL, hInstance, NULL
- );
- if( ! hWndTiler ) return FALSE;
-
- /* Since we took the default size, the bottom of our window is
- * the base Y coordinate for tiling */
- GetWindowRect( hWndTiler, &TileRect );
- TileRect.top = TileRect.left = -1;
- TileRect.right = GetSystemMetrics( SM_CXSCREEN ) + 1;
-
- /* Add our menu items to the System (Control) menu */
- hMenu = GetSystemMenu( hWndTiler, FALSE);
- ChangeMenu( hMenu, 0, NULL, MAXINT, MF_APPEND | MF_SEPARATOR );
- ChangeMenu( hMenu, 0, szTileCols, CMD_TILECOLS,
- MF_APPEND | MF_STRING );
- ChangeMenu( hMenu, 0, szTileRows, CMD_TILEROWS,
- MF_APPEND | MF_STRING );
-
- /* Now display our window as an icon */
- ShowWindow( hWndTiler, SW_SHOWMINIMIZED );
-
- return TRUE;
- }
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
-
- /* Tells whether a window can be tiled, returns TRUE if so.
- * We will tile only top level, resizable windows that are not
- * minimized and not maximized.
- */
-
- BOOL IsTileable( hWnd )
- HWND hWnd;
- {
- DWORD dwStyle;
-
- dwStyle = GetWindowLong( hWnd, GWL_STYLE );
- return(
- ! ( dwStyle & ( WS_POPUP | WS_MINIMIZE | WS_MAXIMIZE ) ) &&
- ( dwStyle & WS_SIZEBOX ) &&
- ( dwStyle & WS_VISIBLE )
- );
- }
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
-
- /* Tiler's window function.
- */
-
- long FAR PASCAL TilerWndProc( hWnd, wMsg, wParam, lParam )
- HWND hWnd; /* Window handle */
- unsigned wMsg; /* Message number */
- WORD wParam; /* Word parameter for the message */
- LONG lParam; /* Long parameter for the message */
- {
- RECT rect; /* A rectangle */
-
- switch( wMsg ) {
-
- /* Destroy-window message - time to quit the application */
- case WM_DESTROY:
- PostQuitMessage( 0 );
- return 0L;
-
- /* Query open icon message - don't allow icon to be opened! */
- case WM_QUERYOPEN:
- return 0L;
-
- /* System menu command message - process the command if ours */
- case WM_SYSCOMMAND:
- switch( wParam ) {
- case CMD_TILECOLS:
- TileWindows( TRUE );
- return 0L;
- case CMD_TILEROWS:
- TileWindows( FALSE );
- return 0L;
- default:
- break;
- }
- break;
-
- /* For all other messages, we pass them on to DefWindowProc */
- default:
- break;
- }
- return DefWindowProc( hWnd, wMsg, wParam, lParam );
- }
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
- /* This function actually tiles the windows. First, it calls
- * CalcWindowRects to determine the window positions. Then, it
- * loops through the windows and moves them into place.
- */
-
- void TileWindows( bColumns )
- BOOL bColumns; /* TRUE = tile columns; FALSE = rows */
- {
- int n;
- HWND hWnd = NULL;
-
- CalcWindowRects( bColumns ); /* Assign window rectangles */
-
- if( nWindows == 0 ) {
- MessageBox(
- hWndTiler,
- "There are no windows that can be tiled.",
- szTitle,
- MB_OK | MB_ICONEXCLAMATION
- );
- return;
- }
-
- /* Move, size, and reorder windows */
- for( n = 0; n < nWindows; ++n ) {
- SetWindowPos(
- Window[n].hWnd,
- hWnd,
- Window[n].rect.left,
- Window[n].rect.top,
- Window[n].rect.right - Window[n].rect.left,
- Window[n].rect.bottom - Window[n].rect.top,
- SWP_NOACTIVATE
- );
- hWnd = Window[n].hWnd;
- if( GetClassWord( hWnd, GCW_STYLE ) &
- ( CS_HREDRAW | CS_VREDRAW ) )
- /* Make sure it's redrawn */
- InvalidateRect( hWnd, NULL, TRUE );
- }
- }
-
- /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
-
- /* Application main program. */
-
- int PASCAL WinMain( hInst, hPrevInst, lpszCmdLine, nCmdShow )
-
- /* Our instance handle */
- HANDLE hInst;
- /* Previous instance of this application */
- HANDLE hPrevInst;
- /* Pointer to any command line params */
- LPSTR lpszCmdLine;
- /* Parameter to use for first ShowWindow */
- int nCmdShow;
- {
- MSG msg; /* Message structure */
-
- /* Allow only a single instance */
- if( hPrevInst ) {
- MessageBeep( 0 );
- return 0;
- }
-
- /* Save our instance handle in static variable */
- hInstance = hInst;
-
- /* Initialize application, quit if any errors */
- if( ! Initialize() ) return FALSE;
-
- /* Main message processing loop */
- while( GetMessage( &msg, NULL, 0, 0 ) ) {
- DispatchMessage( &msg );
- }
-
- return msg.wParam;
- }
-
-
- Figure 14: EnumWindows vs. GetWindow
-
- /* UsingEnumWindows to loop through the windows */
-
- void LoopThroughWindows()
- {
- FARPROC lpEnumProc;
-
- lpEnumProc = MakeProcInstance( MyEnumProc, hInstance );
- EnumWindows( lpEnumProc );
- FreeProcInstance( MyEnumProc, 0L );
- }
-
- BOOL MyEnumProc( hWnd, lParam )
- HWND hWnd;
- LONG lParam;
- {
- /* do something with hWnd here */
- }
-
- And don't forget to EXPORT MyEnumProc, or Windows will crash!
-
- Now, with GetWindow:
-
- void LoopThroughWindows()
- {
- HWND hWnd;
-
- for(
- hWnd = GetWindow( hWndMyWindow, GW_HWNDFIRST );
- hWnd;
- hWnd = GetWindow( hWnd, GW_HWNDNEXT ))
- {
- /* do something with hWnd here */
- }
- }
-
-
- ───────────────────────────────────────────────────────────────────────────
- The Beta Version of Windows 2.0: An Update
- ───────────────────────────────────────────────────────────────────────────
-
- Just at press time, a new release of Windows 2.0 has come along that forces
- me to (happily) eat a few of my words. One of the joys of working with
- alpha- and beta-test software is that things change right under your feet as
- you're writing about them.
-
- In this case, I don't mind, because Microsoft has put in the GlobalNotify
- function. I call it GlobalNifty, because it lets you implement swappable
- data for your application. You are able to mark global memory as
- discardable, but if you set the GMEM_GNOTIFY flag on it, Windows will call
- your notification function when it wants to discard a memory block. You can
- then write it to disk before it gets discarded, which is much cleaner than
- the kludges we had to use before.
-
- There's also a LocalShrink function, which compacts your application's local
- heap and shrinks down your data segment to match. Before, if your local heap
- grew past the initial allocation determined by the .DEF file, your data
- segment grew as needed, but it never shrank back again; LocalShrink allows
- you to do that.
-
- RegisterWindowDestroy is an interesting new function. It's easy to find out
- when one of your own windows is destroyed, since it receives a WM_DESTROY
- message. You may want to find out when another application's window gets
- destroyed, such as in an add-on program that works with an existing
- application and should exit when that application exits. You can do this
- with a RegisterWindowDestroy call, which sets──actually increments──a
- destroy-notification flag on a window. Then, when that window is destroyed,
- Windows sends a WM_DESTROYOTHERWINDOW to all top-level windows to let
- everyone know about the window being destroyed.
-
- For text output, the new ExtTextOut function is like a TextOut with two
- added parameters: a rectangle that can be used for background erasing, text
- clipping, or both, and an optional array of character horizontal spacing
- values. This array lets you set your own spacing, character by character.
- You can find out the default horizontal spacing for a font with the
- GetCharWidth function, which loads up an integer array of spacing values for
- a range of characters in a font.
-
- GetDCOrg gives you the offset from screen to client coordinates for a
- display context. GetUpdateRgn copies a window's update region into an actual
- hRgn that you can then manipulate. IsZoomed tells you if a window is zoomed.
- (Zoomed is still zoomed, even if they do call it Maximized.) Finally,
- GetNumTasks returns the number of tasks that are currently running.
-
- One of the undocumented SetWindowsHook options I mentioned is now
- documented: WH_SYSMSGFILTER. This is the same as WH_MSGFILTER, except that
- it gives you the DialogBox/MessageBox/menu messages for all windows, not
- just your own window. A warning to those who would use the hook functions:
- when Windows calls your hook function, your DS register is set up for your
- data segment, but you're running on the stack of whatever application
- triggered the hook. Therefore, you will run into the infamous DS != SS
- problem, which means that you will have to compile that part of your
- code with the -Aw switch and cannot call certain C library functions. The
- Windows documentation lists which C run-time functions will and won't work
- in the DS != SS environment. This affects all the Windows hooks except for
- WH_MSGFILTER and WH_KEYBOARD.
-
- Windows 2.0 directly supports LIM/EMS and EEMS memory. These bank-switched
- memory schemes provide one or more "windows" (not related to Windows'
- windows) within the 1Mb real-mode address space. Any area of memory on the
- bank-switched card can be mapped into these memory windows. Windows 2.0
- supports this by assigning each application its own bank(s) of memory. A
- single application doesn't get any more memory than it used to, but you can
- run more applications concurrently.
-
- The larger the banked memory window(s), the better. Windows will keep
- application code and data in banked memory if possible, or else will put as
- much code there as will fit. EEMS memory can do a much better job here than
- straight LIM/EMS memory, because of its larger bank windows.
-
- For the most part, the memory banking is transparent to applications, but
- it's important to note that the banking is done on an application-by-
- application basis, and that code and data belonging to other applications
- can be banked out. This means, for example, that you can't give another
- application a global handle to your default data segment, expecting the
- other app to do a GlobalLock on it. Nor can you attempt to get a window
- function address from another app and call it directly, bypassing
- SendMessage. That never was a good idea, but now it's a sure way to crash.
-
- The only methods for intertask communication that will work under the
- EMS/EEMS environment are the clipboard, a shared library's data segment,
- and the Dynamic Data Exchange (DDE) protocol. Memory allocated with the
- GMEM_DDESHARE global memory flag is treated specially, so any app can get to
- it. You can also force global memory to be allocated from the shared area
- with GMEM_LOWER. Avoid this if possible, since the shared memory area is a
- scarce resource.
-
- There is one new Windows function for EMS support──LimitEMSPages. This
- simply lets you set a limit on the amount of EMS memory that Windows will
- assign to your application. At the cost of some extra disk access (because
- all your code won't fit in memory), you can reduce the total memory used by
- your application. You can still explicitly allocate more memory with
- GlobalAlloc calls, regardless of the limit set with LimitEMSPages.
-
- ████████████████████████████████████████████████████████████████████████████
-
- Keeping Up With the Real World: Speedy Serial I/O Processing
-
- ───────────────────────────────────────────────────────────────────────────
- Also see the following related articles:
- MS-DOS(R) Comm Port Syntax Problems
- Status of the Transmit Shift Register Versus the Transmitter Holding
- XON/XOFF
- RS-232C: A Standard Problem
- Sample Code Fragments for TSRCOMM.ASM
- Combining Interrupts and Polling: An Adventure in Programming
- ───────────────────────────────────────────────────────────────────────────
-
- Ross M. Greenberg
-
- At first glance, writing a serial communications program for an MS-DOS(R)
- system looks fairly straightforward. Most high-level languages can interface
- easily with the BIOS, and the BIOS, by definition, knows how to interface
- with the hardware. After you've designed your program with pop-up windows,
- pull-down help screens, fancy sounds, and flashy colors, you'll probably
- build a stub to test your ideas. You may be quite surprised to learn that
- you're limited to under 1200 baud.
-
- Turning to the back of the nearest BIOS Technical Reference manual, you'll
- soon discover how few of the services that you consider basic are provided:
- there is no XON/XOFF processing, little hardware handshaking is performed,
- the baud rates that are allowed seem arbitrarily low, and, most important of
- all, there is no true asynchronous interrupt processing.
-
- Whether you are writing a modem communications program or just attempting to
- get proper XON/XOFF processing of your serial printer in your favorite word
- processing program, some enhancement of the system services available under
- MS-DOS in the handling of serial communications is necessary. This article
- examines the writing of a replacement handler for Interrupt 0x14, the serial
- communications interrupt.
-
- My design goals arose from real-life needs I encountered when writing a
- terminate-and-stay-resident (TSR) communications program. These goals
- included:
-
- ■ asynchronous interrupt-driven I/O of baud rates up to 38,400
- ■ the ability to handle COM ports 1 through 4
- ■ XON/XOFF flow control, on both incoming and outgoing flow
- ■ proper error detection of parity, overrun, and other errors
- ■ transparency for existing software
- ■ extensibility as requirements changed
-
-
- Getting Started
-
- Due to the inherent limitations of the BIOS serial communications service
- routines, MS-DOS must be bypassed to address the hardware directly. The
- moment you interface directly with the hardware within your computer, you
- lose that most precious of commodities, transportability between machines.
- However, compatibility is less of an issue now than it was in the past, and
- these routines work on all IBM-compatible machines, as well as on many
- clones.
-
- Although the real-world applications for the BIOS routines soon outstrip
- their abilities, the programmers' interface to these routines should be
- preserved: they are more or less a standard, and without them existing
- programs would not work properly. They permit you to initialize the comm
- port, send or receive a byte to or from the comm port, and return the status
- of the comm port. These interfaces are through Interrupt 0x14. (For the rest
- of this article, I'll refer to software interrupts as "calls.") Maintaining
- compatibility with the old interface while expanding above and beyond it led
- to some interesting design decisions.
-
-
- Precursor
-
- Writing code to stay resident after termination can be tricky, and adding in
- asynchronous interrupt processing can make things even trickier. When your
- code is called, the only things you know for sure are the code segment (CS)
- and instruction pointer (IP) of the CPU. Registers, including the Data
- Segment (DS) register, are pointing to unknown values. They must all be
- restored to their original values when you return from the interrupt service
- routine, with the CS and IP, as well as the flags of the CPU, restored
- immediately upon execution of the final IRET instruction.
-
- There are a variety of programming practices to get around this
- inconvenience. One method is addressing all data off the code segment using
- what is called a segment override in the assembler code itself. This,
- however, makes the code a little bigger and more cumbersome and requires a
- couple of extra clock cycles for any instruction that uses segment
- overrides, causing the code to execute more slowly.
-
- The method used in this program differs from that approach: immediately upon
- the service of an interrupt, the data segment is saved on the stack, and the
- code segment itself is used for data addressing. It's faster in the long
- run, both in the design cycle and in the actual execution of the code.
- Although there are portions of the code that deal directly with the hardware
- attached to the bus, whenever possible I've used transportable features of
- MS-DOS, including the MS-DOS calls for intercepting and taking over various
- BIOS interrupts.
-
-
- Overall Structure
-
- The code is structured logically into three areas. The invocation code is
- run only once; it sets up the interrupt structure and initializes data
- areas. The second component handles the actual asynchronous interrupts
- themselves, properly handling the two different types of interrupts: receive
- and transmit interrupts. Error interrupts are not generated in this scheme.
- Also, if a hardware interrupt is generated and does not come from the serial
- device itself, the interrupt is handed off to the original interrupt service
- routine, permitting devices that use the same IRQ (Interrupt Request) to be
- processed properly.
-
- Finally, the third portion of the code is a replacement for the original
- BIOS Interrupt 0x14. It allows for a great deal of extensibility to be
- added, though at the price of having some additional programming overhead.
-
-
- Saving Parameters
-
- One of the requirements is the ability to remove this program from memory
- when desired. This implies that the original parameters of the hardware
- must be saved and then restored upon program termination. Therefore, a
- structure for each comm port has been designed to hold all relevant initial
- data. In addition, this structure contains pointers to the beginning and end
- of the transmit and receive interrupt buffers. All of this data must be
- initialized properly, with interrupts turned off, before the actual BIOS
- interrupt vector is taken over and made to point to our own interrupt
- service routines.
-
- Once all of the parameters are saved and interrupt services are enabled, the
- preamble code executes a DOS Terminate and Stay Resident call, reserving
- memory before exiting to COMMAND.COM.
-
-
- Replacing Services
-
- Interrupt 0x14 is the entry point into the BIOS service routines for serial
- communications. By setting register half AH equal to a given function
- number and register DX to the desired comm port (starting with COM1 equal to
- zero), comm software then generates a software interrupt to invoke the given
- service (see Figure 1). I've extended the BIOS services with a new call,
- AH=4, and used the BX register to allow for some of the special operations
- that the new comm driver permits (see Figure 2).
-
- Although the interface to the user program looks the same, there are
- substantial internal differences. When a call is made to initialize the comm
- port, character ready interrupts are enabled, buffer pointers are reset, and
- then the actual hardware is reset to the proper baud rates, parity, word
- length, etc. When a call is made to return the status of the comm port, the
- comm port status is read and processed to reflect the combined status of
- both the comm port and buffers. This status can be further monitored by
- earlier calls with AH=4. However, since multiple interrupts can be caused by
- errors for each "get status" call you make, you may lose some of the actual
- error information. The method that most software employs to deal with error
- status is preserved, however: when an error is apparent, all errors are set
- in the status word if possible.
-
- When reading a character from the comm port, whether the character you're
- receiving is directly from the communications port or from the interrupt
- buffer will be transparent. One particularly interesting aspect of the code
- is its ability to detect an error, store the apparent character, and return
- an error condition in the high bit of AH.
-
- The code is also able to set a variable to specify a looping constant. This
- permits you to wait in the "get a character" routine until a character
- appears in the receive buffer or until the specified number of loops have
- been used up. As a replacement to the original BIOS routines, this is far
- superior to the constant usually specified. Again, you use the AH=4
- extension to the serial communications command set to set this constant. If
- you want, you can set this looping constant to 1/18 of a second and use the
- hardware timer tick interrupt as a countdown timer for error status.
-
- When transmitting a character, there are two options (set with AH=4):
- transmit the characters with the use of interrupts and the transmit buffer
- or attempt to output each character one at a time. The interrupt-driven
- transmit method is much faster, of course, but it has certain disadvantages.
-
-
- Processing Interrupts
-
- The most important feature of the code is its ability to properly handle
- interrupts that occur very frequently──as often as two interrupts for each
- character transmitted or received. Some background information on how
- interrupts are generated and how they are handled by the hardware should
- help you to understand how this code works.
-
- The chip at the heart of the serial communications board in your MS-DOS
- machine is an 8250. The 8250 requires eight contiguous I/O ports, which are
- used to read and write serial data and to read and set various parameters
- (see Figure 3).
-
- One of these parameters determines under what conditions the 8250 should
- generate an interrupt signal on the bus. Depending on how the parameter is
- set, an interrupt can be generated when a new character has been received,
- when the transmit buffer is empty and ready for a new character, or when an
- error condition has been determined. Initially, the code is set to generate
- an interrupt only on error conditions or when a new character is ready to be
- placed in the buffer.
-
- However, when a character is to be transmitted and a check of the status of
- the 8250 shows it is busy, the possibility of queuing characters up in a
- transmit buffer, which transmits the characters asynchronously to any other
- process, becomes very attractive. This is made possible through one of the
- settings of the BIOS call with AH=4, which allows you to determine if a
- character should be queued into the transmit buffer. Otherwise, the code
- will be set to continue testing for a clear transmit buffer or a time-out
- and will not return until either condition has been satisfied.
-
- When an interrupt is received, it is routed through one of two different
- interrupt vectors, namely the interrupt handler of COM1 or COM2. They merely
- set the correct header and then jump to the actual interrupt handler. The
- interrupt handler first determines which kind of interrupt was generated and
- then jumps to the appropriate routine. There is a priority to each
- interrupt, with error conditions having the highest priority, followed first
- by data becoming apparent at the serial port, then by the transmit buffer
- being empty, and finally by a change in other portions of the modem status.
- Also, if this routine determines that the interrupt was not generated by the
- 8250, it will revector the interrupt to the original interrupt service
- routine.
-
- If the interrupt is determined to be the result of a character being
- received, the next character is read from the physical hardware port and
- stored in a circular buffer. If the circular buffer is more than 80 percent
- full and XON/XOFF processing is turned on, then an XOFF character is
- sent──if one hasn't already been sent. Even at high baud rates, the
- remaining 20 percent of the buffer should be sufficient for the remote end
- to notice the XOFF and cease sending characters before the receive buffer
- runs out of room.
-
- If the remote end does not stop sending characters in response to the XOFF,
- or if XON/XOFF processing was not turned on, characters that were received
- while the buffer was full will be discarded, and a BELL character will be
- sent to the remote machine. If the flag is set to indicate that an XOFF
- character was sent due to a high level of data, then an XON is sent when the
- amount of space left in the buffer increases to some predetermined level
- (set as an equate, all XON/XOFF high- and low-water marks can be modifed
- easily). This should allow for processing of enough characters to prevent
- the remote end from timing out.
-
- Error conditions are generated by a fault in the incoming stream of
- characters, which is detected by the 8250, and include such errors as an
- overrun (the character in the transmit buffer was not read before the next
- character arrived), parity (even, off, mark, or space parity was set, and
- the parity of the incoming character did not match what was expected), or
- framing error (a stop bit was not detected by the hardware when one was
- expected──usually indicative of wrong baud-rate errors).
-
- For each character received, both the error flag and error status of the
- chip are read, if the appropriate flag is set with AH=4. If an error
- condition is determined to exist, a bit is set in the error array.
- Subsequent reads of the character will return the actual character read
- (which may not be accurate), along with an error condition status set in AH
- in the same way that the current BIOS calls will set an error condition. For
- space considerations, the actual cause of the error condition is not saved,
- and the return code is set to indicate that all errors were apparent──a
- brute-force method that saves as much as 4 bits for each byte in your
- receive buffer.
-
- Only one of these errors requires any special handling: that is the
- recognition of a Break having been generated from the remote end of a serial
- connection. By an appropriate setting of the call extensions with AH=4, this
- can either be considered an error condition or can safely be ignored.
-
- When a Transmit Buffer Empty interrupt is processed, a quick check is made
- to determine whether any outstanding characters to be transmitted are in the
- transmit buffer. If there are no outstanding transmit requests, transmit
- interrupts are turned off, the interrupt is reset, and control is returned
- to the interrupted program. If there are outstanding characters to be
- transmitted, the character at the head of the circular transmit buffer is
- sent out the appropriate port; pointers, counters, and interrupts are reset;
- and control is returned to the interrupted program by means of the IRET
- instruction. This approach generates an additional interrupt after the final
- character is transmitted, but this is an acceptable trade-off for the
- advantage that you get from transmit interrupt processing.
-
-
- Programming the 8259
-
- When the 8250 generates an interrupt, it is, in fact, only raising a signal
- on the interrupt line of the bus. Attached directly to these lines is the
- interrupt controller on the motherboard, the 8259. This chip is the
- interface between all interrupt generators and the CPU and is a rather
- intelligent and programmable device. The 8259 distinguishes which interrupt
- has been raised and then does some internal processing to determine what
- actions it should take.
-
- The concept of interrupts is a powerful one, and the 8259 expands on this
- power, allowing the programmer to rearrange certain parameters so as to
- practically rewire the hardware in his or her computer. Unfortunately, much
- of the power of the interrupt system is lost in IBM-compatible machines in
- which the ROM BIOS programs the 8259 and causes it to be reset in a less-
- than-polite way.
-
- One of the rules of the interrupt system is that interrupts usually have to
- be reset. Upon receipt of an interrupt on the bus, the basic architecture
- of the 8259 sets bits on an internal register to correspond to each
- interrupt. Each bit is usually prioritized so that the highest priority
- interrupt (IRQ 0) is bit 0, the next highest is bit 1 (IRQ 1), and so on.
- This priority can be modified as desired and is one of the most powerful
- features of the Interrupt Controller Chip.
-
- Once an interrupt has been received and entered into this register, the
- controller determines if the CPU is already handling an interrupt of equal
- or higher priority. If it is not, and if the CPU has allowed interrupts at
- all with an STI or Enable Interrupts instruction, a CPU interrupt is
- generated, and the actual interrupt vector (0-7) is placed on the data bus.
- Immediately upon receipt of an interrupt, the CPU turns interrupts off
- (which disables the 8259 from raising further interrupts), saves the flags
- and the current instruction address, and jumps to the appropriate vectored
- address as set in the interrupt table. Until the interrupt service routine
- (ISR) executes an STI or an IRET, which resets the flags from the stack,
- interrupts are effectively disabled. Generally, the flag word on the stack
- will have interrupts enabled, since they had to be enabled in order for the
- hardware interrupt to be processed in the first place.
-
- Even when the IRET or the STI instruction clears the way for the CPU to
- process interrupts, the 8259 will not generate any interrupts of lower or
- equal priority until it is told to do so by an End of Interrupt (EOI)
- instruction.
-
- The 8259 allows for a number of different End of Interrupt instructions to
- be sent to it. Among these instruction types is the ability to reset a
- specific interrupt or to automatically allow the 8259 to reset the interrupt
- as soon as it is acknowledged by the CPU. The easiest one to implement is a
- nonspecific End of Interrupt; this tells the 8259 to reset the highest
- priority interrupt, which is usually the one that caused the last interrupt
- to be generated.
-
- However, since the concept of "highest priority level" can be reset from
- inside an ISR, generating a nonspecific EOI would cause the highest
- outstanding interrupt to be reset, which may or may not have been the
- interrupt currently being processed. If an interrupt in the 8259 is reset,
- it is as if that interrupt had never existed, and an important hardware
- interrupt could be missed. So, using nonspecific EOIs effectively disables
- many useful features of the 8259.
-
- Unfortunately, each and every EOI instruction in the IBM(R) ROM BIOS (and in
- most of the IBM-compatible machines as well) is a nonspecific one, making it
- difficult to use some of the power of the 8259. COM2 will almost always have
- priority over COM1, and many interesting features that you may wish to take
- advantage of in the 8259 are off-limits, unless you play a few programming
- tricks.
-
- Interrupts are reenabled with the STI instruction as soon as possible after
- interrupt processing has begun. And, due to the problems outlined above,
- nonspecific EOIs are generated at the end of each interrupt. Why was the EOI
- not generated earlier in the code? Once interrupts are reenabled with the
- STI instruction, only interrupts of a higher priority than the interrupt
- currently being processed will be acted upon. Typically, these interrupts
- are acted upon and returned from rapidly, and while they're being acted on,
- further interrupts of a lower priority will only be queued while waiting for
- an EOI. This is safe, in keeping with limited stack space. However, if an
- EOI is generated within the interrupt routine, and another interrupt routine
- takes over for some undetermined time, it is possible to lose the character
- ready or transmit buffer empty interrupt entirely. By using the belt-and-
- suspenders approach of reenabling interrupts immediately upon the start of
- interrupt processing, disabling interrupts before generating the EOI, and
- then executing the IRET instruction, you won't have to worry about the
- system crashing and the snake eating its own tail.
-
-
- Next Version
-
- What might the next version of this program do that this version doesn't?
- Perhaps being able to turn on and off an internal Xmodem protocol would be
- helpful, enabling the protocol layer of file transfers to be transparent.
- Once we get started with that, there are several protocols we can start
- playing with: Ymodem and Zmodem, X.25, UUCP, and maybe even some of the
- synchronous protocols, which usually require an external clock. Now, if we
- add in device driver I/O and a little bit of modem-dialing ability, the
- possibility of having the disk drive of a remote machine be considered local
- isn't all that farfetched, is it?
-
-
- Figure 1: The Original Interrupt 14 Calling Sequence
-
- Set AH to one of these values, and DX to 0 for COM1 or 1 for
- COM2. The requested value is either sent or received in AL.
-
- AH = 0 Port Initialization
- AL = Parameters to Set, AH will return status as in AH = 3
-
- Bits 7 6 5 │ 4 3 │ 2 │ 1 0
- Baud Rate │ Parity │ No. of Stopbits│ Word Length
- 000 - 110 │ 00 - None │ 0 - 1 Stopbit │ 00 - 5 Bits
- 001 - 150 │ 01 - Odd │ 1 - 2 Stopbits │ 01 - 6 Bits
- 010 - 300 │ 10 - None │ │ 10 - 7 Bits
- 011 - 600 │ 11 - Even │ │ 11 - 8 Bits
- 100 - 1200 │ │ │
- 101 - 2400 │ │ │
- 110 - 4800 │ │ │
- 111 - 9600 │ │ │
-
- AH = 1 Send Character
- AL = Character to Send, AH will return status as in AH = 3
- (Bit 7 set indicates unable to send character)
-
- AH = 2 Get a Character
- AL returns character from comm port within time-out period
- (AH returns status as in AH = 3)
-
- AH = 3 Return Status of Comm Port
- AH =
- Bit 7 = Time-Out (Couldn't Send or Receive Character)
- Bit 6 = Transmitter Shift Register Empty
- Bit 5 = Transmitter Holding Register Empty
- Bit 4 = Break Detected
- Bit 3 = Framing Error Detected
- Bit 2 = Parity Error Detected
- Bit 1 = Overrun Error Detected
- Bit 0 = Data Ready
-
- AL =
- Bit 7 = Data Carrier Detect
- Bit 6 = Ring Indicator
- Bit 5 = Data Set Ready
- Bit 4 = Clear to Send
- Bit 3 = Delta (Change in) DCD
- Bit 2 = Delta RI
- Bit 1 = Delta DSR
- Bit 0 = Delta CTS
-
-
- Figure 2: Additions to the BIOS for Interrupt 14
-
- All of these new functions require that AH = 4. The desired new
- function is loaded into AL. The comm port to operate on is loaded
- into DX as in the original interrupt. Statuses are returned in
- AX, if possible.
-
- AL = 0 Return 0ff0 in AX to determine load status
- AL = 1 Initialize ports, desired mode in CX, clear buffers
- AL = 2 Initialize comm port with baud rate and other
- parameters in CL
-
- Bits 7 6 5 │ 4 3 │ 2 │ 1 0
- Baud Rate │ Parity │ No. of Stopbits │ Word Length
- 001 - 19.2K │ 00 - None │ 0 - 1 Stopbit │ 00 - 5 Bits
- 010 - 38.4K │ 01 - Odd │ 1 - 2 Stopbits │ 01 - 6 Bits
- │ 10 - None │ │ 10 - 7 Bits
- │ 11 - Even │ │ 11 - 8 Bits
-
- AL = 3 Set the time-out value to the number of 1/18ths
- of a second in CX
-
- AL = 4 Clear the Input Buffer (Reset its pointers)
-
- AL = 5 Return count in Input buffer in AX
-
- AL = 6 Clear the Transmit Buffer (Reset its pointers)
-
- AL = 7 Return the count in the Transmit Buffer in AX
-
- AL = 8 Uninstall the TSR driver, then release memory
-
-
- Figure 3: Register Usage in the 8250 UART
-
- All register usage is based on the offset from the base port address.
- For COM1 this is 3F8, for COM2 it is 2F8.
-
- BPA + 0 Data
- When read from, the current character will be returned from the
- Receive Buffer Register.
- When written to, the byte is transferred first to the Transmitter
- Holding Register, then to theTransmitter Shift Register, where
- it is actually transmitted.
-
- If the Divisor Latch Access Bit (DLAB and bit 7 of the Line
- Control Register) is set, then this port is the Least Significant
- byte for the Baud Rate Divisor.
-
- BPA + 1 Interrupt Enable Register
- Bits 7──4 NA
- Bit 3 Enable Modem Status Interrupts
- Bit 2 Enable Receive Line Status Interrupts
- Bit 1 Enable Xmit Holding Register Empty Interrupts
- Bit 0 Enable Data Available Interrupts
-
- If the DLAB is set, then this port is the Most Significant
- byte for the Baud Rate Divisor.
-
- BPA + 2 Interrupt Identification Register
- Bits 7──3 NA
- Bits 2──0 (in descending priority)
- 110 Receiver Line Status Interrupt
- 100 Data Available Interrupt
- 010 Transmitter Holding Buffer Empty Interrupt
- 000 Modem Status Interrupt
- 001 No Interrupt Pending (Not an Interrupt)
-
- BPA + 3 Line Control Register
- Bit 7 Divisor Latch Access Bit
- Bit 6 Set Break (1 turns break on, 0 off)
- Bit 5&4 Parity
- 0 0 Odd Parity
- 0 1 Even Parity
- 1 0 Space (Zero) Parity
- 1 1 Mark (Ones) Parity
- Bit 3 Parity Enable
- Bit 2 Number of Stopbits (0=1 Stopbit, 1=2)
- Bit 1&0 Word Length
- 0 0 5 Bits
- 0 1 6 Bits
- 1 0 7 Bits
- 1 1 8 Bits
-
- BPA + 4 Modem Control Register
- Bit 7──5 NA
- Bit 4 Loop Enable (Turns on Test Conditions)
- Bit 3 OUT2 (Hardware AND to Interrupts)
- Bit 2 External Connection, Not Used
- Bit 1 Request to Send
- Bit 0 Data Terminal Ready
-
- BPA + 5 Line Status Register
- Bit 7 NA
- Bit 6 Transmitter Shift Register Empty
- Bit 5 Transmitter Holding Register Empty
- Bit 4 Break Interrupt Received
- Bit 3 Framing Error
- Bit 2 Parity Error
- Bit 1 Overrun Error
- Bit 0 Data Ready
-
- BPA + 6 Modem Status Register
- Bit 7 Data Carrier Detect
- Bit 6 Ring Indicator
- Bit 5 Data Set Ready
- Bit 4 Clear to Send
- Bit 3 Delta (Change in) DCD
- Bit 2 Delta RI
- Bit 1 Delta DSR
- Bit 0 Delta CTS
-
-
- ───────────────────────────────────────────────────────────────────────────
- MS-DOS(R) Comm Port Syntax Problems
- ───────────────────────────────────────────────────────────────────────────
-
- There are certain problems when dealing with MS-DOS, which are caused by
- confusion in syntax. For example, the first communications port found by
- the POST code in the BIOS is loaded into a set memory location (40:0).
- What is actually stored is the base address of the communications
- port──each 8250 requires eight consecutive port addresses. Subsequent
- communications ports are loaded at 40:2, 40:3, and 40:4. (Yes, MS-DOS
- allows for more than two communications ports.)
-
- The problem arises when the first communications port found by the POST is
- really that of an 8250 set up as a physical COM2. Since this is the first
- communications port found, it will be stored at 40:0, which is the address
- reserved for COM1. However, COM2 generates an IRQ 3, whereas COM1
- generates an IRQ 4. Programs that rely on the data found at 40:0 to
- determine which comm ports exist and what their physical interrupt
- characteristics are may have a problem unless the data stored at that
- address is examined and interpreted, which thereby prohibits device
- independence.
-
- ───────────────────────────────────────────────────────────────────────────
- Status of the Transmit Shift Register Versus the Transmitter Holding
- ───────────────────────────────────────────────────────────────────────────
-
- The 8250 chip has some inherent problems that you should be aware of. One
- of them can cause a routine that you might write to appear to transmit
- characters improperly.
-
- When you direct the 8250 to output a character by writing to the
- Transmitter Holding Buffer (a write instruction out to the Base Port of
- the 8250), it immediately transfers that byte to the Transmitter Shift
- Register (an internal register within the 8250) and starts transmitting
- it a serial bit at a time.
-
- However, the moment the Transmitter Holding Buffer is empty, an interrupt
- will be generated to indicate that the transmitter buffer is empty. If you
- have no bytes left to transmit, you might think this is a safe time to
- reset the 8250 as required. There is a good chance, however, that the last
- character is still being transmitted. If you were to reset the 8250, you
- might cause the last character to be transmitted improperly, if it is
- transmitted at all.
-
- It's a good idea to check the status of both the Holding Buffer and the
- Shift Register before you consider a transmission to be over.
- Fortunately, the folks at National Semiconductor have made this very easy:
- when you check the status of the Transmitter Shift Register, you're
- actually checking the status of both registers. The secret of getting the
- highest output rate in the safest manner is for you to check only the
- status of the Transmitter Holding Register when you are attempting to
- output characters, but to check the status of the Transmitter Shift
- Register to determine when the last character has been sent.
-
- ───────────────────────────────────────────────────────────────────────────
- XON/XOFF
- ───────────────────────────────────────────────────────────────────────────
-
- The XON/XOFF protocol, which is often used during non-error-checking
- transmissions of ASCII files, permits the receiving station in a
- communications session to indicate to the transmitting station to stop
- sending characters until it is told to continue.
-
- Even though the code allows you to define a buffer as large as you want
- (which will receive characters as long as there is space in the buffer and
- hardware interrupts), you'll eventually run across the problem of receiving
- characters more rapidly than you can handle them. An example of this is when
- the buffer fills up and you now must write the captured data to disk. This
- takes a finite amount of time, during which interrupts may be turned off for
- more than one character receive interval. If you don't use some protocol,
- you'll lose characters.
-
- The XON/XOFF protocol defines two characters, a Ctrl-S for XOFF and a Ctrl-Q
- for XON, that tell the transmitting side to cease transmission by sending an
- XOFF and to restart transmission by sending an XON.
-
- Life is seldom as easy as it might seem, and this protocol is no exception.
- First, it has been very loosely implemented in many current programs to
- determine that the XON character may be any character. If you design a
- protocol that waits only for the XON character, you might wait a long, long
- time. (The terminal driver in your MS-DOS machine has been loosely
- implemented with an XOFF protocol. Try hitting a Ctrl-S during a long
- listing and then any other character-generating key as if it were an XON
- character.)
-
- Second, this protocol is rather timing-sensitive, especially with the
- increasing popularity of some of the packet-switching networks. This is
- because when the buffer is 80 percent full, an XOFF character is sent to the
- transmitter. It may take a few seconds for the character to be received and
- acted on by the transmitter and for the transmitter to cease sending
- characters. Until the transmitter does receive the XOFF, it is merrily
- dumping characters out at character rates that can be as high as almost
- 1,000 cps. You might have to leave as much as 5 seconds of character space
- in your buffer to handle situations like this. A 5Kb high-water mark is
- probably too much in most situations, however, so the code lets you modify
- both the buffer size and the high-water mark as you require.
-
- You can only send one XOFF character, even if it looks as if the remote end
- is ignoring your first XOFF request. There is always the possibility that
- the packet-switch network has delayed your first XOFF and that subsequent
- XOFFs may actually be taken as XON characters.
-
- ───────────────────────────────────────────────────────────────────────────
- RS-232C: A Standard Problem
- ───────────────────────────────────────────────────────────────────────────
-
- RS-232C is the shorthand abbreviation for Recommended Standard 232,
- Revision C, from the engineering department of the Electronic Industries
- Association. Its full name is Interface between Data Terminal Equipment
- and Data Communication Equipment Employing Serial Binary Data Interchange.
-
- RS-232C defines the wiring at each end of a connection between two
- devices, such as a modem (the Data Communication Equipment, or DCE) and
- your computer (the Data Terminal Equipment, or DTE). It requires that the
- DCE have a female connection and the DTE a male connection. RS-232C
- defines the supposed purpose of 25 different wires, which pins they should
- terminate on and even what voltage levels and current drains and loads are
- allowed, but that's about it (see Figure A).
-
- So, if you have two computers that you want to hook up to one another,
- which one is the DTE and which is the DCE? That's not defined, but the
- standard seems to work only if there is one of each, since it defines the
- "direction" of the flow of information──whether a particular signal is
- generated from the DTE or the DCE. Therefore, you must define one or the
- other as DTE and fool the other side into thinking that it too is a DTE.
-
- You can do this with a null-modem cable; think of it as almost having two
- modems in between two machines. Of the 25 defined pins/wires, only 11 of
- them are used for asynchronous communications──the others are used for
- synchronous communications or reserved for future use (see Figure B).
-
- Usually, for each transmitted data or control function, there is a
- complementary receive data or control function. By properly wiring these
- two mutually exclusive sets of wires, you have designed a cable that can
- trick the other side into thinking it is a modem (see Figure C for a
- complete picture).
-
- Of course, when one of the serial cards mounted within your machine does
- not even follow what little defined standard there is, we've all got a
- problem. It shouldn't surprise you to find that many of the serial cards
- out there are following their own versions of RS-232C. Remember that the R
- stands for "Recommended."
-
-
- Figure A: Pin Usage Table
-
- ╓┌──────┌─────────┌──────────────────────────────────────────────────────────╖
- Pin Circuit Name
-
- 1 AA Protective Ground
- 2 BA Transmitted Data
- 3 BB Received Data
- Pin Circuit Name
- 3 BB Received Data
- 4 CA Request to Send
- 5 CB Clear to Send
- 6 CC Data Set Ready
- 7 AB Signal Ground or Common
- 8 CF Received Line Signal Detect (Data Carrier Detect)
- 9 - Reserved/Unassigned
- 10 - Reserved/Unassigned
- 11 - Reserved/Unassigned
- 12 SCF Secondary Received Line Signal Detect
- 13 SCB Secondary Clear to Send
- 14 SBA Secondary Transmitted Data
- 15 DB Transmission Signal Element Timing
- 16 SBB Secondary Received Data
- 17 DD Receiver Element Timing
- 18 - Reserved/Unassigned
- 19 SCA Secondary Request to Send
- 20 CD Data Terminal Ready (DTR)
- 21 CG Signal Quality Detector
- 22 CE Ring Indicator
- Pin Circuit Name
- 22 CE Ring Indicator
- 23 CH/CI Data Signal Rate Detector
- 24 DA Transmit Signal Element Timing
- 25 - Reserved/Unassigned
-
-
- Figure B: Asynch Set of Pins
-
- Pin Abbrev. Name Direction Function
-
- 1 - Protective Ground None Ground
- 2 TD Transmitted Data to DCE Outbound Data
- 3 RD Received Data to DTE Incoming Data
- 4 RTS Request to Send to DCE DTE wants to send data
- 5 CTS Clear to Send to DTE DCE okays send request
- 6 DSR Data Set Ready to DTE DCE is ready to communicate
- 7 - Signal Common None Common Ground
- 8 DCD Data Carrier Detect to DTE Carrier (Linkup) between
- DTE/DCE
- 20 DTR Data Terminal Ready to DCE Enable DCE (say DTE is ready)
- 22 RI Ring Indicator to DTE Phone is ringing
- 23 DSRD Data Sig. Rate Undefined Complicated, but used to
- Detect negotiate data rates between
- DCE and DTE
-
-
- Figure C:
-
- │ │
- ┌─┴┐ Pin No. Pin No. ┌┴─┐
- │ └─────┐ ┌──┐ ┌─────┘ │
- │TD 2╞═══──────────┘┌─│──────────═══╡2 TD│
- │RD 3╞═══───────────┘ └──────────═══╡3 RD│
- │RTS 4╞═══──┬─────────┐ ┌──═══╡4 RTS│
- │CTS 5╞═══──┘ ┌──│────────┴──═══╡5 CTS│
- │DSP 6╞═══────┐ │ │ ┌──═══╡6 DSP│
- │COMMON 7╞═══─────────│───────────│──═══╡7 COMMON│
- │DCD 8╞═══────│────┘ └────────│──═══╡8 DCD│
- │DTR 20╞═══──┐ ├────────────────│──═══╡20 DTR│
- │RI 22╞═══──│─┘ ├──═══╡22 RI│
- │ ┌─────┘ └──────────────────┘ └─────┐ │
- └─┬┘ └┬─┘
- │ │
-
-
- ───────────────────────────────────────────────────────────────────────────
- Sample Code Fragments for TSRCOMM.ASM
- ───────────────────────────────────────────────────────────────────────────
-
- ;; TSRCOMM.ASM - Written by Ross M. Greenberg
- ;; Sample Code Fragments Follow
- ∙
- ∙
- ∙
- P1_INLEN equ 400h ; Define sizes of input and output
- P2_INLEN equ 400h ; buffers. High-water and low-water
- P1_OUTLEN equ 400h ; marks are a direct reflection of
- P2_OUTLEN equ 400h ; these values
-
- ;; Be careful with these settings if yours have different lengths for
- ;; each of the COM_INBUF's: these only play off COM1_INBUF
-
- HIGH_MARK equ (P1_INLEN/10 * 8) ; send XOFF when buffer is 80% full
- LOW_MARK equ (P1_INLEN/10 * 2) ; send XON when buffer is 20% full
-
- ;; Definitions of all 8250 Registers and individual bit meanings
-
- DATA equ 0h ; DATA I/O is from the base
-
- IER equ 1h ; Interrupt Enable Register
- IER_RDA equ 1h ; Received Data Available int bit
- IER_THRE equ 2h ; Transmitter Hold Reg. Empty int bit
- IER_RLS equ 4h ; Receive Line Status int bit
- IER_MS equ 8h ; Modem Status int bit
-
- IIR equ 2 ; Interrupt Identification Register
- IIR_RLS equ 5h ; *equal* to if Receiver Line Status int
- IIR_RDA equ 4h ; *equal* to if character ready
- IIR_THRE equ 2h ; *equal* to if TX Buffer empty
- IIR_PEND equ 1h ; set to zero if any interrupt pending
- IIR_MS equ 0h ; *equal* to if Modem Status int
-
- LCR equ 3h ; Line Control Register
- LCR_WLS0 equ 0h ; Word Length Select Bit 0
- LCR_WLS1 equ 1h ; Word Length Select Bit 1
- LCR_STOPBITS equ 4h ; number of stop bits
- LCR_PARITYEN equ 8h ; Enable Parity (see SPARITY & EPARITY)
- LCR_EPARITY equ 10h ; Even Parity Bit
- LCR_SPARITY equ 20h ; Stick Parity
- LCR_BREAK equ 40h ; set if break is desired
- LCR_DLAB equ 80h ; Divisor Latch Access Bit
-
- MCR equ 4h ; Modem Control Register
- MCR_DTR equ 1h ; Data Terminal Ready
- MCR_RTS equ 2h ; Request To Send
- MCR_OUT1 equ 4h ; Output 1 (nobody uses this!)
- MCR_OUT2 equ 8h ; Out 2 (Sneaky Int enable)
- MCR_LOOP equ 10h ; Loopback enable
-
- LSR equ 5 ; Line Status Register
- LSR_DATA equ 1h ; Data Ready Bit
- LSR_OVERRUN equ 2h ; Overrun Error Bit
- LSR_PARITY equ 4h ; Parity Error Bit
- LSR_FRAMING equ 8h ; Framing Error Bit
- LSR_BREAK equ 10h ; Break Detect (sometimes an error!)
- LSR_THRE equ 20h ; Transmit Holding Register Empty
- LSR_TSRE equ 40h ; Transmit Shift Register Empty
-
- MSR equ 6 ; Modem Status Register
- MSR_DEL_CTS equ 1h ; Delta Clear To Send
- MSR_DEL_DSR equ 2h ; Delta Data Set Ready
- MSR_EDGE_RI equ 4h ; Trailing Edge of Ring Indicator
- MSR_DEL_SIGD equ 8h ; Delta Receive Line Signal Detect
- MSR_CTS equ 10h ; Clear To Send
- MSR_DSR equ 20h ; Data Set Ready
- MSR_RI equ 40h ; Ring Indicator - during entire ring
- MSR_DCD equ 80h ; Data Carrier Detect - on-line
- CTRL_PORT equ 20h ; The 8259 lives here
- INT_EOI equ 20h ; The End of Interrupt reset value
- INT_MASK_PORT equ 21h ; The mask for the 8259 lives here
- COM1_MASK equ 0efh ;
- COM2_MASK equ 0f7h ;
- INTNO_COM1 equ 0ch ; The physical interrupt number for
- INTNO_COM2 equ 0bh ; Comm1 and for Comm2
-
- PORT1 equ 3f8h ; Physical ports where Comm1 & Comm2
- PORT2 equ 2f8h ; should be. See sidebar 1 for info.
- ∙
- ∙
- ∙
- ;; COMM PORT BLOCK (CPB)
- ;; Comm Port Block defines information unique for each comm port
- ;; and includes information such as what the original interrupt
- ;; vector pointed to, which parameters are set, etc.
- CPB struc
-
- cpb_base dw ? ; base port of comm port (2F8, 3F8)
- cpb_nint_off dw ? ; new interrupt offset address
- cpb_pic_mask db ? ; mask for enabling ints from 8259
- cpb_int_no db ? ; what interrupt we are
- cpb_mode dw ? ; whatever modes we have turned on
- cpb_timeout dw ? ; time-out value off timer tick
- cpb_in_xoff dw 0 ; true if we output an XOFF
- cpb_out_xoff dw 0 ; true if an XOFF was sent to us
- cpb_inbase dw ? ; start of input buffer
- cpb_inlen dw ? ; length of input buffer allocated
- cpb_inhead dw ? ; pointer to next input char location
- cpb_intail dw ? ; pointer to last input char location
- cpb_incnt dw 0 ; count of how many inp chars outstanding
- cpb_inerrors dw ? ; pointer to the error bits
- cpb_outbase dw ? ; start of output header
- cpb_outlen dw ? ; total length of output buffer allocated
- cpb_outhead dw ? ; pointer to next output char location
- cpb_outtail dw ? ; pointer to last output char location
- cpb_outcnt dw 0 ; count of how many outp chars outstanding
- cpb_outend dw ? ; ptr to the end of the output buffer
- cpb_tx_stat dw 0 ; set to no interrupts turned on
- cpb_oint_add dw ? ; original int offset:segment order
- dw ?
- CPB ends
- ∙
- ∙
- ∙
- ;; HANDSHAKING OPTIONS
- BREAK_IS_ERROR_OPTION equ 01h ; Set these bits (OR combination)
- DSR_INPUT_OPTION equ 02h ; to set desired options. Then
- DCD_INPUT_OPTION equ 04h ; set CX to this value, DX to the
- CTS_OUTPUT_OPTION equ 08h ; comm port desired, and generate
- XOFF_INPUT_OPTION equ 10h ; an INT0x14 with AH=4 and AL=1
- XOFF_OUTPUT_OPTION equ 20h
- DTR_OPTION equ 40h
- XON_IS_ANY_OPTION equ 80h
- TX_INTS_ON_OPTION equ 100h
- ;; Masm has a 256-byte static initialization limit. NC is shorter than
- ;; NO_CHARS....
- NC equ 0
-
- ;; WARNING! Do not move the error array away from its approriate
- ;; error array, or you'll probably crash at some point!
-
- com1_inbuf db P1_INLEN dup(0) ; Allocate space for
- com1_errs db (P1_INLEN/8) + 1 dup(0) ; both comm ports:
- com2_inbuf db P2_INLEN dup(0) ; input, error and
- com2_errs db (P2_INLEN/8) + 1 dup(0) ; output buffer
- com1_outbuf db P1_OUTLEN dup(0)
- com2_outbuf db P2_OUTLEN dup(0)
-
- ;; CPB1 and CPB2
- ;; Allocate space and initialize COMM PORT BLOCKS for com1 and com2
- cpb1 CPB <PORT1,offset com1_isr,COM1_MASK,INTNO_COM1,DEF_FLAGS,
- DEF_T_OUT,NO_XON,NO_XON,offset com1_inbuf,P1_INLEN,
- offset com1_inbuf,offset com1_inbuf,NC,offset com1_errs,
- offset com1_outbuf,P1_OUTLEN,offset com1_outbuf,
- offset com1_outbuf,NC>
- cpb2 CPB <PORT2,offset com2_isr,COM2_MASK,INTNO_COM2,DEF_FLAGS,
- DEF_T_OUT,NO_XON,NO_XON,offset com2_inbuf,P2_INLEN,
- offset com2_inbuf,offset com2_inbuf,NC,offset com2_errs,
- offset com2_outbuf,P2_OUTLEN,offset com2_outbuf,
- offset com2_outbuf,NC>
- ∙
- ∙
- ∙
- xmit_int proc near
- cmp cpb_outcnt[si], 0 ; any work to do?
- jnz xmit1 ; yep!
- call till_clear ; wait for transmitter to clear
- call tx_off ; turn xmit interrupts off
- ret ; and return
- xmit1:
- mov bx, cpb_outtail[si] ; get next character to xmit
- inc bx ; now point right on it
- cmp bx, cpb_outend[si] ; cmp to the end
- jnz xmit2 ; if not past the end, jump
- mov bx, cpb_outbase[si] ; past end, reset to the head
- xmit2:
- cli ; don't get interrupted now
- dec cpb_outcnt[si] ; decr. count of chars to go
- mov cpb_outtail[si], bx ; save a pointer to next char
- mov al, [bx] ; get the character in al
- cmp cpb_outcnt[si], 0 ; any work left to do?
- jnz out_it ; yep!
- call till_clear ; wait for transmitter to clear
- call tx_off ; turn xmit interrupts off
- out_it:
- call out_char ; output the character
- ret
- xmit_int endp
- ∙
- ∙
- ∙
- interrupt_table label word ;
- dw offset ms_int ; modem status int (ret only)
- dw offset xmit_int ; transmitter int
- dw offset rda_int ; character ready int
- dw offset err_int ; receiver line error int
- ∙
- ∙
- ∙
- com1_isr proc far ; Entry point for comm1 interrupts
- push ax
- lea ax, cpb1
- jmp short common_isr
- com1_isr endp
-
- com2_isr proc far ; Entry point for comm2 interrupts
- push ax
- lea ax, cpb2
- jmp short common_isr
- com2_isr endp
- ∙
- ∙
- ∙
- common_isr proc near ; IRQ's come to here. If not
- push bx ; ours jump to old int vector
- push cx
- push dx
- push si
- push di
- push ds
- push cs ; addressing off ds as cs
- pop ds ; makes it easier to think
- mov si, ax ; move in the cpb
- mov di, cpb_base[si] ; get the base port
-
- lea dx, IIR[di] ; and then the int ID Reg
- in al, dx ; get the interrupt type
- test al, IIR_PEND ; is there a pending int?
- jz is_mine ; interrupt on *this* chip!
- other_int:
- cli ; turn off ints since this
- ; is non-reentrant
- mov ax, cpb_oint_add[di] ; grab the old int out of
- mov old_int, ax ; the structure
- mov ax, cpb_oint_add[di][2]
- mov old_int[2], ax
- pop ds ; pop everything back
- pop di
- pop si
- pop dx
- pop cx
- pop bx
- pop ax
- jmp dword ptr cs:[old_int] ; jump to whatever was there
- polling_loop: ; this is required to be sure
- ; we haven't lost any ints
- lea dx, IIR[di] ; load the int ID Reg
- in al, dx ;
- test al, IIR_PEND ; is there a pending int?
- jnz clear ; no. time to return
-
- is_mine:
- and ax, 06h
- mov bx, ax
- mov bx, interrupt_table[bx]
- push di ; save di for polling loop
- call bx
- pop di
- jmp polling_loop ; time to check for more work
- clear: ; no further int processing
- pop ds ; pop everything back
- pop di
- pop si
- pop dx
- pop cx
- pop bx
- cli ; interrupts off, then reset
- mov al, INT_EOI ; interrupts on the 8259
- out CTRL_PORT, al
- no_eoi:
- pop ax
- iret ; iret will turn ints back on
- common_isr endp
- ∙
- ∙
- ∙
- int14_functions label word ;
- dw offset init14 ; initialize the port
- dw offset send14 ; send the character in al
- dw offset get14 ; return next char in al,
- ; status in ah
- dw offset stat14 ; get serial status,return in ax
- dw offset newfuncs14 ; all of the new functions
-
- get_baud proc near ; AX is the offset,
- shl ax, 1 ; divisor returned in AX
- push bx ; make the table offset
- mov bx, ax
- mov ax, baudrate_table[bx] ; and get the divisor
- pop bx
- ret
- get_baud endp
-
- baudrate_table label word
- dw 1047 ; 110 baud
- dw 768 ; 150 baud
- dw 384 ; 300 baud
-
- dw 192 ; 600 baud
- dw 96 ; 1200 baud
- dw 48 ; 2400 baud
- dw 24 ; 4800 baud
- dw 12 ; 9600 baud
- dw 6 ; 19200 baud
- dw 3 ; 38400 baud
- ∙
- ∙
- ∙
- funcs_table label word ;
- dw offset new00 ; Each function corresponds to
- dw offset new01 ; AL value used for
- dw offset new02 ; subfunction
- dw offset new03 ;
- dw offset new04 ;
- dw offset new05 ;
- dw offset new06 ;
- dw offset new07 ;
- dw offset new08 ;
-
- newfuncs14 proc near
- cmp al, 08h ; out of bounds?
- jle dispatch ; no
- mov ax, 0ffffh ; yes, error code
- ret
- dispatch:
- call get_cpb ; get si to point to proper cpb
- mov di, cpb_base[si] ; point the ports!
- xor bx, bx
- mov bl, al
- shl bx, 1
- mov bx, funcs_table[bx]
- call bx
- ret
- newfuncs14 endp
-
- new00 proc near
- mov ax, special_return_value
- ret
- new00 endp
-
- new01 proc near
- mov cpb_mode[si], cx ; move the new mode in
- call init_buffers ; and reset the pointers
- ret
- new01 endp
-
- new02 proc near
- lea dx, LCR[di] ; get the Latch
- in al, dx
- or al, LCR_DLAB ; turn on the divisor
- out dx, al ; in the chip
- push cx
- mov ax, cx
- and ax, 00e0h ; only the highest three bits
- mov cl, 5
- shr ax, cl
- add ax, 7 ; let offset start at 8 (19200)
- call get_baud ; then get the correct divisor
- ; allows higher than 9600
- pop cx
- lea dx, DATA[di] ; get the base address
- out dx, ax ; output the whole word
- lea dx, LCR[di] ; get the Latch
- mov al, cl ; get the other parameters and
- and al, 01fh ; mask only parity, stop bits,
- ; word length
- out dx, al ; set the params
- ret
- new02 endp
-
- new03 proc near
- mov cpb_timeout[si], cx
- ret
- new03 endp
-
- new04 proc near
- cli
- mov cpb_incnt[si], NO_CHARS
- mov ax, cpb_inbase[si]
- mov cpb_inhead[si], ax
- mov cpb_intail[si], ax
- sti
- ret
- new04 endp
-
- new05 proc near
- mov ax, cpb_incnt[si]
- ret
- new05 endp
-
- new06 proc near
- cli
- mov cpb_outcnt[si], NO_CHARS
- mov ax, cpb_outbase[si]
- mov cpb_outhead[si], ax
- mov cpb_outtail[si], ax
- sti
- ret
- new06 endp
-
- new07 proc near
- mov ax, cpb_outcnt[si]
- ret
- new07 endp
-
- new08 proc near
- mov si, offset cpb1 ; set up for port1
- cmp cpb_oint_add[si], 0 ; com port installed?
- jz new0801 ; no
- call unset_up ; and kill this comm port
- new0801:
- mov si, offset cpb2 ; set up for port2
- cmp cpb_oint_add[si], 0 ; com port installed?
- jz new0802 ; no
- call unset_up ; and kill this comm port
- new0802:
- cli
- mov dx, old_int14
- mov al, 014h
- push ds
- mov ds, old_int14[2]
- DOSINT 25h ; reset the serial port int
- pop ds
- mov dx, orig_timer
- mov al, TIMER_TICK_INT_NO
- push ds
- mov ds, orig_timer[2]
- DOSINT 25h ; reset the timer_tick int
- pop ds
- push cs
- pop es ; free up our own memory
- DOSINT 49h ; the environment
- sti
- ret
- new08 endp
- ∙
- ∙
- ∙
-
-
- ───────────────────────────────────────────────────────────────────────────
- Combining Interrupts and Polling: An Adventure in Programming
- ───────────────────────────────────────────────────────────────────────────
-
- One of the things that made writing a program such as TSRCOMM so
- interesting was the amount of time spent thinking, "But, that should have
- worked!"
-
- When initially contemplated, TSRCOMM was going to be fully interrupt
- driven; no polling was to be included at all. A good look at the
- hardware, confirmed by experimental programming, showed that it was
- possible for a "dual interrupt" system, one capable of handling transmit
- buffer empty and data interrupts, to actually lose an interrupt while
- processing another.
-
- Therefore, much to my chagrin, you'll find a polling loop right in the
- middle of the COMMON_ISR interrupt routine. The loop causes the code to
- continually cycle once an interrupt is generated until, by polling the
- hardware and reading the appropriate ports, it determines that there is no
- more work to be done during this cycle.
-
- Fortunately, once compiled, this ugliness is rarely noticed.
-
- ████████████████████████████████████████████████████████████████████████████
-
- BLOWUP: A Windows Utility for Viewing and Manipulating Bitmaps
-
- Charles Petzold
-
- When you first start programming for Microsoft(R) Windows, the concept of a
- bitmap seems rather easy to grasp. A bitmap is simply a collection of bits
- that correspond directly to the scan lines and pixels of a rectangular
- display image.
-
- However, working with bitmaps is not quite as easy as understanding them.
- Even the seemingly simple chore of displaying a bitmap on the client area
- of a window can be puzzling. If you search for a Windows function by the
- name of DrawBitmap, you do so in vain. The function does not exist, and by
- the time you learn that a "memory display context" is necessary for the
- job, you may be nostalgically recalling how easy and simple life was when
- you programmed strictly in character mode.
-
- BLOWUP is a program designed to dispel some of the mysteries of bitmaps
- and memory display contexts and provide a little fun as well. With BLOWUP
- you can use the mouse to transfer almost anything that appears on the
- screen to the Windows clipboard in a bitmap format. BLOWUP is also a
- "clipboard viewer" of bitmaps and will display any bitmap currently in the
- clipboard. As its name implies, BLOWUP will blow up or shrink down the
- bitmap to fit its own client area. Figure 1 shows BLOWUP's client area and
- the contents of the clipboard after BLOWUP has been used to capture part
- of the MS-DOS(R) Executive window.
-
- Once the bitmap is in the clipboard, you can transfer the image to any
- Windows program that can handle bitmaps. (Note that some programs, such as
- WRITE, PAINT, and CARDFILE, will convert a color bitmap to monochrome,
- while others, such as Aldus Corp.'s PageMaker(R), will not accept color
- bitmaps at all.)
-
- Although the image in the clipboard is the actual size of the image as it
- appeared on the screen, you can use BLOWUP a second time to transfer the
- expanded image in BLOWUP's client area to the clipboard. BLOWUP thus
- becomes a tool to manipulate bitmaps manually──you can crop them, turn them
- upside down or left to right, shrink them, and blow them up. The BLOWUP
- source code can also help you learn how to create bitmaps, display them on
- the screen, write a clipboard viewer, transfer bitmaps between your
- program and the clipboard, "capture" and track the mouse, and you can even
- draw outside the client area of your window.
-
- With an installed Microsoft(R) Windows Software Development Kit and
- Microsoft(R) C Compiler Version 4.0, you can create BLOWUP.EXE by
- executing:
-
- MAKE BLOWUP
-
-
- Capturing Images
-
- BLOWUP requires a mouse. The program is simple to use once you get the
- hang of it, but here are the precise instructions:
-
- 1. The first step is to click the mouse in BLOWUP's client area. The
- cursor will then be changed to a crosshair.
-
- 2. Move the mouse cursor to the upper left-hand corner of the screen area
- you want to capture. Press on the mouse button.
-
- 3. Drag the mouse (with the button depressed) to the lower right-hand
- corner of the screen area you want to capture. As you move the mouse,
- BLOWUP briefly displays the blocked-out image in reverse video. Release
- the mouse button. The image will be transferred to the clipboard and
- then to BLOWUP's client area.
-
- If you block out the image starting at the lower left-hand corner, the
- image will then be turned upside down, both in the clipboard and in
- BLOWUP's client area. Starting at the upper right-hand corner will flip
- it around the vertical axis; starting from the lower right-hand corner
- will flip it both horizontally and vertically.
-
-
- Capturing the Mouse
-
- The first problem BLOWUP has to solve is how to track the mouse when it is
- outside of BLOWUP's client area. Normally a Windows program receives mouse
- messages only when the mouse cursor is positioned over the program's
- window. In order to get around this restriction, BLOWUP uses a technique
- called "capturing the mouse," which requires one Windows call:
-
- SetCapture (hWnd) ;
-
- After this call, all mouse movement messages and all mouse button messages
- will be directed to the window function whose handle is hWnd. The mouse
- capture is ended by a call to ReleaseCapture.
-
- The lParam parameter that accompanies mouse messages contains the current
- position of the mouse relative to the upper left-hand corner of the
- window's client area. The x coordinate is in the low word of lParam, and
- the y coordinate is in the high word. After you capture the mouse, one or
- both of these coordinates will be negative if the mouse is to the left of
- or above your window's client area.
-
- BLOWUP maintains two Boolean variables, bCapturing and bBlocking, which
- are used to keep track of what mode it is in while it is processing the
- WM_LBUTTONDOWN, WM_LBUTTONUP, and WM_MOUSEMOVE messages. During the first
- button-down message, BLOWUP sets the bCapturing flag, captures the mouse,
- and displays the crosshair mouse cursor. On the second button-down
- message, BLOWUP sets the bBlocking flag and saves the position of the
- mouse in org ("origin"), a structure of type POINT. That's the first
- corner of the rectangle you'll be blocking out.
-
- When BLOWUP receives a WM_MOUSEMOVE message while bBlocking is set, it
- retrieves the new mouse position in another POINT structure called len
- ("length") and subtracts from that the origin. The result is the size of
- the image measured from the origin. (The values can be negative.) BLOWUP
- also calls the routine InvertBlock twice, once to invert the colors of the
- blocked-out image, and the second time in order to change it back to
- normal.
-
-
- Painting
-
- The inversion of the blocked-out rectangle may shock some Windows
- programmers because it requires that BLOWUP paint outside its client area,
- which is normally impossible. When a Windows program prepares for
- painting, it obtains a handle to a display context using the GetDC or
- BeginPaint function. This display context allows a program to paint only
- within its client area.
-
- But BLOWUP's InvertBlock routines use the less common CreateDC function to
- obtain a display context. This function is normally used for obtaining a
- display context for a printer, but it can also obtain a display context
- for the entire screen. The first parameter is the string "DISPLAY", and
- the other three parameters are set to NULL. The origin of this display
- context is the upper left-hand corner of the display.
-
- Because the coordinates of the org structure are relative to the upper
- left-hand corner of BLOWUP's client area, the point must be converted to
- screen coordinates by using the ClientToScreen function first. BLOWUP
- then calls PatBlt ("pattern block transfer") with a raster operation code
- of DSTINVERT to invert the rectangular area of the display.
-
- Painting outside a program's window is generally not a polite thing to do,
- which is why BLOWUP restores the area right away with another call to
- InvertBlock. This is a good compromise──it gives you a visual indication of
- the area you're blocking out with BLOWUP, but it doesn't permanently
- affect the windows of other programs. And because the MS-DOS version of
- Windows is nonpreemptive, there is positively no chance of anything
- changing the screen between the two consecutive calls to InvertBlock.
-
-
- Creating the Bitmap
-
- When BLOWUP receives a WM_LBUTTONDOWN message while the bBlocking flag is
- set, it must create a bitmap containing the blocked-out image and transfer
- it to the clipboard. This job requires us to approach that strange animal
- called the "memory display context" and attempt to make friends with it.
-
- A display context is a data structure that describes a physical display
- device, such as a screen or a printer. When a program obtains a handle to
- a display context, the program is also getting permission to paint on the
- device.
-
- A memory display context is very similar to a normal display context
- except that the display "surface" is a block of memory. When you first
- obtain a handle to a memory display context through the CreateCompatibleDC
- function, this display surface is very small──exactly one pixel wide, one
- pixel high, and monochrome, which is not very useful. What you must do
- before actually working with a memory display context is select a bitmap
- into it by using SelectObject.
-
- When you do this, the bitmap becomes the display surface of the memory
- display context. The upper left-hand corner of the bitmap corresponds to
- the display context coordinate of (0,0). Any image previously in the
- bitmap becomes part of the memory display context's display surface. Any
- drawing you do on that memory display context is actually performed on the
- bitmap. When you delete the memory display context, you are then left with
- a bitmap containing everything that you painted on it.
-
- There are a variety of methods for creating bitmaps; the one BLOWUP uses
- is the CreateCompatibleBitmap call. This function creates a bitmap with
- the same number of color planes and color bits per pixel as the display
- context specified in the first parameter of the function──which in this
- case is the video display context. In BLOWUP, the height and width of the
- bitmap are the absolute values of the two sizes in the len structure. The
- bitmap itself is uninitialized, which means that it contains random data.
-
- Once BLOWUP selects the bitmap into the memory display context, all it
- needs to do is call StretchBlt to transfer the blocked-out area of the
- screen display context to the memory display context. The image winds up
- in the bitmap.
-
- Because the bitmap is the same height and width as the blocked-out display
- area, you may be wondering why I use StretchBlt rather than BitBlt for
- this job. BitBlt would work fine if you only blocked out the image
- starting at the upper left-hand corner; otherwise BLOWUP must flip the
- image, and BitBlt cannot flip images whereas StretchBlt can.
-
-
- Copying the Bitmap
-
- Getting the bitmap into the clipboard is the easy part of the job. BLOWUP
- simply calls OpenClipboard, EmptyClipboard, SetClipboardData, and
- CloseClipboard, and it's done.
-
- Normally a program would delete a bitmap after it has finished using it.
- However, when a bitmap is transferred to the clipboard, the bitmap becomes
- the responsibility of Windows itself. Windows will delete the bitmap the
- next time a program transfers something else to the clipboard.
-
-
- Becoming a Viewer
-
- BLOWUP is also a clipboard viewer, which means that it is notified
- whenever the contents of the clipboard change and will display the
- clipboard contents. Unlike the indiscriminate CLIPBRD.EXE program that
- comes packaged with Microsoft Windows, BLOWUP is very selective──it will
- only display bitmaps and will ignore the other clipboard formats.
-
- Becoming a clipboard viewer requires very little overhead. BLOWUP first
- makes a call to SetClipboardViewer while it is processing the WM_CREATE
- message. SetClipboardViewer returns the window handle of the previous
- clipboard viewer. BLOWUP then saves this as a static variable that is
- called hWndNext. Saving this window handle is very important──Windows
- maintains only one window handle as a "current clipboard viewer," and it
- relies on other programs to participate in the "clipboard viewer chain."
-
- Here's how it works: when the contents of the clipboard change, Windows
- sends the current clipboard viewer (the most recent program to register
- itself as a clipboard viewer) a WM_DRAWCLIPBOARD message. The program that
- receives this message is responsible for sending the message to the next
- clipboard viewer, which is the window whose handle was returned from the
- SetClipboardViewer call. Every clipboard viewer will at that point see the
- message WM_DRAWCLIPBOARD as it ripples down the clipboard viewer chain.
-
- When a program wants to get out of the clipboard viewer chain, it must
- call ChangeClipboardChain. (BLOWUP does this during the processing of the
- WM_DESTROY message right before it terminates.) Notice that the two
- parameters to this function are the program's own window handle and
- hWndNext. Windows will then respond by sending to the current clipboard
- viewer a WM_CHANGECBCHAIN message with wParam equal to the window handle
- of the program removing itself from the chain and the low word of lParam
- equal to the window handle of the next clipboard viewer──the same two
- values that are passed along to ChangeClipboardChain. If BLOWUP finds that
- the clipboard viewer that is removing itself from the chain is the next
- clipboard viewer after BLOWUP──in which case hWndNext will be equal to
- wParam──then it must change its own stored value of hWndNext in order to
- effectively skip over the departing program in future WM_DRAWCLIPBOARD
- calls.
-
- That's the extent of the overhead required for being a clipboard viewer.
- Of course, a clipboard viewer will also do a little something while
- processing the WM_DRAWCLIPBOARD message to display the new clipboard
- contents. After sending the WM_DRAWCLIPBOARD message down the clipboard
- viewer chain, BLOWUP simply invalidates its own client area. This is what
- causes Windows to generate a WM_PAINT message for BLOWUP to recreate its
- client area.
-
-
- Processing the Message
-
- When BLOWUP receives a WM_PAINT message, it must get the bitmap out of the
- clipboard and display it in its client area. BLOWUP opens the clipboard
- with a call to OpenClipboard and then uses GetClipboardData to get a
- handle to the bitmap currently stored in the clipboard. It's possible that
- the clipboard will not contain a bitmap, in which case the function will
- return NULL. If that's the case, BLOWUP closes the clipboard and leaves
- its client area unpainted.
-
- If BLOWUP is successful in getting a bitmap from the clipboard, it creates
- another memory display context. Earlier, when it was processing the
- WM_LBUTTONUP message, BLOWUP used SelectObject to select an uninitialized
- bitmap into a memory display context in order to transfer an image from
- the screen to the bitmap. Now while it is processing WM_PAINT, BLOWUP
- selects the clipboard's bitmap into the memory display context in order to
- transfer the bitmap image to its own client area.
-
- BLOWUP gets the dimensions of the bitmap by a call to GetObject. This
- function copies information about the bitmap into the bm variable, which
- is a structure of type BITMAP. At this point, the only things that we are
- interested in are the bm.bmWidth (the width) and bm.bmHeight (the height)
- fields of the bitmap.
-
- It is the StretchBlt call again that does the transfer, this time from
- the memory display context to the screen display context. The source width
- and height are the dimensions of the bitmap; the destination width and
- height are the dimensions of BLOWUP's client area. StretchBlt then blows
- up or shrinks the image appropriately. StretchBlt takes a little time to
- execute, particularly when working with large display surfaces. It is for
- this reason that BLOWUP sets the cursor to the IDC_WAIT cursor, also known
- as the hourglass, during the transfer.
-
-
- The Stretching Mode
-
- You'll notice in BLOWUP.C that immediately before the call to StretchBlt,
- there is a call to SetStretchBltMode.The "stretching mode" is an attribute
- of the display context and governs what Windows does when it uses
- StretchBlt to reduce the size of an image. You might think that Windows
- simply throws away rows and columns of pixels, but that's not what happens
- in the default case.
-
- By default, Windows uses a stretching mode, which is called BLACKONWHITE.
- When an image is reduced, Windows combines rows or columns of pixels by
- performing a logical AND operation between adjacent bits. A particular
- pixel ends up as white (a 1-bit) only if all the pixels being combined
- into that pixel are also white. This will preserve a black image on a
- white background.
-
- The opposite of this is the stretching mode which is called WHITEONBLACK.
- Windows here performs a logical OR operation between adjacent pixels.
- The result is a black pixel (a 0-bit) only if all the adjacent pixels are
- also black. This preserves white images on a black background.
-
- These two stretching modes can have some strange effects. For instance,
- suppose you had a display context that was colored gray, which for most
- display adapters is accomplished by alternating black and white pixels. If
- you used StretchBlt to reduce the size of the image in half, then the
- default stretching mode, known as BLACKONWHITE, would cause the result to
- be entirely black. The WHITEONBLACK stretching mode would make it white.
-
- The third option is COLORONCOLOR. This is the stretching mode that causes
- Windows to do what you might have thought it was doing anyway──throw away
- rows and columns of pixels. A gray image is copied as gray. This is
- probably the best approach when a program has no knowledge of the
- type of image it will be dealing with. (Alternatively, you could add a
- menu option in BLOWUP to allow changing the stretching mode yourself.)
-
-
- A Few Restrictions
-
- I've been careful to state that BLOWUP can capture almost anything you see
- on the screen. If a pull-down menu is displayed, clicking the mouse in
- BLOWUP's client area to start the capture will shift the input focus to
- BLOWUP and the menu will go away. If a system modal dialog box (a dialog
- box that does not allow the user to switch to another application) is
- displayed, then the dialog box must be exited before you can use BLOWUP.
-
- Microsoft Windows Version 1.04 can't create bitmaps larger than 64Kb. If,
- however, you attempt to capture an entire 8-color high-resolution 84Kb EGA
- screen to the clipboard, the call CreateCompatibleBitmap will fail, and
- BLOWUP will beep to indicate the problem.
-
- You'll be pleased to know that Windows Version 2.0 doesn't have this
- limitation, and Figure 5 proves it. Figure 5 may look like a typical
- Windows 2.0 desktop, but it's actually BLOWUP maximized to use the full
- screen after capturing an 84Kb screen image.
-
-
- Figure 2: BLOWUP make-file
-
- blowup.obj : blowup.c
- cl -c -d -D LINT_ARGS -Gsw -Os -W2 -Zdp blowup.c
-
- blowup.exe : blowup.obj blowup.def
- link4 blowup, /align:16, /map, /line, slibw, blowup
- mapsym blowup
-
-
- Figure 3: BLOWUP.C source code file
-
- /* BLOWUP.C -- Capture Screen Image to Clipboard by Charles Petzold */
-
- #include <windows.h>
- #include <stdlib.h>
-
- long FAR PASCAL WndProc (HWND, unsigned, WORD, LONG) ;
-
- int PASCAL WinMain (hInstance, hPrevInstance, lpszCmdLine, nCmdShow)
- HANDLE hInstance, hPrevInstance ;
- LPSTR lpszCmdLine ;
- int nCmdShow ;
- {
- static char szAppName [] = "Blowup" ;
- HWND hWnd ;
- MSG msg ;
- WNDCLASS wndclass ;
-
- if (!hPrevInstance)
- {
- wndclass.style = CS_HREDRAW | CS_VREDRAW ;
- wndclass.lpfnWndProc = WndProc ;
- wndclass.cbClsExtra = 0 ;
- wndclass.cbWndExtra = 0 ;
- wndclass.hInstance = hInstance ;
- wndclass.hIcon = NULL ;
- wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
- wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
- wndclass.lpszMenuName = NULL ;
- wndclass.lpszClassName = szAppName ;
-
- if (!RegisterClass (&wndclass))
- return FALSE ;
- }
-
- hWnd = CreateWindow (szAppName, szAppName,
- WS_TILEDWINDOW,
- 0, 0, 0, 0,
- NULL, NULL, hInstance, NULL) ;
-
- ShowWindow (hWnd, nCmdShow) ;
- UpdateWindow (hWnd) ;
-
- while (GetMessage (&msg, NULL, 0, 0))
- {
- TranslateMessage (&msg) ;
- DispatchMessage (&msg) ;
- }
- return msg.wParam ;
- }
-
- void InvertBlock (hWnd, org, len)
- HWND hWnd ;
- POINT org, len ;
- {
- HDC hDC ;
-
- hDC = CreateDC ("DISPLAY", NULL, NULL, NULL) ;
- ClientToScreen (hWnd, &org) ;
- PatBlt (hDC, org.x, org.y, len.x, len.y, DSTINVERT) ;
- DeleteDC (hDC) ;
- }
-
- long FAR PASCAL WndProc (hWnd, iMessage, wParam, lParam)
- HWND hWnd ;
- unsigned iMessage ;
- WORD wParam ;
- LONG lParam ;
- {
- static BOOL bCapturing, bBlocking ;
- static HWND hWndNext ;
- static POINT org, len ;
- static short xClient, yClient ;
- BITMAP bm ;
- HDC hDC, hMemDC ;
- HBITMAP hBitmap ;
- PAINTSTRUCT ps ;
-
- switch (iMessage)
- {
- case WM_CREATE:
- hWndNext = SetClipboardViewer (hWnd) ;
- break ;
-
- case WM_SIZE:
- xClient = LOWORD (lParam) ;
- yClient = HIWORD (lParam) ;
- break ;
-
- case WM_LBUTTONDOWN:
- if (!bCapturing)
- {
- bCapturing = TRUE ;
- SetCapture (hWnd) ;
- SetCursor (LoadCursor (NULL, IDC_CROSS)) ;
- }
- else if (!bBlocking)
- {
- bBlocking = TRUE ;
- org = MAKEPOINT (lParam) ;
- }
- break ;
-
- case WM_MOUSEMOVE:
- if (bCapturing)
- SetCursor (LoadCursor (NULL, IDC_CROSS)) ;
-
- if (bBlocking)
- {
- len = MAKEPOINT (lParam) ;
- len.x -= org.x ;
- len.y -= org.y ;
-
- InvertBlock (hWnd, org, len) ;
- InvertBlock (hWnd, org, len) ;
- }
- break ;
-
- case WM_LBUTTONUP:
- if (!bBlocking)
- break ;
-
- bCapturing = bBlocking = FALSE ;
- SetCursor (LoadCursor (NULL, IDC_ARROW)) ;
- ReleaseCapture () ;
-
- if (len.x == 0 || len.y == 0)
- break ;
-
- hDC = GetDC (hWnd) ;
- hMemDC = CreateCompatibleDC (hDC) ;
- hBitmap = CreateCompatibleBitmap (hDC,
- abs (len.x), abs (len.y)) ;
- if (hBitmap)
- {
- SelectObject (hMemDC, hBitmap) ;
- StretchBlt (hMemDC, 0, 0, abs (len.x), abs (len.y),
- hDC, org.x, org.y, len.x, len.y, SRCCOPY) ;
-
- OpenClipboard (hWnd) ;
- EmptyClipboard () ;
- SetClipboardData (CF_BITMAP, hBitmap) ;
- CloseClipboard () ;
- }
- else
- MessageBeep (0) ;
-
- DeleteDC (hMemDC) ;
- ReleaseDC (hWnd, hDC) ;
- break ;
-
- case WM_PAINT:
- hDC = BeginPaint (hWnd, &ps) ;
- OpenClipboard (hWnd) ;
-
- if (hBitmap = GetClipboardData (CF_BITMAP))
- {
- SetCursor (LoadCursor (NULL, IDC_WAIT)) ;
-
- hMemDC = CreateCompatibleDC (hDC) ;
- SelectObject (hMemDC, hBitmap) ;
- GetObject (hBitmap, sizeof (BITMAP), (LPSTR) &bm) ;
-
- SetStretchBltMode (hDC, COLORONCOLOR) ;
- StretchBlt (hDC, 0, 0, xClient, yClient,
- hMemDC, 0, 0, bm.bmWidth, bm.bmHeight,
- SRCCOPY) ;
-
- SetCursor (LoadCursor (NULL, IDC_ARROW)) ;
- DeleteDC (hMemDC) ;
- }
- CloseClipboard () ;
- EndPaint (hWnd, &ps) ;
- break ;
-
- case WM_DRAWCLIPBOARD :
- if (hWndNext)
- SendMessage (hWndNext, iMessage, wParam, lParam) ;
-
- InvalidateRect (hWnd, NULL, TRUE) ;
- break;
-
- case WM_CHANGECBCHAIN :
- if (wParam == hWndNext)
- hWndNext = LOWORD (lParam) ;
-
- else if (hWndNext)
- SendMessage (hWndNext, iMessage, wParam, lParam) ;
- break ;
-
- case WM_DESTROY:
- ChangeClipboardChain (hWnd, hWndNext) ;
- PostQuitMessage (0) ;
- break ;
-
- default:
- return DefWindowProc (hWnd, iMessage, wParam, lParam) ;
- }
- return 0L ;
- }
-
- MAKE File for BLOWUP
-
- blowup.obj : blowup.c
- cl -c -d -D LINT_ARGS -Gsw -Os -W2 -Zdp blowup.c
-
- blowup.exe : blowup.obj blowup.def
- link4 blowup, /align:16, /map /line, slibw, blowup
- mapsym blowup
-
-
- Figure 4: BLOWUP.DEF Module Definition File
-
- NAME BLOWUP
- DESCRIPTION 'Capture Screen Image to Clipboard by Charles Petzold'
- STUB 'WINSTUB.EXE'
- CODE MOVABLE
- DATA MOVABLE MULTIPLE
- HEAPSIZE 1024
- STACKSIZE 4096
- EXPORTS WndProc
-
- ████████████████████████████████████████████████████████████████████████████
-
- Increase the Performance of Your Programs with a Math Coprocessor
-
- Marion Hansen and Lori Sargent
-
- The microprocessor in your personal computer's CPU is powerful, but it
- wasn't designed to handle complex math operations rapidly. Whether it's an
- 8086, 8088, 80286, or 80386, your microprocessor will perform floating-
- point and transcendental calculations far more quickly and with greater
- precision if a math coprocessor is linked to it. Coprocessors also have
- capabilities useful for business computing. A coprocessor can process
- binary coded decimal numbers up to 18 digits long without round-off
- errors, perform arithmetic on integers from 2 x 10-9 through 2 x 109, and
- carry out math functions on real numbers as small as 3.4 x 10-4932 or as
- large as 1.1 x 104932.
-
- When software written to use a coprocessor performs certain types of math,
- it engages the coprocessor rather than the microprocessor. The coprocessor
- performs the calculation and returns the answer to the microprocessor.
- This entire process takes a fraction of the time required by the
- microprocessor executing alone. To give you an idea of how fast a
- coprocessor is, Figure 1 compares spreadsheet recalculation times with and
- without an 8087 math coprocessor.
-
- Besides performing certain kinds of math faster, coprocessors also save
- programming time. Because trigonometric, logarithmic, and exponential
- functions are built into the coprocessor's hardware, the programmer
- doesn't have to write these routines. And with the routines in the chip
- instead of in the code, programs are smaller. Coprocessors generate
- instructions for many numeric operations such as number conversions,
- arithmetic operations, and transcendental functions (tangents, exponents,
- and logarithms).
-
- A coprocessor is the most cost-effective way to increase number-crunching
- power. For a fraction of the cost of an accelerator board, a coprocessor
- can dramatically speed up floating-point calculations. And it won't
- consume a precious expansion slot (or two), since it fits in a socket
- already on the motherboard.
-
- Math coprocessors come in three varieties: the 8087 (for 8086- and 8088-
- based computers), the 80287 (for 80286-based computers), and the 80387
- (for 80386-based machines). The 8087 and 80287 are both available in three
- different speeds. The speed you need depends on how fast your computer
- drives the coprocessor socket, not on the speed of your microprocessor.
- For example, some 10-MHz computers drive the socket at 8 MHz and thus need
- a coprocessor that runs at 8 MHz, not 10 MHz. If you aren't sure which
- speed is correct for your computer, contact the manufacturer.
-
- Hundreds of application programs have been written to take advantage of
- the coprocessor's speed and precision, including business, engineering,
- graphics, statistical, and econometric packages. Many compilers and
- assemblers can benefit from a coprocessor as well. Using a coprocessor
- with one of these programs couldn't be easier, because all interfacing
- between microprocessor and coprocessor is built in. The only difference
- you'll notice is the increased speed.
-
-
- Development Tools
-
- Most of today's compilers and assemblers can generate coprocessor code.
- This includes all recent versions of Microsoft C, Pascal, and FORTRAN
- compilers, as well as Borland's Turbo Pascal. No matter which of these
- languages you're writing in, incorporating complex math into programs is
- not difficult with a coprocessor.
-
- In a high-level language, using the coprocessor is quite painless.
- Coprocessor instructions such as sine, square root, hyperbolic tangent,
- and log are built into manufacturer-supplied library routines.
-
- Assembly language programmers using Microsoft's Macro Assembler Version
- 1.25 or later have the option of writing code that explicitly references
- coprocessor instructions or implicitly does so by linking in a math
- library such as those supplied with the Microsoft C, Pascal, or FORTRAN
- compilers. In addition, a number of other software vendors market
- specialized math libraries that perform many math functions and can be
- linked to programs written in a variety of languages.
-
- Although programs can usually call math library routines with or without a
- coprocessor, programs running on systems with a coprocessor will execute
- significantly faster. Figure 2 illustrates how much faster an 8-MHz
- computer performs floating-point instructions on typical spreadsheet data
- when a coprocessor is installed.
-
- Most high-level languages link an emulation library into any program that
- contains floating-point instructions or data. Code to check for the
- presence of the coprocessor is generated at run time. If a coprocessor is
- detected, it is used. If a coprocessor is not present, the emulation
- library is used. This way, programs written to take advantage of a
- coprocessor can run on systems without one.
-
- Debugging code that contains coprocessor instructions is not much
- different from debugging code written for the microprocessor alone. A good
- debugger, such as the CodeView facility included in Microsoft's C Compiler
- Version 4.0, lets you examine and change all the coprocessor registers,
- including status and control registers. CodeView displays data register
- contents in both their 80-bit internal hexadecimal form and their decimal
- equivalents. This makes debugging floating-point instructions no more
- difficult than debugging microprocessor instructions.
-
-
- Synergy
-
- The coprocessor is an extension of the microprocessor. (In fact, Intel
- calls the coprocessor the numeric processor extension, or NPX.) They share
- the same buses and memory. The microprocessor's status lines and queue
- status lines are directly connected to the coprocessor, so the coprocessor
- is able to track the microprocessor's instruction queue. The coprocessor
- monitors and decodes instructions without any overhead. It reads each
- instruction into its queue but executes only its own instructions,
- treating each microprocessor instruction as a no-operation (NOP) command.
- In turn, the microprocessor treats each coprocessor instruction as a NOP
- and executes only its own instructions. The microprocessor controls
- program execution, and the coprocessor controls numeric operations.
-
- Instead of the 8-bit registers in the 8088, the 16-bit registers in the
- 8086 and 80286, or the 32-bit registers in the 80386, the coprocessor has
- 80-bit data registers, which allow it to hold more information. The
- coprocessor's registers were designed to hold specific types of data and
- are significantly different from the microprocessor's general-purpose
- registers. Nonetheless, the two chips can still share data through common
- memory.
-
-
- Data Types
-
- Coprocessor registers were designed to store 80-bit floating-point
- numbers. This format, which Intel calls temporary real, is compatible with
- the proposed IEEE 754 floating-point standard. A temporary real number is
- composed of a sign bit, a 15-bit exponent, and a 64-bit significand.
- Although the coprocessor stores all data as temporary real numbers, it can
- also read and write data in six other formats: packed decimal, long real,
- long integer, short real, short integer, and word integer (see Figure 3).
- Coprocessor load and store instructions automatically convert the other
- six data types to temporary real format and back again. Microsoft's Macro
- Assembler allows these formats to be declared with the directives DW (word
- integer), DD (short integer and short real), DQ (long integer and long
- real), and DT (packed binary coded decimal and temporary real).
-
- The coprocessor stores numbers in normalized format (scientific notation).
- A number is normalized by shifting the 1 that's furthest to the left up or
- down until it occupies bit 63. The coprocessor assumes the number in the
- significand is a real number between 1 and 2. The exponent field specifies
- how far the digits must be shifted to get the correct number back. Because
- the exponent is stored as an unsigned value, an offset (bias) is added to
- it so negative numbers can be represented. This lets the coprocessor
- compare the magnitude of two numbers without first performing arithmetic
- on the exponents, and execution time is thus shortened.
-
-
- Registers
-
- Coprocessor computations occur in eight data registers. The registers can
- be accessed as a LIFO (last-in-first-out) stack, with instructions
- operating on the top one or two stack elements. Or the registers can act
- as a fixed register set, with instructions operating on explicitly
- designated registers.
-
- Unlike those of the microprocessor, the coprocessor's data registers don't
- have unique names. They're treated as indexed entries in a stack, with the
- top of the stack designated as register ST(0) and the others designated
- ST(1) and so on. Values are loaded into the coprocessor by pushing them
- onto the stack, and some (but not all) are retrieved by popping them off.
- Many coprocessor instructions operate only on the top of the stack. Most
- other instructions default to operating on the stack's top. All register
- addresses are relative to the top of the stack.
-
- A 3-bit top-of-stack pointer in another type of register──the status word
- register──identifies the current top-of-stack register. A push decrements
- the value in this pointer by 1 and loads a value into the new top
- register. A pop increments the value in the pointer by 1 and removes the
- value from the register currently at the top. The stack is circular and
- can be overwritten if not managed properly.
-
- All the coprocessor's numeric opcodes (as opposed to control opcodes) use
- the top of the stack as at least one operand. Some instructions operate
- only on the top of the stack, while others operate on both the top and the
- second stack register. Some take their second operand from another stack
- register, and others can take their second operand from memory.
-
- Besides the eight data registers, the 8087 has five other registers
- accessible to the programmer, each 16 bits in size: status word, control
- word, tag word, operand pointer, and instruction pointer.
-
- The status word can be thought of as a flag register (see Figure 4). It
- contains a busy indicator, a top-of-stack pointer, condition codes, and
- exception indicators. To read the status word from Microsoft C, call the
- built-in _status87 function. To read the status word from the coprocessor
- in assembler, execute an FSTSW instruction to write the status word to
- memory where the microprocessor can examine it.
-
- The control word defines how the coprocessor should react to different
- exception conditions (see Figure 5). It also defines the precision, how
- the results will be rounded, and whether signed or unsigned infinity will
- be used. The control word register has three control fields and six
- exception masks. Masking the exception bit tells the coprocessor to handle
- all occurrences of this exception; leaving it unmasked means that the
- programmer will have to handle the exceptions.
-
- In assembly language, control words are sent to the coprocessor by writing
- them to a memory location and having the coprocessor execute an instruction
- that reads in the control word from memory. Programmers using a high-level
- language can check their library reference guide to see how this is
- implemented in the library they are using. For programmers who do not care
- to set these fields, Intel provides a set of default control conditions. The
- default settings are: exceptions masked, interrupts masked, 64-bit
- precision, rounding to the nearest number, and projective infinity.
-
- The tag word contains information about the contents of each data register
- (see Figure 6). This information is used by the coprocessor primarily to
- optimize performance. The coprocessor stores 2 bits for each data register,
- for a total of four possible tag values.
-
- The coprocessor uses the tag word to keep track of the contents of each of
- its data registers and to report invalid results. The coprocessor also
- uses the tag word to maintain stack integrity information. For example, if
- a register tagged as empty (tag value = 11) is popped from the stack, the
- coprocessor detects stack underflow. Similarly, the coprocessor uses the
- tag word to detect stack overflow when new data is stored in a register
- that wasn't previously empty. Stack underflow and overflow trigger an
- invalid operation exception. Programmers can mask or unmask this exception
- (the default is masked). If either stack underflow or overflow occur and
- the invalid operation exception is masked, the coprocessor adjusts the
- stack pointer and returns a standard result to indicate that the value is
- not meaningful.
-
- The operand pointer and instruction pointer registers provide information
- about the instruction and data that caused an exception and are used with
- user-written error handlers. Most programmers do not employ these
- registers, however, preferring to let the coprocessor handle exceptions.
-
- Unlike the status word and control word, the tag word, operand pointer,
- and instruction pointer cannot be accessed directly. These registers are
- accessed indirectly by writing to memory either the coprocessor's
- environment (using FSTENV) or the coprocessor's state (using FSAVE). The
- 14-byte coprocessor environment consists of the status word, control word,
- tag word, instruction pointer, and operand pointer. The 94-byte
- coprocessor state includes everything in the environment plus the eight
- coprocessor data registers (see Figure 7). The format of the coprocessor
- state and environment depends on the coprocessor's operating mode.
-
- When an exception occurs while the coprocessor is in real mode, it
- supplies the 20-bit addresses of the offending instruction and its memory
- operand (if any), plus the 11 low-order bits of the opcode. In protected
- mode (with the 80287 and 80387 only), the coprocessor supplies the
- selectors and offsets of the offending instruction and its memory operand
- (if any). Although the 80287/80387 real-mode exception pointers have the
- same format as the 8087 exception pointers, the 80287/80387 instruction
- pointer indicates any prefixes preceding the opcode. In contrast, the 8087
- instruction pointer indicates the escape (ESC) instruction opcode.
-
-
- Exceptions
-
- The coprocessor recognizes six exception conditions: invalid operation,
- denormalized operand, division by zero, numeric overflow, numeric
- underflow, and inexact result. The coprocessor's exception masks give
- programmers the choice of trapping exceptions themselves or having the
- coprocessor return a fixed value. When an exception occurs during
- execution of a coprocessor instruction, the coprocessor sets the
- appropriate bit in its status register. The coprocessor then checks its
- control register to determine whether or not that type of exception is
- masked. If the exception is masked, then the coprocessor uses its on-chip
- logic to return a result. The exception indicator bits in the status
- register will hold their value until they are explicitly cleared with
- either a FINIT or FCLEX instruction. Consequently, with exceptions masked,
- programmers do not have to check the status register after every
- instruction. Checking the exception indicator bits periodically ensures
- accurate results.
-
- The other method of handling exceptions is to unmask one or more of the
- exception bits and clear the coprocessor's interrupt enable mask. Under
- these conditions, an exception will trigger an interrupt request. It is up
- to the programmer to write the interrupt handler that will respond to such
- requests. The coprocessor contains a lot of built-in support for writing
- such routines.
-
-
- Instructions
-
- Coprocessor instructions fall into six categories: data transfer, loading
- constants, transcendental calculations, comparison, arithmetic, and
- processor control. A coprocessor instruction can be written in assembler
- in either of two ways: as a microprocessor ESC instruction followed by a
- number (for example, ESC 0BH) or as a specific coprocessor mnemonic
- (FSTP). All versions of Microsoft assemblers later than 1.25 accept
- coprocessor mnemonics; ESC instructions are needed only for older
- assemblers that do not, and are thus rarely used. Programmers writing in
- high-level languages needn't worry about the format of coprocessor
- instructions──the compiler will take care of everything.
-
- A coprocessor mnemonic takes the form of a sequence of letters beginning
- with an F. Figures 8 and 9 give examples of these instructions
- incorporated into assembly language programs. Figure 10 is a sample of
- code created by Microsoft's C Compiler Version 4.0. While the coprocessor
- instructions are not apparent in the source code, you will see them if you
- compile the program with the /Fc option and look at the .COD file.
-
- If an instruction starts with 11011 the microprocessor recognizes it as a
- coprocessor instruction and responds by generating any necessary operand
- addresses, putting them on the address bus, and ignoring the coprocessor
- opcode. The microprocessor then continues fetching and executing
- instructions unless it is instructed to wait for the coprocessor to
- complete its task.
-
- Since the microprocessor and the coprocessor can work on separate tasks
- simultaneously, they can overwrite each other's data or miss instructions
- unless they're synchronized. All high-level languages automatically
- synchronize the activity of the two chips, while assembly language
- programmers must do so explicitly. In exchange for the extra programming
- effort, however, assembly language programmers get more flexibility
- (carefully managed concurrency) and faster performance.
-
- The 80286 and 80386 have instruction synchronization built in, but this is
- not true of the 8088 and 8086 or any of the coprocessors. Consequently,
- programmers must on occasion insert an FWAIT after a coprocessor store
- instruction.
-
- When you are using escape sequences, specify the FWAIT instruction when
- the microprocessor must wait for data from the coprocessor. All floating-
- point mnemonics have an FWAIT as their first byte, so it isn't necessary
- to code one explicitly. (A few coprocessor instructions assume an FN<op>
- form, which keeps the assembler from generating an FWAIT instruction.)
-
- In addition to synchronizing data, the 8086 and 8088 must also synchronize
- 8087 instructions. Because the coprocessor gets its instructions by
- monitoring them as they go into the microprocessor prefetch queue, the
- 8087 can miss an instruction if it is busy executing while the 8086/8088
- is fetching and executing.
-
- Any program that uses a coprocessor should also be able to run without
- one. Before the software tries to use the coprocessor, it should check to
- see if there is one. It can easily do this by attempting to initialize the
- coprocessor and then attempting to read the coprocessor's control word
- after the initialization. (If a coprocessor is present, the control word
- will be set to the default value specified by Intel. Many software
- libraries have this checking function built in. If a coprocessor is not
- found, the program should call an emulation library to handle coprocessor
- instructions or should gracefully exit. Figure 11 provides an example of
- this type of program.
-
-
- Real Vs. Protected
-
- The 8087 operates only in real mode, while the 80287 and 80387 can operate
- in either real or protected mode. All programs written to use the 8087 are
- compatible with the 80287 and 80387 in real mode. Executing the privileged
- SETPM instruction will place the 80287 or 80387 in protected mode. They
- can then be returned to real mode only by a hardware reset.
-
- The microprocessor's operating mode affects coprocessor code in two areas:
- exception handling and memory accesses. The memory image of the
- instruction pointer and data pointer following an FSTENV or FSAVE
- instruction depends on the coprocessor's operating mode (see Figures 8 and
- 9). Any code that examines this information must consider the operating
- mode for accurate interpretation. In protected mode, Interrupt Vector 16
- is dedicated to the numeric exception handling routine. Coprocessor
- instructions that result in exception conditions will trigger an Interrupt
- 16 if the exception is unmasked. Protected mode also has a built-in
- mechanism for handling coprocessor instructions when a coprocessor is not
- present (or if its absence is being emulated). Interrupt 7 is
- automatically triggered if a coprocessor ESC sequence is executed and the
- emulation bit (EM) of the microprocessor's machine status word is set.
- This built-in trapping can help programmers systematically include
- emulation code in their programs.
-
- MS(R) OS/2, Microsoft's new protected-mode version of MS-DOS(R), offers basic
- exception handling for coprocessors by supporting the exception handling
- capabilities of the 80287 and 80387. It doesn't supply a standard
- emulation library for coprocessors; this must be provided by the
- compiler.
-
- When in protected mode, the microprocessor checks all memory accesses
- (including coprocessor operands) for violations of protection rules.
- Coprocessor applications running in protected mode must comply with
- protected-mode memory management regulations. Any violations cause either
- dedicated Interrupt 13 (when the violation occurs on the first word of the
- numeric operand) or dedicated Interrupt 9 (when the violation occurs on
- subsequent words).
-
- If you want to port an 8087 program to a protected-mode system, consider
- reassembling the program on an 80286/80386 assembler. This removes the
- redundant FWAITs and usually gives you a more compact code image. In
- addition, make the following changes to the 8087 program:
-
- ■ Delete interrupt-controller-oriented instructions in numeric exception
- handlers.
-
- ■ Delete 8087 instructions FENI/FNENI (enable interrupts) and
- FDISI/FNDISI (disable interrupts). The 80287 and 80387 ignore these
- instructions, so none of the 80287/80387 internal states will be
- updated.
-
- ■ Be sure Interrupt Vector 16 points to the numeric exception handling
- routine.
-
- ■ Include a microprocessor exception handler for an Interrupt 7, which
- will occur during the execution of coprocessor instructions if the
- microprocessor's machine status word contains the settings TS=1 (task
- switched) or EM=1 (emulation).
-
- ■ Include a microprocessor exception handler for Interrupt 9 (which
- occurs when the second or later word of a floating-point operand falls
- outside a segment) and Interrupt 13 (caused by the starting address of
- a numeric operand falling outside a segment).
-
-
- Figure 1: Recalculating Lotus 1-2-3(tm) spreadsheets on an IBM(r) PC can
- usually be done much more quickly with an 8087 coprocessor.
-
- Standard│░░
- Deviation│▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
- │
- Exponents│░░░
- │▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
- │
- Multiply/│░░░░░░░░░░░░░░░░░ ┌────────────────────┐
- Divide│▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ │ ░░ With an 8087 │
- │ │ │
- Add/│░░░░░░░░░░░░░░░░░░░ │ ▓▓ Without an 8087 │
- Subtract│▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ └────────────────────┘
- ├──┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
- 0 10 20 30 40 50 60 70 80 90 100 110 120 130
-
-
- Figure 2: Calculation times for floating-point instructions decrease
- dramatically when a coprocessor is added to an 8-MHz IBM PC.
-
- Instruction Approximate Execution Time (in microseconds)
-
- With an 8087 Without an 8087
-
- Add/Subtract 10.6 1,000.0
- Multiply (short real nos.) 11.9 1,000.0
- Multiply (temporary real nos.) 16.9 1,312.5
- Divide 24.4 2,000.0
- Compare 5.6 812.5
- Load (long real nos.) 6.3 1,062.5
- Store (long real nos.) 13.1 750.0
- Square Root 22.5 12,250.0
- Tangent 56.3 8,125.0
- Exponentiation 62.5 10,687.5
-
-
- Figure 3: The coprocessor can recognize seven numeric formats, which make
- use of up to 80 bits.
-
- 79 78 64 63 0
- ┌────┬────────┬───────────────────────────────────────────────────┐
- │Sign│Biased │ Significand │Temporary
- │Bit │Exponent│ │Real
- └────┴(3FFFH)─┴───────────────────────────────────────────────────┘
-
- 79 78 72 0
- ┌────┬────┬───────────────────────────────────────────────────────┐
- │Sign│Not │ d Packed Decimal Digits d │Packed
- │Bit │Used│ 17 │ │ 0 │BCD
- └────┴────┴─────┴────────────────────────────────────────────┴────┘
-
- 63 62 52 51 0
- ┌────┬────────┬──────────────────────────────────────┐
- │Sign│Biased │ Significand │Long Real
- │Bit │Exponent│ │
- └────┴─(3FFH)─┴──────────────────────────────────────┘
-
- 63 0
- ┌────────────────────────────────────────────────────┐
- │ 2's │Long
- │ Compliment │Integer
- └────────────────────────────────────────────────────┘
-
- 31 30 23 22 0
- ┌────┬─────────┬─────────────────────┐
- │Sign│ Biased │ Significand │Short
- │Bit │ Exponent│ │Real
- └────┴──(7FH)──┴─────────────────────┘
-
- 31 0
- ┌────────────────────────────────────┐
- │ 2's │Short
- │ Compliment │Integer
- └────────────────────────────────────┘
-
- 15 0
- ┌────────────────┐
- │ │Word
- │ │Integer
- └────────────────┘
-
-
- Figure 4: The coprocessor's 16-bit status word register serves as a flag
- register.
-
- Reserved───────────────────────────┐
- Condition │ Exception Flags (1=Exception occurred)
- Codes──────┬─────────┬──┬──┐ │
- Stack-Top │ │ │ │ │
- Pointer───────┬─┬──┐ │ │ │ │
- Busy───────┐ │ │ │ │ │ │ │ │
- 15 ╔╧╤╧═╤╧╤╧═╤╧╤╧═╤╧═╤╧═╤══╤╧╤══╤══╤══╤══╤══╤══╗ 0
- ║B│C3│ │ST│ │C2│C1│CO│IR│X│PE│UE│OE│ZE│DE│IE║
- ║ │ │ │ │ │ │ │ │ES│ │ │ │ │ │ │ ║
- ╚═╧══╧═╧══╧═╧══╧══╧══╧═╤╧═╧═╤╧═╤╧═╤╧═╤╧═╤╧═╤╝
- │ │ │ │ │ │ └─Invalid Operation
- │ │ │ │ │ └────Denormalized Operand
- │ │ │ │ └───────Divide By Zero
- │ │ │ └──────────Numeric Overflow
- │ │ └─────────────Numeric Underflow
- │ └────────────────Precision
- │
- │ ┌─{Interrupt Request
- │ │ (8027)
- └───────────────────┤ {Error Summary Status
- └─ (80287)
-
-
- Figure 5: The control word register governs how the coprocessor reacts to
- exception conditions.
-
- Infinity Control───┐ Exception Masks (1=Exception masked)
- Rounding Control───────┐
- │ │
- Precision Control────────────┐
- 15 │ │ │ 0
- ╔═╤═╤═╤╧═╤═╪══╤══╪══╤═══╤═╤══╤══╤══╤══╤══╤══╗
- ║X│X│X│IC│R│C │ P│C │IEM│X│PM│UM│OM│ZM│DM│IM║
- ╚╤╧╤╧╤╧══╧═╧══╧══╧══╧╤═╤╧═╧═╤╧═╤╧═╤╧═╤╧═╤╧╤═╝
- ├─┴─┴───────────────┘ │ │ │ │ │ │ │
- │ │ │ │ │ │ │ └─Invalid Operation
- Reserved─────┘ │ │ │ │ │ └───Denormalized Operand
- │ │ │ │ └──────Divide By Zero
- │ │ │ └─────────Numeric Overflow
- │ │ └────────────Numeric Underflow
- │ └───────────────Precision
- │
- │ ┌─{Interrupt Enable
- └──────────────────┤ (8087)
- └─{Reserved (80287)
-
-
- Figure 6: The tag word holds information about the contents of each data
- register.
-
- ┌───────────────────────────────────────────────────────────────────────┐
- │ │█
- │ 15 0 │█
- │ ╔════════╤═══════╤═══════╤═══════╤═══════╤═══════╤═══════╤════════╗ │█
- │ ║ Tag (7)│Tag (6)│Tag (5)│Tag (4)│Tag (3)│Tag (2)│Tag (1)│Tag (0) ║ │█
- │ ╚════════╧═══════╧═══════╧═══════╧═══════╧═══════╧═══════╧════════╝ │█
- │ │█
- │ Tag Values: 00 = valid (i.e., any finite nonzero number) │█
- │ 01 = zero │█
- │ 10 = invalid (i.e., NaN or infinity) │█
- │ 11 = empty │█
- │ │█
- └───────────────────────────────────────────────────────────────────────┘█
- ████████████████████████████████████████████████████████████████████████
-
-
- Figure 7: The coprocessor environment consists of the contents of all
- registers but the data registers. The coprocessor state includes
- all registers.
-
-
- Real Mode
- ┌─────────────────────────────────────┐
- │ Control Word │
- ├─────────────────────────────────────┤
- │ Status Word │
- ├─────────────────────────────────────┤
- Coprocessor │ Tag Word │
- ├─────────────────────────────────────┤
- State │ Instruction Address (15-0) │
- ├───────────────┬─────┬───────────────┤
- │ Instruction │ 0 │ Instruction │
- │Address (19-16)│ │ Opcode (10-0) │
- ├───────────────┴─────┴───────────────┤
- │ Operand Address (15-0) │
- ├──────────────────┬──────────────────┤
- │ Operand │ 0 │
- │ Address (19-16) │ │
- ┌──────────────┴15──────────────11┴12───────────────0┤
- │ ST (0) │
- ├────────────────────────────────────────────────────┤
- │ ST (1) │
- ├────────────────────────────────────────────────────┤
- │ ST (2) │
- ├────────────────────────────────────────────────────┤
- │ ST (3) │
- ├────────────────────────────────────────────────────┤
- │ ST (4) │
- ├────────────────────────────────────────────────────┤
- │ ST (5) │
- ├────────────────────────────────────────────────────┤
- │ ST (6) │
- ├────────────────────────────────────────────────────┤
- │ ST (7) │
- └79─────────────────────────────────────────────────0┘
-
- Protected Mode
- ┌─────────────────────────────────────┐
- │ Control Word │
- ├─────────────────────────────────────┤
- │ Status Word │
- ├─────────────────────────────────────┤
- Coprocessor │ Tag Word │
- ├─────────────────────────────────────┤
- Environment │ Instruction Offset │
- ├─────────────────────────────────────┤
- │ Instruction Selector │
- ├─────────────────────────────────────┤
- │ Operand Offset │
- ├─────────────────────────────────────┤
- │ Operand Selector │
- ┌──────────────┴15──────────────────────────────────0┤
- │ ST (0) │
- ├────────────────────────────────────────────────────┤
- │ ST (1) │
- ├────────────────────────────────────────────────────┤
- │ ST (2) │
- ├────────────────────────────────────────────────────┤
- │ ST (3) │
- ├────────────────────────────────────────────────────┤
- │ ST (4) │
- ├────────────────────────────────────────────────────┤
- │ ST (5) │
- ├────────────────────────────────────────────────────┤
- │ ST (6) │
- ├────────────────────────────────────────────────────┤
- │ ST (7) │
- └79─────────────────────────────────────────────────0┘
-
-
- Figure 8: This assembly language program uses coprocessor instructions
- to calculate the circumference of a circle with a given radius.
- Each of these instructions begins with an F.
-
- title circumference
-
- .287 ; Tell MASM there are coprocessor instructions
- ; in the program.
-
- data segment
- radius DD 2.468
- circumference DD ?
- data ends
-
- code segment
- assume cs:code, ds:data
- start:
- mov ax, data ; Initialize data segment
- ; register
- mov ds, ax
- finit ; Initialize coprocessor
- fldpi ; ST = pi
- fadd st, st ; ST = 2pi
- fld radius ; ST = radius
- ; ST(1) = 2pi
- fmul st, st(1) ; ST = radius*2pi
- fstp circumference ; store result and pop
- fwait ; wait for store to complete
- mov ah, 4ch ; return to DOS
- int 21h
- code ends
- end start
-
-
- Figure 9: This program uses coprocessor instructions to calculate the
- root of each element in an array of binary coded decimal
- integers. The results are stored in another binary coded
- decimal array and can easily be converted to ASCII strings for
- output.
-
- .287 ; Indicate to MASM program contains npx code.
-
- bcd_data segment
- array_1 DT 1234567890, 82, 769823, 84165
- DT 246809, 1526374859, 199, 41290
- DT 98654210, 340126, 2400, 371849
- array_2 DT 12 DUP (?); storage for results
- bcd_data ends
-
- code segment
- assume cs:code, ds:bcd_data
- start:
- mov ax, bcd_data
- mov ds, ax
- finit ; initialize coprocessor
- ; assume default control word
-
- mov cx, length array_2 ; initialize loop counter
- mov si, 0 ; initialize index
-
- process_array:
- fbld array_1[si] ; st(0) = array_1[index]
- fsqrt ; st(0) = sqrt (st(0))
- frndint ; round st(0) to integer
- fbstp array_2[si] ; store bcd result in
- ; array_2[index] and
- ; pop coprocessor stack
- add si, 10 ; increment index to point
- ; to next DT array element
- loop process_array ; DO WHILE
- ; loop counter <= length array_2
- exit:
- fwait ; make sure last store completed
- mov ah, 4ch ; exit to dos
- int 21h
-
- code ends
- end start
-
-
- Figure 10: Using Microsoft C library functions to calculate sine and
- cosine, this program in C draws a circle on the screen of a
- system equipped with a graphics adapter. The coprocessor
- instructions are apparent only after compilation.
-
- #include "stdio.h"
- #include "math.h"
-
- extern set_graph_mode();
- extern set_text_mode();
- extern plot_point();
-
- #define VERTICAL_CENTER 99.5
- #define HORIZONTAL_CENTER 319.5
- #define PI 3.1415927
-
- main()
- {
- char ch;
- float radians,radius,aspect_ratio;
-
- aspect_ratio=2.1; /* adjusts horizontal scaling to account */
- /* for PC's "tall and skinny" pixels */
- radius=90;
-
-
- set_graph_mode(); /* set screen to 640x200 graphics mode */
-
- /* step around the circle in 1/100th radian increments */
- for (radians=0; radians < 2*PI; radians=radians + 0.01)
- {
- long x,y;
-
- x=HORIZONTAL_CENTER+radius*aspect_ratio*cos(radians);
- y=VERTICAL_CENTER+radius*sin(radians);
-
- /* call routine to write a pixel on the screen */
- plot_point((int)x,(int)y);
- }
-
- /* wait for user to hit a key before erasing screen */
- ch=getchar();
-
- /* restore user's screen to text mode */
- set_text_mode();
- }
-
-
- Figure 11: This routine performs the same multiplication function with or
- without a coprocessor. First the program checks for the
- presence of a coprocessor. If it finds one, it executes the
- imul_32 procedure. If not, it jumps to the emulation
- procedure, emulate_imul_32.
-
- title math_module
-
- .287 ; Tell MASM that there are coprocessor
- ; instructions here.
- public init_math
- public imul_32
-
- present EQU 0
- missing EQU 1
-
- code segment public 'code'
- assume cs:code
-
- cp_flag DB 1 ; local flag
- ctrl_word DW 0 ; for storing '87 control word
-
- ;-------------------------------------------------------------;
- ; init_math: Detects math coprocessor and sets global flag ;
- ; which is used to determine whether or not to use ;
- ; coprocessor instructions or emulation code. ;
- ; ;
- ; This procedure must be called before the coprocessor can ;
- ; be used by math routines in this module. ;
- ;-------------------------------------------------------------;
-
- init_math PROC FAR
-
- fninit ; initialize coprocessor
- fnstcw cs:ctrl_word ; store '87 control word
- test byte ptr cs:[ctrl_word+2], 03 ; if bits 8 and 9 are set
- je yes_cp ; then coprocessor present
- mov cs:cp_flag, missing ; else no coprocessor
- jmp init_math_exit
-
- yes_cp:
- mov cs:cp_flag, present
-
- init_math_exit:
- ret
- init_math ENDP
-
- ;-------------------------------------------------------------;
- ; imul_32: Performs signed multiplication on two 32 bit ;
- ; integers. (Note: can also be used to perform ;
- ; fixed point 32 bit decimal multiplication) ;
- ; ;
- ; Input: Two 32 bit integers ;
- ; ds:si pointer to integer A ;
- ; ds:di pointer to integer B ;
- ; ;
- ; Output: 64-bit result returned at [es:bx] ;
- ; ;
- ;-------------------------------------------------------------;
-
- imul_32 PROC FAR
-
- cmp cs:cp_flag, missing ; IF coprocessor missing
- je emulate_imul_32 ; THEN emulate
- ; ELSE use coprocessor
- fild dword ptr [si] ; st(0)= A
- fimul dword ptr [di] ; st(0)=A*B
- fistp qword ptr es:[bx] ; store result and pop stack
- fwait ; wait for store to complete
- jmp imul_32_exit ; coprocessor done, exit
-
- emulate_imul_32:
- ;--------------------------------------------------------------------;
- ; ;
- ; The following code computes A x B where ;
- ; ;
- ; A is a 32 bit integer composed of ;
- ; a low word (A0) and a high word (A1) and ;
- ; B is a 32 bit integer composed of ;
- ; a low word (B0) and a high word (B1) ;
- ; ;
- ; The result is calculated by summing the partial products of ;
- ; individual 16 bit unsigned multiplies. The final result is ;
- ; sign adjusted. ;
- ; ;
- ;--------------------------------------------------------------------;
- ;
- push ax ; save caller's state
- push cx
- push dx
- push bp
-
- A0_x_B0:
- mov ax, [si] ; ax=A0
- mul word ptr [di] ; dx=A0B0H, ax=A0B0L
- mov es:[bx], ax ; store A0B0L - 4th column sum
- mov cx, dx ; cx=A0B0H
- A1_x_B0:
- mov ax, [si+2] ; ax=A1
-
- mul word ptr [di] ; dx=A1B0H, ax=A1B0L
- push bx ; running out of registers,
- ; reuse bx
- mov bx, ax ; bx=A1B0L
- mov bp, dx ; bp=A1B0H
-
-
- A0_x_B1:
- mov ax, [si] ; ax=A0
- mul word ptr [di+2] ; dx=A0B1H, ax=A0B1L
- add cx, bx ; cx=A0B0H+A1B1L
- adc cx, ax ; cx=A0B0H+A1B1L+A0B1L+carry
- pop bx ; need pointer back
- mov es:[bx+2], cx ; store 3rd column sum
- push bx ; still short of register space
- xor bx, bx
- adc bx, 0 ; save carry information
- mov cx, dx ; cx=A0B1H
- A1_x_B1:
- mov ax, [si+2] ; ax=A1
- mul word ptr [di+2] ; dx=A1B1H, ax=A1B1L
- add cx, bx ; cx=A0B1H+stored carry
- adc cx, bp ; cx=A0B1H+A1B0H
- adc cx, ax ; cx=A0B1H+A1B0H+A1B1L+carry
- pop bx ; restore pointer
- mov es:[bx+4], cx ; store 2nd column sum
- adc dx, 0 ; dx=A1B1+carry
- mov es:[bx+6], dx ; store 1st column sum
- ; now adjust for negative numbers
- test_A:
- mov ah, [si+2] ; ah=high byte of A
- or ah, ah ; IF A is negative
- js subtract_B ; THEN subtract B from
- ; high DD of result
- test_B:
- mov ah, [di+2] ; ah=high byte of B
- or ah, ah ; IF B is negative
- js subtract_A ; THEN subtract A from
- ; high DD of result
- jmp emulate_done
- subtract_B:
- mov ax, [di] ; ax=B0
- mov cx, [di+2] ; cx=B1
- sub es:[bx+4],ax ; adjust the two high words
- sbb es:[bx+6],cx
- jmp test_B
- subtract_A:
- mov ax, [si] ; ax=A0
- mov cx, [si+2] ; cx=B1
- sub es:[bx+4], ax ; adjust the two high words
- sbb es:[bx+6], cx
- emulate_done:
- pop bp ; restore caller's state
- pop dx
- pop cx
- pop ax
-
- imul_32_exit:
- ret
- imul_32 ENDP
-
- code ends
- end
-
- ████████████████████████████████████████████████████████████████████████████
-
- TIFF: An Emerging Standard for Exchanging Digitized Graphic Images
-
- ───────────────────────────────────────────────────────────────────────────
- Also see the related article:
- The CCITT/3 Scheme in TIFF
- ───────────────────────────────────────────────────────────────────────────
-
- Nancy Andrews and Stan Fry
-
- In the early days of microcomputers, PC users considered themselves lucky to
- have a paint program with basic drawing tools. Users could create simple
- drawings for documents, print them, and paste them in by hand.
- Unfortunately, the end results looked amateurish and, for many purposes,
- were barely acceptable.
-
- The difficulty was what if users wanted something more complex than what the
- available tools and their drawing skills allowed? What if they wanted to use
- existing line art or photographs? Users really needed to have the ability to
- digitize the professionally drawn art they had on paper and then
- electronically paste it into their documents.
-
- The arrival of scanner hardware solved this problem. Most scanners could
- scan an existing piece of art or a photograph at an amazing 300 dots per
- inch (dpi). This sounded like the answer to a document producer's dreams,
- but the puzzle had one missing piece. Users still needed a standard file
- format so that they could use any scanner to digitize an image, edit it with
- the paint or graphics program of their choice, and than electronically paste
- the image into their documents. Furthermore, having the whole process work
- quickly would be very helpful.
-
-
- Enter TIFF
-
- Aldus Corp., creator of PageMaker for the Macintosh and PC, recognized the
- need for a standard way to exchange digital data, took the initiative, and,
- with the assistance of several vendors, developed Tag Image File Format
- (TIFF). TIFF provides features that support most input devices, supports a
- variety of data compression techniques, and has the flexibility to add new
- features easily in a controlled fashion.
-
- TIFF's features can support scanners, point programs, and cameras (see
- Figure 1). It works just as well with a simple Windows Paint document as it
- does with a complex medical imaging scanner. TIFF files contain tags that
- describe not only the height and width of an image, but also provide
- resolution information, information about gray scale or color, and the type
- of compression scheme being used.
-
- Currently TIFF supports a modified form of the run-length compression, CCITT
- Group 3. CCITT/3 provides reasonable compression on black-and-white images
- that are 200 or 300 dpi by identifying continuous runs of all black or all
- white pixels. It then replaces these with a unique code word that can be
- used to reconstruct the original continuous run of data. You get the
- compression because the code words are typically shorter than the run of
- data they are replacing. CCITT/3's compression on dithered gray scale or
- color images isn't a good because redundancy isn't found in the same way as
- it is in standard black-and-white images. For this reason, TIFF also
- supports a simple packed scheme in which data is packed into bytes as
- tightly as possible with no unused bits, except at the end of a row.
- Specific compression schemes for gray and colored images will be added;
- Microsoft, the current keeper of the TIFF specification, will welcome you
- suggestions.
-
- TIFF makes it possible to add new features without having to rewrite
- supporting software each time a new feature is added. For example, if one
- application wants to add a "date-of-creation" tag to an image, it can be
- included as a tag when the application is written. Other applications that
- read TIFF files don't need to be rewritten unless they intend to make use of
- this new date-of-creation tag. The TIFF specification claims that "very high
- priority has been given to structuring the data in such a way as to minimize
- the pain of future additions." When additional capabilities, such as
- scanning color images, are available, TIFF should be able to accommodate
- them as well.
-
-
- How TIFF Works
-
- Traditional painting file formats, such as PC Paintbrush, use a fixed format
- organization (see Figure 2). The organization and location of each value is
- known prior to reading the file. For example, the x resolution information
- in this example is always stored in bytes 2 and 3 of the record.
-
- TIFF is not positionally based. Instead it uses tagged information, a style
- typically used in database design. Tags define the attributes of the image
- and how the actual image is stored. Each image has a header followed by tags
- that describe information contained elsewhere in the file. Tags consist of
- the name of the tag information, the size and length of the information
- described in the tag, and a pointer to the actual information (see
- Figure 3). Tags can expand to contain as much information as needed about
- the image, so that you can add as many tags as you need. You'll only need a
- few tags for simple images; you may need many tags for complex images.
-
- The header and tags for an image are called the Image File Directory.
- Because of its structure, you can store multiple versions of the same image
- in one file. This could be useful if, for example, you wanted to store a
- full-resolution version of an image for editing and printing and a
- subsampled version for better display performance, as shown in Figure 4.
- Each version has its own IFD with a unique set of attributes and pointers to
- the actual data. TIFF permits you to store the image data in variable-length
- strips or scan lines, giving you random access to any part of the image. If
- you just need one section of a very large image and you've stored the image
- in strips, you can decompress and load only the strips you need. And when
- you modify an image, you need to restore only the modified strips, not the
- entire image.
-
-
- Advantages
-
- For software and hardware developers, the advantages of a standardized
- format for exchanging digital data are obvious. Rather than support a
- proprietary file format for each scanner vendor, desktop publishing
- application, and paint or draw program, developers need to support just one
- file format. Even with a complex file format like TIFF, this is better than
- supporting a constantly growing number of different formats.
-
- TIFF is considered to be a "rich" file format. Rather than reduce all images
- to the lowest common denominator, TIFF is designed to handle many different
- kinds of images, line art, gray scale, and color, and different resolutions
- as well.
-
-
- TIFF
-
- supports different compression schemes and is flexible enough to adapt the
- compression algorithm to fit the type of data. It will expand to handle more
- complex images and compression schemes as the technology advances. Adding
- more features does not require rewriting existing applications if those
- applications don't want to take advantage of the expanded capability.
- Besides handling complex images well, TIFF also works with such simple
- images as those produced by paint programs, because the application can use
- only the parts of TIFF it requires.
-
- Another advantage of TIFF is its machine independence. You can use it with
- Intel or Motorola processors with UNIX, MS-DOS, or Macintosh operating
- systems, which encouraged Mitch Murphy, project manager at OptiGraphics
- Corp., to convert to TIFF.
-
- OptiGraphics, which makes engineering workstations and large-document
- scanners, were looking for a file format to use for all raster images for
- both its PC and UNIX machines. Murphy said it chose TIFF so it could
- "gracefully upgrade and maintain compatibility as products mature." Products
- from Murphy's group, such as View! and Markup!, are Microsoft Windows
- applications that allow you to view and make minor changes to large
- engineering and architectural drawings. The scanner outputs the drawings in
- TIFF, and View! and Markup! read and display the TIFF output. Later, if a
- drawing needs more extensive work, there are products that automatically
- vectorize the TIFF file so it can be used with a CAD system.
-
-
- Limitations
-
- TIFF's richness, which is one of its major advantages, is also the source of
- its limitations. Although it's not terribly difficult for a scanner to write
- a TIFF image file, it is reasonably complicated to write a TIFF reader. Marc
- Cantor, who is president of MacroMind, faults Aldus and Microsoft for not
- including a decoding algorithm with the TIFF specification. As it stands
- now, each company has to write its own parser; Cantor's company, whose
- product Graphic Works reads a scanned TIFF document of any size, wrote its
- own parser and is selling it to others. To remedy the problem, Microsoft
- will be making TIFF tools available.
-
- Another limitation is that there is not a standard TIFF file, nor a common
- subset of features that everyone using the name TIFF must support. Different
- vendors can support different Tag fields of TIFF. OptiGraphics' Murphy says
- that "TIFF is so rich that dialects will emerge." To prevent this, he and
- others suggest using common tools, which are becoming available. Murphy has
- a library of routines to access TIFF files in an orderly way; this helps to
- keep track of all the levels of indirection.
-
- If you contact him at OptiGraphics at (619) 292-6060, he'll send them to you
- for free. However, he cautions that you "use them at your own risk." The
- development engineers at Hewlett-Packard have made a significant
- contribution to the TIFF spec and have written a lucid Guide to the Tagged
- Image File Format. Hewlett-Packard also has some source code for reading,
- writing, compressing, decompressing, and debugging TIFF files. For
- information, call HP's ISV marketing department at (303) 350-4000. Dest
- Corp., a scanner hardware manufacturer, also has a library of routines it
- will license to desktop publishing vendors free of charge. The phone number
- is (408) 946-7100. And Tim Davenport at Aldus will send you sample TIFF
- files; for information, call (206) 622-5500.
-
-
- The New Standard?
-
- TIFF is rapidly becoming the standard for handling bit-mapped images.
- Companies like HP, Aldus, and OptiGraphics have put a significant amount of
- research and development into TIFF. Most echo Murphy's sentiments: now that
- they've committed to TIFF, they want others to commit as well, and they're
- willing to help. David Rosenbloom of Cygnet Technologies, a high-end
- telecommunications firm, chose TIFF rather than a proprietary format for its
- FAX product because it had what it needed, a generic descriptive header with
- an unlimited number of fields and :a shot at becoming some sort of accepted
- standard." Other firms that have publicly announced support for TIFF are
- Dest, DataCopy, Microsoft, Microtek, Media Cybernetics, New Image
- Technology, and Software Publishing. Support for TIFF is bridging the
- traditional rift between the PC and Macintosh camps.
-
- Although Steve Carlson at Aldus is the originator of the TIFF specification,
- Microsoft is now the keeper of TIFF. With Microsoft, a neutral player,
- managing TIFF rather than a hardware or desktop publishing vendor, companies
- are more likely to see TIFF as a standard instead of an aid to competitors.
- Microsoft is currently working with those companies whose products scan and
- archive images. It also wants to work with such page description vendors as
- Adobe, DDL, and Interpress to ensure that the functions TIFF supports are
- mapped closely to future functions of page description languages. If, for
- example, compression schemes are compatible, print time can be significantly
- improved. Manny Vellon of the Microsoft Windows marketing group is the new
- TIFF administrator; you can write to him for a copy of the spec and also
- feel free to send him your suggestions.
-
- Currently, the desktop publishing market is most interested in TIFF, but the
- market is expanding to include OCR, FAX, 3-dimensional CAD, and
- sophisticated medical imaging. Someday, instead of going to your bookshelf
- and reaching for a volume in your published art collection, you may go to
- your CD ROM disc and get high-quality typeset images stored in TIFF format.
- Soon you may be able to receive an engineering drawing as a FAX on your PC,
- use an application to vectorize the drawing, touch it up with your CAD
- program or the specs that accompany the drawing in another FAX, run an
- application to OCR the specs, edit them with your word processor, and then
- send them back in FAX form. These hardware and software applications will be
- able to talk and work together when there is a true standard for exchanging
- digital image data; TIFF is well on its way to becoming that Standard.
-
-
- Figure 1: TIFF's features can support scanners, paint programs, and cameras
- and treats simple paint programs just as it does complex medical
- documents.
- ╔══════════════╗
- ┌░░░▒▒┐ ║ ░░░░░░░░░░░ ║█
- Camera └──░──┘ __________ ║ ░░░≡ ≡≡░░░░ ║█
- ┌───▀▀────────────▀▀▀─┐ ╓───/ /─╖ ║ ░░░≡≡≡≡≡░░░░ ║█
- │╔════╗ ▒░░░░╔════╗ │█ ╔═╝──┴────────┴──╚═╗ ║ ░░░░░░░░░░ ║█
- │║░░░▒║ ╔════╗ ║░▒▒▒║ │█ ║ ---- ║█ ╔═╝──────────────╚═╗
- │║░░░▒║ ║ ░║ ║░░▒▒║ │█ ╚══════════════════╝█ ║ ---- ║█
- │╚════╝ ╚════╝ ╚════╝ │█ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ╚══════════════════╝█
- └─────────────────────┘█ Scanner ┌──── ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ │ │ Desktop Publishing
- │ tiff │
- ╔════════════╗ tiff │ ascii
- ║ ░░░░░░░░ ║█ │ ┌──▄▄▄▄▄▄▄▄──\ │ ╔══════════╗
- ║ ░░░ █ ░░░░ ║█ └─────────│ ██████ █ │ │ ║ ▒▒▒▒▒▒▒▒▒║█
- ║ ░░░ ░░░░ ║█ ───tiff──────│┌──────────┐│─────┤ ║ ▒▒▒▒▒▒▒▒▒║█
- ║ ░░░░▌▐░░ ║█ ││ Disk ││ │ ╔════╩──────────╩══╗
- ╔═╝────────────╚═╗ │└──────────┘│ │ ║ ---- ║█
- ║ ---- ║█ └────────────┘ │ ╚══════════════════╝█
- ╚════════════════╝█ │ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
- ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ │ Word Processsor
- Paint/Draw └────
-
-
- Figure 2: Fixed Format Organization
-
- ╔════╗ ┌────────────────────────────┐
- ║ 0 ║ │ Revision # │
- ╟────╢ ├────────────────────────────┤
- ║ 2 ║ │ X Resolution │
- ╟────╢ ├────────────────────────────┤
- ║ 4 ║ │ Y Resolution │
- ╟────╢ ├────────────────────────────┤
- ║ 6 ║ │ H Pixels │
- ╟────╢ ├────────────────────────────┤
- ║ 8 ║ │ V Pixels │
- ╟────╢ ├────────────────────────────┤
- ║ 10 ║ │ Colorplane │
- ╟────╢ ├────────────────────────────┤
- ║ ║ │ │
- ║ ║ │ │
- ║ ║ │ │
- ║ 64 ║ ≈ Data ≈
- ║ ║ │ │
- ║ ║ │ │
- ║ ║ │ │
- ╚════╝ └────────────────────────────┘
-
-
- Figure 3: TIFF uses tagged information, a style typically used in database
- design. Tags define the attributes of the image and how the actual
- image is stored. Each image has a header followed by tags that
- describe information conatined elsewhere in the file.
-
- ┌─────────────────────┐
- ≈ Tiff Header ───≈──┐
- ├─────────────────────┤ │ ┌──────────┐ ╔═══╗
- ╔═══╗ │ Tag 1 ───────┼──┘ │ Tag Type │█ ║ T ║
- ║ D ║ ├─────────────────────┤ ├──────────┤█ ║ A ║
- ║ I ║ │ Tag 2 │ │ Size │█ ║ G ║
- ║ R ║ ├─────────────────────┤ ├──────────┤█ ║ ║
- ║ E ║ │ Tag 3 │ │ Length │█ ║ E ║
- ║ C ║ ├─────────────────────┤ ├──────────┤█ ║ N ║
- ║ T ║ │ Tag 4 ───┼──┐ │ Data or │█ ║ T ║
- ║ O ║ ├─────────────────────┤ │ │ Pointer │█ ║ R ║
- ║ R ║ │ Tag 5 │ │ └──────────┘█ ║ Y ║
- ║ Y ║ ├─────────────────────┤ │ ███████████ ╚═══╝
- ╚═══╝ │ Tag 6 ───┼──│─┐
- └──────────:──────────┘ │ │
- ┌──────────:──────────┐ │ │
- │ Long Tag Data ────┼──┘ │
- └──────────:──────────┘ │
- ┌──────────:──────────┐ │
- │ Bitmap Image Data ─┼────┘
- └─────────────────────┘
-
-
- Figure 4: The header and tags for an image are called the Image File
- Directory (IFD). It is structured so that you can store multiple
- versions of the same image in one file.
-
- ┌────────────┐ Image Data
- ┌────────────────────┐ ┌─│ Pointer to ├─┐ ┌──────────┐
- │ File Header │ │ │ Strip 1 ─────│ Strip 1 │
- ├────────────────────┤ │ ├────────────┤ ├─┐ ├──────────┤
- ┌─Pointer to first ifd│ │ ≈ ≈ │ │ │ │
- │ └────────────────────┘ ┌──────────┐ │ ├────────────┤ │ │ ≈ ≈
- │ Full Resolution ifd ┌─│Pointer to│ │ │ Pointer to │ │ │ │ │
- │ ┌─────────┬──────────┐ │ │ Plane 1 ─┘ │ Strip n ────┐ ├──────────┤
- └│ # of │ Subfile │ │ ├──────────┤ └┬───────────┘ │ │└│ Strip n │
- │ entries │ Type │ │ ≈ ≈ └┬────────────┘ │ ├──────────┤
- ├─────────┴──────────┤ │ ├──────────┤ ┌───│ Pointer to │ │ │
- │ Tags ─┘ │Pointer to│ │ │ Strip n │ ≈ ≈
- ├────────────────────┤ │ Plane m ─┘ └──────────────┘ │ │
- ┌─Pointer to next ifd │ └──────────┘ ┌────────────┐ ├──────────┤
- │ └────────────────────┘ ┌─│ Pointer to ├─┐ ┌│ Strip 1 │
- │ Subsample ifd ┌──────────┐ │ │ Strip 1 ────┘ ├──────────┤
- │ ┌─────────┬──────────┐ ┌─│Pointer to│ │ ├────────────┤ ├─┐ │ │
- └│ # of │ Subfile │ │ │ Plane 1 ─┘ ≈ ≈ │ │ ≈ ≈
- │ entries │ Type │ │ ├──────────┤ ├────────────┤ │ │ │ │
- ├─────────┴──────────┤ │ ≈ ≈ │ Pointer to│ │ │ ├──────────┤
- │ Tags ─┘ ├──────────┤ │ Strip y ───┼─│ Strip y │
- ├────────────────────┤ │Pointer to│ └┬───────────┘ │ │ ├──────────┤
- │Pointer to next ifd │ │ Plane x ───┐ └┬────────────┘ │ │ │
- └────────────────────┘ └──────────┘ └─│ Pointer to │ ≈ ≈
- │ Strip y │ │ │
- └──────────────┘ │ │
-
-
- ───────────────────────────────────────────────────────────────────────────
- The CCITT/3 Scheme in TIFF
- ───────────────────────────────────────────────────────────────────────────
-
- The CCITT/3 scheme used in TIFF identifies continuous runs, which are called
- run-lengths, of either all white or all black pixels. Each of these run-
- lengths is then replaced with a code word that can later be used to
- reconstruct the continuous run of data.
-
- The compression scheme uses two tables. One table contains code words for
- different continuous runs of white pixels from 0 to 63 pixels in length.
- The other table contains code words for the same continuous runs of black
- pixels. The tables also contain values for some discrete run-lengths above
- 63. If the white- or black-pixel run-lengths exceed 63, combinations of
- codewords can be used to compress these longer run-lengths.
-
- Each row of the bitmap is compressed independently from any other row. In
- addition, each row must start with a white pattern and must end on a byte
- boundary. Starting with a white pattern provides synchronization and allows
- the decompression algorithm know whether to search in the black or white
- run-length table. If the row starts with one or more black pixels, then a
- white run-length of 0 pixels must start the row of compressed data code
- words.
-
- End-of-line (EOL) markers, common to CCITT/3 used for FAX, are not used. If
- EOL markers are used, readers of TIFF files would not be able to interpret
- the information. Also, the TIFF PhotoMetric interpretation tag doesn't
- apply when using CCITT compression. The values for white and black are
- predefined. You may need to invert all binary data before encoding it in
- CCITT format.
-
- Any padding bits added to the uncompressed image row must not be compressed.
- You should only compress the length of data that was specified in the width
- tag field. For example, if a bitmap consisted of 10 pixels across, it would
- be stored uncompressed as 2 bytes so that each row would begin on a byte
- boundary. In this case, six padding bits would have been added. When
- compressing this information, only the first 10 bits should be considered as
- the data to be compressed, not the six padding bits.
-
- ████████████████████████████████████████████████████████████████████████████
-
- Ask Dr. Bob!
-
- Microsoft Pascal Inquiries
-
- Dear Dr. Bob,
-
- I received my first copy of MSJ last week, and from now on I will look
- forward to your column, since it gives me an inkling of what the rest of
- the world is doing. I have several questions about Microsoft(R) Pascal
- Version 3.31.
-
- In your March 1987 column (MSJ, Vol. 2 No.1), you refer to the C library
- function getenv(). Microsoft Pascal 3.31 supplies a part of the C library
- with the functions named SYSTEM and SPAWNLP, but no GETENV or any others.
- Is there a way to get and use them from Microsoft Pascal?
-
- In Microsoft Pascal, how can I make a single procedure definition file
- that can be included in all program modules, even the ones defining the
- procedures in the include file? Normally this gives some kind of
- "duplicate identifier" error.
-
- I use ALLMQQ extensively. Is there a problem with excessive use of ALLMQQ
- and FREMQQ, even if I am careful to free up what I don't use? When I
- allocate several large (25Kb) blocks and then free them up, sometimes I
- can't allocate them again. Granted, there are additional scores of ALLMQQs
- and FREMQQs going on at the same time.
-
- Where can I get information on resident programming techniques? I am
- totally ignorant, and the standard language manuals don't even hint at it.
- ──RHK
-
- If you have the Microsoft C Compiler, Version 3.0 or Version 4.0, you may
- use any of the large-memory model C libraries from a Microsoft Pascal 3.31
- program. Actually, you can mix large model C code with Microsoft Pascal or
- FORTRAN code, if you like. See the appendix on mixed-language programming
- in your Pascal User's Guide.
-
- With regard to GETENV, you are particularly lucky. It is present in the
- subset of the C library included with Pascal 3.31 in the file CEXEC.LIB.
- Figure 1 is a small program that uses GETENV to print out the PATH.
-
- The Microsoft Pascal metacommands for conditional compilation ($if, $then,
- $else, $end) provide a way to make a single module definition file. First,
- create the definition file with the procedure and function definitions
- followed by "extern;", grouping the definitions together by module. Next,
- put an "$if moduleX $else ... $end" conditional around each module's group
- of definitions, where X is the module number, as shown in Figure 2.
-
- Finally, include this definition file in all your program's modules
- following a list of constant definitions. Make all the constants 0, except
- for the one for the current module. In the file for module number 1, for
- instance, you would place the following:
-
- CONST
- module1 = 1;
- module2 = 0;
- module3 = 0;
- dummy = 0;
- {$include:'define.inc'}
-
- The constant "dummy" is required because the compiler keeps one look-ahead
- symbol as described in section 18.3 of the Pascal 3.31 Reference Manual.
-
- Dr. Bob knows of no bugs in the ALLMQQ and FREMQQ memory management
- functions, but it is easy to make a sequence of calls to ALLMQQ and FREMQQ
- that will cause ALLMQQ to fail even when there seems to be lots of free
- space. This is because the memory heap can become fragmented, that is,
- broken into small pieces of alternating free and allocated blocks, so that
- there is no single chunk large enough for the ALLMQQ request. A more
- sophisticated memory management scheme would have a "garbage collector" to
- shuffle memory blocks around and put all the free space together to give
- ALLMQQ a chance to allocate a large block. However, adding a garbage
- collector opens its own can of worms and makes programming more
- complicated.
-
- There is no general-purpose method for working around this fragmentation
- problem. The best you can do is try to allocate large blocks early in your
- program, before the heap gets fragmented. Also, use ALLMQQ and FREMQQ less
- often by reusing a block that's already been allocated.
-
- Glad you asked about TSRs. The December 1986 issue of MSJ (Vol. 1 No. 2)
- has an article on TSRs, "Moving Toward an Industry Standard for Developing
- TSRs." Also, we've seen articles on writing TSRs in recent issues of Byte
- and Dr. Dobb's Journal.
-
- Environment Size in DOS
-
- Dear Dr. Bob,
-
- It appears that with MS-DOS Version 3.2 the only way to increase the
- environment size is with COMMAND /E:xxxx. I have tried putting this
- command into my AUTOEXEC.BAT with an additional /P switch to make it
- permanent. This results in an infinite regression of COMMAND invoking
- AUTOEXEC, which runs COMMAND and invokes AUTOEXEC , which ... you get the
- idea. How can I do this without getting thrown for a loop?──EG
-
- When started with the /P switch, COMMAND.COM looks for an AUTOEXEC.BAT in
- the root directory of the current disk. You can take advantage of this by
- making a small AUTOEXEC.BAT on your boot disk that copies the "real"
- AUTOEXEC.BAT to a RAMdisk and runs the secondary version of COMMAND from
- there. Figure 3 shows what such an AUTOEXEC might look like, where C: is
- the boot disk and D: is a RAMdisk.
-
- In the example, drive C: is the boot disk, drive D: is the RAMdisk, and
- AUTOEXEC.2ND contains the rest of the boot sequence normally found in
- AUTOEXEC.BAT.
-
- An Alternative Suggestion
-
- Dear Dr. Bob,
-
- Your delightfully convoluted response to Forgetful in the March 1987 issue
- of MSJ (Vol. 2 No. 1) prompts me to suggest this alternative. Instead of
- twice compiling an IFDEFed C file and piping it into both an .INC and an
- .H file for inclusion in both assembly and compilation processes, try the
- trick shown in Figure 4, which depends on a basic difference between C and
- MASM comments: C comments can cross line boundaries, while MASM comments
- go to the end of the line.
-
- This file can be directly included in both the assembly and the C source
- code. Perhaps there is an even more compact and bizarre method of achieving
- the same goal, but I can't think of it.──BN
-
- You have a cute idea there, but we find it deficient in two respects.
- First, there are still two distinct copies of each piece of information,
- and it would be all too easy to change one without changing the other.
- This defeats the purpose of the system, which is to make a single
- collection of definitions that will automatically serve for both assembler
- and C programming. Second, none of our C compilers will accept your code,
- as they all require the #define directives either to start in the first
- column or to be the first nonwhite space on a line.
-
- Comments and Corrections
-
- Dear Dr. Bob,
-
- In Dr. Bob's "Self-Modifying Code" in the March 1987 issue of MSJ (Vol.2
- No.1), the answer is incorrect. LINK4 does not accept the MULTIPLE option
- for the CODE statement. I have struggled with the problem of forcing Windows
- to use different code segments for multiple instances when I was writing
- about Windows memory management.
-
- To my mind, Windows should load different code segments for multiple
- instances when you specify that a CODE segment is IMPURE or NONSHARED in
- the module definition file. This is valid syntax, but Windows seems to
- ignore these options.
-
- The only way I found to force Windows to load multiple code segments for
- different instances is by making the .EXE name different from the NAME of
- the module in the .DEF file. I discovered this bug by accident, and it
- certainly doesn't make any sense to me.
-
- I should also note that even if you get Windows to load multiple code
- segments, it will still discard code segments if you specify only MOVEABLE
- without DISCARDABLE. This is implied by the text of your answer, but not
- by the example. You have to make a code segment FIXED to prevent Windows
- from discarding it.
-
- For the stock bitmap question, I think the answer should have included the
- fact that the stock bitmap has a width and height of only one pixel, so it
- is useless for drawing until you select another bitmap onto it.──CP
-
- MULTIPLE (or IMPURE or NONSHARED) code segments are not supported. All
- code segments are by definition shared. It is unfortunate that the
- documentation is not clearer on this point. The documentation is also not
- clear about saying that all CODE can either be FIXED or DISCARDABLE and
- that MOVEABLE implies DISCARDABLE for code segments. As for forcing
- Windows to load multiple code segments for different instances by making
- the .EXE name different from the NAME of the module in the .DEF file, I
- suspect that what was happening was not what you think it was. It
- definitely does not load multiple instances of the same code segment.
-
- Dear Dr. Bob,
-
- In MSJ, Vol. 2 No. 1, Dr. Bob tells a very good story about why
- Windows uses only three of the four planes on the EGA, but unfortunately
- it's totally wrong. As the programmer who first wrote the Windows EGA
- driver, I know what I'm talking about. The mouse cursor is stored in off-
- screen memory in planes 0-2. Windows also uses planes 0-2 for display.
- Plane 3 is not used at all by Windows. Plane 3 by itself wouldn't have
- been enough for the cursor──I wish it had been, as it would have made my
- job a lot easier──since the Windows cursor requires two planes: it supports
- white, black, transparent, and inverted pixels.
-
- Why is the fourth plane unused? When I did the driver originally, it was
- because I had to get the thing working in a matter of days and I didn't
- have time to fool with the fourth plane. Since the fourth plane is most
- logically used as some kind of intensity bit, and this is not independent
- of the R, G, and B bits (that is, you can't make R intensified without
- simultaneously making G and B intensified), this fourth bit doesn't really
- fit into the GDI virtual RGB model very well, so logical <-> physical
- mapping is hard to do.
-
- Since then, the code has been maintained by someone else, and support for
- the fourth plane was never added, probably because of the awkward logical
- <-> physical mapping mentioned above.──BC
-
-
- Figure 1:
-
- program EnvTest(input,output);
-
- type
- chrptr = ^array[0..1000] of char;
-
- var
- path : chrptr;
- i : integer;
-
- function getenv : chrptr [c,varying]; extern;
-
- begin
- write('Dr Bob''s PATH = ');
- path := getenv(ads('PATH'*chr(0)));
- i := 0;
- while (path^[i] <> chr(0)) do
- begin
- write(path^[i]);
- i := i+1;
- end;
- end.
-
-
- Figure 2:
-
- {$if module1 $else}
- {definitions for module1:}
- procedure f1; extern;
- {$end}
-
- {$if module2 $else}
- {definitions for module2:}
- procedure f2(p2:integer); extern;
- {$end}
-
-
- Figure 3:
-
- COPY AUTOEXEC.2ND D:AUTOEXEC.BAT
- D:
- C:COMMAND /p
-
-
- Figure 4:
-
- ;/* ScreenHt=200 ; */ #define ScreenHt (200) /* comment
- ScreenWidth=640 ; */ #define ScreenWidth (640) /* comment
-
- Point STRUC ; */ struct Point { /* comment
- x DW ; */ int x; /* comment
- y DW ; */ int y; /* comment
- Point ENDS ; */ };
-
-
- ════════════════════════════════════════════════════════════════════════════
-
-
- Vol. 2 No. 4 Table of Contents
-
-
- Microsoft(R) Windows/386: Creating a Virtual Machine Environment
-
- Windows/386 exploits the 80386's virtual machine capabilities to offer an
- environment that allows the user to simultaneously run and switch between
- existing MS-DOS and Windows applications. The product fully implements
- preemptive multitasking and data exchange between applications.
-
-
- Programming in C the Fast and Easy Way with Microsoft(R) QuickC(TM)
-
- Making the process of C programming faster and easier has long been a goal
- for developers of Microsoft C programming products. The Microsoft QuickC
- compiler offers an integrated environment that provides programmers with
- high-speed compilation and an easy debugger.
-
-
- Character-Oriented Display Services Using OS/2's VIO Subsystem
-
- Microsoft OS/2 provides application programs with a wide variety of display
- functions. The OS/2 video capabilities detailed in this article are the
- VIO services──the subsystem that lets you get your applications up and
- running quickly without the Windows Presentation Manager.
-
-
- Dynamic Allocation Techniques for Memory Management in C Programs
-
- How well memory is managed can make a real difference in the performance
- of a program, particularly those programs that process unpredictable
- amounts of data. Dynamically allocating memory to hold array and linked
- list data provides your programs with extra flexibility and utility.
-
-
- CD ROM Technology Opens the Doors on a New Software Market
-
- Thanks in part to the success of the compact audio disc, CD ROM is viable
- and growing. Developers with applications requiring large amounts of data
- space, such as parts catalogs, can benefit from CD ROM's huge data
- capacity and low cost duplication.
-
-
- MS-DOS(R) CD ROM Extensions: A Standard PC Access Method
-
- Using the Microsoft MS-DOS CD ROM Extensions frees developers to concentrate
- solely on their applications, eliminating dependence on any particular drive
- technology. This article discusses CD ROM device drivers and the MSCDEX.EXE
- program that interfaces with MS-DOS.
-
-
- Microsoft(R) QuickBASIC: Everyone's First PC Language Gets Better
-
- You can create elegant applications in BASIC by combining the powerful
- Microsoft QuickBASIC 3.0 control structures and statements, the INT860
- interface to MS-DOS and BIOS services, and custom assembly-language
- routines of your own design. Here's how.
-
-
- Ask Dr. Bob
-
-
- EDITOR'S NOTE
-
- Microsoft(R) Windows/386 provides users of 80386-based computers with a
- new operating environment. Its principal benefit is the flexibility to
- simultaneously run nearly any combination of MS-DOS applications, each in
- its own virtual machine. While it does not offer developers a new
- programming interface, we think you will appreciate a behind-the-scenes look
- at the product's design.
-
- Meanwhile, many software developers are working hard to unravel the details
- and intricacies of MS(R) OS/2. The first OS/2 applications will take
- advantage of the rich set of character-based display functions that are part
- of the OS/2 API. In this issue, Ray Duncan explores the use of these
- functions.
-
- However, operating environments aren't all that's new. QuickC gives both
- the professional and beginning C programmer a new tool──the integrated
- working environment first seen in QuickBASIC. CodeView(R)-compatible
- debugging aids and superfast compilation speeds helped create TOUCH.C, a
- useful file date-stamp utility included here. We also have two articles
- about a new kid on the PC block──CD ROM, which finally seems ready for
- general acceptance.
-
- All our source code listings can now be found on DIAL, CompuServe, BIX, and
- two public access bulletin boards. On the East Coast, users can call (212)
- 889-6438 to join the RamNet Bulletin board. On the West Coast, call (415)
- 284-9151 for the ComOne bulletin board. In either case, look for the MSJ
- directory.
-
- A reminder──we read all of our mail, hardcopy and electronic (our MCI
- mailbox is MSJ). Write and let us hear from you. If you're interested in
- submitting a manuscript drop Tony Rizzo, our technical editor, a note.
- He would be more than happy to hear from you.──Ed.
-
-
- Masthead
-
- JONATHAN D. LAZARUS
- Editor and Publisher
-
- EDITORIAL
-
- TONY RIZZO
- Technical Editor
-
- CHRISTINA G.DYAR
- Associate Editor
-
- JOANNE STEINHART
- Production Editor
-
- GERALD CARNEY
- Staff Editor
-
- KIM HOROWITZ
- Editorial Assistant
-
- ART
-
- MICHAEL LONGACRE
- Art Director
-
- VALERIE MYERS
- Associate Art Director
-
- CIRCULATION
-
- WILLIAM B. GRANBERG
- Circulation Manager
-
- L. PERRIN TOMICH
- Assistant to the Publisher
-
- BETSY KAUFER
- Administrative Assistant
-
- Copyright(C) 1987 Microsoft Corporation. All rights reserved; reproduction
- in part or in whole without permission is prohibited.
-
- Microsoft Systems Journal is a publication of Microsoft Corporation, 16011
- NE 36th Way, Box 97017, Redmond, WA 98073-9717. Officers: William H.
- Gates, III, Chairman of the Board and Chief Executive Officer; Jon Shirley,
- President and Chief Operating Officer; Francis J. Gaudette, Treasurer;
- William Neukom, Secretary.
-
- Microsoft Corporation assumes no liability for any damages resulting from
- the use of the information contained herein.
-
- Microsoft, the Microsoft logo, CodeView, MS, MS-DOS, and XENIX are
- registered trademarks of Microsoft Corporation. QuickC and Bookshelf
- are trademarks of Microsoft Corporation. IBM and PC AT are registered
- trademarks of International Business Machines Corporation. PS/2 is a
- trademark of International Business Machines Corporation. dBASE II is a
- registered trademark of Ashton-Tate Corporation. AT&T and UNIX are
- registered trademarks of AT&T. Lotus and 1-2-3 are registered trademarks
- of Lotus Development Corporation. Intel is a registered trademark of Intel
- Corporation. COMPAQ is a registered trademark of COMPAQ Computer
- Corporation. DESKPRO 386 is a trademark of COMPAQ Computer Corporation. CP/M
- is a registered trademark of Digital Research, Inc. Hercules is a registered
- trademark of Hercules Computer Technology. WordStar is a registered
- trademark of MicroPro International Corporation. Zenith is a registered
- trademark of Zenith Radio Corporation.
-
- ████████████████████████████████████████████████████████████████████████████
-
- Microsoft Windows/386: Creating a Virtual Machine Environment
-
- ───────────────────────────────────────────────────────────────────────────
- Also see:
- A Comparison of Current and Future Windows Versions
- ───────────────────────────────────────────────────────────────────────────
-
- Ray Duncan
-
- The past year has seen the emergence of a new class of personal computers,
- based on the IBM(R) PC AT(R) architecture but incorporating an Intel 80386
- microprocessor with a 32-bit memory path for increased performance. The
- pioneer in this category was the COMPAQ(R) DESKPRO 386(TM), which has since
- been joined by the IBM PS/2(TM) Model 80, as well as a score of machines from
- other clone vendors. Until now, the primary benefit associated with these
- machines has been their formidable speed. However, 32-bit operating
- systems and programming tools for the 80386 are still in the development
- stage, while 32-bit applications software waits in the wings until the
- tools stabilize.
-
- Microsoft Corp.'s new product, Windows/386, unmasks the larger potential
- of 80386 machines while protecting the user's investment in MS-DOS(R)-
- compatible hardware and software. The key features of Windows/386 are:
-
- ■ a graphical user interface compatible with Windows 2.0 and the OS/2
- Presentation Manager
-
- ■ true preemptive multitasking of MS-DOS applications, each in a private
- 640Kb memory space
-
- ■ applications that run under Windows/386 receive much more memory than
- they would under Windows 2.0
-
- ■ the ability to run MS-DOS applications in overlapping windows, even
- so-called "ill-behaved" applications that do not rely on MS-DOS or
- the ROM BIOS for screen output
-
- ■ exchange of screen data between both standard MS-DOS and Windows
- applications
-
- ■ emulation of the Lotus/Intel/Microsoft Expanded Memory Specification
- Version 4.0
-
- Windows/386 requires either an 80386-based PC AT-compatible or an AT&T(R)
- 6300 computer, an EGA, VGA, or CGA monitor, at least 1Mb of RAM (2Mb are
- recommended), and a fixed disk. The 80287 or 80387 numeric coprocessors
- are also supported.
-
-
- An Intel Retrospective
-
- Nearly all of the capabilities of Windows/386 depend on a feature of the
- 80386 called virtual 86 mode. To fully appreciate this particular facet of
- the 80386, it is necessary to digress for a moment and review the
- characteristics of the 80386's ancestors, the Intel 8086/88 and 80286
- microprocessors. Each successive generation of Intel processors has
- supported the software that was written for its predecessors by means of
- "execution modes," and virtual 86 mode is the logical culmination of this
- approach to software compatibility.
-
- The first Intel 16-bit processors, the 8086 and 8088 (announced in 1978
- and 1979 respectively), can address a maximum of 1Mb of memory. When
- memory is referenced, the contents of one of the four segment registers is
- shifted left four bits and combined with a 16-bit offset to form a 20-bit
- physical address; the segment registers simply act as base pointers. The
- 8086 and 8088 have no provision for memory protection, virtual memory, or
- privilege levels; any program can access any location in memory or any I/O
- device without restriction.
-
- The Intel 80286 (first shipped in 1982) represents a major increase in
- speed and capabilities over the 8086/88. It can run in either of two
- modes: real or protected. In real mode, the 80286 acts like a fast 8086
- with a few additional machine instructions. It can run virtually all
- 8086/88 software and is limited to 1Mb of memory.
-
- In protected mode, the 80286's mapping of addresses is altered to add a
- level of indirection. The value in a segment register is a selector, which
- is an index to an entry in a descriptor table that contains the base
- address and length of a physical memory segment, segment attributes
- (executable, read-only, or read-write), and privilege information. Each
- time a program references memory, the hardware accesses the associated
- descriptor to generate the physical address and simultaneously checks to
- make sure that the memory access is valid.
-
- This method of protected-mode address generation allows the 80286 to
- support memory protection and virtual memory management within a physical
- address space of 16Mb and a virtual address space of 1Gb. Four levels of
- privilege are also provided, allowing the operating system to be protected
- from applications programs and those programs from each other.
-
- When the Intel 80286 was designed, the dominant software base consisted of
- CP/M(R) applications such as WordStar(R) and dBASE II(R). At the time the 802
- was released, the IBM PC was just a few months old. Hence Intel engineers
- had no way of foreseeing the incredible success of the IBM PC and its
- compatibles, or the eventual need of an enormous user base to make a
- smooth transition from real-mode (8086/88) to protected-mode (80286)
- environments. Although the 80286 was designed to start up in real mode for
- compatibility with 8086 software, and although machine instructions were
- included to switch from real mode to protected mode, no mechanism was
- built into the chip to allow a return from protected mode to real mode
- under operating system control without halting the processor.
-
- This single omission proved to be a hideous technical problem during the
- development of the Microsoft OS/2, since one of the overriding design
- goals for the new operating system was to allow "old" (real-mode)
- applications to run alongside "new" (protected-mode) applications.
- Although experimentation and painstaking optimization eventually led to an
- acceptable solution for the necessary mode switching, another drawback
- remains: the protected-mode operating system cannot be shielded against
- bad behavior on the part of a real-mode program. By the very nature of
- real mode, such a program has a free hand with the hardware and can easily
- crash the machine.
-
-
- The Four Modes
-
- The Intel 80386 (introduced in 1985) is a true 32-bit processor that
- supports a 4Gb physical address space and a 64Tb virtual address space. To
- ensure compatibility with previous processors and to solve the problems of
- support for real-mode applications that were encountered with the 80286,
- the 80386 has no less than four different execution modes. The first is
- the familiar real mode, wherein the 80386 functions as a fast 8086/88-
- compatible processor with some bonus opcodes. Like the 80286, the 80386
- always powers up in real mode and can therefore run any existing 8086
- operating systems and software.
-
- In protected mode, the 80386 can take on two different personalities. It
- can execute a logical superset of the 80286 protected-mode instructions
- and run 16-bit programs, or it can run in its native mode, which offers
- 32-bit instructions, registers, and stacks, and allows individual memory
- segments as large as 4Gb. In either case, the 80386 translates selectors
- and offsets to linear addresses using descriptor tables in much the same
- manner as the 80286. However, an additional level of address
- translation──supported in hardware by page tables──allows much greater
- flexibility in mapping the linear addresses onto physical memory.
-
- Unlike the 80286, the 80386 allows the operating system to switch smoothly
- and quickly back from protected mode to real mode when necessary. But it
- is unlikely that this capability will find much use because of the 80386's
- fourth operating mode: virtual 86 mode.
-
-
- Four Modes of the Intel 80386 Microprocessor
-
- Real Mode Functions as a very fast 8086/88-compatible processor.
-
- Protected Mode Functions in protected mode as an enhanced 286
- (16-bit) processor.
-
- Protected Mode Functions in protected mode using full 32-bit
- (32-bit, native instructions, registers, and stacks.
- mode)
-
- Virtual 86 Mode Runs multiple, protected-mode, virtual 8086 machines,
- each with its own 1Mb of memory space.
-
-
- Device Virtualization
-
- A protected-mode 80386 operating system can create special memory
- segments──up to 1Mb in size──that have a remarkable characteristic: programs
- that run within these segments execute as though they were running on an
- 8086/88 in real mode. Each such segment is called a virtual 8086 machine,
- and each has its own address space, I/O port space, and interrupt vector
- table. Multiple virtual machines can be running simultaneously, each under
- the illusion that it is in complete control of the computer.
-
- The crucial difference between real mode and virtual 86 mode is that
- memory protection, virtual memory, and privilege-checking mechanisms are
- still in effect when a virtual machine is running. Thus, a program
- executing in a virtual 8086 machine cannot interfere with the operating
- system or damage other processes. If the program reads or writes memory
- addresses that have not been mapped into its virtual machine, or if it
- manipulates I/O ports to which it has not been allowed access, an
- exception (hardware interrupt) is generated, and the operating system
- regains control.
-
- The operating system's exception handler can choose to carry out the
- operation on behalf of the program running in the virtual machine,
- possibly substituting other port or memory addresses; it is also able to
- arbitrate requests from multiple virtual machines directed at the same I/O
- port or memory address. This process of intercepting I/O operations,
- where, for example, the operating system creates the illusion of a
- separate disk controller or video controller for each virtual machine
- while only one physical device is present in the system, is called device
- virtualization.
-
- A program that runs in the 80386's native 32-bit protected mode and
- oversees a set of virtual machines is called a 386 control program, or
- virtual machine monitor. Windows/386 is a such a virtual machine monitor;
- it also provides complete device virtualization for the PC's disk
- controller, video adapter, keyboard, mouse, timer chip, and 8259
- programmable interrupt controllers.
-
-
- The User Interface
-
- At first glance, the user interface of Windows/386 appears identical to
- that of Windows Version 2.0: it has the same MS-DOS Executive window,
- pull-down menus, "hot keys," and utilities (Cardfile, Terminal, and the
- like). A program is launched by selecting its executable file, its PIF
- file, or one of its data files with the arrow keys or by double-clicking
- with the mouse, just like under Windows. An open window can be brought to
- the foreground with Alt-Tab or Alt-Esc or by clicking on the window with
- the mouse; also, a window may be resized or moved by clicking and dragging
- its borders (see Figure 1).
-
- The first inkling that something different is going on comes when a
- standard application (that is, an MS-DOS application that is not written
- specifically for Windows) is started. Under Windows 2.0, only those
- relatively rare, "well-behaved" MS-DOS programs that perform all of their
- input and output through standard MS-DOS calls, strictly observe the
- system's memory management conventions, and avoid all direct access to
- hardware can run in a window. "Ill-behaved" programs that access the
- hardware directly for performance reasons──a category that includes nearly
- every popular MS-DOS application──must run full-screen: Windows 2.0 simply
- gets out of the way until such programs terminate, and its multitasking
- comes to a screeching halt.
-
- A standard application under Windows/386 initially comes up full-screen
- just as it does under Windows 2.0. But if the user presses Alt-Enter, the
- program is suddenly running in an overlapping graphics-mode window right
- alongside the true Windows application (see Figure 2). The user can use
- the mouse or the cursor keys to select and copy data from a window
- containing a standard application to any other window and can resize,
- move, or even "iconize" such a window. Furthermore, the user can toggle
- between windowed and full-screen mode with Alt-Enter at any time. When an
- application is full-screen, Windows/386 can place it into a text-display
- mode to allow faster displays, and the application's intrinsic mouse
- support works as though it were running under MS-DOS alone.
-
- Windows/386 associates a drop-down control menu, activated with the
- keycode Alt-Space, with each standard application that can be used to
- affect its window size, position, and behavior (as shown in Figure 2). The
- Settings dialog box (see Figure 3), which is reached through the control
- menu, allows the user to suspend or resume a standard application, specify
- whether it should run full-screen or in a window, and control its
- multitasking behavior. The initial settings for a program can also be
- determined by creating a PIF file for it.
-
- There are three multitasking options available for a standard application:
- foreground, background, and exclusive. When foreground is chosen, the
- application runs only when it is displayed full-screen or when its window
- has been selected, but other applications are allowed to run in the
- background at the same time. The background option means that the
- application will continue to run even when some other application is
- selected (of course, this is not useful for word processors and other
- programs that have nothing to do unless they are receiving keyboard
- input). When the exclusive option is picked, the application runs only
- when it is in the foreground, but while it is active it receives all of
- the CPU time and no other applications are allowed to run at all. Hence
- the exclusive option permits the application to perform as if it were in a
- single-tasking MS-DOS environment.
-
- Figures 4 and 5 show the process of defining an area of the standard
- application for copying, bringing up another application, and transferring
- the defined area.
-
- A little experimentation leads to an additional pleasant surprise: there
- seems to be much more RAM left for a standard application than is usual
- under Windows. In fact, if COMMAND.COM and then CHKDSK are run in a
- window, approximately 580Kb is seen to be available──as though Windows
- weren't present at all. And when multiple standard applications are
- launched, each runs in a separate memory space as large as 640Kb, until
- the physical memory of the system is exhausted. As a fringe benefit,
- devotees of RAM-resident applications (TSRs) can find relief from the
- phenomenon of "RAM cram" by starting multiple copies of COMMAND.COM to
- establish several MS-DOS sessions, and loading a different selection of
- TSRs into each session. This is the magic of the 80386's virtual 86 mode
- in action.
-
-
- How Windows/386 Works
-
- The Windows/386 system actually consists of three separate, mutually
- interdependent elements. The first is a regulation copy of MS-DOS or PC-
- DOS, Version 3.0 or later. The second is a copy of Windows 2.0, which is
- included in the Windows/386 product. The third is the core of the
- Windows/386 product, the Virtual DOS Machine Manager (VDMM).
-
- The MS-DOS component supplies the routine file, time, and date, as well as
- memory allocations services to the application programs running in each
- virtual machine; the MS-DOS SHARE.EXE module can optionally be loaded for
- networklike file locking and sharing support if multiple applications will
- be accessing the same files. Windows 2.0 provides the graphical user
- interface and pointer device support. The VDMM runs in the 80386's native
- 32-bit protected mode and oversees the multitasking and protected-memory
- aspects of the system's operation. The VDMM also takes advantage of the
- 80386's paging capability to supply expanded (Lotus/Intel/Microsoft EMS)
- memory to applications that require it. The EMS services furnished by
- Windows/386 are compatible with the recently announced EMS 4.0
- specification and do not require loading of any special driver.
-
- Windows/386 is started on a system that has already been booted up under
- MS-DOS or PC-DOS in the usual manner. When Windows/386 gains control, it
- loads the Virtual DOS Machine Manager into extended memory (the memory
- above the 1Mb boundary), creates an initial virtual machine (referred to
- as VM1), maps the existing copy of MS-DOS into it using the 80386's page
- tables, and then loads Windows 2.0 on top of MS-DOS in the first virtual
- machine. Control is then passed to Windows 2.0, which reads its
- configuration file (WIN.INI) and presents the familiar MS-DOS Executive to
- the user.
-
- True Windows applications, such as Cardfile and Terminal, run together in
- the virtual machine containing Windows itself (VM1). Windows presents such
- programs with exactly the same applications program interface as in real
- mode, and multiple Windows applications running concurrently are scheduled
- for execution by the internal Windows multitasker──that is, nonpreemptively.
- Just as in real mode, the Windows kernel can use expanded memory pages to
- swap the code and data segments belonging to Windows applications, so that
- the number of Windows applications that can be loaded simultaneously is not
- limited by the 640Kb address space.
-
- When a standard application is started, the VDMM creates a new virtual
- machine and maps the image of MS-DOS, the ROM BIOS data area, and other
- vital structures into it using the 80386 page tables, and then loads the
- application into the virtual machine (see Figures 6 and 7). If the
- application does not have a PIF file, the size of the new virtual machine
- is under the control of the windowmemsize= entry in the WIN.INI file and
- defaults to 640Kb. If a PIF file exists for the program, the size of the
- new virtual machine depends on the Kb required and Kb desired fields in
- the PIF, and on the amount of physical memory actually available (there is
- no demand paging in Windows/386 as in OS/2).
-
- While initializing a new virtual machine, the VDMM also creates a
- corresponding instance of a special application called VMDOSAP in VM1
- (multiple instances share the same code segment, so the overhead in the
- Windows VM for each additional standard application VM is minimal).
- VMDOSAP is analogous to the WINOLDAP module of real-mode Windows; when the
- standard application is running in a window instead of full-screen,
- VMDOSAP is responsible for any necessary translation and clipping of its
- output for the window shape, size, and graphics-display mode. It also
- translates input on behalf of the application when necessary (for example,
- when data is being pasted from another window). The operation of VMDOSAP
- is completely transparent to the application, and VMDOSAP does not occupy
- any memory space within the application's virtual machine.
-
- The final step in initializing the new virtual machine is to allocate a
- "shadow" video buffer for the application and select the application's
- initial display and multitasking status based on the contents of its PIF
- file. The shadow video buffer lies outside the VM's address space and
- holds a copy of the application's virtual screen; it is used to restore
- the display when the application is brought to the foreground and allows
- scrolling when the application is running in a window. If no PIF file
- exists for the program, it begins execution with a full-screen display and
- with its multitasking option set to "foreground"──that is, the application
- is suspended when it is in the background.
-
- The complete isolation of VMDOSAP from the application, and of standard
- applications from each other, is made possible by Windows/386's
- virtualization of the system's input and output devices, as well as by its
- control over each virtual machine's interrupt vector table and I/O port
- address space. When an application executes a software interrupt (to
- invoke an MS-DOS or ROM BIOS service), reads from or writes to an I/O
- port, or accesses a memory address that lies outside its virtual machine
- space, an exception is generated that is fielded by the VDMM.
-
- The VDMM disposes of the exception by examining the machine instruction
- that provoked it and the contents of the CPU registers. For example,
- requests for input and output can be funneled to VMDOSAP, while calls for
- file system services revert to the virtual machine's image of MS-DOS for
- processing. An application that accesses the hardware to select a video
- mode not supported by Windows/386 is not terminated, but is forced to run
- full-screen.
-
-
- Windows/386 Multitasking
-
- The Windows/386 multitasking scheduler uses the standard PC AT 18.2-Hz
- timer tick. Its allocation of CPU cycles across multiple virtual machines
- is determined by the multitasking setting on each application's control
- menu or from its PIF file. In the simplest case, where each active program
- has the "background" option selected, the currently selected application
- gets two-thirds of the CPU cycles, and the remaining one-third are divided
- among the other virtual machines. Applications that hook the ROM BIOS
- timer tick interrupt vector (Interrupt 1CH) receive a proportionate number
- of timer interrupts.
-
- There are also two extreme cases of multitasking worth mentioning. The
- first is when a standard application that runs under the exclusive option
- is selected. Such an application receives all the CPU cycles, and all
- other virtual machines are suspended. The other is encountered when no
- standard applications are running and only the Windows VM is active. In
- this case the internal Windows scheduler apportions the CPU cycles between
- various Windows applications, and the VDMM plays no multitasking role at
- all.
-
-
- Many Machines, One DOS
-
- One of the most interesting aspects of Windows/386 is its relationship to
- MS-DOS. The 80386's paging tables allow a single physical instance of MS-
- DOS to be mapped into each virtual machine's address space at the same
- apparent address. Windows/386 uses internal knowledge of MS-DOS and
- Windows to maintain the integrity of the file system and to prevent
- different applications from executing concurrently within MS-DOS's
- critical regions. It also maintains a copy of the Windows and MS-DOS
- control structures (such as the MS-DOS system file table and the ROM BIOS
- video driver data area) for each virtual machine, and swaps these in and
- out of the MS-DOS image to supply the proper context for the application
- that is running. These tables and structures are referred to as the MS-DOS
- kernel instance data.
-
- Since simply copying the kernel instance data back and forth on every
- timer tick would burn up a significant number of CPU cycles, and since
- applications may execute for prolonged periods (several context switches)
- without referencing MS-DOS or the ROM BIOS, the VDMM uses an interesting
- trick to reduce the multitasking overhead. Before a program is given
- control at the beginning of its time slice, the memory pages in its
- virtual machine that contain MS-DOS instance data, the interrupt vector
- table, and the ROM BIOS data area are marked "not present" in the CPU's
- page table. Only a few pages within the MS-DOS that contain instance data
- are marked "not present" and cause page faults. Each page is demanded
- independently of the others. This way many accesses to DOS cause no
- context switching overboard at all, and when an instance switch is
- required, its overhead is as small as possible.
-
- If the program attempts to access these pages (for example, by inspecting
- the current cursor location in the ROM BIOS data area or executing an
- Interrupt 21H), a page fault is generated that suspends the program and
- transfers control to the VDMM's interrupt handler. The VDMM handles the
- fault for that page by moving in the MS-DOS kernel instance data, marking
- the MS-DOS memory page as "present" in the page table to prevent further
- faults during the same time slice, and restarting the instruction that
- caused the fault. On the other hand, if the program runs to the end of its
- time slice without referencing instance memory, no harm has occurred, and
- the overhead of moving the data needlessly has been avoided.
-
-
- Communications
-
- The techniques used in Windows/386 to virtualize the video controller and
- asynchronous communications controller are also particularly interesting.
- Since direct hardware access to these devices by standard applications for
- performance reasons is common, Windows/386 must stay out of the way as
- much as possible. This ensures that the application's throughput will not
- not be impaired, and that other programs will not be disrupted while still
- providing for preemptive multitasking.
-
- As mentioned earlier, each virtual machine is allocated a "shadow" video
- buffer that is used to save a copy of its complete screen image when the
- application is running in a window or in the background. The buffer is
- located in extended memory, outside the virtual machine's 1Mb address
- space, and may range in size from 16Kb to 256Kb depending on the display
- mode selected by the program.
-
- When an application is running in the background or in a window, the VDMM
- uses the 80386's page tables to map the virtual machine's video refresh
- buffer addresses (segment A000H, B000H, or B800H) onto the shadow buffer.
- The application can modify what it perceives to be the video buffer at
- will (see Figure 8), but the physical screen is not directly affected. At
- intervals, the VDMM checks the "dirty bits" in the page table──which are
- set by the hardware when an address within the page is written to──to
- determine if the application has modified its video buffer. If a write is
- detected, the buffer is compared against an earlier copy. The changes are
- sent to VMDOSAP, which renders them into an appropriate pattern of pixels
- and displays them in the visible portion of the application's window.
-
- When an application is running full-screen, the virtual addresses of the
- video refresh buffer within its virtual machine are mapped onto the
- physical memory belonging to the video controller, and the shadow buffer
- is not used as an intermediary. Thus, the program's control of the video
- display is direct, just as if it were running in real mode and there is no
- speed degradation. When the user switches away from the full screen
- application, the VDMM simply copies the video controller's physical buffer
- to the shadow buffer (the 80386's double-word string move instruction,
- which transfers 32 bits at a time, is used to advantage here) before
- restoring the screen image of the Windows/386 desktop or the next full-
- screen application to be selected.
-
- Applications that run in certain EGA graphics modes receive special
- treatment. The virtual machine's video buffer addresses are mapped onto
- the second 128Kb of the EGA controller's physical refresh buffer. This
- lets the application use all of the EGA features without the overhead of
- emulation, conserves system memory, and allows a rapid switch to full-
- screen operation. (Instead of copying the shadow buffer to the physical
- buffer, the VDMM selects the second graphics page as the active display.)
-
- In contrast to the video controller, the asynchronous communications
- controller, serial port, is fully virtualized at all times. The VDMM
- always handles all communications interrupts and input or output
- operations, and maintains an internal queue of serial port data and status
- information. Even the initialization of the serial port (baud rate, word
- length, parity, and number of stop bits) is virtualized, and this
- information is maintained separately for each virtual machine.
-
- An application signals its intention to use the communications controller
- by attempting to read or write one of the controller's data ports, change
- its interrupt mask bit on the 8259 Programmable Interrupt Controller, or
- capture its interrupt vector. Any of these operations will generate an
- exception that is processed by the VDMM. If the serial port is not in use
- by another program, the VDMM assigns "ownership" of the virtualized
- controller to the program that caused the exception; otherwise, a dialog
- box is displayed and the user is allowed to decide which program will
- retain access to the device.
-
- Once a program establishes ownership of the communications controller, it
- can perform input and output operations, and service communications
- interrupts in a normal manner from its point of view. In actuality, the
- interrupts and I/O operations are simulated by the VDMM, which transfers
- data between the virtual machine and its internal queue. In this fashion,
- the operation of the physical device, which is asynchronous and can
- generate an interrupt at any time, is decoupled from the virtual 86
- machine──which can only execute during its time slice.
-
-
- Portents for the Future
-
- In summary, Windows/386 exploits the 80386's unique capabilities to
- furnish preemptive multitasking, a windowing user interface, data exchange
- between any two applications, and efficient use of large amounts of RAM in
- conjunction with existing MS-DOS and Windows applications. In doing so, it
- overcomes the two most common objections to real-mode Windows: the
- latter's inability to coexist with popular ill-behaved programs such as
- Lotus(R) 1-2-3(R) or Microsoft Word, and its hunger for memory. In addition,
- although Microsoft bills Windows/386 as an interim product, it has some
- interesting implications──both short-term and long-term.
-
- In the near future, until protected-mode applications appear for OS/2 that
- fully exploit its virtual memory management, multitasking, and
- interprocess communications facilities, Windows/386 offers more to 80386-
- based PC owners than does OS/2. Windows/386 allows multiple MS-DOS
- applications to run concurrently, while OS/2 supports only one real-mode
- application at a time. In addition, the Windows/386 memory overhead in
- each virtual machine is typically 80 to100Kb less than the OS/2 overhead
- in the DOS 3.x Box. Finally, since Windows/386 uses the "real" MS-DOS as a
- substrate──unlike OS/2, which in effect emulates MS-DOS──Windows/386 is
- compatible with a broader range of existing applications than is OS/2.
-
- Taking the longer view, the availability of a true 32-bit version of OS/2
- and the Presentation Manager for the 80386 will probably threaten
- Windows/386's market niche. However, Windows/386 has an important role to
- play here too. It serves as a technology testbed for memory management,
- multitasking, device virtualization, and MS-DOS virtual-machine techniques
- that can eventually be absorbed into its successor──a system that will run
- multiple MS-DOS applications, 16-bit 80286 protected mode applications,
- and 32-bit 80386 protected-mode applications simultaneously.
-
-
- ───────────────────────────────────────────────────────────────────────────
- A Comparison of Current and Future Windows Versions
- ───────────────────────────────────────────────────────────────────────────
-
- ╓┌──────────────────────┌──────────────┌──────────────┌───────────┌──────────
- Microsoft Microsoft MS OS.2
- Windows Windows Windows Presentation
- 1.03 & 1.04 2.0 386 Manager
-
- Presentation Spaces Tiled Overlapped Overlapped Overlapped
-
- More Consistent
- User and Keyboard
- Interfaces ── Yes Yes Yes
-
- Microsoft Microsoft MS OS.2
- Windows Windows Windows Presentation
- 1.03 & 1.04 2.0 386 Manager
- Processor 8088 8088 ── ──
- Environments 8086 8086 ── ──
- 286 286 ── 286
- 386 386 386 386
-
- Large Memory
- Support ── EMS/EEMS EMS/EEMS 16Mb
-
- Multitasking Nonpreemptive Nonpreemptive Fully Fully
- Preemptive Preemptive
-
- Enhanced Display
- Performance ── Yes Yes Yes
-
- Runs Exisiting
- Windows (1.03)
- Applications Yes Yes Yes No
- Microsoft Microsoft MS OS.2
- Windows Windows Windows Presentation
- 1.03 & 1.04 2.0 386 Manager
- Applications Yes Yes Yes No
-
- Graphics API GDI GDI GDI GPI
-
- Multiple Document
- Interface ── Yes Yes Yes
-
- Device Drivers ── Enhanced Enhanced New Model
-
- Old Application
- Support ── Improved Improved Improved
-
- Integral Part of OS ── ── ── Yes
-
- Protected Mode
- Applications
- Execution ── ── Yes Yes
-
- Microsoft Microsoft MS OS.2
- Windows Windows Windows Presentation
- 1.03 & 1.04 2.0 386 Manager
- New Development API ── ── ── Yes
-
- New User "Shell" and
- Keyboard Interface ── ── ── Yes
-
- Virtual 86 Mode ── ── Yes ──
-
-
- Figure 6: This diagram shows the relationship between VDMM, MS-DOS, Windows,
- the VMDOSAP module, and standard applications.
-
- ┌───────────────────────────┐ ╔═════════════╗ ╔═════════════╗
- │The Windows/386 Environment│ ║ MS-DOS 3 ║ ║ MS-DOS 3 ║
- └───────────────────────────┘ ║ Application ║ ║ Application ║
- ╚═════════════╝ ╚═════════════╝
- ╔═════════════╗ ╔═════════════╗
- ║ WIN ║ ║ WIN ║ ╔═════════════╗ ╔═════════════╗
- ║ Application ║ ║ Application ║ ║ VMDSAP DOS ║ ║ VMDOSAP DOS ║
- ║ ║ ║ ║ ║ Interface ║ ║ Interface ║
- ╚═════╤═══════╝ ╚═══════╤═════╝ ╚══╤════════╤═╝ ╚╤╤═══════════╝
- │ │ │ ┌────│────┘│
-
- ┌──────────────────────────────────────────────────┬──────────┐
- │ │ │
- │ Windows │ │
- │ │ │
- ├──────────────────────────────────────────────────┤ │
- │ │ │
- │ DOS 3.x │ │
- │ │ │
- ├──────────────────────────────────────────────────┘ │
- │ │
- │ Virtual DOS Machine Monitor (VDMM) │
- │ │
- └─────────────────────────────────────────────────────────────┘
-
-
- Figure 7: Memory occupied by the DOS and other fixed items, such as the ROM
- BIOS, is shared between all virtual machines. Thus the system
- consumes physical memory sufficient for only one copy of each of
- these items and shares this memory between all the virtual
- machines using the memory management capabilities of the 386.
-
- Components Within the
- Address Space of a VDM
-
- ╔═══════════════════════╗ FFFFFH
- ║ ROM ║
- ║ ║
- ╠═══════════════════════╣
- ║ EGA, etc. ║
- ╠═══════════════════════╣ 9FFFFH
- ║ ║
- ║ Application ║
- ║ Area ║
- ║ ║
- ╠═══════════════════════╣
- ║ ║
- ║ DOS ║
- ║ ║
- ╠═══════════════════════╣
- ║ Int Vect ║
- ║ BIOS Data ║ 00000H
- ╚═══════════════════════╝
-
-
- Figure 8: This diagram shows how a display adapter is virtualized.
-
- ┌────────────────────┐
- │Virtual Video Buffer│
- ╔═════════════════╗ └────────────────────┘
- ║ ║
- ║ ROM BIOS ║ ╔════════════════╗
- ║ ║ ╔═══════════════╗ ║ VDMM ║
- ╚═════════════════╩═══║ ╟──────────────┐ ║ & Virtual ║
- ║ ║ Gets Changes╞═║ Display Driver ║
- ┌─────────────────────┐║ Virtual Video ╟──────────────┘ ╚═══════╦════════╝
- │Writes into Video RAM│║ RAM ║ ║
- └─────────────────────┘║ ║ ║
- ╔═════════════════╦═══║ ║ ┌───────╨────────┐
- ║ ║ ╚═══════════════╝ │Sends Changes to│
- ║ ║ │ Windows/VMDOSAP│
- ║ DOS ║ └───────╥────────┘
- ║ Application ║ ║
- ║ ║ ╔══════════════════╗ ║
- ║ ║ ║ ░░░░░░░░░░░░░ ≡≡║ ╔═══════════════╗
- ║ ║ ║ ░░░░░░┌──────────╨────────────────╢ ║
- ╚═════════════════╝ ║ ░░ ══╡Renders Changes into Window║ VMDOSAP ║
- ║ ░░░░░░└──────────╥────────────────╢ ║
- ║ ░░░░░░░░░░░░░ ║ ╚════════════════╝
- ╚══════════════════╝
-
- ████████████████████████████████████████████████████████████████████████████
-
- Programming in C the Fast and Easy Way with Microsoft QuickC
-
- Augie Hansen
-
- The widespread use and popularity of the C language, which began in the
- early 1970s with researchers at Bell Laboratories, is likely to endure. C
- has matured in the marketplace and is becoming standardized, just as
- traditional languages like FORTRAN and COBOL did.
-
- The original purpose of C was to provide a malleable high-level assembler
- for reprogramming the UNIX(R) operating system for portability. Programmers
- have found the language and its support tools to be an inviting and highly
- compliant medium for system-level work and applications programming.
-
- The typical environment for C programmers, whether working on large
- multiuser systems or alone on PCs, has been to use a set of separate
- programs for each step in the process of translating source code into
- executable programs. The sequence is edit, compile (and sometimes
- assemble), link-edit object files and library modules, and then test.
- Depending on the results, the sequence might need to be repeated a number
- of times.
-
- Development can be tedious, even with a reasonable set of support tools,
- and is usually time-consuming. Making C programming faster and easier than
- is possible with traditional methods has become an important objective for
- vendors of C programming products.
-
- In the past few years, we have seen the introduction of C interpreters as
- front-end training, testing, and development systems. Interpreters
- typically give quick feedback to the user by avoiding the edit-compile-
- link-test cycle. However, such systems usually lack the flexibility for
- handling large programming projects.
-
- When an interpreter is used in program development, the source code
- produced is subsequently used as input to more traditional C compiler
- systems, which offer speed and size optimizations, the ability to handle
- multiple modules, provisions for custom libraries, and other convenient
- features.
-
- C compiler environments, the next stage in the evolution of C programming
- systems, are now the rage, and for a good reason: they provide the desired
- quickness on both ends of the development process. Creating a program is
- easier and faster than ever, and the final program runs at warp one.
-
-
- Introducing QuickC
-
- QuickC is an integrated C programming environment that provides all of the
- features you would expect in a mature program-development system while
- offering you the benefit of quick feedback and appropriate hand-holding
- when needed. The QuickC compiler can be obtained as a separate product or
- packaged with the Microsoft C Compiler Version 5.00.
-
- QC.EXE is the QuickC integrated environment program. It is great for
- learning C, useful in prototyping, and completely compatible with
- Microsoft C 5.00. The integrated environment consists of a tightly coupled
- full-screen editor, compiler, linker, and debugger. QuickC also has a
- built-in "make" feature that automates most of the process of compiling
- and linking programs.
-
- QCL.EXE, which is the QuickC equivalent of the C 5.00 CL control program,
- controls standalone operation. You can use the QCL program together with
- MAKE and the special version of the linker, LINK, to compile and link
- programs in the traditional manner.
-
- The use of separate programs is slower than working with the integrated
- environment because the number of disk accesses is significantly greater,
- but the approach lends itself well to automation via batch files and make
- files. Using a separate MAKE program gives you more latitude in preparing
- a program. It also provides greater access to other programs and features
- of the operating system than the built-in program-building feature of the
- integrated environment.
-
- QuickC is compatible with Microsoft C 5.00 in several ways. The compilers
- accept the same source code, use the same object file formats, and link
- with compatible libraries.
-
- Four memory models are supported: small, compact, medium, and large.
- Unless in-structed otherwise, the medium model is the default model used
- by QuickC. QuickC has no huge keyword to objects that exceed 64Kb in size,
- nor does it support the huge memory model.
-
- The near and far addressing keywords, however, are sup-ported. They are
- not part of the formal definition of C, but they are useful when you are
- pro-gramming the segmented archi-tecture of the Intel(R) 8086/80286
- microprocessor family.
-
- The few differences between QuickC and C 5.00 will affect those
- programmers who have exacting requirements for optimization of execution
- speed, for program size, or who need greater addressing flexibility than
- QuickC offers. Using QuickC for fast prototyping and then moving to the
- Microsoft Optimizing C Compiler to fine-tune program performance creates a
- comfortable working environment. The optimizing compiler has features for
- improving code size and execution speed.
-
-
- Integrated Environment
-
- The QuickC integrated environment, contained in QC.EXE, is shown
- schematically in Figure 1. The integrated environment is a collection of
- essential components that work together to increase the speed of creating
- and testing C programs.
-
- The built-in full-screen editor accepts input from both a keyboard and a
- mouse. It is a complete WordStar(R) compatible programmer's editor that
- accepts WordStar commands (a Ctrl-key combination) and IBM-compatible
- keyboard commands such as those found in Microsoft Word and many other
- visual text editors. Also, it is integrated with the compiler so that you
- can find errors and fix them quickly.
-
- Compiling at 7000 lines per minute, the QuickC in-memory compiler can
- capture information about errors (up to 26 of them) in a single
- compilation and feed the information back to the editor. For each error
- found, the editor places the cursor on the location in the source file
- where the error occurred.
-
- The compiler can produce a memory-based program or a disk-based
- executable; the choice is yours. Usually you will run entirely in memory
- to get the greatest speed advantage during development and then save the
- program to disk for later use as a standalone program.
-
- To help you find program bugs, QuickC incorporates a source-level debugger
- that is a comprehensive subset of the well-known CodeView(R) debugger. The
- debugger lets you single-step program execution, establish watch
- expressions and display their values, set multiple breakpoints, and search
- for functions by name. In addition, you can switch between displays of the
- program and its output by using a simple keypress command.
-
- Multimodule programs necessitate a significant amount of bookkeeping work.
- Such work is best left to computer programs, and QuickC obliges. A built-
- in program maintainer patterned after MAKE automatically prepares a make
- file from a program file list that you provide. As you modify your
- program, QuickC will keep each object module up to date with respect to
- its source file and controls linking if the executable file is to be
- preserved as an EXE file on disk.
-
-
- Core Library
-
- QuickC has a set of run-time library routines built right into the
- compiler. If a program uses only core routines, all external references
- can be resolved in memory, resulting in very high-speed program
- preparation. Figure 2 lists the core routines.
-
- If the program needs any routines that are not in the core set, they must
- be read from a library file on disk. QuickC uses the medium-memory model
- by default, so it normally looks for the library file MLIBCE.LIB or
- MLIBC7.LIB in the default library directory. The library to be chosen
- depends on whether floating-point emulation or 8087/80287 routines are
- used. Alternatively, QuickC looks for the component medium-model files if
- you choose not to create a combined library during installation. The
- component libraries are MLIBC.LIB, MLIBFP.LIB, EM.LIB or 87.LIB, and
- LIBH.LIB.
-
- Because searching a large file like MLIBCE.LIB takes a lot of time, you
- can speed up the process by creating a small library that contains only
- the routines your program needs. QuickC searches for a file named
- QUICK.LIB, or one you name on the QC invocation command line, if it cannot
- resolve external references by using the core routines.
-
- The QuickC system interacts with you via a menu bar and pull-down menus
- that you can control from the keyboard and a mouse, if one is installed.
- All frequently used selections can also be made by a shortcut method, such
- as pressing a function key, a Ctrl-key or Alt-key combination, or some
- other special key.
-
- Some of the available menu windows can be seen in Figures 3, 4, 5, and 6.
-
- Figure 3, which shows the editing window, adorned with scroll bars, and
- the File menu, indicates how the QuickC drop-down menus appear. Once a
- menu is selected, either from the keyboard by typing the first letter of
- its name while holding down the Alt key or by clicking a mouse button on
- the menu name, you can use either the arrow keys plus Enter or a mouse
- click to select an option.
-
- The figure shows the highlight on the "Set Program List" option. When you
- select it, this option leads to a dialog box (signified by "...") on which
- you select the names of files that comprise the program you want to
- compile. The dialog box lists all source files in the current or specified
- directory. You must indicate which of them comprise the sources of your
- program by using a "point and shoot" selection process. The selected files
- make up the program list that QuickC uses to build the program.
-
-
- In-Memory Compilation
-
- The advantage of in-memory compilation is that all source files, temporary
- files, and most library routines are held in high-speed, primary memory.
- This dramatically cuts the time it takes to produce an executable program
- compared with the traditional approach, which is usually very disk-
- intensive.
-
- The integrated environment facilitates debugging with close editor-
- debugger interaction. By placing the cursor on an error in your source
- code, you are spared the time and effort it takes to reload the editor,
- read in the source file, and move the cursor to the location of the
- error.
-
- In the integrated environment, errors are described in sufficient detail
- so that reference to manuals is usually unnecessary. For example, Figure 4
- shows an error message in the pop-up window at the bottom of the screen.
- The message is detailed enough to be helpful, and the cursor points to the
- line containing the error.
-
- Raw compilation speed on a given machine is about three-to-one in favor of
- QuickC compared with a standard C compiler. The programmer perceives the
- improvement in speed to be even greater because there is no need for
- continually loading and exiting a series of programs and suffering the
- concomitant disk-access times.
-
-
- Library Support
-
- The standard run-time library provided with QuickC includes all of the
- routines that accompany the Microsoft Optimizing C Compiler. The library,
- compatible with XENIX(R), provides extensive DOS support (bdos and intdos,
- for example), and includes routines that conform to the draft-proposed
- ANSI standard for C.
-
- A new graphics library is included in the QuickC package. It contains more
- than forty graphics routines that handle configuration, palette and
- attribute settings, outputting text and images, and a variety of other
- color and monochrome graphics tasks.
-
- A new set of DOS and BIOS interface routines add considerably to the
- support for PC hardware interactions. While not portable to other hardware
- environments, these routines allow you to get the most from a PC and its
- peripheral devices.
-
-
- Standalone Operation
-
- For programmers who prefer the traditional approach, QuickC provides
- separate programs for compiling and linking. QCL.EXE reads a list of
- optional control switches and filenames from the DOS command line and
- produces standard Microsoft format object files that you can put into
- libraries (use LIB.EXE) or link with other objects and libraries in order
- to produce executable programs.
-
- MAKE can control the entire process; it reads a list of instructions from
- a make file, and uses the time stamps on files to determine which files
- need to be recompiled and linked. MAKE is more flexible than the program
- maintenance feature built into the QuickC integrated environment.
-
- The library maintainer, LIB.EXE, permits you to add modules to a library,
- delete modules, copy modules to disk files, and combine libraries. It
- allows you to create your own custom libraries and modify existing
- libraries to meet special needs. For instance, to create a custom
- QUICK.LIB file of routines for a given program, you can use LIB to extract
- copies of the needed routines and then use LIB again to prepare the
- QUICK.LIB file.
-
-
- Sample Session
-
- Figures 7 and 8 show the two source files from which the TOUCH program, a
- useful adjunct to the MAKE program provided with the compiler, is built.
- TOUCH updates the date and time stamps associated with a file to the
- current date and time. This gives you a convenient way to force all or
- selected files for a program to be remade, even if the files were not
- modified since the last successful compile.
-
- TOUCH accepts command-line options -c and -v. TOUCH normally creates a
- named file if the file does not exist. The -c option tells TOUCH not to
- create any files. The -v option tells TOUCH to be verbose, giving the user
- a play-by-play description of what it is doing. This option caters to
- those programmers who like chatty programs.
-
- Figure 9 lists the make file and linker response files produced by the
- automatic program maintainer feature of QuickC. The make file is
- compatible with and can be used by the standalone MAKE command. The linker
- response file provides information needed by the LINK program when it
- combines object modules and libraries to produce an executable program.
-
- The source code is presented in its final form. However, to demonstrate
- the debugging capabilities of QuickC, we will introduce several deliberate
- errors into TOUCH.
-
- Program errors fall into two general categories: syntactic and semantic.
- Syntax errors result from how a program is put together. The rules for
- writing programs in C, as is the case for nearly all computer languages,
- are precise and rigid. Compilers have to be unforgiving in insisting on
- correct syntax. A missing semicolon, which effectively concatenates two
- perfectly good statements into one illegal one, is one of the more common
- syntax errors.
-
- Semantic errors result from the contextual meaning of a program's
- statements. Does the program run to completion and produce correct
- results? If not, it probably contains semantic errors, which can be
- further categorized into execution errors or logic errors.
-
- Execution errors are requests written in a perfectly acceptable way that
- are impossible to fulfill, for example, dividing by zero. A program
- containing such an error aborts with a run-time error. Logic errors also
- get by the compiler because the syntax is correct, and the program
- containing them runs fine, but it produces incorrect results. Such a
- program is not likely to become a best-seller.
-
- QuickC contains the tools to detect and correct both syntax errors and
- semantic errors. Syntax errors are caught during compilation and reported
- one at a time in a pop-up window. Up to 26 compile-time errors are
- maintained in a list that can be scanned in either direction.
-
- The "Warning Level" setting in the compile dialog box controls the level
- of checking and reporting to be done by QuickC (see Figure 6). The higher
- the warning level (they range from 0 to 4), the more detailed the checking
- and reporting. The resulting list of warnings and error messages will
- guide you in wringing out all compile-time errors.
-
- Figure 4 shows an error caught by the compiler. It is the common error of
- using assignment where the programmer really intended a test for equality.
- The statement
-
- if (fclose(fp) = EOF)
- { ... }
-
- should use "==" instead of "=". The error is detected because a function
- is not an lvalue, so nothing can be assigned into it. If the left-hand
- side of the assignment were an 1value, the compiler would be happy and
- compile without comment. Your program would execute, but probably
- incorrectly.
-
- Another common error is a missing parameter in an fprintf statement. Try
- leaving the stderr parameter out of an error message line. The compiler
- will detect the problem because it uses function prototypes to determine
- how many parameters a function takes and what data types the parameters
- have. In this case, QuickC will report incorrect parameter types. Because
- fprintf takes a variable number of arguments, the compiler cannot
- determine that the number of parameters is incorrect.
-
- You could still have semantic errors lurking about in your program, which
- the compiler cannot detect. Traditionally, C programmers have resorted to
- inserting printf statements in their programs to check values at critical
- locations during test runs. This is tedious, time-consuming, and
- unnecessary because of the built-in debugger provided in the QuickC
- integrated environment.
-
- To use the debugger, which contains an extensive subset of CodeView
- features, you must first compile your program with the Debug option set so
- that symbolic information needed by the debugger is preserved in the
- executable program (see Figure 6). Select the Run item (Alt-R) from the
- menu bar and then the Compile(C) dialog box. Use the Tab key to select
- the Debug option and the Spacebar to toggle it on. Press Enter to compile
- the program.
-
- After successful compilation, select the Debug item (Alt-D) from the menu
- bar and set watch expressions and breakpoints. A watch expression can be
- as simple as a variable name or as complex an expression as you need to
- check program operation. You can set watch expressions one at a time or
- several at once by separating them from each other with a semicolon. Watch
- expressions are displayed in a separate watch window at the top of the
- screen during program execution (see Figure 5).
-
- A breakpoint is a line in the program at which execution should stop to
- let you observe the values of watch expressions or just to observe the
- displayed output using the screen-switching feature of QuickC. It is
- simple, effective, and a lot easier and faster than placing printfs all
- over your program.
-
- You can set multiple watch expressions and breakpoints. If the ones you
- set initially don't do the job, it is a simple matter to clear them and
- set new expressions and breakpoints. There is no need to recompile as
- there would be with embedded printf statements.
-
- Introducing deliberate errors into TOUCH.C will help you to learn how to
- use the debugger. Try changing the sense of some logical tests (use "!="
- instead of "==") and observe the effect on the output.
-
- Deleting the two lines in TOUCH.C just before the first while loop
- produces an interesting result. The program will not skip the program name
- argument (argv[0]), and all following arguments will be treated as
- filenames even if they are preceded by a dash, which is the specified
- option flag. To fix the problem, you could set watch expressions on the
- command-line argument parameters, argc and *argv, by typing "argc; *argv"
- in the watch dialog box and put breakpoints on statements where these
- values should have just changed, such as the first statement inside each
- loop.
-
- You can provide a command line by selecting the Set Runtime Options (O)
- from the Run (Alt-r) menu before you run the program. Type only the
- command-line arguments; do not type the program name──it is already known
- to QuickC. Use something like
-
- -c one two three
-
- as a command line. After running the program, you can use the DOS Shell
- (D) selection of the File menu (Alt-F) to get to a temporary DOS command
- line. Run a DIR command and check the list of files. Don't be surprised to
- find that TOUCH created files named "-c", "one", "two", and "three", even
- though we instructed it not to create any files.
-
- The thorough syntax-checking and integrated debugging features provided by
- QuickC are as important to a programmer as the swiftness of the compiler.
- Become familiar with these easy-to-use tools and you will never want to be
- without them.
-
-
- Summary
-
- For easy learning and use of a C compiler, raw compilation speed, and
- overall flexibility, it's hard to beat QuickC. Compiling in the integrated
- environment makes trying out an idea exhilarating. Modifying the program
- until it does exactly what you want is as simple as playing "what if" with
- a spreadsheet. It's about time programmers received the same kind of help
- that financial analysts have had for years.
-
- When we took the big step from batch processing to interactive processing
- on mainframes back in the 1960s, programmer productivity received a great
- boost. The move to swift and capable integrated programming environments
- on personal computers provides the opportunity to make similar, perhaps
- even greater, productivity gains in the 1980s.
-
-
- Figure 1: You can use separate libraries insteaad of the combined library.
- The default memory model is medium. You can also select small,
- compact, or large.
-
- ┌──────────────────────────────────────────────────┐
- │ The QuickC Integrated Environment and Libraries. │
- └──────────────────────────────────────────────────┘
- ╔═══════════════════════════╗ ╔════════════════════════════╗
- ║ QC.EXE ║█ ║ MLIBCE.LIB* ║█
- ╠═══════════════════════════╣█ ╠════════════════════════════╣█
- ║ Full-screen Editor ║█ ║Combined Library File Merges║█
- ╟───────────────────────────╢█ ║ the following libraries: ║█
- ║ Compiler ║█ ║ ║█
- ╟───────────────────────────╢█ ║ MLIBC.LIB ║█
- ║ Source-level Debugger ║█ ║ MLIBFP.LIB ║█
- ╟───────────────────────────╢█ ║ LIBH.LIB ║█
- ║ Program List Maintainer ║█ ║ EM.LIB (or 87.LIB) ║█
- ╟───────────────────────────╢█ ╚════════════════════════════╝█
- ║ ║█ █████████████████████████████
- ║ ║█ ╔════════════════════════════╗
- ║ "Core Subroutine Library" ║█ ║ QUICK.LIB ║█
- ║ ║█ ╠════════════════════════════╣█
- ║ ║█ ║ Quick Library ║█
- ╚═══════════════════════════╝█ ╚════════════════════════════╝█
- ████████████████████████████ █████████████████████████████
-
-
- Figure 2: The core library routines are defined in QC.EXE and speed up
- linking time by avoiding unnecessary disk accesses.
-
- Core Library Routines
-
- abort _fmalloc isatty puts strlen
- access _fmsize itoa read strlwr
- atof fopen kbhit realloc strncat
- atoi fprintf longjump remove strncmp
- atol fputc lseek rewind strncpy
- bdos fputs ltoa rmdir strnset
- brk fread malloc rmtmp strpbrk
- calloc free _memavl sbrk strrchr
- chdir _freect memccpy scanf strrev
- chmod fscanf memchr segread strset
- clearerr fseek memcmp setbuf strspn
- close fstat memcpy setjmp strtok
- creat ftell _memmax setmode strupr
- dosexterr fwrite _memset setvbuf system
- eof agetch mkdir signal tell
- _exit getche movedata spawnl time
- exit getcwd msize spawnv tmpfile
- _expand _getdate _nfree sprintf tmpnam
- fclose _gettime _nheapchk sscanf tolower
- fflush getenv _nheapset strcat toupper
- _ffree gets _nheapwalk strchr tzset
- _fheapchk halloc _nmalloc strcmp ultoa
- _fheapset hfree _nmsize strcmpi unlink
- _fheapwalk int86 onexit strcpy vfprintf
- fgetc int86x open strcspn vprintf
- fgets intdos printf strdup vsprintf
- filelength intdosx putch stricmp write
- flushall
-
-
- Figure 7: TOUCH.C
-
- /*
- * PROGRAM: TOUCH
- *
- * DESCRIPTION: Update the last modification time of a file or a
- * group of files to the current time.
- *
- * ENTRY: A list of files to "touch". The filenames can be preceded
- * by one or both of the following options:
- *
- * -c don't create any files
- * -v operate in verbose mode (report activity)
- *
- * SYNTAX:
- * touch [-cv] filename ...
- */
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <ctype.h>
- #include <io.h>
- #include <errno.h>
- #include <sys\types.h>
- #include <sys\stat.h>
- #include <sys\utime.h>
-
- #define ERR 0x7FFF
- #define MAXNAME 8
-
- typedef enum { FALSE, TRUE } BOOLEAN;
- extern void usage(char *);
-
- int
- main(argc, argv)
- int argc;
- char *argv[];
- {
- int ch; /* character buffer */
- char *cp; /* character pointer */
- int badcount; /* number of files that cannot */
- /* be successfully updated */
- struct stat statbuf; /* buffer for stat results */
- BOOLEAN errflag; /* error flag */
- BOOLEAN cflag; /* creation control flag */
- BOOLEAN vflag; /* verbose control flag */
- FILE *fp; /* file pointer */
-
- static char pgm[MAXNAME + 1] = { "touch" };
-
- /*
- * Initialize flags and variables
- */
- errflag = cflag = vflag = FALSE;
- badcount = 0;
-
- /*
- * Move past the command name argument and look for
- * optional arguments (signaled by a leading dash)
- */
- ++argv;
- --argc;
- while (argc > 1) {
- cp = *argv;
- if (*cp != '-')
- /* not an option flag */
- break;
-
- /*
- * Having seen an option flag ('-'), look at
- * any option letters that follow it in the
- * current argument string
- */
- for (++cp; *cp != '\0'; ++cp)
- switch (*cp) {
- case 'c':
- /* don't create files */
- cflag = TRUE;
- puts("CREATION flag set");
- break;
- case 'v':
- /* verbose -- report activity */
- vflag = TRUE;
- break;
- default:
- fprintf(stderr, "%s: unknown option %c\n",pgm, *cp);
- usage(pgm);
- exit(ERR);
- }
- ++argv;
- --argc;
- }
-
- /*
- * Update modification times of files
- */
- for (; argc-- > 0; ++argv) {
- if (stat(*argv, &statbuf) == -1) {
- /* file doesn't exist */
- if (cflag == TRUE) {
- /* don't create it */
- ++badcount;
- continue;
- }
- else if ((fp = fopen(*argv, "w")) == NULL) {
- fprintf(stderr, "%s: Cannot create %s\n",
- pgm, *argv);
- ++badcount;
- continue;
- }
- else {
- if (fclose(fp) == EOF) {
- perror("Error closing file");
- exit(ERR);
- }
- if (stat(*argv, &statbuf) == -1) {
- fprintf(stderr, "%s: Can't stat %s\n", pgm, *argv);
- ++badcount;
- continue;
- }
- }
- }
- if (utime(*argv, NULL) == -1) {
- ++badcount;
- perror("Error updating date/time stamp");
- continue;
- }
- if (vflag == TRUE)
- fprintf(stderr, "Touched file %s\n", *argv);
- }
-
- return (badcount);
- }
-
-
- Figure 8: USAGE.C
-
- /*
- * usage()
- *
- * DESCRIPTION: Display an informative usage message using the
- * actual program name, which may have been changed by the user.
- *
- * ENTRY: A pointer to the program name.
- */
-
- #include <stdio.h>
-
- void
- usage(pname)
- char *pname;
- {
- fprintf(stderr, "Usage: %s [-c] [-v] file ...\n", pname);
- fprintf(stderr, "\t-c Do not create any files\n");
- fprintf(stderr, "\t-v Verbose mode -- report activities\n");
- }
-
-
- Figure 9: Make File
-
- #
- # Program: Touch
- #
-
- .c.obj:
- qcl -c -Iusage.c -W0 -Ze -Zid -AM $*.c
-
- usage.obj : usage.c
-
- touch.obj : touch.c
-
- Touch.exe : usage.obj touch.obj
- del Touch.lnk
- echo usage.obj+ >>Touch.lnk
- echo touch.obj >>Touch.lnk
- echo Touch.exe >>Touch.lnk
- echo Touch.map >>Touch.lnk
- link @Touch.lnk;
-
-
- Link Information - Touch.lnk
- ============================
- touch.obj+
- usage.obj
- Touch.exe
- Touch.map
-
- ████████████████████████████████████████████████████████████████████████████
-
- Character-Oriented Display Services Using OS/2's VIO Subsystem
-
- ───────────────────────────────────────────────────────────────────────────
- Also see the related article:
- A Survey of OS/2 Display Services
- ───────────────────────────────────────────────────────────────────────────
-
- Ray Duncan
-
- OS/2 provides the programmer with a fast and powerful set of video I/O
- functions. This particular group of API calls is important because the
- average user bases his assessment of the quality of application software
- largely on the ease and speed of his interaction with the program. He has
- neither the opportunity nor the experience necessary to peek into the
- source code and appreciate the elegance of its structure or the
- appropriateness of its algorithms, but the rapidity with which the
- application builds and updates its screen displays speaks for itself.
- Naturally, control of the video display has become one of the major areas
- of concern for the PC programmer.
-
- Unfortunately, the built-in video drivers available in MS-DOS(R) have done
- little to aid the programmer in his quest for competitive performance. The
- speed with which MS-DOS transfers text to the screen might be adequate for
- EDLIN, but it certainly doesn't suffice for sophisticated word processors,
- while the degree of control MS-DOS provides over character colors and
- attributes, display mode, and cursor shape and position is rudimentary at
- best. Consequently, the authors of most popular MS-DOS applications have
- felt justified in abandoning portability, bypassing the operating system
- altogether, and directly manipulating the video controller to achieve the
- desired throughput.
-
- In the real mode, single-tasking world of MS-DOS, such hardware-dependent
- display techniques rarely cause any significant problems, but the same
- cannot be said for the multitasking, protected-mode OS/2 environment. A
- program that tries to commandeer the video controller will either be
- terminated with a protection fault, or, if it manages to circumvent the
- memory-protection mechanisms, wreak havoc among the other processes that
- are active in the system. The designers of MS(R) OS/2 correctly perceived
- that they could head off such anarchy only by removing the motivation for
- programmers to go around the operating system and placed a high priority
- on the creation of a battery of video services rich enough and efficient
- enough to satisfy the needs of any reasonable application program.
-
- And that is just what they did. MS OS/2 provides application programs with
- a huge assortment of display functions that cover the full range of
- hardware independence, from the sophisticated graphical and rich text
- capabilities of the Microsoft(R) OS/2 Presentation Manager to the ability of
- an application to temporarily lock onto the logical or physical display
- buffer and modify its contents. (See "A Survey of OS/2 Display Services.")
- This article will focus specifically on the VIO subsystem, which occupies
- the middle ground (both in complexity and power) of OS/2's video cap-
- abilities. The other techniques for driving the display under OS/2 will be
- discussed in future articles.
-
-
- The VIO Subsystem
-
- OS/2's VIO subsystem provides character-oriented display services suitable
- for program editors, simple pop-up utilities, compilers and interpreters,
- and the like. The VIO function calls can be regarded as a superset of
- those available from MS-DOS and the IBM(R) PC ROM BIOS in real mode, and
- their actions can be grouped into the following general categories:
-
- ■ transferring strings of text to the screen at any position
- ■ controlling such text attributes as blink, underline, and color
- ■ controlling the cursor shape and position
- ■ scrolling and clearing the screen
- ■ setting the display mode
- ■ support for partial or full-screen pop-up windows
-
- The VIO subsystem is implemented as a dynamic link library (VIOCALLS.DLL)
- that resides on the system disk. Programs are bound to the VIO routines
- when they are loaded for execution, rather than at link time. Thus, the
- VIO library can be replaced at any time without recompiling or even
- relinking the client applications.
-
- Although the VIO subsystem does not support graphics operations aside from
- providing a means to get in and out of graphics display modes, programs
- that confine themselves to the use of VIO calls and do not access the
- logical or physical screen buffer will run properly in a window under the
- Presentation Manager without any changes. How is this possible? Under the
- Presentation Manager, the normal VIO dynalink library is replaced by a new
- "Windows-aware" library that maps each character and its attributes into
- an appropriate pattern of pixels and clips the output appropriately for
- the application's current window size.
-
- Figure 1 shows a summary of the VIO function calls. We'll take a close
- look at some of the most commonly used VIO services, together with sample
- C and MASM source code. Note that the C code shown assumes use of the
- large model, and the parameter called a "VIO handle" in the function
- descriptions is always zero in the Software Development Kit version of MS
- OS/2.
-
-
- Displaying Text
-
- There are a number of different VIO functions that can be used to place
- text on the screen. The simplest is VIOWRTTTY, which is called with the
- address and length of a string and a VIO handle and returns a status code
- of zero if the write succeeded or an error code if it failed. The control
- characters line feed, carriage return, backspace, and bell code are
- properly handled, automatic line wrap and screen scrolling are provided
- (unless they are intentionally disabled), and the cursor position is
- updated to lie at the end of the newly displayed text. Figure 2 is an
- example of the use of VIOWRTTTY.
-
- The VIOWRTTTY call is functionally similar to a call to DOSWRITE with the
- stdout handle (the preassigned handle for the standard output device), but
- is much faster and immune to redirection. As with DOSWRITE, and unlike the
- other VIO calls, ANSI escape sequences can be included in the text stream
- to control cursor position and character attributes.
-
- Ordinarily, the only error code returned by VIOWRTTTY is an invalid VIO
- handle. Providing the function with an invalid string address or length
- does not result in an error code but may well cause a General Protection
- Fault (Trap D), terminating the process.
-
- Three additional VIO calls are available for displaying text:
- VIOWRTCHARSTR (write character string), VIOWRTCHARSTRATT (write character
- string with attribute), and VIOWRTCELLSTR (write string of
- character/attribute pairs).
-
- These services are faster than VIOWRTTTY and offer direct control over
- screen placement. They are not sensitive to the presence of such control
- characters as carriage return or line feed; any control characters
- embedded in the string are simply displayed as their graphics character
- equivalents. These functions do not affect the current cursor position and
- only support limited line wrapping: if a string is too long for the
- current line, it will wrap onto the next line. However, if the end of the
- screen is reached, any remaining characters are discarded, the screen is
- not scrolled, and an error code is not returned.
-
- VIOWRTCHARSTR is the simplest of the three functions listed above. It
- accepts the address and length of a string, the screen position at which
- to begin writing the string, and a VIO handle. The new text assumes the
- attributes of the characters that were previously displayed at the same
- screen positions.
-
- VIOWRTCHARSTRATT is similar to the VIOWRTCHARSTR function, except for one
- additional parameter: the address of an attribute byte that is applied to
- every character in the string (see Figure 3). On monochrome adapters, the
- attribute byte specifies normal or reverse video, blink, underline, and
- intensity (see Figure 4). On color adapters in text modes, the attribute
- byte contains the background color in the upper four bits and the
- foreground color in the lower four bits (see Figure 5).
-
- VIOWRTCELLSTR displays a string that consists of alternating character and
- attribute bytes.VIOWRTCELLSTR is designed to be used in combination with
- VIOREADCELLSTR to restore an area of the display that was previously saved
- into a buffer. A programmer would not ordinarily select this function for
- normal text output, because generation of initialized strings with
- embedded attribute bytes is awkward in most languages, such strings are
- relatively bulky, and it is rare that an application needs to display a
- string where the attribute byte of each successive character is
- different.
-
- The VIO functions VIOWRTNCHAR (write string of identical characters),
- VIOWRTNATTR (write string of identical attribute bytes), and VIOWRTNCELL
- (write string of identical characters and attributes) offer some special
- capabilities that supplement the previously discussed text display
- functions and require similar parameters. VIOWRTNATTR replicates an
- attribute byte across a selected area without changing the text at that
- position and can rapidly alter the appearance of a display field.
- VIOWRTNCHAR and VIOWRTNCELL replicate a character or a character/attribute
- pair respectively and allow extremely efficient drawing of borders and
- similar screen objects.
-
-
- Scrolling and Clearing
-
- The four kernel services VIOSCROLLUP, VIOSCROLLDN, VIOSCROLLLF, and
- VIOSCROLLRT can clear a window of arbitrary size and position or scroll it
- up, down, left, or right any number of columns or rows. Any desired
- character and attribute byte can be specified to fill the newly blanked or
- scrolled lines. All four functions have the following parameters: the
- screen coordinates of the upper-left corner and lower-right corner of a
- window, the number of lines to be scrolled or blanked, the address of a
- character and attribute pair to be used to fill the blanked lines, and a
- VIO handle.
-
- Clearing the entire display in any mode, without first determining the
- screen dimensions, can be accomplished as a special case call of any of
- the four scroll functions, using an upper-left coordinate of (0,0), a
- lower-right coordinate of (-1,-1), and the value -1 as the number of lines
- to scroll. Figure 6 demonstrates clearing the screen to ASCII blanks with
- a normal video attribute. Scrolling a selected portion of the screen is
- also easy, as shown in Figure 7.
-
-
- Cursor Control
-
- The function VIOSETCURPOS positions the cursor, while the parallel
- function VIOGETCURPOS allows a program to obtain the current cursor
- location. Both VIOSETCURPOS and VIOGETCURPOS use text coordinates and
- assume a home position of (0,0) at the upper-left corner of the screen;
- neither function can be called in graphics modes. Figures 8 and 9 show
- examples of typical VIOSETCURPOS and VIOGETCURPOS calls.
-
- The functions VIOGETCURTYPE and VIOSETCURTYPE are used to get or alter the
- cursor height, width, and hidden/visible attributes. Both functions use a
- four-word data structure with the same format to communicate the attribute
- values──quite convenient when the cursor must be altered or hidden
- temporarily (see Figure 10).
-
-
- Display Mode Control
-
- The functions VIOGETMODE and VIOSETMODE allow programs to query or select
- the video display mode. Both calls use a data structure that contains
- flags for the adapter type, color burst enable, and text versus graphics
- mode; the number of displayable colors; the number of alphanumeric (text)
- columns and rows; and the number of pixel columns and rows. Another
- function, VIOGETCONFIG, returns the type of monitor and video adapter
- installed in the system and the amount of memory present on the adapter.
-
- This approach is open-ended: it allows the application to deal with the
- adapter on the basis of its capabilities and doesn't require the
- programmer to remember an ever-expanding list of "mode numbers" that are
- less and less related to the display modes they represent. For example,
- the code in Figure 11 selects 80 X 25, 16-color text mode with color
- burst enabled.
-
-
- Pop-up Support
-
- The functions VIOPOPUP and VIOENDPOPUP allow background processes to
- temporarily assert control of the screen and interact with the user.
- These processes, launched with the DETACH command, are placed in a
- special "black box" screen group; ordinarily the programs in this group
- cannot perform KBD or MOV input calls, and thus their output to the screen
- is discarded.
-
- In order to gain access to the screen, the background process first calls
- VIOPOPUP. This function has a wait/no-wait option: if wait is specified,
- the calling process is suspended until the screen is available; if no-wait
- is specified, the function returns immediately with an error code if the
- screen cannot be preempted, for example, if another background process has
- already called VIOPOPUP. If the VIOPOPUP call is successful, the current
- contents of the screen, which belong to the foreground process, are saved
- away, the screen is blanked and placed into an 80 X 25 text mode, and all
- further VIO calls by the background process that issued VIOPOPUP are
- directed to the active display.
-
- At this point, the background process can interact freely with the user.
- Other processes continue to run normally with their output going to the
- usual virtual screen buffer, until they require input or call VIOPOPUP, at
- which point they can be suspended. When the background process is finished
- with its pop-up activities, it must call VIOENDPOPUP to release control of
- the screen. The display belonging to the foreground process is then
- automatically restored. Figure 12 shows an example of this procedure.
-
- Since screen group switching is disabled during a VIOPOPUP...VIOENDPOPUP
- sequence and the abrupt transition from a normal display to the largely
- blank display of a pop-up (with a possible concomitant change in display
- mode) can be startling and disruptive to the user, use of VIOPOPUP should
- be kept to a minimum and reserved for true background processes. An
- ordinary application that wishes to use pop-up windows as part of its
- interaction can achieve more pleasant results by using the VIOREADCELLSTR
- and VIOWRTCELLSTR functions to save and restore small portions of the
- display.
-
-
- Summary
-
- MS OS/2's VIO subsystem provides a set of character-oriented display
- functions for use by so-called "kernel apps," programs that use only OS/2
- kernel services and do not rely on the presence of the Presentation
- Manager. Most of the programs initially ported to OS/2 from MS-DOS will
- likely be based on VIO services exclusively. This is because the VIO calls
- do not require any drastic changes to the converted program's internal
- logic; they are sufficiently powerful to make direct hardware access
- unnecessary and can be invoked directly from high-level languages. Also,
- the Presentation Manager itself will not be available during the
- introductory period.
-
-
- ───────────────────────────────────────────────────────────────────────────
- A Survey of OS/2 Display Services
- ───────────────────────────────────────────────────────────────────────────
-
- The MS OS/2 Presentation Manager is based on Microsoft(R) Windows and
- provides applications with a uniform graphical user interface. It supports
- text fonts, windowing and clipping, pull-down menus, pop-up dialog boxes,
- pointing devices, and a broad range of high-performance graphic drawing
- and painting operations. Applications written to take full advantage of
- the Presentation Manager's services must have a special structure and must
- abide by an intricate set of conventions. However, the programmer's payoff
- for making this effort is complete portability between machines and output
- devices that support the Presentation Manager, transparent adjustment of
- the program's output to compensate for the display's resolution and aspect
- ratio, the ability to exploit available fonts, and a shortened learning
- curve for the user.
-
- Character-oriented applications can avoid the complexity of the
- Presentation Manager graphical interface, while retaining device
- independence and the ability to run in a window, by using the function
- DOSWRITE together with the preassigned handles for the standard output (1)
- and standard error (2) devices to send their output to the screen. ANSI
- escape sequences embedded in the output allow for control of the video
- mode, foreground and background colors, and cursor position. This method
- is analogous to the use of Interrupt 21H Function 40H (Write to File or
- Device) under MS-DOS Versions 2.x and 3.x and is most appropriate for
- filters and other utility programs where redirectability of the output is
- an important consideration.
-
- For more flexibility and higher performance, character-oriented
- applications can employ the OS/2 kernel's VIO family of services. The VIO
- functions allow scrolling in all four directions, control over the cursor
- shape, more versatile assignment of character attributes and colors, and
- reading strings back from the screen buffer, among other things. The VIO
- calls are roughly analogous to the ROM BIOS video driver (Interrupt 10H)
- calls available under MS-DOS 2.x and 3.x in that they are immune to
- redirection of the standard output and mostly ignore control codes
- embedded in the text. Applications that use VIO calls and avoid other
- hardware dependence will still run in a window under the Presentation
- Manager.
-
- Finally, we come to two hardware-dependent display methods that are
- allowed by MS OS/2. Applications that need to present a graphics display
- without the aid of the Presentation Manager, drive the controller in a
- mode or resolution not supported by OS/2's built-in screen driver, or have
- other special requirements might use these methods.
-
- The first hardware-dependent display technique is to obtain a selector
- from OS/2 that gives the application direct access to the screen group's
- Logical Video Buffer. After making modifications to the contents of the
- buffer, the program issues an additional command to refresh the actual
- video display──this call has no effect if the application's screen group is
- not currently in the foreground. Such applications will obviously not run
- in a window under the Presentation Manager and may also have unexpected
- effects on other programs running in the same screen group, but they will
- not conflict with the operation of programs in other screen groups.
-
- The second, and potentially more destructive, hardware-dependent display
- technique is to obtain the actual physical address of the video refresh
- buffer from OS/2. After "locking" the screen with a function call, the
- application can write to the refresh buffer and modify the state of the
- video controller directly, "unlocking" the screen with another function
- call when it is finished. While the lock is in effect, the user is
- prevented from switching to another screen group, and background tasks are
- unable to obtain control of the screen so as to attract the user's
- attention.
-
-
- Figure 1: VIO Subsystem Services at a Glance
-
- Text Display Functions
-
- VioWrtTty Write text string to display, then reposition cursor
- at end of string. Line feeds, carriage returns, tabs,
- and backspaces are interpreted properly. Line-wrap
- and screen scrolling are provided.
-
- VioWrtCellStr Write string of alternating characters and attribute
- bytes to screen at specified position. Cursor
- position is not affected.
-
- VioWrtCharStr Write character string to screen at specified
- position. Each character takes on the attribute of the
- previous characters at the same position. Cursor
- position is not affected.
-
- VioWrtCharStrAtt Write character string to screen at specified
- position, applying same attribute byte to each
- character. Cursor position is not affected.
-
- VioReadCellStr Read string of characters and attributes from
- specified position in display buffer to local buffer.
-
- VioReadCharStr Read string of characters from specified position in
- display buffer to local buffer.
-
- Replication Functions
-
- VioWrtNAttr Replicate attribute byte on screen n times, starting
- at specified position. Cursor position is not affected.
-
- VioWrtNCell Replicate character and attribute byte on screen n
- times, starting at specified position. Cursor position
- is not affected.
-
- VioWrtNChar Replicate character on screen n times, starting at
- specified position. Cursor position is not affected.
-
- Cursor Size and Position
-
- VioGetCurPos Get cursor position.
-
- VioSetCurPos Set cursor position.
-
- VioGetCurType Get cursor shape and size.
-
- VioSetCurType Set cursor shape and size.
-
- Scroll or Clear Screen
-
- VioScrollDn Scroll entire screen or portion of screen down by 1
- to n lines, filling new lines with specified character
- and attribute, or erase part or all of screen.
-
- VioScrollLf Scroll left or clear screen as above.
-
- VioScrollRt Scroll right or clear screen as above.
-
- VioScrollUp Scroll up or clear screen as above.
-
- Display Mode Control
-
- VioSetANSI Turn interpretation of ANSI escape sequences on
- or off.
-
- VioSetCP Select code page used to display text data.
-
- VioSetFont Downloads a display font into the video adapter and
- defines the dimensions of a character cell.
-
- VioSetMode Select current display mode.
-
- VioSetState Set palette registers, border color, or
- blink/intensity toggle.
-
- Mode Information
-
- VioGetANSI Get state of ANSI driver (on or off).
-
- VioGetBuf Get selector for logical video buffer of current
- screen group and buffer length.
-
- VioGetConfig Get information about adapter type, display type,
- and amount of memory on adapter board.
-
- VioGetCP Get identifier for current code page in use for text
- display.
-
- VioGetFont Get character cell dimensions and address of bit
- table for current or specified font.
-
- VioGetMode Get current display mode. Information returned
- includes adapter type, number of colors, vertical and
- horizontal resolution in both characters and pixels.
-
- VioGetPhysBuf Get selector for physical video buffer.
-
- VioGetState Get current settings of palette registers, border
- color, and blink/intensity toggle.
-
- Miscellaneous Functions
-
- VioPrtSc Print screen.
-
- VioPrtScToggle Turn echo of display to print device on or off.
-
- VioScrLock Disable screen switching (used by a process that is
- updating the physical video display buffer directly).
-
- VioScrUnLock Enable screen switching (used when a process is
- finished with direct access to physical display
- buffer).
-
- VioShowBuf Force update of physical display buffer from logical
- buffer.
-
- Pop-up Support
-
- VioPopUp Allocate full-screen text-mode pop-up display (used
- by background process, for example, a TSR
- program, or to notify user of errors while process's
- screen group is not selected).
-
- VioEndPopUp Deallocate pop-up display screen.
-
- VioSavReDrawWait Allows a process to be notified when its screen
- should be saved or redrawn. Used by graphics mode
- program to recover from screen group switch or a
- pop-up by another program.
-
- VioSavReDrawUndo Cancels a VioSavReDrawWait call by another thread
- within the same process.
-
- VioModeWait Allows a process to be notified when it should
- restore the video display mode. Used by graphics
- mode program to recover from screen group switch
- or a pop-up by another program.
-
- VioModeUndo Cancels a VioModeWait call by another thread
- within the same process.
-
- VIO Function Replacement
-
- VioRegister Register video subsystem. Allows replacement of
- system's default routine for any or all VIO functions
- with new driver routines.
-
- VioDeRegister Deregister video subsystem.
-
-
- Figure 2: Using VIOWRTTTY to write the string "Hello World" to the screen
- at the current cursor position, then move to a new line (scrolling
- if necessary). The cursor position is updated appropriately after
- the write.
-
- Microsoft Macro Assembler:
- ∙
- ∙
- ∙
- push ds ; address of string to display
- push offset DGROUP:msg
- push msg_length ; length of string
- push 0 ; VIO handle (reserved)
- call VIOWRTTTY ; transfer to OS/2
- or ax,ax ; did write succeed?
- jnz error ; jump if write failed
- ∙
- ∙
- ∙
- msg db 'Hello World',0dh,0ah
- msg_length equ $-msg
-
- Microsoft C:
-
- extern unsigned far pascal VIOWRTTTY(char far *, unsigned, unsigned);
- ∙
- ∙
- ∙
- int status;
- static char msg[]="Hello World\r\n";
- ∙
- ∙
- ∙
- status=VIOWRTTTY(msg, sizeof(msg)-1, 0);
- ∙
- ∙
- ∙
-
-
- Figure 3: Using the VIOWRTCHARSTRATT function to write to the screen. This
- code displays the string "Hello World" in reverse video at cursor
- location (10,5), column 10, row 5. The VIOWRTxxx calls support
- high-speed output of strings of any length (up to the size of the
- screen), with simultaneous control over character position and
- attributes.
-
- Microsoft Macro Assembler:
- ∙
- ∙
- ∙
- push ds ; address of string
- push offset DGROUP:msg
- push msg_length ; length of string
- push 5 ; Y position
- push 10 ; X position
- push ds ; address of reverse video attrib
- push offset DGROUP:rev_attr
- push 0 ; VIO handle (reserved)
- call VIOWRTCHARSTRATT ; transfer to OS/2
- or ax,ax ; did write succeed?
- jnz error ; jump if write failed
- ∙
- ∙
- ∙
- msg db 'Hello World'
- msg_length equ $-msg
-
- rev_attr db 70h ; reverse video attribute
-
- Microsoft C:
-
- extern unsigned far pascal VIOWRTCHARSTRATT(char far *, unsigned,
- unsigned, unsigned, char far *, unsigned);
- ∙
- ∙
- ∙
- int status;
- static char msg[]="Hello World";
- char rev_attr=0x70;
- ∙
- ∙
- ∙
- status=VIOWRTCHARSTRATT(msg,sizeof(msg)-1,5,10,&rev_attr,0);
- ∙
- ∙
- ∙
-
-
- Figure 4: Attribute byte for monochrome display adapter (MDA), EGA in
- monochrome text mode, or Hercules(R) Graphics Card in text mode.
-
- 7 │ 6 5 4 │ 3 │ 2 1 0
- BL │background │I │forground
-
- BL = blink bit
- I = intensity (highlight or bold)
-
- Background Foreground Display
- 000 000 no display
- 000 001 underline
- 000 111 normal video
- 111 000 reverse video
-
-
- Figure 5: Attribute byte for graphics adapter in text mode is divided into
- two four-bit fields that control the foreground and background
- colors. The color assignments show are immutable for the original
- Color/Graphics Adapter and are the defaults for the Enhanced
- Graphics Adapter (other color assignments can be obtained by
- programming the EGA palette with VIOSETSTATE).
-
- 7 6 5 4 │ 3 2 1 0
- background │foreground
-
- Color Assignments
- 0 black
- 1 blue
- 2 green
- 3 cyan
- 4 red
- 5 magenta
- 6 brown
- 7 white
- 8 dark gray
- 9 light gray
- 10 light green
- 11 light cyan
- 12 light red
- 13 light magenta
- 14 yellow
- 15 intensified white
-
-
- Figure 6: Clearing the screen to ASCII blanks with a normal video attribute,
- using one of the four VIO scroll functions. This special case call
- with an upper-left window coordinate of (0,0), lower-right
- coordinate of (-1,-1), and -1 as the number of lines to scroll
- does not require that the programmer first determine the
- dimensions of the active display.
-
- Microsoft Macro Assembler:
- ∙
- ∙
- ∙
- push 0 ; Y upper-left
- push 0 ; X upper-left
- push -1 ; Y lower-right
- push -1 ; X lower-right
- push -1 ; number of blank lines
- push ds ; address of character/attrib
- push offset DGROUP:init_cell
- push 0 ; VIO handle (reserved)
- call VIOSCROLLUP ; transfer to OS/2
- or ax,ax ; did scroll call succeed?
- jnz error ; jump if call failed
- ∙
- ∙
- ∙
- init_cell db 20h,07h ; ASCII blank, normal video
-
- Microsoft C:
-
- extern unsigned far pascal VIOSCROLLUP(unsigned, unsigned, unsigned,
- unsigned, unsigned, char far *, unsigned);
- ∙
- ∙
- ∙
- int status;
- static char init_cell[2]; /* initialization parameter */
- init_cell[0]=0x20; /* character = ASCII blank */
- init_cell[1]=0x07; /* attribute = normal video */
- ∙
- ∙
- ∙
- status=VIOSCROLLUP(0, 0, -1, -1, -1, init_cell, 0);
- ∙
- ∙
- ∙
-
-
- Figure 7: Scrolling selected portions of the screen in any direction can be
- accomplished very easily with the VIOSCROLLxx functions──a useful
- building block for windowing packages. For example, this code
- scrolls the bottom half of the screen up one line, filling the
- newly blanked line with ASCII blanks having a normal video
- attribute, and leaving the top half of the screen untouched.
-
- Microsoft Macro Assembler:
- ∙
- ∙
- ∙
- push 12 ; Y upper-left
- push 0 ; X upper-left
- push 24 ; Y lower-right
- push 79 ; X lower-right
- push 1 ; lines to scroll
- push ds ; address of character/attrib
- push offset DGROUP:init_cell ; for new line
- push 0 ; VIO handle
- call VIOSCROLLUP ; transfer to OS/2
- or ax,ax ; did scroll succeed?
- jnz error ; jump if error
- ∙
- ∙
- ∙
- init_cell db 20h,07h ; ASCII blank, normal video
-
- Microsoft C:
-
- extern unsigned far pascal VIOSCROLLUP(unsigned, unsigned, unsigned,
- unsigned, unsigned, char far *, unsigned);
- ∙
- ∙
- ∙
- int status;
- static char init_cell[2]; /* initialization parameter */
- init_cell[0]=0x20; /* character = ASCII blank */
- init_cell[1]=0x07; /* attribute = normal video */
- ∙
- ∙
- ∙
- status=VIOSCROLLUP(12, 0, 24, 79, 1, init_cell, 0);
- ∙
- ∙
- ∙
-
-
- Figure 8: Cursor positioning with the VIO calls. This code locates the
- cursor on the first column of the last line of the screen.
-
- Microsoft Macro Assembler:
- ∙
- ∙
- ∙
- push 24 ; Y coordinate, row
- push 0 ; X coordinate, column
- push 0 ; VIO handle (reserved)
- call VIOSETCURPOS ; transfer to OS/2
- or ax,ax ; did function succeed?
- jnz error ; jump if function failed
- ∙
- ∙
- ∙
-
- Microsoft C:
-
- extern unsigned far pascal VIOSETCURPOS(unsigned, unsigned, unsigned);
- ∙
- ∙
- ∙
- int status;
- ∙
- ∙
- ∙
- status=VIOSETCURPOS(24,0,0);
- ∙
- ∙
- ∙
-
-
- Figure 9: Reading the cursor position with VIOGETCURPOS. The cursor
- location is returned in text coordinates that assume a home
- position of (0,0).
-
- Microsoft Macro Assembler:
- ∙
- ∙
- ∙
- push ds ; address to receive row (Y)
- push offset DGROUP:CurY
- push ds ; address to receive column (X)
- push offset DGROUP:CurX
- push 0 ; VIO handle
- call VIOGETCURPOS ; transfer to OS/2
- or ax,ax ; was cursor position obtained?
- jnz error ; jump if function failed
- ∙
- ∙
- ∙
- CurY dw ?
- CurX dw ?
-
- Microsoft C:
-
- extern unsigned far pascal VIOGETCURPOS(unsigned far *,
- unsigned far *, unsigned);
- ∙
- ∙
- ∙
- int curx,cury,status;
- ∙
- ∙
- ∙
- status=VIOGETCURPOS(&cury,&curx,0);
- ∙
- ∙
- ∙
-
-
- Figure 10: Example of altering the cursor to a block and then back to its
- original shape.
-
- Microsoft Macro Assembler:
- ∙
- ∙
- ∙
- push ds ; address of structure to
- push offset DGROUP:CurData ; receive cursor information
- push 0 ; VIO handle
- call VIOGETCURTYPE ; transfer to OS/2
- or ax,ax ; did we get cursor info?
- jnz error ; jump if call failed
-
- mov ax,CurStart ; save cursor starting line
- mov CurPrev,ax
-
- mov CurStart,0 ; force starting line to
- ; zero to get block cursor
-
- push ds ; address of cursor data block
- push offset DGROUP:CurData
- push 0 ; VIO handle
- call VIOSETCURTYPE ; transfer to OS/2
- or ax,ax ; did we change cursor?
- jnz error ; jump if call failed
- ∙
- ∙
- ∙
- mov ax,CurPrev ; restore original cursor shape
- mov CurStart,ax
-
- push ds ; address of cursor data block
- push offset DGROUP:CurData
- push 0 ; VIO handle
- call VIOSETCURTYPE ; transfer to OS/2
- or ax,ax ; did we change cursor?
- jnz error ; jump if call failed
- ∙
- ∙
- ∙
- CurData label byte ; cursor data structure
- CurStart dw ? ; starting scan line
- CurEnd dw ? ; ending scan line
- CurWidth dw ? ; width (0=default)
- CurAttr dw ? ; attribute (0=visible, -1=hidden)
-
- CurPrev dw ? ; previous starting line for cursor,
- ; used to restore shape later
-
- Microsoft C:
-
- struct CursorData {
- unsigned cur_start;
- unsigned cur_end;
- unsigned cur_width;
- unsigned cur_attribute;
- };
-
- extern unsigned far pascal VIOGETCURTYPE(struct CursorData far *, unsigned);
- extern unsigned far pascal VIOSETCURTYPE(struct CursorData far *, unsigned);
- ∙
- ∙
- ∙
- struct CursorData CurData;
- int status,CurPrev;
- ∙
- ∙
- ∙
- status=VIOGETCURTYPE(&CurData,0); /* get current cursor data */
-
- CurPrev=CurData.cur_start; /* save cursor start line */
- CurData.cur_start=0; /* set start line to 0 */
-
- status=VIOSETCURTYPE(&CurData,0); /* force block cursor */
- ∙
- ∙
- ∙
- CurData.cur_start=CurPrev; /* previous cursor start line */
-
- status=VIOSETCURTYPE(&CurData,0); /* restore cursor size */
- ∙
- ∙
- ∙
-
-
- Figure 11: Using VIOWRTTTY to write the string "Hello World" to the screen
- at the current cursor position, then move to a new line
- (scrolling if necessary). The cursor position is updated
- appropriately after the write.
-
- Microsoft Macro Assembler:
- ∙
- ∙
- ∙
- push ds ; address of mode data structure
- push offset DGROUP:TextMode
- push 0 ; VIO handle (reserved)
- call VIOSETMODE ; transfer to OS/2
- or ax,ax ; did mode set succeed?
- jnz error ; jump if mode set failed
- ∙
- ∙
- ∙
- TextMode dw 8 ; length of data passed
- db 1 ; non-MDA, text mode, color enabled
- db 4 ; 16 colors
- dw 80 ; 80 text columns
- dw 25 ; 25 text rows
-
- Microsoft C:
-
- struct ModeData {
- unsigned length;
- unsigned char type;
- unsigned char color;
- unsigned col;
- unsigned row;
- unsigned hres;
- unsigned vres;
- };
-
- extern unsigned far pascal VIOSETMODE(struct ModeData far *, unsigned);
- ∙
- ∙
- ∙
- int status;
- struct ModeData TextMode; /* init mode params */
- ∙
- ∙
- ∙
- TextMode.length=8; /* amount of data passed */
- TextMode.type=1; /* non-MDA, text mode, color */
- TextMode.color=4; /* 16-color mode */
- TextMode.col=80; /* 80 columns */
- TextMode.row=25; /* 25 rows */
-
- status=VIOSETMODE(&TextMode,0); /* force mode change */
- ∙
- ∙
- ∙
-
-
- Figure 12: Example of the use of VIOPOPUP and VIOENDPOPUP by a background
- process to interact with the user. Note that if a successful
- VIOPOPUP call is made, the program must be careful not to take
- any branch away from the main line of execution toward
- VIOENDPOPUP.
-
- Microsoft Macro Assembler:
- ∙
- ∙
- ∙
- ; First put up pop-up Window...
- push ds ; address of Wait flags
- push offset DGROUP:WaitFlag
- push 0 ; VIO handle
- call VIOPOPUP ; transfer to OS/2
- or ax,ax ; did we capture display?
- jnz error ; no, jump to error handler
-
- ; now display pop-up message...
- push ds ; address of message
- push offset DGROUP:msg
- push msg_len ; length of message
- push 12 ; Y
- push (80-msg_len)/2 ; X (center it)
- push 0 ; VIO handle
- call VIOWRTCHARSTR ; transfer to OS/2
-
- push 0 ; pause for 5 seconds
- push 5000
- call DOSSLEEP ; transfer to OS/2
-
- ; Take down pop-up window...
- push 0 ; VIO handle
- call VIOENDPOPUP ; (should never fail)
- ∙
- ∙
- ∙
- msg db 'Hello World'
- msg_len equ $-msg
-
- WaitFlag dw 1 ; bit 0=1 if Wait until pop-up
- ; is available, =0 if no wait
-
- Microsoft C:
-
- extern unsigned far pascal DOSSLEEP(unsigned long);
- extern unsigned far pascal VIOWRTCHARSTR(char far *, unsigned,
- unsigned, unsigned, unsigned);
- extern unsigned far pascal VIOPOPUP(unsigned far *, unsigned);
- extern unsigned far pascal VIOENDPOPUP(unsigned);
- ∙
- ∙
- ∙
- static char msg[]="Hello World";
- ∙
- status=VIOPOPUP(&WaitFlag, 0); /* pop-up screen */
-
- /* display msg at center screen */
- status=VIOWRTCHARSTR(msg,sizeof(msg)-1,12,(80-sizeof(msg))/2,0);
-
- status=DOSSLEEP(5000L); /* pause for 5 seconds */
-
- status=VIOENDPOPUP(0); /* release pop-up screen */
- ∙
- ∙
- ∙
-
- ████████████████████████████████████████████████████████████████████████████
-
- Dynamic Allocation Techniques for Memory Management in C Programs
-
- Steve Schustack
-
- Most end-user PCs have between 64Kb and 640Kb of memory, and it is safe to
- assume that many of these machines will, at some point, have more RAM
- added on. Though we know what the lower and upper bounds of installed
- memory are, we can never be certain about how much memory any given
- machine might have.
-
- Programs can be written to run in specific, fixed-size environments, or
- they can be designed to dynamically adjust to any given amount of RAM.
- Programs that support such flexible memory management capability certainly
- offer an extra measure of utility. I began to consider this when my
- favorite word processor did not recognize the 128Kb of additional memory
- I had just installed in my own machine; instead it completely ignored the
- additional RAM and merrily continued to access the PC's disk.
-
- Had the programmers who wrote my word processing program utilized dynamic
- memory allocation techniques, the program would have been able to use the
- additional memory I had installed to hold more of my text, and perhaps to
- hold more of the application as well.
-
- Dynamic allocation allows programs to adapt to their environments, taking
- advantage of any memory available on an as-needed basis. Such programs
- dynamically allocate memory to hold array and linked list data rather than
- using fixed array dimensions, which are set by the programmer in array
- declarations. The only limitation on the array sizes becomes the amount of
- memory available on the user's system.
-
- This article will focus on using C's standard library functions to write C
- programs that employ data structures that grow and shrink dynamically
- during program execution. We will implement these functions through
- variable-sized arrays and linked lists, which are applications of dynamic
- memory allocation.
-
- Standard library functions such as malloc and free are among the
- programmer's tools explored here. The function malloc, for example,
- accepts as its argument the number of bytes of memory needing to be
- dynamically allocated, and returns a pointer to a block of free memory at
- least that large, provided enough memory is available. A call to the
- function free returns dynamically allocated memory to the pool of memory
- available to a program.
-
-
- The Problem
-
- Like most programming languages, C requires that when you declare an array
- you must specify its size. This often becomes a limitation of the program,
- however, because it sets an upper bound on the amount of data the program
- can process. Why not simply make the arrays so large that they are sure
- to satisfy even the largest uses?
-
- Regrettably, memory is not limitless. The more memory a program needs, the
- larger the system required to run it. As terminate-and-stay-resident (TSR)
- programs become more popular and operating system memory requirements
- grow, it makes a great deal of sense for programs to conserve memory by
- using only what is needed. A PC's memory has, in fact, become a shared
- resource. What are a programmer's options for reserving memory to hold a
- program's data?
-
-
- Memory Organization
-
- A program's memory is organized into areas known as heap, stack, data, and
- code. The heap is at the top of a program's memory space and is the source
- of dynamically allocated memory. The size of the heap depends on the sizes
- of the other areas, because the heap consists of all available memory not
- assigned to one of the other areas.
-
- The stack is just below the heap in memory; its size is set by LINK,
- either to 2048 bytes by default, or to the size given to LINK with the
- /STACK option. The data area contains static and external variable data.
- Executable machine instructions fill the code area.
-
-
- Allocation of Memory
-
- When a program obtains memory for its exclusive use, the memory is said to
- be allocated to that program. C programs have two options for memory
- allocation. The most common way memory is allocated is by simple
- declaration of variables. The alternative is to dynamically allocate
- memory by calling certain standard library functions, such as malloc,
- calloc, and realloc.
-
- Memory for static and external variables is allocated and initialized only
- once, prior to execution of the program, at the time the program is loaded
- from disk into RAM. It remains allocated until the program terminates its
- execution.
-
- All other variables, namely those of auto or register storage class, are
- allocated each time the function (or block) in which they are declared is
- entered. The memory for auto and register variables is released
- (deallocated) when the flow of control leaves the declaring function (or
- block).
-
- Because memory for statics and externals is allocated throughout
- execution, limiting the use of static and external variables tends to
- conserve RAM. Auto and register variables, on the other hand, need only be
- allocated when a given function is active (on the calling stack). A
- function is active when it is called by main or any other function.
- Lifetime, scope (visibility), and initialization are other factors to
- consider when selecting storage classes for your variables.
-
- In order to dynamically allocate memory, the amount of memory needed must
- first be known. The C keyword operator sizeof is used to find the number
- of bytes allocated to any variable that has been declared. The sizeof
- operator may also be used on data types that are enclosed in parentheses.
- This means that a program can expect the value of the expression "sizeof
- (int)" to be two or four, depending on whether it was compiled and whether
- it is executing on a 16-bit or on a 32-bit CPU (see Figure 1).
-
-
- Variable-length Arrays
-
- Dynamic allocation of a block of memory takes place during execution. The
- size of the dynamic block is specified using arguments to standard library
- functions such as malloc, calloc, and realloc. This is in contrast to the
- dimension of an array, which is fixed by a constant or by a constant
- expression in a declaration.
-
- The elementary program SUM_NUM, listed in Figure 2, introduces the
- standard library function calloc. Within SUM_NUM, calloc returns the
- address of a block of memory whose size is specified by passing the number
- of cells (elements) to be allocated, along with the size in bytes of each
- cell. Simply declare a pointer to the type of data to be stored, and
- assign the return value from calloc to that pointer variable, as follows:
-
- long *nums;
- ∙
- ∙
- ∙
- nums = (long *) calloc(how_many,sizeof(long));
-
- These two statements create a dynamically allocated array of "how_many"
- long integers, and cause the pointer "nums" to point to that array.
-
- The "(long *)" before calloc, which is a cast operator indicating "pointer
- to type long," is optional and specifies a conversion that will avoid
- compiler warnings such as "different levels of indirection." If a function
- is not declared prior to being called, or if it is not explicitly declared
- as being of a particular type (like "long"), the compiler assumes that
- "int" is the type of value returned by that function. Assigning "nums" a
- value from calloc without the "(long *)" cast would therefore result in
- trying to assign an int value to a variable declared as long, resulting in
- a mismatch of levels of indirection and causing a compiler warning
- message.
-
- If the amount of memory requested is not available, then calloc returns
- the value of NULL, which happens to be defined in stdio.h as zero. If you
- don't test for this special null pointer value and store data at address
- 0, you will get a "null pointer assignment" error when your program
- terminates. Clobbering data in memory not dynamically allocated and not
- allocated to any variable, such as data at address 0, can easily go
- unnoticed and create hard-to-find errors, hard-to-find because the program
- may only misbehave while handling some rare and unusual case not tested
- during program development.
-
-
- Pointers and Arrays
-
- An understanding of C's use of pointers and arrays is essential,
- especially in relation to memory management schemes. The pointer and array
- data types are very closely related──perhaps more than you realized.
- Whenever you refer to an array in C by using that array in an expression,
- such as passing it to a function or getting the value of one of its
- elements, the value of the name of the array is taken to be the starting
- address of that array. So if you have a pointer variable that has been
- made to point to an array of the same data type, you can use that pointer
- in expressions as if it were the array (see Figure 3).
-
- The fact that pointers behave very much like arrays means that you can
- operate on elements of the array of data pointed to by the variable nums
- in SUM_NUM.C (in Figure 2), as if nums were an ordinary array of long
- integers. The address of an element pointed to by nums in the expression
- "&nums[inum]" is passed to the function scanf. The value of the element is
- added to sum in the statement "sum += nums[inum];".
-
- Even if you are a newcomer to C's pointer operations, you have been
- applying this concept every time you've written a function that operated
- on elements of an array that was passed to it. The parameters of a
- function that receive data from an array are declared using empty
- brackets, as in the expression get_param[ ]. What C actually passes to the
- function is the array's starting address. The function treats the
- parameter as an array, even though the parameter is in fact a pointer. One
- way to tell the array itself apart from a pointer to it is by using the
- sizeof operator. In Figure 3, the sizeof operator returns different values
- for p_longs and long_array, as indicated.
-
- A consequence of this type of argument-passing is that a called function
- cannot directly tell the dimension of an array that was passed to it as an
- argument. This is because the array itself is not available to it, so its
- size cannot be tested. Instead, the value passed to the function is a
- pointer to the array's first element, and as a pointer variable, it is
- either 2 or 4 bytes in size (an int or a long in a 16-bit machine).
-
-
- Free Memory
-
- When a program terminates, all of the memory allocated to it is released.
- This includes memory allocated dynamically by calling C library functions,
- like calloc. When allocated memory is released, it is deallocated and
- becomes available for future allocations. If your program requires
- repeated dynamic allocations, then you can call the function free to avoid
- running out of memory. Call free to release blocks of memory when they are
- no longer needed. The free function expects as its argument the same
- address that was returned by a previous call to calloc, malloc, or
- realloc, for example:
-
- nums = (long *) calloc(how_many,sizeof(long));
- free(nums); /* deallocates memory nums points to */
-
- Never pass an address to free that was not first given to you by calloc,
- malloc, or realloc; otherwise things will get nasty. Each block of
- dynamically allocated memory is preceded by internal system maintenance
- information, which includes the size of that block, as well as a pointer
- to the next block. If that maintenance information has been clobbered by
- storing data off the end of another block or the beginning of this block,
- or the information is simply not there, then the pool of available memory
- will become "polluted" by that call to free. Library dynamic allocation
- functions will erroneously determine that free memory is already
- allocated, or even worse, that already allocated memory is free to be
- reallocated.
-
- To illustrate the concept of dynamically allocating data we will look at a
- particular application, namely sorting a directory. Directory information
- is an unknown and unpredictable quantity. A directory sorting program
- would therefore have to do one of two things: either some specific size
- would have to be predetermined, or the sorting routine could be designed
- to dynamically allocate whatever memory it might need. Though the program
- may seem simple, it provides an excellent example of dynamic memory
- allocation and lends itself well to the memory management technique called
- linked list processing.
-
-
- Problem Definition
-
- The number of files in a directory can vary and change in unpredictable
- ways. Directories can vary in size from as little as two files ("." and
- ".."──an empty directory) to whatever upper bound the most current release
- of MS-DOS(R) allows. Because this upper bound may change with the next
- release of the operating system, a utility to sort an MS-DOS directory
- should be able to deal with this change in a manner that will not
- necessitate rewriting the program.
-
- The DIR command outputs information about files in a directory.
- Unfortunately, the order in which files are listed is not very useful. A
- solution to both problems will be discussed.
-
- The MS-DOS filter program SORT can be used to reorder the listing of
- files, to be sorted by file name and extension, for instance. Just tell
- SORT what column to begin sorting on, and it does the job. Using a pipe
- with the SORT and DIR command provides a somewhat useful but rather
- limited directory listing and sorting capability.
-
- Furthermore, a number of things are poorly handled by SORT. For example, a
- file modified in the early afternoon will be listed ahead of a file
- modified late in the morning of the same day, because MS-DOS uses "a" and
- "p" to represent A.M. and P.M., rather than a 24-hour clock. Combining DIR
- and SORT can be useful, but something better is needed.
-
-
- Linked Lists
-
- Linked lists are a very powerful and flexible way of using dynamically
- allocated memory. They are suited to applications in which data of
- different types (integer, character, and floating point) can be combined
- in chainlike structures of unpredictable lengths. Each element of a linked
- list is like a record in a memory-resident file of records. A record holds
- keys for other records, which it points to. These records link together to
- form structures ranging from a simple single chain to exotic networks of
- chains.
-
- Linked lists come in a variety of sizes and shapes, with some common
- characteristics and terminology. They can become quite complex in
- structure. We will be looking at the simplest type, the singly linked
- list, which consists of a series of elements, called nodes, that are
- joined to form a chain. It is only possible to move along this type of
- list in one direction, from the starting node of the list, its head, to
- the end node, its tail.
-
- The ease of inserting a new link between two links in a list makes linked
- lists great for storing data in a variety of different orderings. If each
- new link added to a list is inserted at the head of the list, then the
- list behaves like a push-down stack (also known as a last-in-first-out
- (LIFO) queue). If new links are stored only at the tail of the list, a
- first-in-first-out (FIFO) queue is the result. Any ordering is possible if
- new links can be inserted anywhere in the list. Sorting text data
- alphabetically is easily accomplished.
-
- The data in Figure 4 was output by the DIR command and will be used to
- construct two kinds of linked lists: first a push-down stack, then a
- sorted list. The evolution of a push-down stack from an empty list is
- demonstrated in Figures 5, 6, 7, 8, 9, and 10. The list itself is
- displayed in Figure 11. A memory diagram showing the state of the
- list after each code segment is executed follows each segment in
- Figures 5, 6, 7, 8, 9, and 10.
-
- The link structure tag, s_dir_node, is declared in Figure 5, along with
- p_head and p_new, which are both declared to be pointers to that type of
- structure. The member "next", the first member of an s_dir_node structure,
- is also a pointer to an s_dir_node structure. This type of structure is
- called self-referencing because it contains a pointer to its own data
- type. The member "next" is the means for joining structure links to form a
- linked list. The pointer to the head of the list, p_head, is assigned the
- null pointer NULL (from stdio.h) to create an empty list.
-
- Enough memory to hold a single s_dir_node structure is dynamically
- allocated by calling the standard library function malloc in Figure 6. The
- argument passed to malloc is the number of bytes required to be allocated.
- The pointer returned by malloc is the address of the newly allocated
- block, but it is type pointer to char, so it must be converted to type
- pointer to an s_dir_node structure using the "(struct s_dir_node *)" cast
- operator. The type-converted address is assigned to p_new as the final
- action of this first statement. The memory diagram indicates that the new
- link is at address 3800.
-
- The remaining two statements in Figure 6 assign values to the members of
- the allocated link by using the arrow "->" operator. The expression
- "p_new->dir_line" refers to the member dir_line in the s_dir_node
- structure pointed to by p_new. NULL is assigned to "p_new->next" to make
- it the tail of the list. This link is also the head of the list.
-
- The variable p_head is made to point at the same link that p_new points to
- via the assignment in Figure 7. In Figure 8, another new link is allocated
- at address 3900. The combined actions in Figures 9 and 10 place this
- second new link at the head of the list, in front of the previous head, in
- push-down stack fashion.
-
- The "for" loop in Figure 11 lists each link in the list, starting at the
- head and moving, one link per iteration, to the tail. One interesting
- aspect of the loop is that, rather than counting up or down, it moves
- through a chain of pointers. The expression "p_new = p_new->next"
- accomplishes that movement by assigning to p_new the value of the member
- "next" in the link p_new points to.
-
- Notice that in the format string passed to printf, inside the loop, the %u
- (unsigned integer) format specifier is used to display addresses, since
- addresses are never negative.
-
- Now that you've had a taste of building linked lists and operating on
- them, take a moment to examine the comparison between linked lists and
- arrays in Figure 12. Linked lists facilitate handling unpredictable
- amounts of data. It is easy to change the order of the data, by inserting
- a new link or chain of links between two existing links on the list; it is
- just as simple to remove a single link, or chain of links.
-
- Accessing the different elements of an ordinary array is quite fast and
- provides random direct access. The elements in a linked list must be
- accessed in order, from the head, moving link-by-link, to the tail.
- Different paths through (or orderings of) a single list can be achieved by
- adding additional next-link pointers to the link structure, however, which
- speeds up the process somewhat. The term "doubly linked list" refers to a
- linked list that contains a pointer to the previous link as well as a
- pointer to the next one. The overhead of storing link pointers in each
- link is another price paid for linked lists, but not for arrays. Keeping
- those facts in mind, let's proceed to a more real-world application of
- linked lists.
-
-
- BY_TIME Program
-
- The BY_TIME program may be used to sort DIR command output for all files
- in a directory, or only those selected by a wildcard file specification.
- The file information is placed in order with the most recently created or
- modified first. The sort is accomplished by inserting a link for each
- file's information, read from standard input, into its correct position in
- the list so that the list is always in order. A DOS command to invoke
- BY_TIME to list the *.EXE files in date and time sequence appears in
- Figure 13, along with some sample output.
-
- The organization and structure of the program BY_TIME demonstrates a
- coding style that is appropriate for developing larger applications. The
- main function hides the details of the data manipulations that take place
- in the lower-level functions, but it does give a clear overall picture of
- the processing to be performed. Each function is short and concise, doing
- a single job. No external data is used; instead the paths through which
- the data flows are made more clear by being passed as arguments.
-
- The BY_TIME program is built by separately compiling each of the C source
- files and then linking the resulting object files (see Figures 14, 15,
- 16, 17, 18, 19, and 20).
-
- Each node in BY_TIME's linked list is a familiar structure with two
- members, a pointer to another node and a string of information, output by
- DIR, about each file. The structure of the nodes is defined in the header
- file, BY_TIME.H in Figure 14, so that it can be #include'd in each source
- file that needs it.
-
- The first action taken by the main function in Figure 15 is a call to the
- function bgn_list, passing the address of a dummy head node that was
- declared as an s_dir_node structure. The function bgn_list in Figure 16
- assigns dummy data (not for a file from DIR) into that node, with a date
- value that is high enough to ensure that that node will always be the
- head, given the date and time ordering requirements for this list. This
- special head helps keep the code clean, because it prevents the head node
- from being treated as a special case when inserting new links. The head of
- the list is not dynamically allocated──it is an ordinary auto structure
- type variable.
-
- The initialized list is now ready to grow with the user's file
- information. The "while" loop in main alternates between calls to get_dir
- and sav_dir, until get_dir returns zero. Each call to get_dir (listed in
- Figure 17) places one line of file information in the buffer that was
- passed to it. It returns 1 if successful; otherwise it returns 0,
- indicating that the end of input has been reached.
-
- Once DIR information for the first file has been input by the function
- get_dir, the first block of dynamically allocated memory is requested and
- put to use in sav_dir, as shown in Figure 18. The function sav_dir is
- responsible for inserting new links in such a way that the list of files
- is always in most-recently-created-or-modified-first sequence.
-
- The insertion process begins with a loop that searches the list for the
- correct insertion point. The function time_b4 in Figure 19 is called in
- the test expression of the sav_dir "for" loop. The time_b4 function
- compares the date and time in the input buffer with the date and time in a
- link in the list and returns 1 if the buffer time is earlier; otherwise it
- returns 0.
-
- Memory for the new node is dynamically allocated by a call to malloc in
- sav_dir. Notice that a test for NULL is made to verify that enough memory
- is available for the new node. The last two statements in sav_dir break
- the connection between the two appropriate links and splice in the new
- link, thereby creating a list that is one node longer, with all nodes in
- the correct order.
-
- Each DIR output line is displayed by the function sho_list in Figure 20,
- as it moves from the head to the tail of the list. This takes place after
- the end of input has been reached and the list is built. The code in
- sho_list loops through the list, displaying the data portion of each link,
- just as the loop in Figure 11 did.
-
-
- Beware of Dangling Pointers
-
- As with the free function previously mentioned, care must be taken when
- dealing with memory management C code, especially when referencing memory
- through pointers. The program DANGLE.C in Figure 21, for example, is
- guilty of memory mismanagement, because it refers to values in unallocated
- memory. The results don't make sense at first glance, until you realize
- that a nasty bug is at work. The output consists of three different lines
- of garbage. Try it and see for yourself.
-
- The memory pointed to by p_text was not allocated in the three cases where
- its address was passed to puts and printf for output. It was allocated
- while str_dang was executing, but after str_dang returned the address of
- its variable auto_string, the memory for auto_string became deallocated.
- Automatic variables within a function become deallocated when that
- function returns to its caller. Next, the auto_string memory became
- reallocated for a new use, so the string pointed to by p_text changed, as
- if by magic. The new use was for printf, or one of its subfunctions.
-
- Memory for all of a program's static and external variables remains
- allocated for the entire time the program is executing. If the declaration
- of auto_string were preceded by the word "static," then instead of three
- different lines of junk, you would see three identical lines of 123456789.
-
-
- Summary and Beyond
-
- Programs that process unpredictable amounts of data residing in memory can
- benefit from dynamically allocated arrays. These arrays may be made up of
- simple single-element data, or they may be arrays of complex structures.
- Once allocated, these dynamic arrays may be operated on by using the
- square brackets "[ ]" operator to access individual elements such as
- ordinary arrays.
-
- If changes in the ordering of the data are part of the data's processing,
- then linked lists should be considered to hold the data. The trade-offs in
- this decision involve how the data will be accessed. Will random accesses
- be frequent, or will the list only be processed in sequence? Will the
- overhead of node pointers for each link be acceptable? Are the people who
- will maintain this code fluent C programmers who understand pointers,
- dynamic allocation, and linked list theory?
-
- Only the singly linked list was examined here in detail. The doubly linked
- list, which can be traversed forward or backward, received a mention. A
- tree is another, more complex, type of linked list. The head of a tree may
- have two or more links it points to, which are sometimes called children,
- as in a family tree. Each of the child nodes may also have one or more
- children. Nodes without children are called leaves. No loops within a tree
- are permitted, just as a person cannot be his own parent or grandparent.
- Applications of trees include sorting, parsing expressions in compilers
- and translators, and processing directories of files and their
- subdirectories.
-
- For more information about managing memory, dynamic allocation, and linked
- lists, I encourage you to read the chapter "Efficient Use of Memory" in my
- book Variations in C (Microsoft Press: 1985).
-
- How well memory is managed can make a real difference in the value of a
- program to those who use it. May your pointers not dangle and your lists
- stay linked.
-
-
- Figure 1: Using sizeof to Determine Memory Needs
-
- main()
- {
- short counter; /* 2 bytes of RAM allocated. */
- static char name_list[1000][25] = '\0'; /* 25000 bytes */
- int status_vector[10]; /* 20 bytes on 16-bit CPU's, */
- /* 40 bytes on 32-bit CPU's. */
-
- printf("sizeof counter %d, name_list %d, status_vector %d\n",
- sizeof counter, sizeof name_list,
- sizeof status_vector);
-
- printf("int's are %d bytes, I'm a %d-bit CPU\n",
- sizeof (int), 8 * sizeof (int));
- }
-
- This output will result from the program above:
-
- sizeof counter 2, name_list 25000, status_vector 20
- int's are 2 bytes, I'm a 16-bit CPU
-
-
- Figure 2: SUM_NUM.C
-
- /****************************************************************
- * SUM_NUM.C: Input and total a variable number of integers.
- * Show list of numbers and cumulative subtotals in output.
- */
- main()
- {
- long *nums; /* Pointer to array of numbers to sum */
- short how_many; /* Number of numbers to input and sum */
- short inum; /* Counter to index into nums array */
- long sum; /* Sum of numbers in nums array */
-
- printf("How many numbers to sum? ");
- if (1 != scanf("%hd", &how_many) || how_many < 2)
- exit(1); /* Terminate sum_num program */
-
- /* Dynamically allocate memory for how_many long's */
- nums = (long *) calloc(how_many, sizeof (long));
-
- for (inum = 0; inum < how_many; ++inum) /* Input nums */
- {
- printf("Enter #%d: ", inum + 1);
- if (1 != scanf("%ld", &nums[inum]))
- how_many = inum; /* Quit early */
- }
- for (sum = inum = 0; inum < how_many; ++inum)
- {
- sum += nums[inum]; /* Add number to sum */
- printf("%3d: %10ld %10ld\n", /* Show results */
- inum + 1, nums[inum], sum);
- }
- printf("\nThe sum of the %d numbers entered is %ld\n",
- how_many, sum);
- }
-
-
- Figure 3: Use of Pointers with Arrays
-
- main()
- {
- long long_array[100]; /* Ordinary array of long's */
- long *p_longs; /* Pointer to type long */
-
- p_longs = &long_array[0]; /* point at long_array */
- p_longs[8] = 32;
- printf("%d equals %d\n", p_longs[8], long_array[8]);
- printf("sizeof p_longs %d, sizeof long_array %d\n",
- sizeof p_longs, sizeof long_array);
- }
-
- Which outputs:
-
- 32 equals 32
- sizeof p_longs 2, sizeof long_array 400
-
-
- Figure 4: Sample Data from Original DIR Command
-
- MEMO TXT 46 11-18-86 6:26p
- DIROUT TXT 1206 1-17-87 7:25p
-
-
- Figure 5: Declaring the Link Structure Tag
-
- #include <stdio.h> /* For #define of NULL pointer */
- #define DIR_LINE_LEN 39 /* DIR output line length */
- struct s_dir_node /* Linked list node structure */
- {
- struct s_dir_node *next; /* Next node pointer */
- /* or NULL (0) */
- char dir_line[DIR_LINE_LEN + 1]; /* DIR output line */
- }
-
- struct s_dir_node *p_head; /* Pointer to head of list */
- struct s_dir_node *p_new; /* Pointer to new link */
- p_head = NULL; /* List is empty now */
-
- ┌──────┐
- p_head │ 0000 │
- └──────┘
- ┌──────┐
- p_new │ ???? │
- └──────┘
-
-
- Figure 6: Allocating for a New Link
-
- /* Dynamically allocate a new link and assign data to it */
-
- p_new = (struct s_dir_node *) malloc (sizeof (struct s_dir_node));
- strcpy (p_new->dir)line,
- "DIROUT TXT 1206 1-17-87 7:25p");
- p_new->next = NULL; /* No next node - this is the tail */
-
- ┌──────┐
- p_head │ 0000 │
- └──────┘
- ┌──────┐
- p_new │ 3800 │
- └───┬──┘ ┌──────┐
- └─────┤ 0000 │ DIROUT TXT 1206 1-17-87 7:25p
- └──────┘
-
-
- Figure 7: First Link
-
- p_head = p_new; /* Empty list grows to a single link */
-
- ┌──────┐
- p_head │ 3800 ├─────┐
- └──────┘ │
- ┌──────┐ │
- p_new │ 3800 │ │
- └───┬──┘ ┌──┴───┐
- └─────┤ 0000 │ DIROUT TXT 1206 1-17-87 7:25p
- └──────┘
-
-
- Figure 8: Allocating for Additional Links
-
- /* Allocate a second link and assign data to it */
-
- p_new = (struct s_dir_node *) malloc(sizeof (struct s_dir_node));
- strcpy(p_new->dir_line,
- "MEMO TXT 46 11-18-86 6:26p");
- p_new->next = NULL; /* No next node yet */
-
- ┌──────┐
- p_head │ 0000 │
- └───┬──┘ ┌──────┐
- └─────┤ 0000 │ DIROUT TXT 1206 1-17-87 7:25p
- └──────┘
- ┌──────┐
- p_new │ 3900 │
- └───┬──┘ ┌──────┐
- └─────┤ 0000 │ MEMO TXT 46 11-18-86 6:26p
- └──────┘
-
-
- Figure 9: Second Link
-
- p_new->next = p_head; /* New link points to list's head link */
-
- ┌──────┐
- p_head │ 0000 │
- └───┬──┘ ┌──────┐
- └─────┤ 0000 │ DIROUT TXT 1206 1-17-87 7:25p
- └───┬──┘
- ┌──────┐ │
- p_new │ 3900 │ │
- └───┬──┘ ┌───┴──┐
- └─────┤ 3800 │ MEMO TXT 46 11-18-86 6:26p
- └──────┘
-
-
- Figure 10: Completed Links
-
- p_head = p_new; /* List grows again, now 2 links long */
-
- ┌──────┐
- p_head │ 0000 ├─────┐
- └──────┘ ┌──┴───┐
- │ 3800 │ MEMO TXT 46 11-18-86 6:26p
- └──┬───┘
- ┌──────┐ │
- p_new │ 3900 │ │
- └───┬──┘ │
- │ ┌──┴───┐
- └─────┤ 0000 │ DIROUT TXT 1206 1-17-87 7:25p
- └──────┘
-
-
- Figure 11: Output Result from the Linked List
-
- /* Now that the linked list is built, display it in a for loop */
- for (p_new = p_head; p_new != NULL; p_new = p_new->next)
- printf("At addr: %4u, next: %4u, dir_line: %-.12s\n",
- p_new, p_new->next, p_new->dir_line);
-
- * * * * * * * * * * * * * * * * * * * * * *
- Output:
-
- At addr: 3900, next: 3800, dir_line: MEMO TXT
- At addr: 3800, next: 0, dir_line: DIROUT TXT
-
-
- Figure 12: Linked Lists vs Arrays
-
- Tradeoffs exist between storing data in linked lists and in arrays.
-
- Linked lists Arrays
-
- Variable length Fixed dimension with
- maximum size
-
- Will use memory added Ignores memory added
- by user by user
-
- Fast to insert or remove Slow to insert or
- a link remove an element
-
- No random access, sequential Random direct
- access to lists only accesses are fast
-
- Memory overhead for node No pointer memory
- pointers overhead
-
- Code to manipulate lists Code to manipulate
- is harder to read arrays is easier to read
-
-
- Figure 13: Sample Output of BY_TIME Program
-
- DIR *.EXE | BY_TIME
-
- BY_TIME EXE 5654 1-26-87 8:44p
- VWC EXE 16886 1-17-87 8:22p
- GREP EXE 9336 4-06-87 6:43p
- PR EXE 11604 3-16-86 11:07p
- CFIND EXE 8146 6-20-85 5:30p
- CHARCNT EXE 8146 4-07-87 9:53p
- LISTING EXE 6266 4-07-85 9:27p
- VISIBLE EXE 6402 4-07-85 9:24p
-
-
- Figure 14: BY_TIME.H
-
- /****************************************************************
- * BY_TIME.H: Header file for BY_TIME program, define linked
- * list structure as next node pointer and line of DIR text
- */
- #define DIR_LINE_LEN 39 /* DIR output line length */
- struct s_dir_node /* Linked list node structure */
- {
- struct s_dir_node *next; /* Next node pointer */
- /* or NULL (0) */
- char dir_line[DIR_LINE_LEN + 1]; /* DIR output line */
- };
-
-
- Figure 15: BY_TIME.C
-
- /****************************************************************
- * BY_TIME.C: Filter sorts DIR output in linked list, most recent
- * first. This is the main function of the program.
- */
-
- #include <stdio.h> /* For BUFSIZ symbol */
- #include "by_time.h" /* Linked list struct */
- main()
- {
- struct s_dir_node head; /* First node in list */
- char in_buf[BUFSIZ]; /* Input buffer for */
- /* DIR output */
- bgn_list(&head); /* Initialize list */
- while (get_dir(in_buf)) /* Input DIR info for */
- /* next file */
- sav_dir(in_buf, &head); /* Save file info in */
- /* sorted linked list */
- sho_list(&head); /* Show sorted list */
- }
-
-
- Figure 16 BGN_LIST.C
-
- /****************************************************************
- * BGN_LIST.C: Initialize head of list to dummy highest value
- */
- #include <stdio.h> /* For NULL (zero) pointer */
- #include "by_time.h" /* For linked list struct */
-
- bgn_list(p_head)
- struct s_dir_node *p_head; /* Pointer to head of list */
- {
- /* Date in head is greater than DIR date for any file */
- strcpy(p_head->dir_line,
- "ZZZZZZZZ ZZZ 99999 99-99-99 99:99p");
- p_head->next = NULL; /* No next node - empty list */
- }
-
-
- Figure 17: GET_DIR.C
-
- /****************************************************************
- * GET_DIR.C: Input DIR info for next file from stdin
- */
- #include <stdio.h> /* For NULL (zero) pointer */
-
- int get_dir(buf)
- char *buf; /* Buffer for read and pass line back */
- {
- char *rtn; /* save gets() return */
-
- /* Loop: Input lines until no more input or got line */
- /* starting with an uppercase letter (has file data) */
- while ((rtn = gets(buf)) && /* Input a DIR line */
- (buf[0] < 'A' || buf[0] > 'Z')) /* For file? */
- ;
- return (rtn != NULL); /* Return 1 if got data, else 0 */
- }
-
-
- Figure 18: SAV_DIR.C
-
- /****************************************************************
- * SAV_DIR.C: Allocate new node in list, save DIR info in it
- */
- #include <stdio.h> /* For NULL (zero) pointer */
- #include "by_time.h" /* For linked list struct */
-
- sav_dir(buf, p_head)
- char *buf; /* Line of DIR output to save */
- struct s_dir_node *p_head; /* Pointer to head of list */
- {
- struct s_dir_node *p_next; /* Pointer to next */
- /* node in list */
- struct s_dir_node *old_p_next; /* Pointer to previous */
- /* next node, parent of current next node */
- struct s_dir_node *p_new; /* Pointer to new node */
-
- /* Loop: for each node in list until end of list or */
- /* insert point that will keep list sorted is found */
- for (p_next = p_head->next, old_p_next = p_head;
- p_next && time_b4(buf, p_next);
- old_p_next = p_next, p_next = p_next->next)
- ;
- /* Dynamically allocate memory for new node - DIR output
- * line. Note use of the cast (struct s_dir_node *)
- * operator in the assignment to avoid this message:
- * warning 47: '=' : different levels of
- * indirection
- */
- p_new = (struct s_dir_node *)
- malloc(sizeof (struct s_dir_node));
- if (p_new == NULL) /* malloc() failed, out of RAM */
- {
- puts("Out of memory!!!");
- return;
- }
- strcpy(p_new->dir_line, buf); /* Save DIR line in */
- /* newly alloc'd node */
- p_new->next = old_p_next->next; /* New node points to */
- /* rest of list */
- old_p_next->next = p_new; /* Insert new node in */
- /* list */
- }
-
-
- Figure 19: TIME_B4.C
-
- /****************************************************************
- * TIME_B4.C: Return 1 if date and time in buf is before date
- * and time in node p_next points to, otherwise return 0.
- */
- #include "by_time.h" /* For linked list struct */
- int time_b4(buf, p_next)
- char *buf; /* Line of DIR output to find */
- /* insert point in list for */
- struct s_dir_node *p_next; /* Pointer to node in list to */
- { /* compare time in buf with */
- int rtn; /* Return value from strncmp() */
-
- /* compare year, month, day, am/pm, hour, and minute */
-
- if (rtn = strncmp(&buf[29], &(p_next->dir_line)[29], 2))
- return (rtn < 0); /* Years differ */
- if (rtn = strncmp(&buf[23], &(p_next->dir_line)[23], 2))
- return (rtn < 0); /* Months differ */
- if (rtn = strncmp(&buf[26], &(p_next->dir_line)[26], 2))
- return (rtn < 0); /* Days differ */
- if (buf[38] != (p_next->dir_line)[38]) /* am/pm's */
- return (buf[38] == 'a'); /* differ */
- if (rtn = strncmp(&buf[33], &(p_next->dir_line)[33], 2))
- return (rtn < 0); /* Hours differ */
- if (rtn = strncmp(&buf[36], &(p_next->dir_line)[36], 2))
- return (rtn < 0); /* Minutes differ */
- return (0); /* Dates and times are equal */
- }
-
-
- Figure 20: SHO_LIST.C
-
- /****************************************************************
- * SHO_LIST.C: Show sorted linked list - output it to stdout
- */
- #include <stdio.h> /* For NULL (zero) pointer */
- #include "by_time.h" /* Linked list struct */
-
- sho_list(p_head)
- struct s_dir_node *p_head; /* Pointer to head of list */
- {
- struct s_dir_node *p_next; /* Pointer to next */
- /* node in list */
-
- for (p_next = p_head->next; /* Start at first node */
- p_next != NULL; /* Still more list? */
- p_next = p_next->next) /* Move down a node */
-
- puts(p_next->dir_line); /* Output a DIR line */
- }
-
-
- Figure 21: DANGLE.C──Beware of Dangling Pointers
-
- /****************************************************************
- * DANGLE.C: Danger of referencing a dangling pointer. That is a
- * pointer to deallocated memory.
- */
- main()
- {
- char *p_text;
-
- p_text = str_dang();
- puts(p_text);
- printf("%s\n", p_text);
- printf("%s\n", p_text);
- }
-
- str_dang()
- {
- char auto_string[10];
-
- strcpy(auto_string, "123456789");
- return (&auto_string[0]);
- }
-
- ████████████████████████████████████████████████████████████████████████████
-
- CD ROM Technology Opens the Doors on a New Software Market
-
- Tony Rizzo
-
- Along with the introduction of major new CD ROM applications from
- Microsoft(R) and other software publishers, the confident glow of success
- that usually follows a "good year" can be seen on the faces of all the
- participants in the CD ROM industry these days. After a somewhat shaky
- entry into the personal computing environment, a new stability has settled
- on the CD ROM world that is finally fostering the growth of a new PC
- software market──and expectations for dramatic growth are running high.
-
- Two factors in particular contribute to this new stability. First, the
- commercial success of compact audio discs give CD ROM a strong, low-cost
- manufacturing capability, prompting considerable interest from potential
- users of the medium. Second, the creation of a standardized logical disc
- format and new systems software from Microsoft allows CD ROM drives to
- easily interface with MS-DOS(R)-based personal computers so as to provide a
- high level of software and hardware compatibility.
-
- CD ROM applications extend the use of PCs from information processing to
- include information accessing. It has proved to be a perfect medium for PC
- applications requiring:
-
- ■ a substantial amount of data space
-
- ■ wide and inexpensive distribution of medium-volatile data, such as a
- parts catalog──it is considerably less expensive to press and ship new
- discs in quantity than it is to print new copies of reference works or
- update looseleaf references
-
- ■ random searching and accessing of large databases
-
- ■ distribution of large standardized reference works, such as dictionaries
- and specification catalogs, to be integrated into a PC user's
- environment
-
- Additionally, state-of-the-art hardware now gives developers the ability
- to integrate text with video and audio information, which will ultimately
- give rise to multimedia PC applications.
-
-
- The Audio Connection
-
- CD ROM evolved from Sony and Philips's development of the audio compact
- disc. Their new optical format offered the ability to store very large
- quantities of digital information on a relatively small and damage-
- resistant disc. More importantly, their combined work led to a definition
- for the physical format of CD ROM that has become a de facto industry
- standard.
-
- As with compact audio discs, a major advantage of CD ROM is the fact that
- once a master disc is made, any number of discs can be quickly and
- inexpensively pressed from it. The success of compact audio discs led to
- the creation of a growing number of pressing facilities, which, in turn,
- has considerably reduced the cost of mass-producing both compact audio and
- CD ROM discs.
-
- The production process involves the following:
-
- ■ all data relevant to a given application is assembled
-
- ■ the data is indexed and structured for retrieval
-
- ■ a CD ROM image in High Sierra format is created from the data files──
- this step can be done by a service bureau or by using special systems
- such as CD Publisher
-
- ■ a High Sierra data image is then mixed with control information such as
- error correction code and recorded on a master tape (premastering)
-
- ■ the master tape is shipped to a pressing plant, where the master disc is
- then created (mastering)
-
- ■ the desired number of CD ROM discs is pressed from the master disc
-
- After production of the master disc, which can cost from $3,000 to
- $10,000, distribution discs can be pressed for as little as $3 per disc in
- quantities of 100 or more. If one considers that a single CD ROM disc can
- hold 660 Mb of data──more than 1500 floppy disks──it is a very cost
- effective and efficient way to distribute information.
-
- The CD ROM drive market also benefits from the success of compact audio
- discs. Technological gains made in audio hardware lead directly to
- improvements in CD ROM drive technology. More than ten manufacturers
- currently make CD ROM drives. Prices, which now average less than $1,000,
- will continue to drop as volume picks up.
-
-
- The CD ROM Disc
-
- The specification Sony and Philips created for CD ROM discs was included
- in a document called "CD ROM," informally known as the Yellow Book. Though
- not an officially recognized standard, its universal acceptance by hardware
- developers has created a stable and well-defined physical environment.
-
- The specification states that a CD ROM disc contains a stream of micron-
- size pits that are read by a laser. The pits, laid out in a spiral, define
- bit patterns that are grouped into bytes. Bytes are then grouped to form
- sectors that are each 2352 bytes long: 304 bytes are dedicated to providing
- system and hardware information (for example, error correction code), and
- the remaining 2048 bytes contain user data (see Figure 1).
-
-
- CD ROM Drives
-
- Most magnetic disks store information on concentric tracks of information.
- Each track is divided into an equal number of sectors, each of which holds
- the same amount of information; that is, the largest outer sectors hold the
- same amount of information as the smallest inner sectors. As sectors get
- smaller, information is more densely packed.
-
- Because the drives spin at a constant rate, because the number of sectors
- is the same on each track, and because the outer tracks move at a faster
- rate than the inner tracks, information on the largest sectors must be
- spaced out far enough so that it takes the same amount of time to read
- them as it does to read the smallest sectors (see Figure 2). This "spacing
- out" results in the loss of a significant amount of possible data space,
- but because the drive spins at a constant rate, and track and sector
- location is specific and constant, the seek times of the read/write heads
- can be very fast.
-
- CD ROM works differently. There is only one continuous spiral track. Each
- sector is the same physical size, allowing for substantially more data to
- be stored──there is no wasted sector space (see Figure 3). To read data
- stored in this format, however, the drive must spin at a variable rate.
-
- As the head moves towards the inner layers of the spiral the rotational
- speed increases, and as the head move towards the outer layer the
- rotational speed decreases. This change in rotational speed is required to
- maintain a constant linear velocity (CLV), which ensures that the speed
- with which the outer and inner layers of the spiral travel, relative to
- the read head, is the same. The drive first seeks to the approximate
- location of a sector within the spiral, and then must read through the
- sectors in order to find the exact sector it is looking for.
-
- This mechanical characteristic of CD ROM results in relatively slow seek
- times, generally ranging from .2 to 1 second (200 to 1000 ms). The benefit
- of the design is a much denser and substantially greater data space (see
- Figure 4). Good CD ROM layout and design means placing related data close
- together──this ensures that a good deal of the time an application will
- seek information that is nearby. Seeks that are close to each other can
- have a minimum seek time of as little as 10 ms.
-
-
- The Pioneers
-
- Most of the pioneer CD ROM projects involved the distribution of text-
- based information. Aside from building the applications themselves, early
- CD ROM developers had to deal with a number of issues over which they did
- not have much control.
-
- The major problem was compatibility. Though a physical disc format
- existed, there was no equivalent standard for logical disc formats. Worse,
- there was no standard hardware or software PC interface for CD ROM drives,
- and each project required the writing of system software to access the CD
- ROM drive from a PC. This resulted in a number of proprietary logical disc
- formats and interfacing techniques. Users found themselves in the position
- of having to invest in expensive hardware that was incompatible with any
- software not specifically designed for it. Developers had to face the
- probability that proprietary hardware and software would not be widely
- accepted.
-
-
- The Road to Success
-
- It became clear that, if CD ROM was to succeed, the industry would require
- hardware and software compatibility, as well as the ability to easily
- interface CD ROM drives to MS-DOS-based PCs.
-
- An industry committee of dedicated CD ROM participants that became known
- as the High Sierra Group (HSG) was formed to iron out the particulars of
- creating a standard logical disc format. At the same time, Microsoft
- undertook development of the MS-DOS CD ROM Extensions, software so as to
- interface CD ROM drives to MS-DOS.
-
-
- The HSG Proposal
-
- The High Sierra Group work is based on the Sony/Philips physical format
- specification. The HSG logical format provides two levels of CD ROM disc
- definitions. The first level deals with the volume as a whole, and the
- second deals with the actual files and directories that make up a volume's
- information base. The full proposal also provides for several levels of
- operating system support. The lowest level (level 1) supports MS-DOS and
- similar operating systems, while the higher levels support operating
- systems like XENIX(R) and VMS.
-
- The HSG logical format limits itself to using only the 2048-byte user data
- segment of the physical sector, which it calls a logical sector (see
- Figure 5). The proposal anticipates larger sector sizes on future media.
- The underlying hardware, device driver and system software will accommodate
- such changes, maintaining compatibility with older formats while taking
- advantage of newer formats.
-
- The HSG format defines specifications for file identifiers, file
- directories, and subdirectories. File layout is defined in terms of
- logical blocks, extents, and optional XARs (eXtended Attribute Records).
- Files containing actual information can be grouped within hierarchical
- directories, just as they are in XENIX or MS-DOS. Directories, which are
- themselves files, contain entries that provide the information necessary
- for locating the entire contents of every file. Directories can contain
- subdirectories nested to a maximum of eight levels.
-
- The HSG specification takes into account the seek-performance limits of CD
- ROM drives. For example, the proposal does not define an absolute location
- for directories. This means directories can be placed close to the most
- commonly used files, which would help keep seek time to a minimum, thereby
- optimizing read/seek performance.
-
- Another optimization in the HSG file structure is the Path Table. Because
- of the design of CD ROM drives, finding and tracking subdirectories can be
- a painfully slow process, especially when information is located deep
- within a hierarchy──for example, within the sixth, seventh or eighth level
- of subdirectory──or at distant parts of the spiral track itself.
-
- The Path Table is an index into the hierarchical directories (see
- Figure 6). It provides a way of quickly determining the starting sector of
- any directory at any level in the hierarchy without having to traverse the
- entire directory path, thus keeping the number of seeks necessary to look
- up a directory or file to a minimum. The sectors that contain the Path
- Table will be cached in memory by the MS-DOS CD ROM Extensions,
- significantly improving seek performance.
-
- From there, the system can search the directory to find actual files that
- it needs. Because searching for directories should be very fast, while
- searching for files can be very slow, the designer is cautioned against
- using directories with lots of files in them. Otherwise, an application
- might need to scan many sectors of a given directory to find a particular
- file, resulting in seek time penalties. A single CD ROM directory sector
- can hold about 40 directory entries.
-
- The specification also defines Volume Descriptors, which provide
- information for the entire contents of a CD ROM volume. Volume Descriptors
- are the only "fixed-location" entities defined by the HSG specification,
- from which any other information can be traced. Each CD ROM volume must
- have at least one Volume Descriptor that provides directory and Path Table
- information. Types of information that can be kept in the Volume
- Descriptors include the standard ASCII character set (Standard File
- Structure Volume Descriptor), author names, and creation dates.
-
- A specification is also provided for handling multidisc applications,
- where data exceeds the 660Mb capacity of one volume.
-
- The HSG completed its work on May 28, 1986. Since that time, most CD ROM
- vendors have embraced the group's set of specifications.
-
-
- The High Sierra Groups Initial Members
-
- Apple Computer
- Digital Equipment Corp.
- Hitachi
- LaserData
- Microsoft Corp.
- Phillips
- Reference Technologies Inc.
- Sony Corp.
- 3M
- TMS Inc.
- VideoTools
- XEBEC
-
-
- The MS-DOS CD ROM Extensions
-
- The MS-DOS CD ROM Extensions are designed to allow a user to purchase any
- CD ROM drive from any manufacturer, to install the drive on any MS-DOS-
- based personal computer, and to read any publisher's discs that use the
- HSG format in that drive or in any other CD ROM drive.
-
- Microsoft has implemented the Extensions through a terminate-and-stay-
- resident (TSR) program known as MSCDEX.EXE, which is installed on top of
- MS-DOS and handles the interfacing of CD ROM Device Drivers to MS-DOS
- itself. The set of existing device driver commands was enhanced so that
- they would support the unique characteristics of the CD ROM drives.
-
- Microsoft has opted to distribute the MS-DOS CD ROM Extensions through CD
- ROM drive manufacturers. Each drive manufacturer provides its own device
- driver(s) and a copy of Microsoft's MSCDEX.EXE program with the CD ROM
- drive itself, both supplied on a setup diskette. CD ROM application
- developers need not supply any systems software with their applications.
- This approach is analagous to the floppy and hard disk market. Software
- developers can simply make the same assumptions about CD ROM drives that
- they make about magnetic drives and disks.
-
- Once the hardware is in place, the setup program is run by the user and
- makes the necessary modifications to CONFIG.SYS (to install the device
- driver) and AUTOEXEC.BAT (to include the call to MSCDEX.EXE). Once the
- system is rebooted the PC will recognize the newly installed CD ROM drive,
- and users and application programs can access the information on any CD
- ROM disc adhering to the HSG specifications.
-
- For a technical discussion of the MS-DOS CD ROM Extensions, see the
- accompanying article MS-DOS CD ROM Extensions: A Standard PC Access Method.
-
-
- Development Issues
-
- A number of features unique to the CD ROM environment must be taken into
- consideration during application design. The first and foremost is that CD
- ROM is read-only. Most software assumes, for example, that the current
- disk is always writable; this will create obvious problems if the current
- drive is a CD ROM drive. And of course, index and search strategies, file
- layouts and judicious use of directories must be considered.
-
- One must also keep in mind that MS-DOS is, in High Sierra format terms, a
- level 1 operating system──for example, filenames are limited to eight
- characters plus a three-character extension. The MS-DOS CD ROM Extensions
- will eventually support most features of the High Sierra format, such as
- multivolume sets.
-
- The HSG specification and the MS-DOS CD ROM Extensions have made the
- development process substantially easier. Many of the obstacles faced by
- the pioneer CD ROM developers and marketers have been cleared: The MS-DOS
- CD ROM Extensions have eliminated most of the systems-level programming
- that was previously required, and the HSG specification ensures logical
- disc compatibility. The developer need only concentrate on the application
- itself.
-
-
- Applications
-
- Typical CD ROM applications include parts catalogs, information databases
- such as those offered by on-line database services, specialized applications
- like medical diagnoses, and applications requiring the distribution of very
- large texts, such as an encyclopedia. According to Carl Stork, Microsoft's
- director of marketing for CD ROM, 150 vertical applications using CD ROM are
- currently shipping, and the use of the High Sierra format is booming.
-
- Microsoft itself has developed what can be called the first general-
- purpose CD ROM product available to the PC user. Microsoft(R) Bookshelf(TM)
- offers ten reference works on a single CD ROM disc. Bookshelf is designed
- to work as either a standalone program or interactively with programs such
- as word processors, allowing immediate on-line access to the ten reference
- works while creating or editing a document.
-
- Applications will soon include video and audio components. Digital Video
- Interactive (DVI) is a new technology that is ideally suited for CD ROM; it
- allows for full-motion video and graphics at the same data rates as those
- of a CD ROM.
-
- Optical media technology has finally hit its stride, and CD ROM is leading
- the way through its vastly improved drive technology, the High Sierra format
- specification, and the MS-DOS CD ROM Extensions.
-
- Developers with existing applications that lend themselves to CD ROM
- technology or with applications that would benefit from the CD ROM format
- now have the entire PC market available to them. And those exciting new
- applications that CD ROM is ideally suited for can now be brought to this
- market, offering personal computer users new and added functionality, as
- well as increased productivity.
-
- The combination of desktop personal computers and CD ROM is proving to be a
- winning hand. Disc compatibility has paved the way for mass market
- acceptance of CD ROM, which is bound to encourage the creation of a new
- software market and provide this new technology with a large degree of
- success.
-
-
- Figure 1: The physical sector is 2352 bytes long; 2048 bytes can contain
- user data.
-
- Type of Information Allocated Space
-
- Synchronization Data 12 bytes
- Header Data 4 bytes
- User Data 2048 bytes
- Error Detection Code (EDC) 4 bytes
- Unused Space 8 bytes
- Error Correction Code (ECC) 276 bytes
-
-
- Figure 4: Performance and storage characteristics of several types of
- media.
-
- ╓┌─────────────┌────────────┌─────────┌────────┌──────────┌────────────┌─────╖
- │ Small │ Large │ │ │ Large │ ▌
- │ Winchester │ Optical │ Floppy │ Magnetic │ Winchester │ CD ▌
- Media │ Disk │ ROM │ Disk │ Tape │ Disk │ ROM ▌
- ─────────────┼▀▀▀▀▀▀▀▀▀▀▀▀┼▀▀▀▀▀▀▀▀▀┼▀▀▀▀▀▀▀▀┼▀▀▀▀▀▀▀▀▀▀┼▀▀▀▀▀▀▀▀▀▀▀▀┼▀▀▀▀▀┤
- Media ▌ │ │ │ │ │ │
- Cost ▌ │ │ │ │ │ 10- │
- (in U.S. $) ▌ N/A │ 15-30 │ 1-5 │ 10-20 │ N/A │ 20 │
- ─────────────┼────────────┼─────────┼────────┼──────────┼────────────┼─────┤
- Drive ▌ │ │ │ │ │ │
- Cost ▌ │ 7,000- │ 200- │ 3,000- │ 10,000- │ 500-│
- (in U.S. $) ▌ 500-3,000 │ 100,000 │ 1,500 │ 15,000 │ 150,000 │ 2500│
- ─────────────┼────────────┼─────────┼────────┼──────────┼────────────┼─────┤
- Capacity ▌ │ 1,000- │ 0.36- │ │ │ 550 │
- │ Small │ Large │ │ │ Large │ ▌
- │ Winchester │ Optical │ Floppy │ Magnetic │ Winchester │ CD ▌
- Media │ Disk │ ROM │ Disk │ Tape │ Disk │ ROM ▌
- ─────────────┼▀▀▀▀▀▀▀▀▀▀▀▀┼▀▀▀▀▀▀▀▀▀┼▀▀▀▀▀▀▀▀┼▀▀▀▀▀▀▀▀▀▀┼▀▀▀▀▀▀▀▀▀▀▀▀┼▀▀▀▀▀┤
- Capacity ▌ │ 1,000- │ 0.36- │ │ │ 550 │
- (in Mb) ▌ 5-50 │ 4,000 │ 1.20 │ 30-300 │ 50-4000 │ 680 │
- ─────────────┼────────────┼─────────┼────────┼──────────┼────────────┼─────┤
- Media ▌ │ │ │ │ │ │
- Size (in) ▌ 5.25 │ 12.0 │ 5.25 │ 10.50 │ 14.0 │ 4.72│
- ─────────────┼────────────┼─────────┼────────┼──────────┼────────────┼─────┤
- Access Time ▌ │ 0.03- │ 0.03- │ │ │ 0.40│
- (in sec.) ▌ 0.03-0.30 │ 0.40 │ 0.05 │ 1-40 │ 0.01-0.08 │ -1 │
- ─────────────┼────────────┼─────────┼────────┼──────────┼────────────┼─────┤
- Density ▌ │ │ │ │ │ 35, │
- (bits/in.) ▌ 15,000 │ 35,000 │ 10,000 │ 6,250 │ 15,000 │ 000│
- ─────────────┼────────────┼─────────┼────────┼──────────┼────────────┼─────┤
- Data Rate ▌ │ │ │ │ │ │
- (Kb/sec.) ▌ 625 │ 300 │ 31 │ 500 │ 2,500 │ 150 │
- ▀▀▀▀▀▀▀▀▀▀▀▀▀┴────────────┴─────────┴────────┴──────────┴────────────┴─────┘
-
-
-
- Figure 5: The HSG proposal focuses only on the 20448-byte user data space of
- the 2352-byte physical sector defined by Sony and Philips. The
- logical sector can then be further divided into logical blocks as
- per the HSG proposed definition.
-
- ╔═══════════════════╗
- ║ Logical Sector ║
- ╚═════════╤═════════╝
- │
- ╔═══════════════╧═════════════════╗
- │ │
- ┌────────┬──────────┬─────────────────────────────────┬───────╥───────┐
- │ Sync │ Header │ 2048 bytes user data │ EDC ║ ECC │
- └────────┴──────────┴─────────────────────────────────┴───────╨───────┘
- │ │
- ╚═══════════════════════════════════╤═════════════════════════════════╝
- │
- ╔═════════╧═════════╗
- ║ Physical Sector ║
- ╚═══════════════════╝
-
-
- Figure 6: The Path Table provides the means to move directly to the location
- of the first logical sector of any directory and subdirectory.
-
- VOLUME DESCRIPTOR
- ├──────────────────────────────────────────┐
-
- ROOT ╔═══╗
- ┌─────┬─────┬──┴──┬─────┬─────┐ ║ ║
- │ ┌─────┬─────┬─────┬─────┬─────┬───────────────────────║ P ║
- │ │ │ │ │ │ │ │ │ │ │ │ ║ A ║
- ║ T ║
- ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ║ H ║
- │ A │ │ B │ │ C │ │ D │ │ E │ │ F │ ║ ║
- └┬─┬┤ └───┘ └───┘ └───┘ └┬─┬┤ └───┘ ║ T ║
- │ │└──────┐ │ │└──────┐ ║ A ║
- ║ B ║
- ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ║ L ║
- │ G │ │ H │ │ I │ │ J │ │ K │ │ L │ ║ E ║
- └───┘ └───┘ └───┘ └───┘ └───┘ └───┘ ║ ║
- ║ ║
- │ │ │ └─────┴─────┴─────────────────────║ ║
- └─────┴─────┴─────────────────────────────────────────────╚═══╝
-
- ████████████████████████████████████████████████████████████████████████████
-
- MS-DOS CD ROM Extensions: A Standard PC Access Method
-
- Tony Rizzo
-
- Developers of CD ROM applications need not concern themselves with the
- details of interfacing CD ROM drives to the MS-DOS environment.
- Microsoft(R), working closely with CD ROM drive manufacturers, has developed
- the software necessary to accomplish this. The MS-DOS CD ROM Extensions,
- in conjunction with the High Sierra logical disc format, free developers
- to concentrate solely on their applications and eliminates dependence on
- any particular manufacturer's drive technology.
-
- The MS-DOS CD ROM Extensions consist of a device driver for a CD ROM and a
- RAM-resident program called MSCDEX.EXE that interfaces with MS-DOS. Together
- they provide an interesting solution to some rather thorny MS-DOS/CD ROM
- interfacing problems.
-
-
- The Dilemma
-
- CD ROM device drivers are of concern primarily to manufacturers of CD ROM
- drives, who write and package them with their products in accordance with
- Microsoft's "CD ROM Device Driver Specification." However, an understanding
- of how these drivers work will benefit CD ROM application developers.
-
- Unlike conventional hard and floppy disks, CD ROM discs are read-only,
- with file structures that can occupy up to 660Mb of data space, part of
- which might be audio or video information. There is no File Allocation
- Table (FAT) as there is on a normal MS-DOS file system, and there is no
- need to dynamically track disc space for allocating data. Furthermore, CD
- ROM drives cannot be accessed via the standard MS-DOS Interrupt
- 25h/Interrupt 26h (read/write disk sector) mechanism, and they cannot be
- looked at by the operating system at a physical level because they are not
- in MS-DOS file format. Files and directories are not organized as MS-DOS
- expects them, which is why CHKDSK, FORMAT, and other MS-DOS utilities will
- not work on CD ROMs.
-
- MS-DOS interfaces to standard magnetic media through block device drivers
- that deal with disk drives at the hardware level and transfer blocks of
- data in multiples of a given sector size. Also, block drivers are able to
- support multiple devices (for example, two or more hard disks at once), as
- well as removable media.
-
- MS-DOS makes some assumptions about block device drivers. It assumes there
- is a FAT, and it will attempt to read one when the device driver is
- initialized. MS-DOS also assumes that it will need to assign drive letters
- to each unit supported by a particular device driver. Neither of these
- assumptions is relevant to CD ROM drives, nor can MS-DOS deal with CD
- ROM's 660Mb data space, since MS-DOS can support a maximum disk space of
- only 32Mb.
-
- MS-DOS also accepts a character device driver, which deals with
-