PCjs Machines

Home of the original IBM PC emulator for browsers.

Logo

Microsoft Systems Journal (Vol. 1)

The following document is from the Microsoft Programmer’s Library 1.3 CD-ROM.

Microsoft Systems Journal Volume 1

────────────────────────────────────────────────────────────────────────────

Vol. 1 No. 1 Table of Contents

────────────────────────────────────────────────────────────────────────────

Advanced Reuter Terminal Gives Traders Windows on Financial World

The Advanced Reuter Terminal (ART), a dedicated hardware and software system
that runs under Microsoft(R) Windows, enables currency traders to view and
analyze up-to-the-minute financial data. An exclusive interview reveals the
developing team's experiences.


DDE: A Public Protocol for Advanced Application Linkages

An extension of Microsoft Windows data exchange facilities, the Dynamic
Data Exchange (DDE) protocol allows developers to establish sophisticated
application linkages, ranging from automatic updating of documents to
processing of real-time data.


New Intel Graphics Coprocessor Makes Windows Sparkle

Intel has a new way to boost the display speed of Windows and other
graphics applications: the 82786, a high-powered coprocessor that
improves workstation performance, moving graphics functions from
software to high-speed hardware.


TI's Programmable Graphics Processor Perks Up PC Pixels

The first true graphics microprocessor, Texas Instruments' 32-bit TMS34010
executes at 6 MIPS, addresses up to 4 gigabytes of memory, supports displays
of up to 64K by 64K pixels, and includes high-speed pixel-processing
instructions.


Latest Dialog Editor Speeds Windows Application Development

An improved version of the Microsoft Windows Dialog Editor (supplied with
the software development kit) simplifies designing and testing a dialog
box──one of the key elements of a Windows application. Here's how to use it
and what to watch out for.


Ask Dr. Bob


EDITOR'S NOTE

This first issue of the Microsoft Systems Journal is devoted in its entirety
to Microsoft Windows operating environment, Microsoft's most important
system software product since MS-DOS(R).

Windows has encountered remarkable success both as a retail product and as
a development environment since its release last November. While much of
the product's initial popularity can be attributed to its flexibility as
a "switcher" for traditional applications and the included sample
applications, its ultimate success will depend on the acceptance of native
Widows applications.

Reuters' ART. As a definitive example of the capabilities of Microsoft
Windows and its flexibility to serve particular user interface designs,
we examine the Advanced Reuter Terminal (ART), the most sophisticated
Windows application to date. A superb example of design and coding
craftsmanship, the ART would be worth featuring even if it were not based
on Windows.

We also present profiles of two graphics processors that will have a
significant impact on future graphics systems: the Intel 82786 and the
Texas Instruments TMS34010. And we take a look at DDE, a new standard
for interprogram data exchange, as well as examine the improved Dialog
editor. In addition, Dr. Bob tackles some questions about Windows from
developers. In sum, we hope to present a developer's view of Microsoft
Windows.

Future Issues. In future issues we will focus on other areas of interest to
developers, including C programming, device drivers, extended and expanded
memory, debuggers, code portability, and the inside view of MS-DOS and
Microsoft XENIX(R). We will tackle the subjects that interest serious
developers most──from user interface design to guidelines for writing
ternimate-and-stay-resident programs. Our aim is to confront the real
issues confronting serious developers.

Write to Us. We invite you to write to us, to subscribe, and equally
important, to tell us what you think we should include in the Journal.
Beginning in January 1987, our issues will expand to full size, more than
twice the length of this special issue.

We hope the Journal will become one of the most important publications you
receive.──Ed.

JONATHAN D. LAZARUS
Editor

MICHAEL LONGACRE
Design

Copyright(C) 1986 Microsoft Corporation. All rights reserved; reproduction
in part or in whole without permission is prohibited.

Microsoft Systems Journal is published every other month by 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 XENIS are registered trademarks
of Microsoft Corporation. Intel is a registered trademark of Intel
Corporation. IBM is a registered trademark of International Business
Machines Corporation. Lotus is a registered trademark of Lotus
Development Corporation. Texas Instruments is a registered trademark of
Texas Instruments, Inc.

────────────────────────────────────────────────────────────────────────────

ART Showcases Microsoft Windows

MSJ Interview

One of the finest examples to date of software running under Microsoft
Windows is a product called the ART, the Advanced Reuter Terminal. When you
think of Reuters, you think of news, but to currency traders who buy and
sell millions of dollars each day, Reuters means finance. Its financial
information service is the lifeblood of their trade, providing the timely
and accurate information on which their success depends. The ART is a
complete hardware and software solution that supplies a high-resolution
color monitor and features multiple windows showing text and graphics
displays of up-to-the-minute financial information. It has the equivalent
display capacity of ten of the previous Reuter Monitor terminals, which
limited users to viewing information on a monochrome text display. What's
more, ART users can perform data analyses by choosing options from pull-down
menus.


ART's Configuration

ART hardware consists of an 80286-based (PC-AT compatible) NCR computer with
1.5 megabytes of RAM, a 20-megabyte hard disk, high-resolution (EGA)
display, 1200-baud modem, Reuters proprietary communications board, a
Reuters programmable keyboard, and, of course, a mouse.

But the heart of the system is the Windows-based software. Developed by the
Reuters office in London, it offers such capabilities as real-time graphics,
user-defined montage pages, limit and news watching, and graphics analysis.
Real-time graphics are made possible by calling up a graphics window and
linking it to a particular currency value displayed on the screen. Each time
the value of the currency changes, a new tick is added to the graph.

The ART provides automatic limit watching, using dialogs in which users
enter upper and lower limits for any currency displayed on a stored page.
When limits are broken, the ART gives audible and visible signals allowing a
dealer to react instantly. News watching works the same way; the ART
immediately alerts the user when the preselected word or phrase appears in a
headline. In sum, the ART system allows the user to determine the screen's
content and layout, an asset to active traders.

To find out about some of the background development of the ART, we spoke
with Paul Lucke, manager of the Reuters micro section in London, and asked
him some questions about developing a product under Microsoft Windows.

To find out about some of the background and development of the ART, we
spoke with Paul Lucke, manager of the Reuters micro section in London, and
asked him some questions about developing a product under Microsoft Windows
operating environment.


Why Windows?

MSJ: Why did you choose Microsoft Windows rather than some of the other
graphics environments such as GEM?

REUTERS: Clearly we wanted a bit-mapped graphics display because a major
component of the system is drawing graphs and performing analytical studies.
That cut out the non-bit-mapped graphics systems, which brings you down to
Windows and GEM.

We started out by buying a Laser (computer) as soon as they arrived on the
scene. At the time it looked as if that was the way user interfaces were
going. After we did some development work on the Laser, we saw the IBM PC
running Microsoft Windows and figured that this particular combination was
the way to go. We felt that Microsoft had the makings of a market leader, so
we took Windows on as an alpha test site and put up some experimental demos
using Reuters data. Our objective was to turn data into information, and we
believed that Microsoft Windows was the right vehicle for doing so.

Clearly, when GEM came out, we had to consider it as well, particularly
because an alpha release of any product has its fair share of bugs. But we
found GEM's method of window interfacing, at least at the time we reviewed
it, to be inferior to Microsoft's tiled windows concept.

Think of it as two kinds of desks: the untidy desk and the tidy desk. Tidy
is the way we wanted to go. When information changes, traders need to see it
as it happens. They can't afford to have it obscured underneath another
window. So any kind of obscuring device must be something temporary, like
pull-down menus, which disappear once you select an item.

Also, some of the simple things were better implemented in Windows──like the
accuracy of the pointing. We just found it easier to use than GEM.


Microsoft Support

MSJ: How did you find Microsoft's support?

REUTERS: We took care only to go to our contact at Microsoft when we were
dealing with a problem that was clearly a fault of the product. That way we
were able to encourage him to listen to us. And he offered good support on a
one-to-one basis.

Clearly, something like the DIAL system would have been useful, had it been
available. We have a North American operation that has a DIAL account, and
users report that it has recently improved considerably. One of the things
that's been particularly useful is watching the bulletin board as other
people's problems are discussed.

MSJ: You depend on having information updated instantly. Did you run into
any problems when you were working with your implementation?

REUTERS: We have to go through a lot of tricks to design the system in such
a way that the time-critical aspects of the application are catered to.
There must be an easier way.

What's also a major problem is 640K restrictions. It's imperative that we're
able to access lots of memory. For large data areas you can do something
with RAM disks, but we really need MS-DOS 5.0. It seems to be receding into
the far distance; every quarter it seems to slip a quarter. The whole point
of a windows environment is to have multiple simultaneous applications
running, and if memory limitations prevent loading more than two
applications, that's really bad news.

If Microsoft is going to keep faith with the developers of Windows
applications, it has to come out with MS-DOS 5.0──or at least a
mechanism for making large chunks of executable memory available.


Cautious Interest

MSJ: You mentioned that in the course of developing your product you had to
come up with some tricks to perform cerain tasks. Do you ever share this
information with other developers? Would, for instance, a user group benefit
from your experiences, or would you rather keep that kind of information to
yourself?

REUTERS: I'd characterize our attitude as "cautious interest." You must
understand that the major users of Microsoft Windows in the U.K. are either
our customers or competitors. And so, while we will certainly chat about our
experiences and exchange information on bugs and so on, we would not be
quite as forthcoming as perhaps a company that was trying to advertise
itself.

MSJ: The keyboard used with ART is quite different from the normal PC-style
keyboard. Tell us something about it.

REUTERS: The keyboard uses an 80186, which gives it more processing power
than a PC. It is completely programmable, which we need since we have many
different installations, which drive different kinds of terminal keyboards.
The keyboard we use lets us emulate any of these.

MSJ: What is the function of your proprietary communications board?

REUTERS: The board has its own processor and handles the communications
link, which is a multi-drop line protocol. The board also de-couples
communications processing from Windows processing and does page-caching.
Information pages are put into cache memory so that dealers will get a
quicker response when changing pages.

MSJ: Are you looking at any of the new graphics chips like the Intel 82786
and TI34010 to enhance the system?

REUTERS: Yes. We've developed a super-high-resolution graphics board that
uses the Intel chip, and we are waiting for Intel to release the final
version. We were also impressed with the TI chip, and we're looking at it
seriously. We'll use the one that's up and running at board production
time.

MSJ: Your windows work a little differently from the typical Microsoft
windows. Why did you choose to have a common menu bar?

REUTERS: We eliminated the menus because we didn't want to waste any
precious screen space, and found that it simplified usage.


Mouse-o-phobia?

MSJ: What has been the reaction of dealers to the mouse?

REUTERS: We've had a positive response at demonstrations we've given. No one
who has used it has ever let it go. We have found, however, that some people
take time to adjust to pointing with it──as long as two to three hours.

MSJ: Now that you've had considerable experience with it, what is your
overall impression of the Microsoft Windows environment?

REUTERS: Windows is an extremely powerful set of tools. We could not have
brought to market a product of the quality of the ART in such a relatively
short time frame without it. The learning curve is reasonably short, too. An
experienced C programmer will take only a matter of days to get a simple
application running. And in perhaps four to five weeks he'll become fully
productive, partly as a result of support within the team.

A large number of people on the team now know how to use Microsoft Windows.
Also, the examples of working systems make the learning curve easier. From
that point of view, I'm convinced Windows is the right choice for Reuters.
As an environment for running third-party applications, Windows might not be
the ideal choice, but we're using it for specialized applications within the
financial marketplace.

The thing that makes it so useful is its ability to adapt quickly to
different high-resolution screens. For example, we pulled in a Wyse PC with
a 1280 by 786 monochrome screen. In a couple of hours we got the whole
system running in a totally different resolution, different character sets,
different fonts and looking nice. If you've got a Windows driver for your
display, you can port your application onto it and make use of the
facilities provided by that display. It's that flexible.

MSJ: Is there anything else?

REUTERS: One of the most imaginative ideas I've seen yet is that Microsoft
has been advertising in a specialist magazine, called DOT.EXE. It's targeted
at ISVs and offers, for four hundred pounds, a one-day course in Microsoft
Windows, which includes an ISV tool kit and a Microsoft Mouse. We'll
probably be plugging our new people into it.

The credit for developing ART should go to a team of high-powered
programmers led by Chris Oswald. They are the ones who deserve the credit
for doing an incredible job.

────────────────────────────────────────────────────────────────────────────

Dynamic Data Exchange Enhances Application Connectivity

Harvey Berger

One of the most valuable benefits of Microsoft Windows is that enables you
to use a number of application programs in combination to solve a problem.
With Windows you can transfer text, numbers, or pictures from one
application to another. A classic example is the ability of Windows to take
a table or a graph from a spreadsheet program and insert it into a word
processing document.

The Windows Clipboard is what serves as the intermediate repository for such
information exchanges among applications. At your direction, information is
placed on the Clipboard by a CUT or COPY operation in one application and
then inserted into the context of a second application with a PASTE
operation.

On the surface the process seems straightforward──a selection, a CUT or
COPY, and then a PASTE──and manual. But behind the scenes the two
applications and Windows combine to effect the actual transfer. The donating
application takes control of the Clipboard and places the selected
information on it in one or a series of formats. The receiving application
then selects a format and requests the data from the Clipboard.

In many cases, to save memory and reduce processing overhead, the donating
applications may defer actually rendering the selected data until another
applications requests it.


A Step Forward

The Clipboard's facilities are quite flexible, but they require manual
intervention for each transfer, and the Clipboard can hold only one "item"
at a time. In a multitasking environment like Windows, the need rapidly
develops for more sophisticated data exchange facilities. Windows Dynamic
Data Exchange (DDE) is intended to facilitate implementation of more complex
information transfers than are possible with a single or even multiple
clipboards.

DDE is a formal, public protocol for data exchange built on Windows
messaging, and the shared memory facility. DDE is much more an agreement
among cooperating applications than it is a particular body of code or
executable routine. It's a special usage of Windows messages.

Windows applications routinely receive messages corresponding to user- and
system-initiated actions ranging from keystrokes to request to repaint the
screen. DDE uses a set of formerly reserved message numbers to implement the
protocol. In the Windows environment any application may send a message to
any other application, without explicit knowledge of the nature of the other
application.

DDE is designed to facilitate implementation of a wide variety of data
exchanges, ranging from simple one-time transfers of selected data to
automatic parallel transfers of real-time data. Since many DDE relationships
are likely to be more complicated than those of the Clipboard, DDE is
described in terms of a server and client applications rather than in terms
of a donor and a receiver, where a single server may be supplying data to
many clients and a single client application may be communicating with many
servers simultaneously.


Data Formats

DDE and the Clipboard share a number of common facilities, most notably an
extensible set of data exchange formats. Initially Microsoft Windows
recognized a limited set of public formats for data exchange (TEXT, BIT MAP,
META-FILE, SYLK, DIF, and so forth) and custom formats. Concurrent with the
announcement of DDE, Microsoft has announced five additional clipboard data
formats (Rich Text, Table, Sub-Table, WKS, and DBF).

Windows also allows cooperating applications to register custom formats for
private usage. The new formats allow the economical transfer of data with
its formatting information.


Object Orientation

A key distinction between Clipboard and DDE exchanges is DDE's object
orientation. The Clipboard is used to transfer a user selection from one
application to another. Each Clipboard transfer is independent; the sequence
of transfers corresponds to the sequence of user operations. With DDE each
transfer consists of an item associated with a "name."


Names and Topics

When requesting or supplying data, the names are provided to allow the
communicating task to identify the source or disposition of the data item.
Names describe either the location or the contents of a data item. In a
spreadsheet, each cell has an address (for example, R2C3 for the cell in the
second row and third column). Some spreadsheets also have named cell ranges
(R1:4C5, for example, might be named TOTALS). In either case, a natural
relationship exists between names and data items. With other kinds of
applications, like word processors, names are likely be to more positionally
oriented. The DDE protocol requires a name or a positional reference for
each item to be transferred.

By definition, each name, as recognized by the server, refers uniquely to a
specific data item. The choice of names and topics are the private concern
of a server and its clients. Some servers will associate names relevant to a
given subject with a topic. Client applications typically ask potential
servers if they recognize (and can supply data about) the topic. Where the
server's names are in themselves very complete descriptions of data items, a
sample name may be used as the topic. Servers that define topics should be
worded carefully to avoid accidental duplication.


Applications

In the first example the Clipboard is used to paste a chart into a word
processing document. With DDE it is possible to construct more complex
scenarios. In addition to saving the pasted chart, the word processor might
store the name of the chart, the file name worksheet it was associated with,
and the file name of the application program. Given that information, the
word processor could ensure that it had the latest version of the chart.

First the word processor could, after verifying existence of the worksheet
file, start executing the spreadsheet application, specifying the worksheet
file name and a DDE startup parameter. Then, using DDE, the word processor
could request retransfer of the graph. The exact procedures to be used are
left up to the developers of the respective applications. Microsoft will
most likely issue guidelines soon for future application development.


Chain Reaction

A scenario in which the chart is the result of calculations in a spreadsheet
is also reasonable to envision. When printing the document, the word
processor might request the chart from the graphics application, which in
turn might extract the applicable data from the spreadsheet. The integration
becomes involved, but the burden moves from the user repeating manual steps
(which he might forget) to the PC using DDE.


DDE Sequences

A sequence of DDE exchanges between a client and a server are referred to as
sessions.(Figures 2, 3, 4, 5, 7, 8, and 9 show portions of sample
client and server applications.) A session typically begins with an INQUIRE
message. A client application sends a WM_DDE_ INQUIRE to every other active
application (potential server) in the system. The INQUIRE message includes a
topic or a sample name. If an active server is willing to exchange data
related to the topic, it in turn responds with a WM_DDE_ ACK message.

After establishing a session, the client and the server exchange messages
according to the operating rules of the server. Servers may have
implementation restrictions on the number of clients or the types of
transfers it will honor. DDE provides eight message types that establish
transfer protocols.

The terms client and server are largely semantic, since either application
can send (or receive) any message type. In practice, a given application may
be functioning as a server to one application and as a client to the same or
another application.

A simple example of a DDE transfer would consist of the following sequence:

  1.  Application One broadcasts an INQUIRE message to those windows in
      search of a server that will discuss CHEMICAL FORMULAS.

  2.  APP2, with a database of chemical formulas, responds to APP1 with an
      ACK (acknowledgement) message.

  3.  APP1 then sends a REQUEST message specifying WATER as the desired item
      name.

  4.  APP2 responds with a text string UPDATE message pointing to WATER=H2O.

  5.  APP1 then sends a REQUEST message for SALT.

  6.  APP2 responds with an UPDATE message specifying SALT=NaCl.


Real-Time Data

DDE was developed in response to the users' need for the coupling of data
from separate applications. The stock market is another classic example of
the need for DDE. During trading hours there's a constant flow of data as
each transaction is processed by the exchanges. Market watchers need to
filter the data stream and isolate the transactions of interest to them. The
selected data is then analyzed to determine the required action. One of the
first demonstrations of DDE modeled this process.

A SignalTM receiver (supplied by Lotus Information Network Corporation)
offered a continuous feed of stock prices (for up to 600 selected stocks).
Normally the Signal receiver (connected to a serial port) supplies data to
special Lotus(R) software. For this demonstration Windows Terminal was
modified to detect price changes and respond to DDE messages. A simple bar-
charting program was then written to receive the DDE UPDATE messages.

Interestingly the SIGNAL monitor program was written in New York City by
consultant Louis Cutrona, and the Charting program was written by Mark
Taylor of Microsoft. When brought together, the two programs were able to
communicate through DDE after being developed 3,000 miles apart.


ADVISE-DATA

In the first example above, the transfers are relatively simple. For each
REQUEST message there's a corresponding UPDATE. The ADVISE message allows
for more complex transfers. In the following example it accommodates the
automatic transfer of real-time data as it changes.

  1.  APP3 broadcasts an INQUIRE message to those windows, looking for a
      server with NY STOCK PRICES.

  2.  The Signal Monitor Application Four (APP4) responds with an ACK.

  3.  APP3 sends APP4 an ADVISE message that specifies IBM.(R)

  4.  APP4 responds with an ACK indicating that it recognizes IBM and can
      supply data about it.

  6.  APP4 then sends the current price data for IBM.

  7.  Steps 3 to 5 are repeated for each stock of interest.

  8.  Each time APP4 learns of a new price for stock about which it has
      received an ADVISE, it sends an UPDATE message with the new price.


Atom Management

To speed processing and simplify the development of DDE Servers, DDE
applications exchange "atoms" rather than strings. A central atom manager
allows all applications in the system to register strings in exchange for a
16-bit atom. The atom manager (a shared executable library) will also
translate the atom to its corresponding string. A single atom pool is
maintained for all DDE applications in the system.

In anticipation of sophisticated client/server relationships, DDE offers
optional specification of the Clipboard format for data exchange,
acknowledgement of messages, and shared memory controls. Private
client/server priority schemes are also accommodated.

Developers interested in the details of DDE, the central atom manager, or
the new Clipboard formats can download the specifications documents and
sample programs from either DIAL or GENIE.

On the basis of early response to the DDE design specification, a
significant number of the new Microsoft Windows applications now being
developed will incorporate DDE provisions. The nature of the implementation
depends on the type of application and its particular features.


Macros

Many applications, including spreadsheets, databases, and charting programs,
include user-definable macros or other programming capabilities. The DDE
design anticipates the combination of these applications based entirely on
user programming. If the application implements a substantive part of DDE
capabilities, the user can supply the topics and item names, constructing
data relationships not contemplated by the application developer.
Generalized applications can thus be combined into tightly coupled
customized applications suites.

This process is not as complex as it might sound. In the bar chart example
described above, the user can specify, via a dialog box, the particular
stocks to be tracked. In a more generalized implementation, the user might
also have been allowed to specify the topic, directing the DDE message to a
different server. If, hypothetically, another server existed in the PC, it
could supply the data to be plotted. Thus one instance of the bar chart
program might display stock market data, while another instance displays
temperature, pulse, and blood pressure from a server connected to
appropriate sensors.

This example may be far-fetched in the choice of data to be displayed side
by side, but it's reasonable to imagine the development of common analytical
software that can be applied to disparate tasks. Microsoft Windows and DDE
expand the potential market for analytical software by providing a protocol
that is truly application independent.


Outside Influence


So far this discussion has been limited to communications among
applications running on the same PC. The principles of DDE are, however,
applicable to transfers of data across machines, even computers of different
types.

The DDE protocol assumes that (if possible) a request for data exchange will
be satisfied by "broadcasting" a request to all other tasks in the PC and
that one of those contacted will supply the desired data. If the data
desired is not available locally, DDE provides for the use of "agents."

An agent is an application that serves as the importer/exporter of data.
When an agent application receives an INQUIRE request for data about a
given topic, it determines whether it can satisfy the request by in turn
sending the message to its "correspondents" in other machines, which may be
able to satisfy the request.

A given agent might use a local area network, micro-mainframe link, or
other communications facility to reach its correspondents. The correspondent
agent may then issue a DDE INQUIRE (if it is located in a PC) to locate the
ultimate server. The exchange of data then proceeds without either the
client or the server being aware of the agents.


Access Control

While DDE does not explicitly provide for access control or data security, a
server application has a number of alternatives at its disposal to validate
the authority of a given client before exchanging data with it. The server
could require a password or other access parameter to be sent along with the
plete communication faciT message back to the client for the password, or
using the window handle provided, access a lot of information (from Windows)
about the client.

The DDE protocol is a framework on which software developers can construct
private or public exchange mechanisms. When exchanging data with private
(well-known) clients, servers can supplement DDE with message priority, data
queueing, and private message protocols. In addition to Microsoft Windows'
built-in facilities for inter-application communication, very complete
communication facilities designs can be implemented.


The Future

The fact that Microsoft Windows is independent of any physical device
permits applications to take full advantage of the capabilities of a wide
variety of current and future displays, printers, and pointing devices.
Application developers are freed from having to be concerned in their
programs with the peculiarities of particular devices. DDE extends the same
concept to data exchange. If an application honors DDE requests, it's then
able to communicate with all other applications that do so, without having
to consider their peculiarities.

DDE is an important step forward in inter-application communication, since
it provides a public protocol that applications can subscribe to rather
than requiring each application developer to have to consider all the
possible applications that users might want to exchange data with. The user
can then combine programs and achieve results that no single application can
offer.


Figure 1:  DDE Messages

╓┌─────────────────┌───────────────────┌──────────────────┌──────────────────╖
DDE Messages      Purpose             Argument 1         Argument 2

WM_DDE_INITIATE   Ask if a window     Atom ─► Topic
                  will exchange
                  items related
                  to a topic
WM_DDE_TERMINATE  End the exchange    Atom ─► Topic
                  of items related
                  to the topic
WM_DDE_ACK        Response to a       Atom               Acknowledgement
                  DDE message                            code
WM_DDE_REQUEST    Request a given     Atom ─► Item name  Desired data
                  item                                   format
WM_DDE_DATA       Contains a          Atom ─► Item name  Handle to
                  requested data                         data object
                  item
WM_DDE_ADVISE     Request a given     Atom ─► Item name  Desired data
                  item and automatic                     format
                  updating
DDE Messages      Purpose             Argument 1         Argument 2
                  updating
WM_DDE_UNADVISE   Request end of      Atom ─► Item name  Desired data
                  automatic updating                     format
                  for a given time
WM_DDE_POKE       Contains an         Atom ─► Item name  Handle to
                  unsolicited data                       data object
                  item
WM_DDE_EXECUTE    Request execution                      Handle to data
                  of indicated                           object containing
                  command                                command


Figure 2:  Client's Window Procedure

typedef struct {
    WORD wDDEMODE;
    WORD wFormat;
}  DDEADVISEBLOCK;
typedef struct {
    WORD wClientRelease;
    WORD wFormat;
    WORD hServerWnd;
    WORD ClipData;
}  UPDATEINFO;
ATOM aCompare;
BOOL bAccepted;

long FAR PASCAL SampleRcvrWndProc(hWnd, message, wParam, lParam)
     switch (message)
{
                                    ∙
                                    ∙
                                    ∙
      case WM_DDE_ACK:
          aCompare  = HIWORD(lParam);
          bAccepted = LOWORD(lParam);
               /* Is this acknowledgement of first inquire message? */
           if ((aCompare == aStockInfo) && bAccepted)
               hwndServer = wParam;
           else {
               /* is this the acknowledgement of an advise message? */
               for (i = 0; i<NUMSTOCKS; i++) {
                   if (aCompare == stockinfo[i].Atom) {
                        /* Record Server's response to Stock Symbol */
                                    ∙
                                    ∙
                                    ∙
                   }
               }
           }

           break;
        case WM_DDE_DATA:
           ProcessNewData(wParam, lParam);
           break;
        default:
           return((long)DefWindowProc(hWnd, message, wParam, lParam));
           break;
        }
        return(0L);
    }


Figure 3:  Client's Window Creation Procedure

SampleRcvrCreate(hwnd)
HWND hwnd;
{
HMENU hMenu;
DDEADVISEBLOCK far *lpmem;
int i;
         /* DDEAddAtom is part of the global atom manager
          * in the dynamically linked library -- DDE.EXE */
aStockInfo = DDEAddAtom((LPSTR)"NYSE.COM.STK");
         /* For the first message, broadcast it to everyone;
          * When we return from this call, hwndServer will be valid
          * if a server exists. NULL as loword of lParam means server
          * can be any application. */
SendMessage((HWND) -1, WM_DDE_INITIATE, hwnd, MAKELONG(NULL, aStockInfo));
DDEDeleteAtom(aStockInfo);
        /* If Server isn't available, return. hwndServer
         *         can't be null in a call to PostMessage */
if (hwndServer == NULL) return;
      /* Some Dummy Stocks for this example */
stockinfo[0].Atom = DDEAddAtom((LPSTR)"T");
stockinfo[1].Atom = DDEAddAtom((LPSTR)"GM");
stockinfo[2].Atom = DDEAddAtom((LPSTR)"IBM");
stockinfo[3].Atom = DDEAddAtom((LPSTR)"INDU");
                                    ∙
                                    ∙
                                    ∙
   for (i = 0; i< NUMSTOCKS; i++) {
       hmem[i] = GlobalAlloc(GHND, (DWORD)4);
       lpmem = (DDEADVISEBLOCK far *)GlobalLock(hmem[i]);
       lpmem->wDDEMODE = 0;    /* I'm not going to ACK his updates */
       lpmem->wFormat = CF_TEXT;
       GlobalUnlock(hmem[i]);
       PostMessage( (HWND) hwndServer, WM_DDE_ADVISE, hwnd,
          MAKELONG(hmem[i],stockinfo[i].Atom)  );
        }


Figure 4:  Client's Data Update Procedure

ProcessNewData(hwnd, lStuff)
HWND hwnd;
DWORD lStuff;
{
    ATOM     aData    = HIWORD(lStuff);
    HANDLE   hDDEData = LOWORD(lStuff);
    UPDATEINFO far *lpDataBlock;
    LPSTR lpData;
    HDC hdc;
    int i,c;
         /* Make sure we're getting info only from our server */
         /* Note: hwndServer is a  global variable */
    if (hwnd != hwndServer)
        return;
    lpDataBlock = (UPDATEINFO far *)GlobalLock(hDDEData);
    for (i = 0; i<NUMSTOCKS; i++) {
        if (aData == stockinfo[i].Atom) {
                                    ∙
                                    ∙
                                    ∙
             }
        }
        GlobalUnlock(hDDEData);
        GlobalFree(hDDEData);
    }


Figure 5:  Server's Window Procedure

/* Procedures which make up the window class. */

long FAR PASCAL TermWndProc(hwnd, message, wParam, lParam)
                                ∙
                                ∙
                                ∙
    switch (message)

        {
                                ∙
                                ∙
                                ∙
         case WM_DDE_INITIATE:
                dde_initiate( hwnd, wParam, lParam );
                break;
         case WM_DDE_ADVISE:
                dde_advise( hwnd, wParam, lParam );
                break;
         case WM_DDE_UNADVISE:
                dde_unadvise( hwnd, wParam, lParam );
                break;
         case WM_DDE_CLOSE:
                dde_cancel  ( hwnd, wParam, lParam );
                break;
         case WM_DDE_REQUEST:
                dde_request( hwnd, wParam, lParam );
                break;
         case WM_DDE_EXECUTE:
                dde_execute( hwnd, wParam, lParam );
                break;
                              ∙
                              ∙
                              ∙
        default:
            return(DefWindowProc(hwnd, message, wParam,lParam));
            break;
    }
    return(0L);
  }


Figure 6:  Foreign Intrigue

╔══════════════════════════════════════╗
║ ┌──────────┐             ┌─────────┐ ║
║ │          │             │         │ ║
║ │  APP  5  │►DDE REQUEST►│  LOCAL  │ ║
║ │          │             │         │ ║
║ │  CLIENT  │◄◄◄DDE ACK◄◄◄│  AGENT  │ ║
║ │          │             │         │ ║
║ └──────────┘             └─────────┘ ║
╚══════════════════════┌─►──►──►──►──►──►──►──►─┐
                          LOCAL AREA NETWORK   ▼
                       └─◄──◄──◄──◄──◄──◄──◄──◄─┘═══════════════════════╗
                                 ║  ┌─────────┐             ┌──────────┐║
                                 ║  │         │             │          │║
                                 ║  │ FOREIGN │►DDE REQUEST►│  APP  6  │║
                                 ║  │         │             │          │║
                                 ║  │  AGENT  │◄◄◄DDE ACK◄◄◄│  CLIENT  │║
                                 ║  │         │             │          │║
                                 ║  └─────────┘             └──────────┘║
                                 ╚══════════════════════════════════════╝


Figure 7:  Server's Initialization Procedure

typedef struct

{
        WORD    wHandshake;
        WORD    wFormat;
        HWND    hServerWnd;
        char    szData[ MAXDATACHARS ];
} DDEUPDATEBLOCK;

typedef struct

{
        WORD    DDEMode;
        WORD    wFormat;
} DDEADVISEBLOCK;

typedef struct

{
       int     cActive;                  /* Number of active clients */
       ATOM    aSymbol;                  /* Atom identifies symbol */
       char    szSymbol[ MAXSYMCHARS ];  /* Text of the symbol name */
       HWND    hwndClient[ MAXCLIENTS ]; /* Handles of active clients */
} HOTITEMS;

static  HOTITEMS        DDE[ MAXHOTITEMS ];
static  DDEUPDATEBLOCK  DDEUpdateBlock;
static  ATOM            aStockInfo;
                /* This atom means DDE_INITIATE is for us */
void     dde_init()
{
         /* Atoms for WM_DDE_INITIATE */
         aStockInfo = DDEAddAtom( (LPSTR) "NYSE.COM.STK" );
                                    ∙
                                    ∙
                                    ∙
{


Figure 8:  Server's INITIATE Response Procedure

void    dde_initiate( hwnd, hwndClient, lData )
    HWND    hwnd;
    WORD    hwndClient; /* Contains client's window handle */
    DWORD   lData;
    {
        ATOM    atom;
        atom = (ATOM) HIWORD( lData );
        if( atom == aStockInfo )  {
            if( IsWindow( (HWND) hwndClient ) )
                SendMessage( (HWND) hwndClient,WM_DDE_ACK,
                       hwnd, MAKELONG( TRUE, atom )  ): )
    }


Figure 9:  Server's Response to Advise Request

void    dde_initiate( hwnd, hwndClient, lData )
HWND    hwnd;
WORD    hwndClient; /* Contains client's window handle */
DWORD   lData;
{
     ATOM                    atom;
     BOOL                    b;
     GLOBALHANDLE            gh;
     DDEADVISEBLOCK FAR *    lpDDEAdviseBlock;
     int                     i, j, k;
     LPSTR                   lps;
     char *                  p;
     char                    sz[ 20 ];
     b = FALSE;
     atom = (ATOM) HIWORD( lData );
               /* Lookup Symbol in Table */
     if( SymbolIsAvailable( atom ) )  {
         gh = (HANDLE) LOWORD( lData );
         lpDDEAdviseBlock = (DDEADVISEBLOCK FAR *) GlobalLock( gh );

            if( lpDDEAdviseBlock->wFormat == CF_TEXT ) {
                for( i = 0; i < MAXHOTITEMS; i++ ) {
                    if( DDE[i].aSymbol == atom ) {
                        for( j = 0; j < MAXCLIENTS; j++ )  {
                           if(
                              DDE[i].hwndClient[j]==(HWND)hwndClient){
                                 b = TRUE; /* Already there */
                                 break;
                          }  }
                     if( !b )   /* if atom is there but client isn't */
                      {   for( j = 0; j < MAXCLIENTS;j++ )
                          {        /* Look for an empty slot */
                             if( DDE[i].hwndClient[j]==(HWND)NULL )
                               { DDE[i].hwndClient[j]= (HWND)hwndClient;
                                 DDE[i].cActive++;
                                 b = TRUE;
                                 break;
                  }   }   }  }
                  if( b ) break;
          }
          if( !b )        /* If atom is not already in the table */
          {  for( i = 0; i < MAXHOTITEMS; i++ )
             {  if( DDE[i].cActive <= 0 )
                {   DDE[i].aSymbol = atom;
                    DDEGetAtomName( atom, (LPSTR) DDE[i].szSymbol,
                                    MAXSYMCHARS - 1 );
                    DDE[i].szSymbol[MAXSYMCHARS - 1 ] = '\0';
                    DDE[i].hwndClient[ 0 ] =(HWND) hwndClient;
                    DDE[i].cActive = 1;
                    b = TRUE;
                    break;
          }  }  }
                GlobalUnlock( gh );
        }
         /* Acknowledge one way or the other */
        if( IsWindow( (HWND) hwndClient ) )
            SendMessage( (HWND) hwndClient,
                WM_DDE_ACK, hwnd, MAKELONG( b, atom ) );
        if( b )
        {
            /* Transmit latest data we received */
            if(  ( gh = GlobalAlloc( GMEM_MOVEABLE, (DWORD)
                    sizeof(DDEUPDATEBLOCK)))
                  && ( lps = (LPSTR) GlobalLock( gh ) ) )
                {   for( p = (char *) &DDEUpdateBlock, k = 0;
                         k < sizeof( DDEUPDATEBLOCK );
                         *lps++ = *p++, k++ );
                    GlobalUnlock( gh );
                    PostMessage( (HWND) hwndClient, WM_DDE_DATA, hwnd,
                         MAKELONG( gh, atom ) );
        }       }
           return;
   }

────────────────────────────────────────────────────────────────────────────

New Intel Graphics Coprocessor Makes Windows Sparkle

───────────────────────────────────────────────────────────────────────────
Also see related tables:
  82786 Features
  82786 Graphics Instruction
───────────────────────────────────────────────────────────────────────────

The new graphics coprocessor from Intel, the 82786, is expected to
substantially enhance the performance of Microsoft Windows and other
graphics applications on the IBM PC, XT, AT, and compatibles. The chip
performs near-instantaneous screen updates, draws graphic primitives such
as lines and circles at more than 2 million pixels per second, and creates
text at a rate of 25,000 characters per second. It also supports
multitasking and ultra-high-resolution displays.

The 82786 has four major components: a graphics processor, display
processor, bus interface unit, and dynamic RAM controller. These parts
of the chip communicate over a built-in asynchronous bus.

The graphics processor accepts commands from applications programs to draw
bit maps, which create objects in memory. The processor contains a set of
on-chip graphics commands that draw points, lines, rectangles, circles,
polygons, and other primitives, to relieve the host CPU of the task of
creating these figures from scratch. To display any of these figures on the
screen, the CPU sends a single 16-bit request to the 82786 graphics
coprocessor.

To handle specialized drawing functions, the 82786 includes commands such as
BitBLT (bit block transfer), ChaBLT (character-block transfer), Incremental
Point, and Fill. BitBLTs are performed upon request from the CPU at a rate
of 24 megabits per second. ChaBLTs occur at the rate of 25,000 characters
per second. The Incremental Point command is used to draw more complex
figures. An incremental point list describes the figure's shape, and, once
the CPU sends the list to the 82786, the graphics processor draws the shape
at a rate of 2 million pixels per second. The Fill command instructs the
graphics processor to fill in any shape with horizontal lines.

The coprocessor also has non-drawing instructions. For example, the Link
command executes an unconditional jump to another command, and Enter Macro
and Exit Macro are used to call and return, respectively, from a
subroutine.

The 82786 can receive a block of commands at one time from the CPU, and do
it while executing other commands.

Many of the graphics processor's functions are implemented in hardware
rather than software. These include clipping, mouse support, character-set
support, logical operations, and bit-plane masking.

The amount of display memory is the only limitation on the number of
character sets that the 82786 can support. Any one can be activated by a
single command. All character sets can have proportional spacing or kerning.

The next major functional block of the 82786 is the display processor. This
part of the 82786 is independent of the graphics processor. Microsoft
Windows would use the display processor to control display contents.

The display processor transfers multiple bit maps from memory to window
areas on the screen. It also provides a hardware cursor and colored borders
around the windows. An almost unlimited number of windows can be displayed
on the screen.

To create windows, the display processor divides the screen into strips and
tiles. A strip is one or more horizontal scan lines, and a tile is a section
of a strip. A collection of tiles forms a window.

Unlike the graphics and display processors, the bus-interface unit and
dynamic RAM controller are invisible to the CPU software. The bus interface
unit maintains the integrity of a display by arbitrating requests for
graphics memory. The dynamic RAM controller handles up to 4 megabytes of
graphics memory and screen rereshing.


82786 and Windows

What will the 82786 do for a product like Microsoft Windows? According to
Richard Bader of Intel, "The 82786 will make Windows so responsive that a
computer like the IBM PC will no longer stand in the way of what the user
wants to do. Operations that took seconds to execute previously will now
happen instantaneously. The 82786 enhances the intimacy between user and
machine, and thus enhances productivity."

Will Windows developers need to understand the intricacies of the 82876 in
order to achieve this high performance? Says Bader, "When an 82786 board-
level product comes to market, it will have a Windows driver, which would
then make the 82786 virtually transparent to the applications programmer──as
transparent as the Microsoft Graphics Device Interface allows. When an
applications programmer uses any graphics board, he needs to interrogate the
GDI interface to find out specific characteristics of the board such as the
resolution it supports and whether or not it supports color. The application
is then adapted to those physical characteristics. But for the most part
there is a great deal of transparency provided by GDI, and this will be
applicable to new 82786 graphics boards."


An Ideal Application

One application that stands to gain significantly from the appearance of
82786 boards is desktop publishing. Intel's Bader points out, "As Windows
evolves, we expect that it will be ideally suited for desktop publishing.
And since the 82786 is so well suited as a GDI device, we believe that the
82786 is also applicable to that market." Many of the desktop publishing
packages currently on the market take quite a long time to reformat
information on the screen. And displaying a variety of fonts on the screen
is time consuming and awkward. Continues Bader, "The speed of the 82786 will
change all that. Microsoft Windows in conjunction with the 82786 will give a
good performance."

The 82786 supports a wide range of monitors, which is also a plus for
desktop publishing. It easily supports systems that are 640 by 480 with 64
colors. Additionally, Bader points out, "We can also support large-format
screens, full-page displays, with a resolution in the order of 1200 by 800,
1400 by 1400, or 1000 by 800 with gray scales. What we've found in a number
of instances is that people are looking for that 17-inch high-resolution
monochrome monior. They're looking for extremely crisp, stable, high-
performance text displays, and that's exactly what the 82786 is capable of
providing."

Intel and Microsoft have entered a cooperative program to provide Microsoft
Windows software driver technology for systems designs using the Intel
82786.

───────────────────────────────────────────────────────────────────────────
82786 Features
───────────────────────────────────────────────────────────────────────────

  ■  High-performance graphics

  ■  Fast polygon and line drawing

  ■  High-speed character drawing

  ■  Advance DRAM controller for graphics memory up to 4 megabytes

  ■  Supports up to 200-MHz CRTs
     ──up to 640 by 480 by 8 bits (1K∙1K∙8) or
     1400 by 1400 by 1 bit (2K∙2K∙2)

  ■  Up to 1,024 simultaneous colors

  ■  Integral DRAM controller, shift registers, and DMA Channel

  ■  IBM PC Computer Graphics Adaptor compatbility

  ■  Hardware windows

  ■  Fast bit-block copies between system and bit-map memories

  ■  Integral video DRAM support──up to 1900 by 1900 by 8

  ■  Multitasking support

  ■  International character support

  ■  Provides support for rapid filling with patterns

  ■  88- pin grid array leadless chip carrier

  ■  Advanced CHMOS technology


───────────────────────────────────────────────────────────────────────────
Graphics Instructions
───────────────────────────────────────────────────────────────────────────

  64 Bit Block Transfer within bit-map
  AE Bit Block Transfer across bit-maps
  06 Define Texture: Opaque
  0A Define Character Set: Word
  64 Bit-Block Transfer Within Bit-Map
  AE Bit-lock Transfer Across Bit-Maps
  06 Define Texture: Opaque
  07 Define Texture: Transparent
  0A Define Character Set: Word
  0B Define Character Set: Byte
  1A Define Bit-Map
  3D Define Colors
  41 Define Logical Combination
  46 Define Clip Rectangle
  4D Define Inter character Space
  4E Define Character Orientation
  68 Draw Arc: Exclusion
  69 Draw Arc: Inclusion
  53 Draw Point
  B4 Draw Incremental Points
  BA Draw Series of Horizontal Lines
  54 Draw Line
  58 Draw Rectangle
  73 Draw Polygon
  74 Draw Polyline
  8E Draw Circle
  A6 Draw Character String: Opaque
  A7 Draw Character String: Transparent
  44 Enter Pick Mode
  45 Exit Pick Mode
  4F Move to Absolute Position
  52 Move to Relative Position

────────────────────────────────────────────────────────────────────────────

Programmable Graphics processor from TI Perks up PC Pixels

───────────────────────────────────────────────────────────────────────────
Also see the related TMS34010 tables:
  TMS34010 Features
  TMS34010 Graphics Instruction
───────────────────────────────────────────────────────────────────────────

Joe Desposito

Texas Instruments(R) has developed the first graphics microprocessor, the
TMS34010. This is a significant development in the world of graphics chips,
which to date has been populated with less powerful graphics coprocessors
and controllers. The TMS34010 is a 32-bit processor that executes 6-million
instructions per seconds (MIPS), addresses up to 4-gigabytes of memory, and i
a general-purpose instruction set.

The chip, which will enhance all graphics applications, is of particular
interest to developers using Microsoft Windows on the IBM PC and
compatibles, where up until now performance has been less than spectacular.
The TMS34010 will be contained in an add-on board that will handle all the
graphics processing, replacing the monohrome color graphics or enhanced
graphics display adaptor.


Inside the TMS34010

Like most microprocessors, the TMS34010 has an ALU, general registers, stack
pointer, program counter, instruction decoder, and control unit. However, it
also contains components that optimize the chip for graphics processing.

It has a far greater number of general registers contained in the
microprocessor than that found in general-purpose processors. Thirty 32-bit
registers are divided into an A file and a B file. This large number
prevents the shifting of bits between registers that often occurs with
graphics processing. It uses a barrel shifter for high-speed bit rotations
and can rotate a field of up to 32-bits in a single instruction cycle.

The microprocessor uses a four-segment instruction cache for loading
instructions from memory before they are executed. Each segment has eight
subsegments, each holding eight bytes of code. This 256-byte instruction
cache speeds up the execution of graphics algorithms by capturing loops of
instructions. Once a loop is captured, "fetch" memory cycles in the loop
halt. Loop execution is fast because the chip can access instruction cache,
internal register files, and external memory simultaneously instead of
sequentially.

The chip's input/output registers include interrupt registers, host
interface registers (for communicating with a host processor), video timing
registers (to drive a CRT), and local memory control registers.

To further increase speed, the TMS34010 employs some of the principles of
reduced-instruction-set computer (RISC) chips; for example, it executes most
of its instructions in a single 160-ns cycle.


Pixel Processing

In addition to approximately 100 general-purpose instructions, the TMS34010
uses special high-speed instructions called PixBLTs (pixel block transfers)
to handle character display and bit-mapped graphics. These instructions are
an extension of BitBLTs (bit-block transfers) found in some graphics
controllers. Transfers are made between source and destination arrays using
linear addressing or X-Y coordinates.

PixBLT instructions include Boolean and arithmetic operations, expansion of
a single bit to one of two colors, transparency detection, and plane-
masking. These types of instructions can also be combined to perform a much
larger set of operations.

The TMS34010 operates on single-bit pixels, which are usually associated
with monochrome graphics, by using its Boolean operations on source and
destination arrays. The common combining operations are Replace, OR, NOT-
AND, AND, and XOR.

For multiple-bit pixels, which are usually associated with color and gray-
scale graphics, the processor uses arithmetic operations such as Maximum,
Minimum, Add, Add with Saturate, Subtract, and Subtract with Saturate. The
Maximum and Minimum functions are used to create a sense of depth and to
smooth jagged lines, a technique called antialiasing. Add with Saturate and
Subtract with Saturate are useful for dedicating multiple color planes to
each of the three primary colors.

A PixBLT instruction can expand a bit to one of two colors by taking one bit
per pixel data from memory, expanding it to multiple-bit color data, and
placing it anywhere in the frame buffer. PixBLT instructions also handle
windowing, allowing the placement of windows of variable size and content
anywhere on the display, and clipping, which prevents drawing outside the
boundaries of a window.

The way the TMS34010 handles text generation is quite different from other
processors. First, a display list, which defines what is to be displayed,
appears. For normal block text, the list is simple; say, a list of ASCII
character codes. As text becomes more complex, requiring, for example,
proportional spacing and kerning, the display list becomes more complicated.
The TMS34010 general-purpose instructions typically break down the display
list, and then a PixBLT is used to move this image from off-screen memory
onto the screen.

Texas Instruments is supporting the TMS34010 with a line of software
development and support products, including low-level assembly language
tools, a C compiler with a run-time support package, a function library,
and support of the CGI (computer graphics interface) standard. Hardware
and software development systems are also available.


Building Applications

Since the TMS34010 is a microprocessor, a software developer has a great
deal of flexibility in determining what runs on the graphics processor and
what runs on the host processor. Since the chip will also support a number
of graphics standards such as CGI and Microsoft's GDI (graphics device
interface), many applications can be ported by using these standards.

One way that developers can tap the potential of the chip is to port a
graphics environment like Microsoft Windows to it at GDI levels. Thus any
software application running through Windows could be accelerated by a 32-
bit graphics processor.

However, many of the graphics cards being developed are relatively "soft"
cards, in that they are RAM based. In effect, the command set for the board
will be downloaded to it. As Kevin McDonough of Texas Instruments points
out, "This will give the cards tremendous flexibility and tremendous
performance advantages. For example, a program such as AutoCAD running under
Microsoft Windows will run much faster in the Windows environment. You can
probably gain another performance factor of 10 by doing specific algorithms
and downloading them to the graphics processor for specific applications."

According to McDonough, graphics boards incorporating the TMS34010 processor
are currently under development at Video 7, Number 9, and other vendors.

The distinguishing feature of the TMS34010 is that the processor is not
limited to a few algorithms. Rather, any algorithm, be it a line, ellipse,
circle, or whatever, can be programmed. And similarly, for text, the chip
can be programmed for any format.

Karl Guttag, also of Texas Instruments, points out that the chip is also
excellent at performing data compression algorithms. "You know that when you
draw images, they have to be stored on disk. And since the images take up a
lot of disk space, it makes sense to compress them. Data compression mostly
involves bit manipulation, which the TMS34010 excels at, so it turns out
that this chip is better than any general-purpose processor for doing image
compression."

Texas Instruments and Microsoft have entered a cooperative program to
provide Microsoft Windows software driver technology for systems designs
using the TMS34010.

With graphics boards that incorporate the TMS34010 processor now under
development, a new world of graphics will soon open up to personal computer
users.

───────────────────────────────────────────────────────────────────────────
TMS34010 Features
───────────────────────────────────────────────────────────────────────────

  ■  160-ns instruction cycle time

  ■  Fully programmable 32-bit general-purpose processor with 128-megabyte
     address range

  ■  Pixel processing, X-Y addressing, and window clip/pick built into the
     instruction set

  ■  Programmable 1,2,4,8, or 16-bit pixel size with 16 Boolean and 6
     arithmetic pixel processing options (Raster-Ops)

  ■  31 general purpose 32-bit registers

  ■  256-byte LRU on-chip instruction cache

  ■  Direct interfacing to both conventional DRAM and multiport video RAM

  ■  Dedicated 8/16-bit host processor interface and HOLD/HLDA interface

  ■  Programmable CRT control (HSYNC,VSYNC,BLANK)

  ■  High-level language support

  ■  Full line of hardware and software development tools including a C
     compiler

  ■  68-leaded packaging (PLCC)

  ■  5-volt CMOS technology Graphics Instruction


───────────────────────────────────────────────────────────────────────────
TMS34010 Graphics Instruction
───────────────────────────────────────────────────────────────────────────

ADDXY     RS,RD           Add Registers in XY Mode
CMPXY     RS,RD           Compare X and Y Halves of Registers
CPW       RS,RD           Compare Point to Window
CVXYL     RS,RD           Convert XY Address to Linear Address
DRAV      RS,RD           Draw and Advance
FILL      L               Fill Array with Processed Pixels: Linear
FILL      XY              Fill Array with Processed Pixels: XY
MOVX      RS,RD           Move X Half of Register
MOVY      RS,RD           Move Y Half of Register
PIXBLT    B,L             Pixel Block Transfer: Binary to Linear
PIXBLT    B,XY            Pixel Block Transfer: Binary to XY
PIXBLT    L,L             Pixel Block Transfer: Linear to Linear
PIXBLT    L,XY            Pixel Block Transfer: Linear to XY
PIXBLT    XY,L            Pixel Block Transfer: XY to Linear
PIXBLT    XY,XY           Pixel Block Transfer: XY to XY
PIXT      RS,*RD          Pixel Transfer: Register to Indirect
PIXT      RS,*RD.XY       Pixel Transfer: Register to Indirect XY
PIXT      *RS,RD          Pixel Transfer: Indirect to Register
PIXT      *RS,*RD         Pixel Transfer: Indirect to Register
PIXT      *RS.XY,RD       Pixel Transfer: Indirect XY to Register
PIXT      *RS.XY,*RD.XT   Pixel Transfer: Indirect XY to Indirect Y
SUBXY     RS,RD           Subtract Registers in XY Mode
LINE      Z               Line Draw

Key to abbreviations: S (source register), D (destination register),
R (register file select), K (constant), Z (draw option)

────────────────────────────────────────────────────────────────────────────

Developers' Aid: Dialog Editor

Charles Petzold

The Microsoft Windows dialog box is both one of the most important elements
of a Windows application and the most difficult element to design. Since
dialog boxes allow the user to select or enter information too complex for
an application's menu, they must be clearly and logically organized. Yet
the graphical image of the dialog box is normally derived from a template
in a Resource Script (.RC) file, where all the coordinates and sizes are
specified as numbers. For the programmer, editing the .RC file to get these
coordinates and sizes right (both in terms of clarity and aesthetics) is
mostly a matter of trial and error.


New Dialog Editor

The only help the original Microsoft Windows Software Development Kit
offered in designing dialog boxes was the barely adequate DLGEDIT.EXE. If
you've tried DLGEDIT and rejected it, don't overlook version 2 of the dialog
editor, now called DIALOG.EXE. It's a complete rewrite and a major
improvement. A beta test version of the new dialog editor is distributed at
Windows Development Seminars and can also be downloaded from the Microsoft
SIG on CompuServe.

Aside from it's practical value in helping you create attractive dialog
boxes for Windows applications, DIALOG is also an excellent tool to learn
about the various styles, controls, and options of dialog boxes. The only
problem is that it is distributed without documentation. But like most
Windows applications, some creative playtime unlocks most of it's
treasures.

DIALOG saves files in two different formats: a .RES format (the compiled
format that it uses for later editing) and a human-readable ASCII format
with the extension DLG. The .DLG file contains the complete dialog template
as it would normally appear in a .RC file, with style parameters expressed
in terms of the macro definitions from WINDOWS.H (such as BS_PUSHBUTTON) and
IDs identifying each control as specified in a header file for the
application.

One .RES file created by DIALOG can contain many different dialog templates
for the application. You can begin a new file within DIALOG by selecting New
from the File menu. To start a new dialog template, select New Dialog from
the Edit menu and switch between dialog templates by using the View Dialog
option of the File menu.

To fill up the dialog box with various controls, use the Control menu, which
lists 12 basic types of controls (such as Check Box, Radio Button, Push
Button). If the control uses text, it will simply show the word TEXT. You
can select the control for editing simply by clicking on it. By hooking the
sides or corners you can stretch and size it, and by hooking the center you
can move it around.


A Matter of Style

The Control menu lets you select from the basic types of controls. The
Styles menu allows you to further refine them, replace the word TEXT with
something else, and assign an ID to each control. These IDs are usually
macro definitions from the application's header (.H) file. Your source
program can then reference each control by these names.

Although DIALOG can read a header file and let you use the name (rather than
the number), it asks for the header file only when reading an existing RES
file. So, before you get too far, you'll want to save a new file using the
Save As option from the Files menu, create a header file with define
statements for the IDs, read the file back into DIALOG with File Open, and
enter the name of the header file when DIALOG asks for it.

From the Styles menu you have a choice of Class Styles and Standard Styles.
The options here let you refine the way the control appears on the screen
and how it works.

For instance, you might select Push Button from the Control menu, which will
display text in a rounded rectangle and is often used for things like OK and
CANCEL If you want a default setting for the push button (which has a bold
outline), you first select the push button control for editing, bring down
the Class Styles option of the Styles menu, and select Def Push Button.
Another example: The dialog box itself is initially shown with a simple
frame. You can add a caption bar and a system menu box by selecting those
options from Standard Styles.

Controls in a Windows dialog box are generally organized by "groups" and
"tab stops." Controls marked as tab stops can be navigated using the tab
key. Within a group, however, you can use the arrow keys. You can mark
controls as groups and tab stops from the Standard Styles option of the
Styles menu. However, if you've been designing your dialog box in a free-
form manner, you'll find that the tab stops will bounce around from item to
item in the order that you've created them rather than a visually logical
order.

Fortunately, you can both re-order the controls and select groups and tab
stops through the Order Groups option of the Options menu. This shows a list
of all the controls you've created. Selecting a control with the mouse and
placing the horizontal black bar cursor in a different location moves the
control. To define a group, you have to mark only the first and last control
of the group. You would usually flag the first control of the group as a tab
stop also, but you can pick another control as the tab stop if you'd like
the cursor to jump to the middle of a group.


Make Sure It Works

Of course, now you'll have to recompile to see if the thing works right.
Well, not quite yet. You can now pick Test Mode from the Options menu and
test the tab stops and groups from within DIALOG. This test mode is one of
the nicest features of the program.

DIALOG is not perfect. You should watch out for a few things: If you want to
delete a specific control from the dialog box, first select it and then pick
Clear Control from the Edit menu. The Cut Dialog option of the Edit menu
deletes the whole dialog box and everything in it. You can get it back by
selecting Paste Dialog from the Edit menu, but it's a little disconcerting
to see the whole thing disappear.

Icons do not work right. You should be able to identify an icon by typing
the name in the Text field of the Class Styles box. However, DIALOG will
ignore this name. You can either avoid using icons in dialog boxes or enter
in the name later using a text editor.

The worst problem does not lie with DIALOG. Preferably, you should be able
to include the .DLG file created by DIALOG in the application's Resource
Script (.RC) file, as:

  #include "myprog.dlg"

However, the RC.EXE resource compiler version 1.00 will not correctly
process an include file in the resource script unless the include file
contains only define statements. Until this problem has been fixed, you'll
probably have to copy the .DLG file into your .RC file manually. You'll also
want to use a different name for the .RES file that is created by DIALOG and
used for future editing. Otherwise, DIALOG's .RES file will be overwritten
when you run the RC resource compiler. Although DIALOG can read and properly
modify a RES file that it did not create and which contains information
other than the dialog boxes, it would probably be safer to let DIALOG work
with its own .RES file.


Required Reading

Chapter 7 (pages 147 to 174 of the Microsoft Windows Software Development
Kit Programming Guide (for a general discussion of controls and dialog boxes
of controls and dialog boxes).

Section 6.2.6 (pages 292 to 307 of the Programmer's Reference (for the
format of the DIALOG template).


Figure 1:

TRYOUT DIALOG 11, 18, 208, 165
 CAPTION "Mythical Windows Spreadsheet"
 STYLE WS_BORDER | WS_CAPTION | WS_DLGFRAME | WS_SYSMENU | WS_POPUP
 BEGIN
  CONTROL "Number Format"  11,"button", BS_GROUPBOX    | WS_CHILD, 12, 4, 64,
  CONTROL "General"    ID_GEN, "button",BS_RADIOBUTTON | WS_GROUP | WS_TABSTO
  CONTROL "Fixed"      ID_FIX, "button", BS_RADIOBUTTON | WS_CHILD, 16, 28, 4
  CONTROL "Scientific" ID_SCI, "button", BS_RADIOBUTTON | WS_CHILD, 16, 40, 5
  CONTROL "Currency"   ID_CUR, "button", BS_RADIOBUTTON | WS_CHILD, 16, 52, 4
  CONTROL "Comma"      ID_COM, "button", BS_RADIOBUTTON | WS_CHILD, 15, 65, 4
  CONTROL "Default Column Width:" 8, "static", SS_LEFT | WS_GROUP | WS_CHILD,
  CONTROL " 9" ID_DCW, "edit", ES_LEFT | ES_RIGHT | WS_BORDER | WS_TABSTOP |
  CONTROL "Decimal Places:" 17, "static", SS_LEFT | WS_CHILD, 88, 28, 66, 10
  CONTROL " 2" ID_DPL, "edit", ES_LEFT | ES_RIGHT | WS_BORDER | WS_TABSTOP |
  CONTROL "Fast Entry Mode" ID_FEM, "button", BS_AUTOCHECKBOX | WS_TABSTOP |
  CONTROL "Flag Circular Calcs" ID_FCC, "button", BS_CHECKBOX | WS_TABSTOP |
  CONTROL "Text Format"   19,  "button", BS_GROUPBOX | WS_CHILD, 12, 84, 64,
  CONTROL "Left"       ID_LEF, "button", BS_RADIOBUTTON | WS_GROUP | WS_TABST
  CONTROL "Right"      ID_RIT, "button", BS_RADIOBUTTON | WS_CHILD, 16, 108,
  CONTROL "Centered"   ID_CEN, "button", BS_RADIOBUTTON | WS_CHILD, 16, 120,
  CONTROL "Recalculation" 3, "button", BS_GROUPBOX | WS_GROUP | WS_CHILD, 88,
  CONTROL "Auto"       ID_AUT, "button", BS_RADIOBUTTON | WS_GROUP | WS_TABST
  CONTROL "Manual"     ID_MAN, "button", BS_RADIOBUTTON | WS_CHILD, 96, 108,
  CONTROL "Row-Wise"   ID_ROW, "button", BS_RADIOBUTTON | WS_CHILD, 148, 96,
  CONTROL "Col-Wise"   ID_COL, "button", BS_RADIOBUTTON | WS_CHILD, 148, 108,
  CONTROL "Natural"    ID_NAT, "button", BS_RADIOBUTTON | WS_CHILD, 148, 119,
  CONTROL "OK" 0,     "button", BS_DEFPUSHBUTTON | WS_GROUP | WS_TABSTOP | WS
  CONTROL "Cancel" 0, "button", BS_PUSHBUTTON | WS_TABSTOP | WS_CHILD, 120, 1
 END



NULL
                );
            }
        }

        return 0L;

      case WM_CREATE:

────────────────────────────────────────────────────────────────────────────

Ask Dr. Bob!

Multitasking?

Dear Dr. Bob,

I'm confused: Is Microsoft Windows a multitasking environment or not?
Sometimes it seems like it is, and sometimes it seems like it isn't. What's
the story, Dr. Bob?──Confused

Dear Confused,

Most people think of multitasking as a process requiring time-slicing. In a
traditional multitasking environment, the operating system switches between
different tasks by allocating short periods of time to each. Microsoft
Windows does not yet time-slice between applications. Windows will probably
use time-slicing for multitasking only when MS-DOS also does so.

Instead, Windows has a nonpreemptive multitasking scheduler for running
Windows applications (programs written specifically for Microsoft Windows).
A Windows application will not be interrupted if it does not want to be
interrupted; only when it specifically relinquishes control do other
applications get a chance to run.

A Windows application normally sits in a loop waiting to receive messages,
usually executing a GetMessage call. The messages it receives cover all the
events (keystrokes, mouse movements, window resizing, and so forth) that
affect the application. When it gets a message, the Windows application
dispatches it to the appropriate procedure in the program. This procedure
can either act on the message or ignore it.

Microsoft Windows switches between applications when an application polls
for a message by using the GetMessage call or the similar WaitMessage call.
Once it has the message, the application can take as long as it wants,
without interruption. In the interim other applications (for instance, the
CLOCK) will stop working.

Polite Windows applications check frequently for messages while performing
lengthy chores. This way they can yield control to other applications and
give them an opportunity to run.

However, if a Windows application needs an unbroken chunk of time (say, for
high-speed real-time data acquisition), it can take it. The application can
interrogate the mouse and the keyboard, write to the screen, and do
everything it needs to do──all without interruption. It just has to avoid
looking for more messages.

So Microsoft Windows is multitasking, but it's up to the individual Windows
applications when to allow multitasking with other applications and when not
to.

For "good old applications" (MS-DOS applications that don't write directly
to the display and that can run in a window), Windows yields control to
other applications during keyboard input and screen output. But for old
applications that do write directly to the display, Microsoft Windows
suspends all multitasking but still allows the user (in most cases) to
switch among applications.


Compilers

Dear Dr. Bob,

Why do I need to use Microsoft compilers for developing Windows
applications?──Puzzled

Dear Puzzled,

Right now the only compilers that support Microsoft Windows are the
Microsoft C and Pascal compilers. You can also program Windows applications
in assembler, but the most convenient way to do that would be to use
assembler routines called from a C program. Microsoft has made public the
relatively minor support for Windows that a C compiler requires, and so
other compilers may also support Microsoft Windows.

A C compiler for Windows requires procedures to be "Pascal" (arguments are
pushed on the stack from left to right and that the procedure itself adjusts
the stack). Normally, C pushes arguments right to left and adusts the stack
after returning from the procedure. The C compiler must also allow mixed-
model programming with the "near" and "far" key words. Procedures called
from Microsoft Windows must have a small prolog and epilog so that Windows
can adjust data segment references after moving data.


No Software Interrupt

Dear Dr. Bob,

Is there some kind of software interrupt or other hook I can use to access
Microsoft Windows procedures from within a program I write in another
language, such as BASIC or Turbo Pascal? Can I get at Windows graphics
functions any other way?──Frustrated

Dear Frustrated,

No. Windows functions are not accessed through a software interrupt. The
Windows loader resolves all function references in the new format executable
at load time. This means that only programs in the new EXE format can access
Microsoft Windows functions.

It might be possible to write a shell program as a Windows application that
would provide a path for other programs that are not Windows applications to
call Windows procedures. But this task would be very difficult. Moveover,
these programs would almost certainly have to be fixed in memory and could
not be moved.

The WINOLDAP module allows some old applications to run in a window, by
providing one type of shell that intercepts DOS and BIOS calls and
translates these calls into Windows functions. This shell is one of Windows'
more complex features, since it must deal with many of the nasty things that
old applications do. A more extensive shell──even one that included only
graphics support──would have to be more complex.


Executive

Dear Dr. Bob:

Can I execute a Windows application from within another Windows
application?──Stumped

Dear Stumped,

Sure. That's what the MS-DOS Executive does. The recommended method for
executing a program from a Windows application is with an assembly language
subroutine using the EXEC function call 0x4B. Windows intercepts this MS-DOS
call and can correctly load the new format executable.

The EXEC function call returns to the calling program right after the child
process has been loaded. Under MS-DOS, the calling program doesn't get
control back until the child process has terminated.

You can even execute an old application from a Windows application or vice
versa. You can get a little sense of what happens in this latter case by
running COMMAND.COM from Microsoft Windows. If you lie in the PIF file and
say that COMMAND.COM writes directly to the screen, you'll find that
executing a Windows application from the MS-DOS command level results in the
"This program requires Microsoft Windows" message.

If you run COMMAND.COM in a window and enter the name of a Windows
application, however, it will load correctly and take over the area of the
screen occupied by COMMAND.COM. When you bring up COMMAND.COM again, you'll
see the cursor sitting at the MS-DOS prompt. The Windows application has
been loaded, and Windows has returned control back to COMMAND.COM.


Expanded RAM

Dear Dr. Bob,

Can Microsoft Windows use an expanded memory board?──Fuzzy

Dear Fuzzy,

Yes and no. Microsoft Windows can take advantage of a RAM-Disk on an
expanded memory board but not of expanded memory itself. However, Windows
supports the use of expanded memory by old applications by preserving and
restoring the state of the Expanded Memory Manager.

Let's look at these two topics separately.

The presence of any high-speed disk storage will improve Windows'
performance. Windows uses the disk for storing temporary files from Windows
applications and for swapping old applications in and out of memory. The
disk used for these purposes can be any real or virtual disk on your system,
including any type of RAM-Disk. A RAM-Disk installed in expanded memory is
ideal.

If your RAM-Disk is installed as drive D: (for instance), put the following
statement in your AUTOEXEC.BAT:

  SET TEMP=D:

This statement directs Microsoft Windows applications to store temporary
files (for instance, those created by WRITE) on the RAM-Disk. These
temporary files begin with a tilde (~) and have the extension TMP.

If you do Windows program development, you should also include the following
statement in your AUTOEXEC.BAT:

  SET TMP=D:

This statement directs the Microsoft C Compiler to use the RAM-Disk for its
temporary files.

To use drive D: as a swapping disk for old applications, replace the line in
your WIN.INI file that reads

Beyond using a RAM-Disk in expanded memory, neither Microsoft Windows nor
Windows applications use expanded memory directly. (A future version of
Windows may have more support for expanded memory.)

However, Microsoft Windows can detect the presence of an expanded memory
board. If it finds one, Windows can let old applications use expanded memory
in the multitasking environment without memory conflicts. Windows will save
the contents of the expanded memory board registers and restore them when
switching among applications.

Some early Expanded Memory Managers did not include full support of the
"Get/Set Page Map" (function 15) of the Expanded Memory Specification
Release 3.20. This call is the one Microsoft Windows uses to save the
contents of the expanded memory board registers. For this reason, Release
3.20 versions of the Expanded Memory Manager are included with Windows.
These drivers are called EMM.PC and EMM.AT; they work only with the Intel
Above Boards. You can also use any Expanded Memory Manager, Release 3.20 or
later, supplied with an Intel Above Board. Before using any other expanded
memory board, check with the manufacturer to determine if the Expanded
Memory Manager provides Release 3.20 support of the "Get/Set Page Map"
call.


Math Coprocessor

Dear Dr. Bob,

Can Windows applications use an 8087 or 80287 math coprocessor?
──Perplexed

Dear Perplexed,

Yes. Microsoft Windows provides full support for saving the state of the
math coprocessor and restoring it when switching among applications. That's
the good news. However, Windows applications cannot yet use the 8087
floating point libraries provided with the Microsoft C Compiler (version 3
or 4). This problem lies with the libraries themselves rather than with
Windows support of the 8087.

If you want to use floating point in a Windows application written in C, you
must currently use the /FPa compiler switch and link with the SLIBFA.LIB,
MLIBFA.LIB, or LLIBFA.LIB──depending on your memory model──alternate math
library. The alternate math library, which uses a subset of the IEEE
standard, is faster and smaller (but less accurate) than the 8087-emulation
code of the other floating point math libraries. The alternate math library
does not use an 8087 even if one is present.

When the 8087 floating point libraries are eventually modified for Windows,
upgrading the program should only require relinking it with LINK4.

In the interim, you could define your own floating point routines that use
either the regular C library routines or the assembly language subroutines
using the 8087, depending on the presence of the math coprocessor.


Printf

Dear Dr. Bob,

What happens ins a Windows application if I try to write to the Display in
the conventional way using the C function printf?──Stymied

Dear Stymied,

Nothing. Microsoft Windows intercepts all DOS and BIOS output headed for the
display and dumps it into the bit bucket, where it is never seen again.

If you need to write formatted text output to the screen, you can use
sprintf and display the resultant string with Windows TextOut procedure.


Instances

Dear Dr. Bob, What happens when I run the same program simultaneously in
Microsoft Windows? Are the two versions entirely distinct or do they share
code and data?──Unclear

Dear Unclear,

When an application is loaded more than once, the loadings of the
application are referred to as different "instances." Generally, multiple
instances of the same application share the same code segment but have
different data segements. Windows applications also share resources such as
menus, icons, cursors, and bitmaps, since such resources are read-only
data.

During initialization, an application can determine whether or not it is the
first instance of that application. If it is not the first instance, it can
retrieve some data that must be shared from the previous instance.


════════════════════════════════════════════════════════════════════════════


Vol. 1 No. 2 Table of Contents

Aldus: Preparing PageMaker for the Move to Windows

The success of Aldus Corporation's PageMaker(TM) has made this program
practically synonymous with desktop publishing. Now, Aldus is moving
PageMaker to Microsoft(R) Windows and rewriting the program in C for both
the PC and the Macintosh.


Moving Toward an Industry Standard for Developing TSRs

Terminate-and-stay-resident programs such as Borland International's
SideKick(TM) have become popular tools for many PC users. A report from the
May gathering of software developers attempting to formulate guidelines for
peaceful coexistence among TSRs.


A Step-by-Step Guide to Building Your First Windows Application


At first, creating a Microsoft Windows-based application seems to be a
complex process involving an oddly structured C program that uses Windows
functions and macros. This simple program, WSZ, provides a model for
building a Windows application.


New XENIX Version Will Be First to Run On the Intel 80386

In early 1987, Microsoft will release XENIX(R) System V/386, the first
version of XENIX to make full use of Intel's new 32-bit 80386
microprocessor. In this exclusive interview, XENIX Development Manager
Martin Dunsmuir tells what to expect.


A New Generation of Debugging Arrives with CodeView

The CodeView(TM) debugger included with the Microsoft(R) C Compiler 4.00,
the latest debugging tool to become available, marks improvement over
SYMDEB by including symbolic local variable and subroutine names with
additional debugging capabilities.


Ask Dr. Bob


EDITOR'S NOTE

Welcome to Issue 2 of Microsoft Systems Journal, up in size from the last
issue of MSJ, but already too small to accommodate the problem-solving
and problem-avoiding technical articles that many of you have already come
to expect──judging from the first subscription mailing response. We are,
of course, delighted by your interest and approval.

There's still time to become a charter subscriber to Microsoft Sytems
Journal and avoid missing the solutions, insights, and inspirations we're
dedicated to bringing you. Consider what's coming up: a closeup of the IRMA
3270 board (from a developer's point of view, of course), a comparison of
page description languages, details of the MS-DOS CD ROM extensions, and a
step-by-step debugging session using CodeView. Down the road: authoritative
examinations of operating systems developments and language enhancements.

Wish List. How many times have you said to yourself, "How could they have
gone to all the trouble to design such a useful product and left out _____?"
MSJ is so committed to the principle of product improvement that we'll pay
you a $100 for your idea if we publish it, and you can be sure that the
manufacturer will hear of its sins of omission directly from the editors
of Microsoft Systems Journal.

Write to Us. Our aim at Microsoft Systems Journal is to tackle the issues
confronting serious developers, but we need your help. Write us, or send us
a message on MCI Mail (MSJ). Tell us what you want to see in the Journal.
Or let us pass on your most recent programming snafu to Dr. Bob. And, of
course, we'd like your feedback on these first two issues. Whether you've
got a question, comment, or complaint, we'd like to hear from you.──Ed.


Masthead

JONATHAN D. LAZARUS
Editor

BARRY OWEN
Managing Editor

CHRISTINA G. DYAR
Assistant Editor

MICHAEL LONGACRE
Art Director

VALERIE MYERS
Associate Art Director

WILLIAM B. GRANBERG
Circulation Manager

L. PERRIN TOMICH
Office Manager

Copyright(C) 1986 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 Microsoft Corporation. Apple is a registered
trademark and Macintosh is a trademark of Apple Computer, Inc. IBM is a
registered trademark of International Business Machines Corporation.
PageMaker is a registered trademark of Aldus Corporation. Lotus and 1-2-3
are registered trademarks of Lotus Development 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.

────────────────────────────────────────────────────────────────────────────

Aldus: Preparing Pagemaker(TM) for the Move to Windows

Kevin Strehlo

Several years after the end of Ethel Merman's heyday as the queen of musical
comedy on Broadway, a reporter said to her, "Broadway has been very good to
you." Merman was quick to retort, "Yes, and I have been very good for
Broadway."

Such is the mutually advantageous relationship between the Apple
Macintosh(TM) and Aldus Corporation's PageMaker, the leading desktop
publishing software. While the Macintosh has certainly been very good for
PageMaker, PageMaker has been very good for the Mac.

Pagemaker's featured position in Apple advertising has helped push it to the
top ten in the bestseller charts, while it returned the favor by selling
lots of Macs (Aldus research shows that one out of three of Macintosh users
bought their computers primarily to use PageMaker). Indeed, the PageMaker
program has been such a success that the Mac and desktop publishing have
become practically synonymous.

It comes as little surprise that Aldus is attempting to repeat its success
by making a move to Microsoft Windows(R), the logical vehicle for cracking
the lucrative PC marketplace. What may be surprising, however, is the
Seattle-based company's decision to throw away the code of its original
Pascal-based version of PageMaker in favor of a complete rewrite in C that
will run on both the PC and the Macintosh.

From the start, Aldus Corporation had planned to merge the Macintosh version
of the PageMaker page layout program with the version it was developing
under Microsoft Windows. Rather than release the Macintosh version first,
Aldus decided to wait until the PC version of PageMaker was out the door.


A Rare Problem

In Aldus's effort to share source code between the Mac and the PC, however,
the decision to wait was reversed by a problem in one of the environments.
It was a rare case where the problem was located in the Mac.

"We had made all the data structures in the original Mac version of
PageMaker 'resources' and saved them out to files using the Mac's Resource
Manager," says Jeremy Jaech, programming manager for Aldus. "That turned out
to be a mistake."

The Resource Manager was designed to read small, indexed data structures
(such as fonts and dialog boxes) into memory. It wasn't intended to
continually write large pieces of data back to disk. Aldus found itself
plagued with less-than-optimum performance and occasional file corruption
caused by persistent bugs in the Resource Manager's garbage collection
routines.

Fortunately the PC team was already working on the solution. "There is no
equivalent to the Resource Manager under Windows, so we spent a lot of time
up front designing and implementing a file system that did exactly what we
wanted to do on the PC," says Jaech.

But because they had not cleanly separated the file system layer from the
rest of the Mac code, sliding that file system underneath the Mac code would
necessitate a major rewrite. They decided to bite the bullet and merge the
Mac and Windows versions right away.


The Edge

The project was organized into three teams: the core code team and──because
of the significant differences between the two environments──two teams that
would each write the unavoidable environment-specific modules, dubbed the
Macintosh edge code and the Windows edge code.

One of the first choices was fundamental. Building for the lowest common
denominator of the Macintosh and Windows would result in failure to take
advantage of the most appealing features of both. Instead, the Aldus team
decided to write for the union of the two environments' capabilities.

That often meant that the edge code for one environment had to emulate a
capability that was missing from the other. For example, the Mac provided
continually scalable fonts and Windows did not.

"Microsoft went for the concept of readability," explains Dave Walter, the
lead programmer for the core code group. "But a lot of things PageMaker does
involve showing the form rather than the content. We need to be able to get
a piece of type up on the screen the actual size it's going to be. We don't
really care if it's readable."

Thus, given a request from the core code drawing routine for a particular
size and face of type, the Windows edge code read the character metrics of
the printer font and returned the appropriate bit map for screen display.

Fortunately, Windows did make provisions for handling fractional character
values──a feature missing from the Mac Toolbox until the 128K ROM
upgrade──so the PC version of PageMaker was able to portray line lengths
accurately on-screen despite a screen resolution that is much lower than
(and not necessarily an even divisor of) the final 300 DPI printer output.

When possible, the environments were equalized in the core code rather than
in the edge code. When the user drags a guide on PageMaker's page layout
board, for example, Aldus's Walter wanted the code that actually moved it to
be part of the core. That meant accommodating the differences between the
way the two environments handled the pattern used to represent that guide.

The Windows GDI (graphics device interface) drawing routines automatically
align a pattern so that it begins and ends at the same point, ensuring it is
"in phase." The Mac Quickdraw routines do not align patterns for you; the
application must rotate patterns into proper phase for drawing. To deal with
the discrepancy, Walter says, "We just used an 'if def Mac' or 'if def
Windows' clause."


Complex Code

Sometimes, however, the environments' differing approaches made the patch
code so complex that it was forced out of the core and into the edges,
Walter says.

For example, under Windows, scrolling involved handling a series of messages
that described the user clicking on the cursor keys or clicking the thumb, a
simple procedure. But tracking a scroll bar on the Mac required calling the
Mac Toolbox's TrackControl function. Unfortunately, the arguments
TrackControl passed were different depending on whether the user was
clicking on the thumb or a cursor key.

"You actually have to have two different scrolling procedures on the Mac,"
Walter explains.

In other ways, Windows is less orthogonal and therefore more complex to work
with than the Mac environment.

For example, under Windows the function GetMessagePos fetched the position
of the mouse, while GetCurrentPos fetched the cursor position. "But the
first returns the value to you as a Long (pointer), while the other gives
you a pointer to a pointer," says Walter.

Clyde McQueen, who wrote the shared memory management and file handling code
for the two environments, had to deal with a number of similarly
disconcerting choices as he strove to keep as much of the code in core as
possible.

"On the Mac it's neat and clean to dereference from your handle to get a
pointer," says McQueen. Doing a dereference once outside a loop would be far
more efficient than doing it inside a thousand times, of course, and it
would be perfectly safe as long as he knew the object pointed to would not
be moved. Since times of memory movement are pretty well defined on the Mac,
he wouldn't have to lock the object down.

Under Windows, however, an object is automatically locked whenever you
dereference its handle to get a pointer. (This will allow Microsoft to flesh
out Windows in the future with preemptive scheduling of tasks.)

McQueen had a choice: he could add overhead by locking memory "needlessly"
on the Mac or move that central part of the memory management system out of
the core code. He decided that the lesser of these evils was locking memory
on the Mac.


Low Memory

In another case, the problem was the lack of an early warning under Windows
that you were about to run out of memory. "I can get a ballpark," says
McQueen, "but it's not reliable." A clause in the core code lets the program
know it's operating in the Windows environment and thus has to be careful
about running out of memory.

"That's why a lot of user operations under the Windows version of PageMaker
result in a little disk hit," McQueen says. Such behavior is less acceptable
for PageMaker on the Mac, where the program is much more likely to be
running on floppy disk drives. Thus, if the clause senses it is on the Mac,
writing data out to disk or purging code segments is deferred until a global
memory warning is received.


Fixing the Bugs

To work out the early kinks in his memory management and file handling
routines, McQueen used Heapwalker, the Windows utility designed to track
objects in the global heap. Since he knew the number and approximate sizes
of objects he was dealing with, Heapwalker let him see instantly when
something had gone awry.

But discovering a bug was not the same as fixing it. For bug hunting,
McQueen turned to home-brewed tools he called "scopes" (see Figure 1☼).

"The scopes were a way to quickly examine the memory structures I was
using," McQueen says. The scopes were modeless dialog boxes, written as an
excuse to learn the structure of a Windows application. They proved
exceedingly useful.

Although they could alter as well as display memory, viewing turned out to
be far more valuable than altering. McQueen could often tell what had gone
wrong simply by examining his memory structures. Clicking on the "next"
button he had built into the scope would display the next record, allowing
him to follow whatever errant trail he had blazed through memory.

"It sure beat trying to figure out what was going on from SYMDEB's
hexadecimal dump," he says.


Working Environments

Once the Mac and Windows projects merged, however, the development of the
core code could be done in either environment. For Windows, the Microsoft C
Compiler and the Make program were used; on the Mac, Lightspeed C and
Polytron's Polymake were used.

"Some of the problems we've got when we're dealing with an Intel processor
are the segmentation of memory and the existence of two different pointers,
near and far," says Walter. To avoid such processor-specific dependencies,
he says, "We found that using warning level 2 on the Microsoft C Compiler
was crucially important for our development."

The Aldus team found the Microsoft debugger for Windows helpful. "But what
we're really looking forward to is a future version of CodeView(TM) that
works with Windows applications──it's certainly a big help with DOS apps,"
says Ted Johnson, lead programmer on the Windows edge code.

To an outsider, it might seem Aldus had something to lose by going with the
common code approach. The marketplace for PC page layout programs was wide
open, after all, and an early presence in the market would be a great
advantage. So why waste time making sure that the Windows code already under
development would also work on the Mac?

One reason was to get the PC product out sooner. It was simply too hard to
find good Windows programmers at such an early stage in the game. "The PC
project was definitely resource starved," Johnson explains, "until the Mac
team's talents were brought to bear."


Common Benefits

The benefits to Aldus of a common code approach, says Jaech, are several.
"Since 80 percent of the code is shared, you usually implement a feature
just once, not twice. Also you only have to fix a bug once, unless it's
environment specific. And from a maintenance point of view, people over in
customer service trying to deal with phone calls won't have to remember two
sets of technical issues and problems."

Finally there's the advantage of file compatibility. PageMaker files will be
interchangeable between the Mac and the PC, as will import filters for key
word processing products.

"We didn't plan it that way," says Jaech. But, as with most of the spinoffs
of the decision to write common source code, it seems to have worked out for
the best.

────────────────────────────────────────────────────────────────────────────

Moving Toward an Industry Standard for Developing TSRs

Nancy Andrews

Terminate-and-stay-resident programs──"pop ups"──are like people: they can't
live together but they can't live alone either. If you are running Microsoft
Word, for example, you cannot check spelling interactively with Turbo
Lightning. If you interrupt Lotus(R) 1-2-3(R) with SideKick, strange things
can happen when you return. If you run XyWrite you can't use any TSRs. And
if you run two or more TSRs, there's no knowing how they will interact.

Currently all software developers program their TSRs a little differently.
There is no established set of programming rules to ensure smooth
interaction with other TSRs and with the operating system itself. To make a
TSR work with everyone else's requires an enormous programming
commitment──it must be tested against everything in the marketplace. This
includes not only other TSRs but normal applications as well.

Some normal applications make life difficult for TSRs. XyWrite, for example,
steals the hardware keyboard interrupt, thus locking out any TSR that also
requires INT 9. Are there "offenders," TSRs that purposely try to be
exclusive, taking the system resources they need without regard for others?

John Socha, chief programmer at Peter Norton Computing, says no. Different
software vendors have depended upon different approaches to solving problems
in a world where there are no rules.

Others who are not so kind cite Borland as an offender; Borland's SideKick
takes the keyboard interrupt, for example, and then at each timer tick
checks to see if anyone else has taken it. If another program has it, then
SideKick will steal it back. These factors complicate writing a TSR, making
it a tough job to deliver a good and flexible product.

One of the reasons all TSRs work differently is that TSR controls are not
built into MS-DOS(R). There was no such thing as a TSR in the UNIX(R) world.

"It was a radically new idea," says Socha, "to be able to change the
behavior of an existing program──a dynamic rather than a background
process──and to add new features without having the source code."

Later operating systems like the Apple(R) Macintosh did incorporate TSR
controls into the operating system. The Mac makes its desk accessories
constantly available on the Apple menu and handles their interactions for
you.

Because current versions of MS-DOS do not provide similar capabilities,
Microsoft is part of the team working toward developing standards or
guidelines. According to Adrian King, Microsoft's Director of Operating
Systems, future versions of MS-DOS will address this at the outset. King,
however, declined to comment about which versions, when, and how.


The Solution

A group of software developers co-chaired by Adrian King of Microsoft and
Tony Middleton of Softronics, and hosted by Softronics, met in Colorado in
May. King said that his goal was to put together standards "of maximum
benefit to the software community that could be wholeheartedly endorsed by
as many organizations as possible." Lynn Stricklan from Softronics said
that what he wanted was standards that would make TSRs "clean and
nonviolent."

Participants at the conference agreed to agree that they were not able to
issue a standard──a formal definition of rules. Instead, they aimed for the
creation of a set of programming guidelines, and they called their efforts
"Programming Guidelines for MS-DOS Co-Resident Software."


The Guidelines

Microsoft produced a draft of these guidelines. The original draft included
the following: a set of operational rules for TSRs, specifications for their
order of loading, a set of data records that describe operational
characteristics, and a program interface to allow other applications to
avail themselves of a TSR's functions.

Two parts included in the original draft but recognized as unfeasible were a
mechanism for TSRs to uninstall and a procedure for loading TSRs into
extended memory, leaving memory below 640K available for normal
applications.

Operational rules include two major areas: interrupts and maintaining
software variables for changes that are made directly to the hardware. Major
problems have occurred with the keyboard interrupts INT 9 and INT 16h. Some
applications hook INT 9 and bypass INT 16h, thus preventing others from
seeing the keyboard request.

Other applications take complete control over the keyboard with INT 9,
presumably to trap keystrokes that the BIOS does not generate, such as Ctrl-
Up Arrow. The proposed way to handle interrupt conflicts is to always use
INT 16h to get a keystroke and to chain the interrupts. Essentially,
chaining stores the old interrupt vector inside the program before replacing
it with its own vector.

The code sample in Figure 1 illustrates how to chain into INT 9. This
routine doesn't do anything useful now──you can insert your own code for
that. It simply passes control back to the old INT 9 routine, which can be
either the ROM BIOS routine or some other memory-resident program like
Prokey. But as long as everyone does, in fact, chain interrupts, all should
function properly and it shouldn't matter who gets control next.

Other proposed interrupt operational rules prohibit setting a timer to
retrieve an interrupt vector and using a stack only when interrupts are off
or in a protected or non-reentrant area.

The remaining operational rules require that the BIOS data area be updated
in order to reflect any of the following alterations: changing video modes,
setting the high-order attribute bit for background intensity, using a
software cursor, or changing the EGA palette directly.

If your program is interrupted by another TSR, it ought to know what to
restore when it relinquishes control.

The proposed data records to describe operational characteristics of TSRs
include a Program Identification Record and an Interrupt Information Record
(see Figure 2). Each TSR will have only one Program Information Record but
will have an Interrupt Information Record for each interrupt vector it
intercepts. The record includes a pointer to the old routine. This allows
any program to trace through the chain of installed programs. The
Interrupt_Priority value in the Interrupt Information Record will be
assigned based on the type of program, and it can be used to control the
order of loading TSRs. Keyboard macro processors such as SuperKey and
Prokey need to be last in the chain and will be given high priority
numbers.

The guidelines also propose a TSR program interface. Such an interface would
permit users to write programs that call functions provided by a TSR. This
way a Turbo Pascal program could, for example, run a SuperKey macro.
Proposed functions include Get Program Information By Name or Number
functions, Activate and Deactivate functions, and Enable and Disable
functions. This interface would be implemented by a set of subfunctions to
function 52h on INT 15h, the cassette interrupt. INT 15h was chosen because
it's used infrequently and has a lot of unused function numbers.


More Details To Come

The participants generally concurred with the proposed guidelines and
determined that more detailed work was needed in four areas. These include:


  1. A revised set of rules for interaction with DOS. Currently TSRs depend
     on several undocumented MS-DOS features such as the IN_DOS flag (see
     Figure 3), the critical error flag, and some undocumented system calls.
     Microsoft's Adrian King has agreed to provide this information. Both
     Borland and Lotus say that this information is critical for TSRs to
     work consistently.

  2. A standard model for keyboard handling. This will most likely include
     scan codes for keys (such as Ctrl-Cursor Up) that don't currently
     exist. David Rose of Rosesoft and Roger Schlafley of Borland have been
     put in charge of this area.

  3. More extensive work on data structures. This will refine and expand the
     data structures section in the original guidelines. Norton's John Socha
     is in charge of data structures.

  4. A kernel to be added to DOS to take charge of RAM-resident programs.
     This kernel will tell the operating system what programs to install and
     where to install them, and it will allocate resources based on the
     priority of the requester. Lynn Stricklan of Softronics is
     investigating the feasibility of the kernel approach. Softronics
     currently uses a kernel to manage its RAM-resident programs.

Participants agreed to have work completed in 90 days and to publish results
on the CompuServe Microsoft SIG. Unfortunately, the 90 days have elapsed,
and the work has not yet been completed. But King reports that the
guidelines are "not dead." He expects to finish his part, and he encourages
others to do the same. He says he hopes that we'll see a new draft on the
SIG in early November.


But Will They Work?

Participants in the drafting of the TSR guidelines qualify their work. They
want it to be understood that guidelines are "evolving," that right now they
are a "moving target" and "will definitely change," that this is not a "be
all and end all" solution, and that "enforcement is not possible." They
caution against unmitigated optimism, pointing out that even if the
guidelines are completed and generally accepted, developers will still have
to deal with users who choose not to upgrade.

But even with these qualifications, the possibility that both software
developers and end users will benefit looks good. Writing a TSR will still
be a difficult and complex task, but once the TSR is written, if it plays by
the rules, software developers will know that it will work with any other
TSR or application that also plays by the rules. If the big players──Lotus,
Microsoft, Borland──agree to support the guidelines, others will as well,
and the guidelines can do the job they're intended to do. So far this looks
like a real possibility.

David Reed from Lotus acknowledged that there are some incompatibilities
between 1-2-3 and the guidelines, and said that once a consensus document
has been produced, Lotus will make 1-2-3 and Spotlight, its TSR, conform. He
points out that it is to their advantage to have their applications work
with everyone else's and that this will directly reduce their support
costs.

Microsoft has taken an active role in this. It co-chaired the conference and
agreed to provide the necessary information about DOS. Beyond that the
company has also made some changes to its standard applications so that they
will work better with TSRs.

David Intersimone at Borland says that when everyone else writes well-
behaved applications──that is, when the guidelines have been agreed to and
implemented──Borland will too. He claims that SideKick programmers had no
choice but to grab INT 9 back because some of the applications SideKick was
expected to work with were ill-behaved and users expected SideKick to appear
when they pressed Ctrl-****. And he says that Borland has gone out of its
way to help developers work around the idiosyncrasies of SideKick.

Phillipe Kahn says, "The whole issue of standardization should not be driven
by the software publishers or the press but by the users." And when
standardization actually happens, end users can't help but be the ultimate
beneficiaries. They'll be able to choose from the full range of available
software──to work with any combination of TSRs and applications without
conflict.


Figure 1:  Code Sample for Chaining Interrupts

This code sample illustrates how to chain into INT 9. This routine won't
be useful until you insert your own code; it just passes control back to the
old INT 9 routine.

CODE_SEG        SEGMENT
        ASSUME  CS:CODE_SEG
        ORG     100h

BEGIN:          JMP     INIT_VECTORS   ;Initialize vectors and attach
                                       ;to DOS
OLD_KEYBOARD_INT        DD      ?      ;Address for ROM routine

;-----------------------------------------------------------------;
; This procedure intercepts the INT 9 keyboard vector. Right now it
;  just calls the old INT 9 routine.
;
;-----------------------------------------------------------------;
INTERCEPT_KEYBOARD_INT  PROC NEAR

        ###                             ;Put your code here
        CLI                             ;Disable interrupts
        JMP     OLD_KEYBOARD_INT        ;Chain back to the old Int 9
INTERCEPT_KEYBOARD_INT  ENDP

;-----------------------------------------------------------------;
;  This procedure initializes the interrupt vectors and installs this
;  program. This routine also throws itself away after the install.
;
;-----------------------------------------------------------------;
INIT_VECTORS    PROC NEAR
MOV     AH,35h                     ;Get the old keyboard interrupt vector
MOV     AL,9                            ;This is INT 9
INT     21h                             ;Old vector now in ES:BX
MOV     Word Ptr OLD_KEYBOARD_INT,BX
MOV     AX,ES
MOV     Word Ptr OLD_KEYBOARD_INT[2],AX

MOV     AH,25h                  ;Set the new KEYBOARD_INT vector
MOV     AL,9                            ;This is INT 9
LEA     DX,INTERCEPT_KEYBOARD_INT
INT     21h

MOV     AX,3100h                        ;Terminate but stay resident
                                        ;with
                                        ;return code of
0
LEA     DX,INIT_VECTORS         ;End of resident portion
ADD     DX,15                           ;Round up paragraph number
MOV     CL,4                            ;Convert from bytes to
                                        ;paragraphs
SHR     DX,CL                           ;Divide by 16
INT     21h                             ;Terminate but stay resident
INIT_VECTORS    ENDP

CODE_SEG        ENDS

        END     BEGIN


Figure 2:  Proposed Data Structures for TSRs

These are the proposed data records to describe operational characteristics
of TSRs. Each TSR will have one Program Information Record but will have an
Interrupt Information Record for each interrupt record it intercepts.

Program Identification Record -- PIDR

Program_Record_ID       DB      "PIDR"
Version_Number          DW      BCD version number
Interrupt_List          DW      Near Pointer to list of interrupts
Startup_Shift_Keys      DW      Near Pointer to shift-pairs
Startup_Key_Codes       DW      Near Pointer to startup codes
Options_Area            DW      Near Pointer to local data area
Program_ID_String       DB      ASCIIZ string

Interrupt Information Record -- IINR

Down_Pointer            DD      Far pointer to old interrupt routine
Program_ID                      DW      Near Pointer to PIDR
Interrupt_priority              DB      Between 0 and 255 giving priority
Function_Numbers                DB      Null-terminated function list

To correlate interrupts with the associated IINR, each interrupt
procedure must be preceded by a 6-byte record. The first four bytes
identify it as a valid interrupt record, and the last two bytes point
to the IINR for more information.

        DB      "SRID"
        DW      Offset Interrupt_Record

Intercept_Interrupt     Proc Far
###
###
Intercept_Interrupt     Endp
-----------------------------------------------------------------


Figure 3:  The IN_DOS Flag

The IN_DOS flag is an undocumented MS-DOS feature that TSRs depend on.

IN_DOS_FLAG     DD      ?       ;Address of IN_DOS flag

MOV     AH,34h
INT     21h                     ;Get pointer to IN_DOS flag
MOV     Word Ptr IN_DOS_FLAG,BX
MOV     Word Ptr IN_DOS_FLAG[2],ES
                                ;Above four lines are init code -- they
                                ;are done once to get address of flag

                                ;Following 7 lines done each time to
                                ;check if IN DOS
PUSH    BX                      ;See if you can popup
PUSH    DS
LDS     BX,IN_DOS_FLAG  ;Set DS:BX to point to IN_DOS flag
CMP     Byte Ptr [BX],0 ;Are we inside DOS?
POP     DS
POP     BX
JNZ     DO_NOT_POPUP_NOW

*This routine is not foolproof. For example, your program would never popup
if you were at the DOS prompt. Information to make it usable in all
situations will be provided with the revised guidelines (on CompuServe in
the Microsoft SIG).

────────────────────────────────────────────────────────────────────────────

A Step-by-Step Guide to Building Your First Windows Application

Charles Petzold

The notion of "building" a Windows application rather than simply "writing"
one implies that the job involves more than just cranking out hot C code.
This is true. While the core of most Microsoft Windows applications
certainly is a C program, it is a C program that is oddly structured and
makes heavy use of Windows functions, macros, and type definitions.

A typical Windows application also requires several other files besides the
C program. Much of the difficulty for programmers first approaching
Microsoft Windows is the large amount of overhead and the new concepts
needed just to get a small program off the ground.

To clear away some of the mystery that surrounds building a Windows
application, we'll put together a fairly simple program called Whatsize, or
WSZ. WSZ displays the dimensions of its own window in units of pixels,
millimeters, or inches. The program also allows you to switch this display
between black text on a white background and white text on a black
background.

Despite its limited functionality, WSZ illustrates some basic Microsoft
Windows concepts; demonstrates the use of such familiar Windows accessories
as menus, icons, and dialog boxes; and offers some insight into the nature
of device-independent applications.


Getting Set Up

I will assume that you have a Microsoft Windows Software Development Kit,
the Microsoft C Compiler 4.00, a hard disk, and a mouse. Some of the
procedures in building the Windows application are slightly different from
the earlier C compiler included with the Software Development Kit.

The files you'll need for this project are shown below in one possible
subdirectory organization. Notations in parentheses indicates whether a file
comes from the C Compiler 4.00 (C4) or the Software Development Kit (SDK).

  \MSC
    CL.EXE      (C4)
    P1.EXE      (C4)
    P2.EXE      (C4)
    P3.EXE      (C4)
    MAKE.EXE    (C4)
    RC.EXE      (SDK)
    RCPP.EXE    (SDK)
    LINK4.EXE   (C4)
    WINSTUB.EXE (SDK)
    MAPSYM.EXE  (SDK)
    SYMDEB.EXE  (SDK)

  \MSC\INC
    WINDOWS.H   (SDK)

          (and regular C header files from C4 if needed)

  \MSC\LIB
    SLIBC.LIB   (C4)
    SLIBW.LIB   (C4)
    SLIBFA.LIB  (C4)
    LIBH.LIB    (C4)
    (and other memory models from C4 if needed)

  \WINDOWS
    Installed version of Windows (SDK)
    ICONEDIT.EXE (SDK)
    DIALOG.EXE (optional)

  \WINDOWS\WSZ
    All WSZ files

The environment variables necessary for the C compiler to get at the
libraries and the include files are

  SET PATH=\MSC
  SET LIB=\MSC\LIB
  SET INCLUDE=\MSC\INC

Microsoft Windows programmers generally do most of their development work
(particularly editing source code and compiling) outside of Windows in
regular old MS-DOS. This is due partly to the inadequacy of current Windows
text editors and the problems that occur when running the C compiler in a
limited memory environment.


Creating an Icon

We'll begin with one exception to this rule. Let's get into Microsoft
Windows and start with a part of WSZ where you can ignore what I've done and
exercise your own creativity.

Most Windows applications have an icon that Windows displays when the
program is parked downstairs at the bottom of the screen. You can create an
icon for WSZ by using ICONEDIT (see Figure 1☼). Choose Icon from the mode
menu, pick a pen color, a pen size, and then use the mouse to draw. For the
icon I created for WSZ, I painted the surface white and then drew a question
mark in black.

The resolution of the bit-mapped icon you created in ICONEDIT will be
considerably reduced when Windows displays the icon when your program runs.
This is why ICONEDIT shows the icon in a reduced size at the left of the
screen. If you use thin lines (three or fewer pixels wide) in your icon,
they could possibly disappear when the icon is displayed. You'll eventually
want to see how the icon looks on both a low-resolution (IBM CGA or
equivalent) and medium-resolution (EGA or Hercules) display.

When you finish creating your miniature masterpiece, save the icon as
WSZ.ICO in the subdirectory WSZ.


Resources

That icon you just created is called a "resource." It will eventually become
part of the WSZ.EXE executable file, but it is neither part of your
program's code nor part of your program's data. Icons are not the only type
of resource you can have in your .EXE file. Resources also include dialog
boxes, menus, and (most preferably) all displayable text.

Think of a resource as read-only data that is conveniently stored right in
the application's .EXE file. In most cases, resources are not loaded from
the disk into memory until they are needed. (This is why you'll often see a
disk access when you pop up a dialog box.) Simple Microsoft Windows calls
retrieve these resources from the disk so your program can use them. Because
resources are read-only, Windows may discard them from memory when they are
no longer needed and then reload them later if necessary. In an operating
system that implements virtual memory, a resource currently in memory would
not have to be saved to the disk because it would still be available in the
same form from the .EXE file.

Resources are defined in a resource script (.RC) file, which is an ASCII
file you create in a normal text editor. The resource script for WSZ is
called WSZ.RC and is shown in Figure 2. This file includes definitions for
the icon, all of the displayable text strings used in the C program, a menu,
and two dialog boxes.

The dialog boxes are usually the most complex part of the resource script
file. You can either work out the dialog box coordinates manually (as I've
done here) or by using DIALOG.EXE (see Figure 3☼; also see "Latest Dialog
Editor Speeds Windows Application Development," MSJ Vol. 1, Issue 1).

The resource script file also contains all the displayable text strings that
the C program will use. You are not required to put them in the resource
script. In fact, defining text strings in the C program usually makes both
the source file and the .EXE file somewhat smaller. The program will run
faster (since it doesn't have to retrieve strings from disk) but will
require more memory space because the strings have to remain in memory
rather than reside on the disk. However, if you want to adapt Windows
applications for foreign-language markets, collecting all displayable text
into one relatively small file will be enormously helpful.

The STRINGTABLE option in the resource script file can handle only single
lines. If you need to include larger text strings in your program (for
instance, for a "Help" window like PIFEDIT's), put this text into separate
files and use the "user-defined" resources in the resource script file. You
can then use the FindResource, LoadResource, and LockResource functions
within your program to get pointers to those resources. The user-defined
resource is a general-purpose facility that allows you to imbed any read-
only data into the .EXE file and access it very easily with just three
simple Microsoft Windows calls.


Avoid Header Loss

The names in the WSZ.RC file that begin with the letters "ID" are really
just numbers that are defined as macros in the WSZ.H header file shown in
Figure 4. It's possible to use macro names here in a resource script because
the RC.EXE resource compiler has its own C-like preprocessor. Using names
instead of numbers makes both the resource script and your C program easier
to understand and maintain.

The WSZ.RC file also has an include statement for WINDOWS.H, which comes
with the Windows Software Development Kit. WINDOWS.H is a massive 80K header
file that is much more important for compiling Windows C programs than
resource scripts. Here it is used only to define the macro names beginning
with "WS" (window style).


The C Program

Now that we've gotten some of the preliminaries out of the way, we're ready
to start looking at some C code.

The complete WSZ.C file is shown in Figures 5, 6, 7, 8 and 9. This file
functions: WinMain, Initialization, MainWndProc, AboutWndProc, and
UnitsWndProc.

WSZ.H and WINDOWS.H are specified as include files at the top of the
program. You will often find that it is helpful when you are programming
Windows applications to consult the WINDOWS.H header file. It contains
declarations for every Windows function and loads of #define and typedef
definitions that ought to be used within Windows applications.

Every Windows application requires a WinMain procedure. This is the entry
point to the application. The WinMain procedure for WSZ is shown in Figure
5.

The input parameters to WinMain include two "handles," one to the current
instance and another to the previous instance. When you load a Windows
application, that's the first "instance" of the program, and there is no
previous instance. If you load the same application without terminating the
first, the previous instance handle will refer to the copy loaded
previously.

You'll run across handles a lot in Windows programming. Almost everything in
Microsoft Windows (menus, windows, memory) is represented by a handle. If
you look at the WINDOWS.H file you'll discover that the various handle data
types are just plain old integers. Handles are just numbers that Windows
uses to refer to things.

WinMain also receives a command line parameter string. If the user of your
application entered a parameter in the File Run box, this is where your
application will get the parameter. The fourth parameter to WinMain
("nCmdShow") indicates the initial state of the application. It could be a
window (if the application was invoked by File Run) or an icon (if File Load
was used).

After WinMain calls the Initialization routine, it sits in a small loop
polling for messages from Microsoft Windows. When it gets a message, WinMain
dispatches it off to somewhere else. Right now, this looks a little
mysterious. Let's leave WinMain in this loop while we take a look at the
Initialization function.


Initialization

The Initialization function (see Figure 6) does several chores necessary to
get the application moving. The two most important chores are defining a
"window class" and a "window." More than one window may use the same class.
The window class defines such things as the standard cursor, the icon, and
the window procedure. Creating the window defines the style and title of the
window.

If you run more than one copy of WSZ in Windows, different instances of the
application will use the same code segment, but different data segments.
This reduces memory use considerably. Different instances of the same
application will have different windows, but the windows will use the same
window class. This is the reason we define the window class only if this is
the first instance of the program.

Although each instance of a Windows application will generally have its own
data segment, the instances are able to share some data. This is illustrated
by the two GetInstanceData calls.


Window Procedure

When we last left WinMain, it was sitting in a loop getting messages from
Microsoft Windows and dispatching them somewhere. Where these messages
ultimately go is the MainWndProc procedure shown in Figure 7. This is a
window procedure. It receives messages from Windows regarding everything
that affects the WSZ window. Every keystroke, every mouse click, even every
movement of the mouse is a message. Within a window procedure, you can
ignore many of these messages by passing them on to the Windows function
DefWindowProc.

The message handling in window procedures makes Windows programming seem
very strange to programmers who have previously worked in more traditional
environments. Usually a program makes a call to the operating system to find
out what's going on. Microsoft Windows is different. From the viewpoint of
the window procedures, it seems like Windows is calling your program to tell
you what's going on. If you feel comfortable with that concept, stick with
it. Otherwise, you can think of DispatchMessage as initiating the call to
the window procedure.

MainWndProc handles only a few of the messages that are sent to it by
Windows. The WM_CREATE message is sent by Windows as a result of the
CreateWindow call in Initialization. WSZ takes this opportunity to retrieve
some of the information that will not be changing during the Windows
session. This information includes the size (in both millimeters and pixels)
of the entire display surface and the dimensions of a character that is in
the default system font.

Microsoft Windows sends a WM_SIZE message to the window procedure when the
window is first displayed and whenever the window is resized. The new
dimensions of the window are encoded in the lParam variable. The dimensions
refer only to the "client area," which excludes the caption bar, menu, and
scroll bars. This is the area that a Windows application is free to write
in.

A window procedure will receive a WM_ERASEBKG any time Windows wants it to
erase the background of the window client area. The color of the background
is defined as part of the window class. Since WSZ will be coloring the
background itself, all we need to do is tell Windows not to erase the
background.

Windows sends window procedures a WM_PAINT message whenever the client area
needs repainting. This can happen after the window has been resized (in
which case it has been preceded by a WM_SIZE message) or after a pop-up menu
has been removed. This is where WSZ will do most of its work. It calculates
values to write the display, colors the whole client area with PatBlt, loads
strings from the resource table with LoadString, and then writes text to the
screen with a combination of DrawText and the regular C sprintf function.

Generally, Windows applications use TextOut rather than DrawText for simple
text strings. However, DrawText provides such amenities as centering and
word-wrap options. The text is drawn in a rectangle indicated by the rect
structure. The rectangle is defined to allow a top and bottom border equal
to the height of a character and a left and right border equal to the width
of a character.

Creating a device-independent application requires that you not make any
assumptions about the size of the display or the client area or the
character sizes. You will probably find that it works best to dynamically
design window displays within your program based around the size of a
default character rather than around hard-coded constants.


Dialog Boxes

The WM_SYSCOMMAND message means that the user of the application has
selected something from the system menu. Normally we wouldn't care, except
that we've added "About..." to the system menu. If About... has been
selected, then WSZ calls DialogBox to indicate that control should now
transfer to the procedure called AboutWndProc (see Figure 8).

Because the About dialog box is fairly simple (just some text and an "Ok"
button), AboutWndProc is also simple. Microsoft Windows is responsible for
actually displaying the dialog box and passing messages back to
AboutWndProc. The only message that we really care about is a WM_COMMAND
message, which means that the Ok button has been hit.

When the MainWndProc receives a WM_COMMAND message, it knows that the user
has selected something from the window menu (rather than from the system
menu). The two choices from the Color menu option (ID_BLACK and ID_WHITE)
simply require changing a checkmark and then saving the CurrentColor. The
Units menu selection then passes control to UnitsWndProc (see Figure 9),
another dialog box.

Note that both the menus and dialog boxes make full use of the same mouse
and keyboard interface as in other Windows applications. Yet nowhere does
WSZ make any explicit keyboard or mouse calls.


Definitions

Finally (well, almost finally), a Windows application requires a Definition
(.DEF) file. The Definition file contains information about the program
segments and procedures that LINK4 (the Windows linker) needs in order to
construct the .EXE file. WSZ.DEF is shown in Figure 10v1n2a3fa.

The code and data segments are both defined as MOVEABLE, which means that
Microsoft Windows can move them around in memory in order to make room for
other programs. The idea that Windows may be moving your program around in
memory may give you the willies, and it should. Many Windows bugs result
from doing such things as saving far pointers (where they become invalid) or
not properly locking and unlocking allocated memory.

The MainWndProc, AboutWndProc, and UnitsWndProc routines are very special
because they are called by Windows. These routines must be specified as
EXPORTS in the WSZ.DEF file. The STUB called WINSTUB.EXE is a small program
that LINK4 embeds in the .EXE file. Try running WINSTUB.EXE on the DOS
command level to see what it does.


Putting It All Together

So far we've created five different source files with the extensions .ICO,
.RC, .H, .C, and .DEF──all for just one application. Now we have to run
these files through the RC.EXE resource compiler, the CL.EXE C compiler, and
the LINK4 linker.

You may be wondering how you're going to keep this mess straight. If you
decide to change the icon file, what do you have to do to bring everything
up to date? It's a problem, and that's why all good Windows programmers use
MAKE.

MAKE.EXE is a program maintainer. The "make-file" for an application
contains sections that look like this:

  target: dependent
          command

If the file change date of any "dependent" file is later than the file
change date of the "target" file, then the "command" is run. Otherwise, it
is not run.

The make-file for WSZ is shown in Figure 11. By convention, make-files have
no extension, so the file is just called WSZ. By executing

  MAKE WSZ

after making changes to your source files, you can bring the whole thing up
to date.

The make-file also serves as a form of documentation of the different pieces
that contribute to the application and indicates how to recreate the program
from the source files.


From Source to .EXE

Figure 12 shows a system flowchart of the various WSZ files and how they
eventually get into the WSZ.EXE executable file.

Some of this should look normal. The CL.EXE C Compiler uses the WSZ.C source
code, and two header files to create a WSZ.OBJ object file. The command line
options may look excessive, but they are all there for a reason. The -Gw
option, for instance, adds some special Windows Prolog code to far
procedures.

LINK4.EXE is much like the normal LINK program. It uses the WSZ.OBJ file and
C .LIB library files and creates an executable WSZ.EXE file. But this .EXE
file is not the same format as the .EXE files we've been using for 5 years
with MS-DOS. These .EXE files have expanded header information, and some of
this information will come from the WSZ.DEF definition file.

One apparent oddity in this flowchart is that the resource compiler (RC.EXE)
must be run twice, once at the beginning and again at the end. This is
because the resource compiler has to do two things: First, it must compile
your ASCII .RC file into a binary .RES file, and second, it must append data
from the .RES file to your .EXE file and set up pointers in the .EXE file
header to reference each resource.

To do both these steps at once (assuming LINK4 has already created an .EXE
file), you would execute

  RC FILENAME [.RC]

This creates the compiled .RES file from your .RC file and copies the
contents of the .RES file to the .EXE file. However, this approach creates
problems with program maintenance. If you need to relink following a C
source code change, you don't need to recompile the resource script. All you
need to do is add the existing .RES data to the new .EXE file.

For this reason it's better to use RC.EXE in two separate steps:

  RC -r FILENAME [.RC]

creates the compiled .RES file from the .RC file, and

  -RC FILENAME.RES

copies the data from the .RES file to the .EXE file.

If you change the .RC file now, you'll have to recompile it with RC.EXE,
relink, then use RC.EXE to add the new compiled resources to the .EXE file.
But if you change something that affects only the C compilations (which is
more often the case), then you need only relink and add the .RES file to the
.EXE file. You don't have to recompile the resource script.


The Rewards

Building a Windows application up from the beginning is no picnic. Yet
neither is writing your own graphics routines, menu logic, and dialog
boxes──and Microsoft Windows does all that for you.


Figure 2:  WSZ.RC

Th resource script for WSZ, called WSZ.RC. This file includes definitions
for the icon, all of the displayable text strings used in the C program, a
menu, and two dialog boxes.

#include <windows.h>
#include "WSZ.h"

WhatSize ICON WSZ.ico

STRINGTABLE
    BEGIN
        IDS_NAME,   "WhatSize"
        IDS_ABOUT,  "About..."
        IDS_TITLE,  "What Size is the Window?"
        IDS_FORMAT, "%.3G %s by %.3G %s"
        ID_MILLIM,  "millimeters"
        ID_INCHES,  "inches"
        ID_PIXELS,  "pixels"
    END

WhatSize MENU
    BEGIN
        POPUP "Colors"
            BEGIN
                MENUITEM "Black on White", ID_BLACK
                MENUITEM "White on Black", ID_WHITE
            END
        MENUITEM "Units", IDM_UNITS
    END

AboutBox DIALOG 20, 20, 144, 75
    STYLE WS_POPUP | WS_DLGFRAME
    BEGIN
        CTEXT "What Size?",             -1, 37,  5,  68,  8
        ICON  "WhatSize",               -1,  9, 23,   0,  0
        CTEXT "A Windows Application",  -1,  0, 14, 144,  8
        CTEXT "Version 1.00",           -1, 38, 34,  64,  8
        DEFPUSHBUTTON "Ok",           IDOK, 53, 59,  32, 14, WS_GROUP
    END

UnitsBox DIALOG 20, 20, 100, 90
    STYLE WS_POPUP | WS_BORDER
    CAPTION "What Size Units"
    BEGIN
        RADIOBUTTON "Pixels",ID_PIXELS,20,5,60,15,WS_GROUP | WS_TABSTOP
        RADIOBUTTON "Millimeters", ID_MILLIM, 20, 25, 60, 15
        RADIOBUTTON "Inches",      ID_INCHES, 20, 45, 60, 15
        DEFPUSHBUTTON "Ok",        IDOK,      10, 70, 32, 15, WS_GROUP
        PUSHBUTTON "Cancel",       IDCANCEL,  58, 70, 32, 15
    END


Figure 4:  WSZ.H Header File

The WSZ.H header file contains numbers that are defined as macros, which
become the names in the WSZ.RC file that begin with the letters "ID".

#define IDS_NAME        101
#define IDS_ABOUT       102
#define IDS_TITLE       103
#define IDS_FORMAT      104

#define ID_PIXELS       105
#define ID_MILLIM       106
#define ID_INCHES       107
#define ID_BLACK        108
#define ID_WHITE        109

#define IDM_ABOUT       110
#define IDM_UNITS       111


Figure 5:  WinMain Function for WSZ

The WinMain procedure for WSZ. Every Windows application requires such a
procedure, which is the entry point to the application.

/*  whatsize -- Windows application in C */

#include <windows.h>
#include "wsz.h"

long FAR PASCAL MainWndProc (HWND, unsigned, WORD, LONG) ;
BOOL FAR PASCAL AboutWndProc (HWND, unsigned, WORD, LONG) ;
BOOL FAR PASCAL UnitsWndProc (HWND, unsigned, WORD, LONG) ;

FARPROC lpAbout ;
FARPROC lpUnits ;
HANDLE  hInst;
int     CurrentUnits = ID_PIXELS ;
int     CurrentColor = ID_BLACK ;

int PASCAL WinMain (hInstance, hPrevInstance, lpszCmdLine, nCmdShow)
    HANDLE hInstance, hPrevInstance;
    LPSTR  lpszCmdLine;
    int    nCmdShow;
    {
    MSG    msg;

    if (!Initialize (hInstance, hPrevInstance, lpszCmdLine, nCmdShow))
        return FALSE ;

    while (GetMessage ((LPMSG)&msg, NULL, 0, 0))
        {
        TranslateMessage ((LPMSG)&msg) ;
        DispatchMessage ((LPMSG)&msg) ;
        }
    return (msg.wParam) ;
    }


Figure 6:  Initialization Function for WSZ

The Initialization function performs several of the chores necessary to get
the application moving. The two most important chores are deining a "window
class" and a "window."

BOOL Initialize (hInstance, hPrevInst, lpszCmdLine, nCmdShow)
    HANDLE   hInstance, hPrevInst ;
    LPSTR    lpszCmdLine ;
    int      nCmdShow ;
    {
    char     *szAppName [10] ;
    char     *szAbout [10] ;
    char     *szTitle [30] ;
    WNDCLASS wndclass ;
    HWND     hWnd;
    HMENU    hMenu;

    LoadString (hInstance, IDS_NAME, (LPSTR) szAppName, 10) ;
    LoadString (hInstance, IDS_ABOUT, (LPSTR) szAbout, 10) ;
    LoadString (hInstance, IDS_TITLE, (LPSTR) szTitle, 30 ) ;

    if (!hPrevInst)
        {
        wndclass.style        = CS_HREDRAW | CS_VREDRAW;
        wndclass.lpfnWndProc  = MainWndProc;
        wndclass.cbClsExtra   = 0;
        wndclass.cbWndExtra   = 0;
        wndclass.hInstance    = hInstance ;
        wndclass.hIcon        = LoadIcon (hInstance, (LPSTR) szAppName);
        wndclass.hCursor      = LoadCursor (NULL, IDC_ARROW);
        wndclass.hbrBackground= (HBRUSH) GetStockObject (WHITE_BRUSH);
        wndclass.lpszMenuName = (LPSTR) szAppName;
        wndclass.lpszClassName= (LPSTR) szAppName;

        if (!RegisterClass ((LPWNDCLASS) &wndclass))
            return FALSE;
        }
    else
        {
        GetInstanceData(hPrevInst,(NPSTR)&CurrentUnits,sizeof(int));
        GetInstanceData(hPrevInst,(NPSTR)&CurrentColor,sizeof(int));
        }

    hWnd = CreateWindow (
            (LPSTR) szAppName,  /* class name       */
            (LPSTR) szTitle,    /* caption title    */
            WS_TILEDWINDOW,     /* windows style    */
            0,                  /* x (ignored)      */
            0,                  /* y (ignored)      */
            0,                  /* width (ignored)  */
            0,                  /* height (ignored) */
            (HWND) NULL,        /* parent (none)    */
            (HMENU) NULL,       /* menu (use class) */
            (HANDLE) hInstance, /* instance handle  */
            (LPSTR) NULL) ;     /* parameters       */

    hInst = hInstance ;

    lpAbout = MakeProcInstance ((FARPROC) AboutWndProc, hInstance) ;
    lpUnits = MakeProcInstance ((FARPROC) UnitsWndProc, hInstance) ;

    hMenu = GetSystemMenu (hWnd, FALSE) ;
    ChangeMenu (hMenu, 0, NULL, 999, MF_APPEND | MF_SEPARATOR) ;
    ChangeMenu (hMenu, 0, (LPSTR) szAbout, IDM_ABOUT,
      MF_APPEND | MF_STRING) ;


    hMenu = GetMenu (hWnd) ;
    CheckMenuItem (hMenu, CurrentColor, MF_CHECKED) ;

    ShowWindow (hWnd, nCmdShow) ;
    UpdateWindow (hWnd) ;

    return TRUE ;
    }


Figure 7:  MainWndProc Procedure for WSZ

MainWndProc is a window procedure that receives messages from windows on
everything that affects the WSZ window.

long FAR PASCAL MainWndProc (hWnd, message, wParam, lParam)
    HWND       hWnd;
    unsigned   message;
    WORD       wParam;
    LONG       lParam;
    {
    HMENU      hMenu ;
    static int CharHorzPix, CharVertPix ;
    static int ScrnHorzPix, ScrnVertPix ;
    static int ScrnHorzMil, ScrnVertMil ;
    static int WindHorzPix, WindVertPix ;

    switch (message)
        {
        case WM_CREATE:
            {
            TEXTMETRIC tm ;
            HDC        hDC = GetDC (hWnd) ;

            GetTextMetrics (hDC, (LPTEXTMETRIC) &tm) ;
            CharHorzPix = tm.tmAveCharWidth ;
            CharVertPix = tm.tmHeight ;

            ScrnHorzPix = GetDeviceCaps (hDC, HORZRES) ;
            ScrnVertPix = GetDeviceCaps (hDC, VERTRES) ;
            ScrnHorzMil = GetDeviceCaps (hDC, HORZSIZE) ;
            ScrnVertMil = GetDeviceCaps (hDC, VERTSIZE) ;

            ReleaseDC (hWnd, hDC) ;
            }
            break ;

        case WM_SIZE:
            WindHorzPix = LOWORD (lParam) ;
            WindVertPix = HIWORD (lParam) ;
            break ;

        case WM_ERASEBKGND:
            return TRUE ;

        case WM_PAINT:
            {
            PAINTSTRUCT ps ;
            char        szFormat [20] ;
            char        szUnits [20] ;
            char        szBuffer [60] ;
            float       nHorz = (float) WindHorzPix ;
            float       nVert = (float) WindVertPix ;
            RECT        rect ;

            if (CurrentUnits != ID_PIXELS)
                {
                nHorz *= (float) ScrnHorzMil / ScrnHorzPix ;
                nVert *= (float) ScrnVertMil / ScrnVertPix ;
                }
            if (CurrentUnits == ID_INCHES)
                {
                nHorz /= 25.4 ;
                nVert /= 25.4 ;
                }

            BeginPaint (hWnd, (LPPAINTSTRUCT) &ps) ;

  PatBlt (ps.hdc, 0, 0, WindHorzPix, WindVertPix,
               (CurrentColor == ID_WHITE) ? BLACKNESS : WHITENESS) ;

            if (CurrentColor == ID_WHITE)
                {
                SetTextColor (ps.hdc, RGB (255, 255, 255)) ;
                SetBkColor (ps.hdc, RGB (0, 0, 0)) ;
                }
            LoadString (hInst, IDS_FORMAT, (LPSTR) szFormat, 20) ;
            LoadString (hInst, CurrentUnits, (LPSTR) szUnits, 20) ;

            rect.bottom = WindVertPix - (rect.top  = CharVertPix) ;
            rect.right  = WindHorzPix - (rect.left = CharHorzPix) ;

            DrawText (ps.hdc, (LPSTR) szBuffer,
                sprintf(szBuffer,szFormat,nHorz,szUnits,nVert,szUnits),
                (LPRECT) &rect, DT_CENTER | DT_WORDBREAK) ;

            EndPaint (hWnd, (LPPAINTSTRUCT) &ps) ;
            }
            break;

        case WM_SYSCOMMAND:
            switch (wParam)
                {
                case IDM_ABOUT:
                    DialogBox(hInst,(LPSTR)"AboutBox",hWnd,lpAbout);
                    break;

                default:
                    return DefWindowProc(hWnd,message,wParam,lParam);
                }
            break;

        case WM_COMMAND :
            switch (wParam)
                {
                case ID_BLACK:
                case ID_WHITE:
                    hMenu = GetMenu (hWnd) ;
                    CheckMenuItem(hMenu,CurrentColor,MF_UNCHECKED);
                    CheckMenuItem (hMenu, CurrentColor = wParam,
                         MF_CHECKED) ;
                    InvalidateRect (hWnd, (LPRECT) NULL, TRUE) ;
                    break ;

                case IDM_UNITS:
                    if (DialogBox (hInst, (LPSTR) "UnitsBox", hWnd,
                              lpUnits))
                         InvalidateRect (hWnd, (LPRECT) NULL, TRUE) ;
                    break ;

                default :
                    return DefWindowProc (hWnd, message, wParam, lParam) ;
                }
            break ;

        case WM_DESTROY:
            PostQuitMessage (0) ;
            break;

        default:
            return DefWindowProc (hWnd, message, wParam, lParam) ;
            break;
        }
    return (0L) ;
    }


Figure 8:  AboutWndProc Procedure for WSZ

AboutWndProc is the window procedure for the dialog box called up by
selecting the About option from the menu.

BOOL FAR PASCAL AboutWndProc (hDlg, message, wParam, lParam)
    HWND     hDlg ;
    unsigned message ;
    WORD     wParam ;
    LONG     lParam ;
    {
    switch (message)
        {
        case WM_INITDIALOG :
            return TRUE ;

        case WM_COMMAND :
            EndDialog (hDlg, TRUE) ;
            return TRUE ;

        default:
            return FALSE ;
        }
    }


Figure 9:  UnitsWndProc Procedure for WSZ

UnitsWndProc is the window procedure for the dialog box called up by
selecting the Units option from the menu.

BOOL FAR PASCAL UnitsWndProc (hDlg, message, wParam, lParam)
    HWND       hDlg ;
    unsigned   message ;
    WORD       wParam ;
    LONG       lParam ;
    {
    static int NewUnits ;

    switch (message)
        {
        case WM_INITDIALOG :
            CheckRadioButton (hDlg,ID_PIXELS,ID_INCHES,CurrentUnits);
            SetFocus (GetDlgItem (hDlg, NewUnits = CurrentUnits)) ;
            return FALSE ;

        case WM_COMMAND :
            switch (wParam)
                {
                case ID_MILLIM:
                case ID_INCHES:
                case ID_PIXELS:
                    NewUnits = wParam ;
                    CheckRadioButton (hDlg,ID_PIXELS,ID_INCHES,NewUnits);
                    break ;

                case IDOK:
                    CurrentUnits = NewUnits ;
                    EndDialog (hDlg, TRUE) ;
                    break ;

                case IDCANCEL:
                    EndDialog (hDlg, FALSE) ;
                    break ;

                default:
                    return FALSE ;
                }
            break ;

        default:
            return FALSE ;
        }
    return TRUE ;
    }


Figure 10:  WSZ.DEF Definition File

The Definition file for WSZ, called WSZ.DEF contains information about the
program segments and procedures that LINK4, the Windows linker, requires for
constructing the .EXE file.

NAME    WhatSize

DESCRIPTION 'A Windows Application'

STUB    'WINSTUB.EXE'

CODE    MOVEABLE
DATA    MOVEABLE MULTIPLE

HEAPSIZE  1024
STACKSIZE 4096

EXPORTS
        MainWndProc     @1
        AboutWndProc    @2
        UnitsWndProc    @3


Figure 11:  Make-File for WSZ

The make-file for WSZ is a program maintainer that keeps everything up to
date. It serves as a record of the different pieces that contribute to the
application and indicates how to recreate the program from the source files.

wsz.res: wsz.rc wsz.ico wsz.h
    rc -r wsz.rc

wsz.obj: wsz.c wsz.h
    cl -d -c -W2 -AS -Gs -Gw -Oas -Zpd -FPa wsz.c

wsz.exe: wsz.obj wsz.res wsz.def
    link4 wsz, wsz/align:16, wsz/map, slibw, wsz.def
    mapsym wsz
    rc wsz.res


Figure 12:  This system flowchart of the various WSZ files shows how the
            files eventually get into the WSZ.EXE executable file.

┌──────────┐   ┌───────────┐   ┌─────────────┐   ┌─────────┐
│  WSZ.RC  │   │  WSZ.ICO  │   │  WINDOWS.H  │   │  WSZ.C  │
└─────┬────┘   └─────┬─────┘   │    WSZ.RC   │   └─┬───────┘
      └──────────┐   │         └─┬────┬──────┘     │  ╔════════════╗
                 │   │       ┌───┘    │        ┌───┘  ║   P1.EXE   ║
╔════════════╗  ╔▼═══▼═══════▼═╗  ╔═══▼════════▼═╗  ┌─►   P2.EXE   ║
║  RCPP.EXE  ◄──╢  RC.EXE(-r)  ║  ║    CL.EXE    ╟──┘ ║   P3.EXE   ║
╚════════════╝  ╚═══════╤══════╝  ╚══════╤═══════╝    ╚════════════╝
                  ┌─────▼─────┐    ┌─────▼─────┐      ┌───────────┐
                  │  WSZ.RES  │    │  WSZ.OBJ  │┌─────┤  WSZ.DEF  │
                  └─────┬─────┘    └─────┬─────┘│     └───────────┘
                        │   ╔════════════▼══╗   │ ┌───────────────┐
                        └───►               ◄───┘┌┤  WINSTUB.EXE  │
                            ║     LINK4     ◄────┘└───────────────┘
                            ║               ◄───────────┐
                            ╚═╤═══════════╤═╝        ┌──┴─────────┐
                     ┌────────▼──┐  ┌─────▼─────┐    │ SLIBC.LIB  │
            ┌────────┤  WSZ.EXE  │  │  WSZ.MAP  │    │ SLIBW.LIB  │
      ╔═════▼════╗   └───────────┘  └─────┬─────┘    │ SLIBFA.LIB │
      ║  RC.EXE  ║                  ╔═════▼════╗     │ LIBH.LIB   │
      ╚═════╤════╝                  ║  MAPSYM  ║     └────────────┘
      ┌─────▼─────┐                 ╚═════╤════╝
      │  WSZ.EXE  │                 ┌─────▼─────┐
      └───────────┘                 │  WSZ.SUM  │
                                    └───────────┘

────────────────────────────────────────────────────────────────────────────

New XENIX Version Will Be First to Run On the Intel 80386

Joe Desposito

Microsoft's XENIX(R) System V/386 is expected to be released during the
first quarter of 1987. This version of the product is especially
significant, as it is the first version of XENIX to take full advantage of a
32-bit microprocessor──namely, Intel's 80386. To learn more about XENIX 386
we went straight to the source: Martin Dunsmuir, Microsoft's XENIX
Development Manager.

MSJ: We're here to talk about Microsoft XENIX System V/386. But could you
first give us a brief background on the other versions of XENIX?

MARTIN DUNSMUIR: The product out there at the moment is XENIX 286, for which
there is a number of large OEMs (original equipment manufacturers),
particularly IBM(R), Compaq(R), Altos, Tandy, and Intel(R). There have been
two releases of XENIX 286: System 3, which went out in the '84 time frame,
and System V, which came out in '85. The first release, System 3, was really
targeted at the Intel 310 Machine. With the exception of IBM──we made a
version available on the AT──most other versions of XENIX System 3 were on
proprietary architectures. Then, when we went to System V, the emphasis
moved to the AT architecture and its clones.

Except for the Altos, which has its own architecture and is intended to be
used in a turnkey environment, all of the XENIX 286 versions are on the IBM
AT or clones──for example, IBM, Compaq, the TI Business Pro, and the Sperry
IT.

In the last year, the Intel 80386 microprocessor has appeared. And the
machines that are currently being manufactured or are on the verge of being
shipped──which use the 80386──are AT clones that replace the 80286 with the
80386. So what we're aiming at is a release of XENIX 386 that will run on
these machines. Now that's not to say that somebody like Altos, for example,
can't take the source of what we produce and make it work on their own
proprietary architecture. In fact, Altos is planning to do that──they've
already started.

MSJ: You mentioned 80386 machines. One that has already hit the marketplace
is the Compaq 386. What's your opinion of it?

DUNSMUIR: That's the machine that we've been using for development. We
actually had some of the earliest prototypes and now we have some real
models of it. It's really very good. It's very fast──about three times the
speed of the AT. The only slow part of it is the disk, which, of course,
operates at the same speed as it does on the AT. When you speed up the
processor and you don't speed up the disk in proportion, the effective speed
of the whole system obviously doesn't increase as much as that of the
processor.


The 386 Explosion

MSJ: At the fall Comdex, there is expected to be an explosion of 386
accelerator cards for the IBM AT and other 286 machines. Have you worked
with any of them?

DUNSMUIR: Well, the question really is: What machines will XENIX run on, or
rather, what does it run on so far? It runs on any machines containing an
80386 that have an AT compatible architecture. This includes the Compaq
Deskpro 386, of course, but also machines running the Intel motherboard and
386 turbo cards plugged into ATs.

MSJ: In terms of the microprocessor itself, the 80386──what are some of the
advantages that it has over the 80286, and how do these advantages relate to
XENIX?

DUNSMUIR: One advantage the 80386 has over the 80286 is that it is much
faster──about three times the speed in terms of raw CPU power. And it also
has this large address space. On the 286 your program can be very large, but
basically it is composed of 64K segments.

The problem with this is that people who have written applications for
UNIX(R) in general find it difficult to bring those applications down onto
XENIX 286. In other words, the applications are bigger than the 64K limit
that you can get with one segment. Originally we hoped to put a real UNIX
machine on everybody's desktop──or a small multiuser UNIX machine──but the
number of ISVs (independent software vendors) who actually have gone to
XENIX 286 has been somewhat less than we had hoped. That's not to say that
the volumes aren't very good. XENIX 286 is probably the single most-
installed derivative of UNIX ever. But the number of applications that have
been written specifically for XENIX 286 is smaller than we had hoped at the
outset.

With the 386, you can easily bring your application down from, say, a VAX or
a Sun or whatever, because you don't have this problem with these segments
getting in the way of your large programs.


Possible Problems

MSJ: That sounds good in theory, but in practice, won't there be some snags
when you try to port a UNIX program running on a VAX to a microcomputer like
the Compaq 386 running XENIX?

DUNSMUIR: On the VAX, the pointers and the integers in your program are all
32 bits. Now, that means that if you simply compile your program on the 286,
then it breaks because the integers on the 286 are 16 bits and the addresses
are, too──unless you use multiple segments. So you're forced to use multiple
segments.

Now, when you use multiple segments, you've got a choice: You can go to
what's called the large model, which means that the compiler automatically
generates the instructions that change the segments when it needs to. This
simulates the large address space in the 286 but reduces performance by
about 20 percent. The alternative is to rewrite your application to optimize
the use of the segments.

On the 80386, however, segments are so large that there are essentially no
segments. As a result, all you need to do is recompile, and the thing will
run. And XENIX provides the right set of subroutines for the application to
call, because it's a standard UNIX programming interface.

A caveat to this arises, for example, if the program does graphics through
some known standard interface. For instance, on the Sun they may be driving
the graphics interface directly. Then the programmer is going to have to
emulate that or provide support. But in terms of some straightforward
application like a compiler or anything that's terminal-based, the program
will just recompile and run.


A Big Change

MSJ: All the recent Intel processors like the 8088, 8086, and 80286 use 64K
segments. The 80386 processor represents a radical change, doesn't it?

DUNSMUIR: It certainly does. On the 286 you have these segments, and each
segment has a number that you load into the segment register. The segments
themselves can be up to 64K. So to actually address a piece of memory you
specify the segment and the offset, which is a 16-bit number. On the 386,
segments exist, too──except that the offsets are 32-bit numbers. This means
that once you've loaded the segment register, you can address 4 gigabytes of
memory, rather than 64K, without reloading the segment register.

On the 386 you can emulate 286 programs. You can actually make a segment
look like a 16-bit segment or a 32-bit segment. Basically, the operating
system sets up the segment descriptor table as appropriate, depending on
whether you're running a 286 or a 386 program.

MSJ: Are there other characteristics that distinguish the 80386 from its
predecessors?

DUNSMUIR: Underneath the segmentation of the 386 is also paging, which
doesn't exist on the 286. Underneath the 4-gigabyte segment are page tables,
which map each 4K piece of that address space onto some piece of physical
memory──or not, as the case may be. You don't have to actually have all of
the physical paging present in that 4-gigabyte segment. Typically, you have
only a few. A big program might have 4 megabytes, say, of actual paging
mapped. But the advantage of having the very large address space is that you
can actually put different parts of the program at completely weird,
enormous addresses. So you can say: Okay, shared memory regions, for
example, start at 2 gigabytes. But then you must make sure that the
operating system maps the shared memory regions up to the 2-gigabyte virtual
address used in the program.

This is very similar to the way it works, for example, on the VAX or a 68020
machine like the Sun. Any of these 32-bit minicomputers really do it the
same sort of way. And that's what the 386 brings you on a chip. It brings
you the minicomputer architecture. Microsoft hopes that the people who write
UNIX applications for 32-bit minicomputers like the VAX will bring their
application, which is presumably written in C, down onto XENIX 386 and then
make a low-end version available. That's one of the main thrusts.

MSJ: Where does that leave users with XENIX 286 applications?

DUNSMUIR: There are, of course, applications that have been written
specifically for XENIX 286. These will be available on XENIX 386 simply
because it runs all the 286 software. One of the advantages of the 386 is
its downward compatibility. If somebody wants a higher-performance multiuser
solution, he can just use his old XENIX-286-based programs on the 386
machine, and they'll run just fine.

MSJ: XENIX 286 was primarily aimed at the multiuser market. Will the same
hold true for XENIX 386?

DUNSMUIR: The market position of XENIX 386 will be the small, multiuser
marketplace. That's the main thrust. And that's where XENIX 286 has been
successful. However, we now have the ability to create, if you like, a
workstation with the 386, which we didn't have on the 286. So we will also
be trying to address the workstation marketplace. I know that OEMs are
planning to compete with the low-end VAX and Sun stations, and they'll offer
a very cost-effective solution if, for example, you want to do CAD/CAM or
desktop publishing, or anything like that.


DOS Overlap

MSJ: Doesn't this overlap with the potential market for MS-DOS 5.0?

DUNSMUIR: Yes. When it becomes available, MS-DOS 5.0 will offer a very good,
single-user workstation environment that will be very similar to a UNIX
workstation in many ways. The main difference between an MS-DOS 5.0 and a
XENIX 386 workstation is the existence of a UNIX market that is distinct
from any other single-user market. UNIX allows you to have an application
that will run on anything from a mainframe to a micro. In contrast,
applications designed to run under MS-DOS 5.0 won't run under any other
version. Hence, there will probably always be a UNIX workstation market
unless UNIX disappears completely. This single-user workstation group is a
separate group that we'll address with XENIX.

MSJ: Will XENIX 386 have any brand-new features?

DUNSMUIR: For the single-user workstation group, we've got to provide new
features with XENIX. The features that make a good multiuser operating
system──for example, supporting terminals──are not the sort of features. . .
well, they're a subset, but they're not really the features that you need
for a good, single-user workstation. The features that spring to mind for a
single-user workstation are windowing, some sort of graphics capability, the
ability to run 32-bit applications (which is what you get with the 386), and
a networking facility so that you can connect your workstation to a larger
machine, whatever that might be. What we're trying to do is to put together
all of these various pieces in XENIX 386.

MSJ: So XENIX System V/386 is slated to appear in the first quarter of
1987?

DUNSMUIR: Right. The beta version will be shipped to OEMs in late November.
We plan to ship them the final product in early January. Now, the time delay
for any of those OEMs to get a product out in the field is about 2 months at
least. So you probably won't see any XENIX 386 operating systems appearing
from our OEMs until something like April. But in the meantime, the Santa
Cruz Operation (SCO) will be making available a retail version of the same
software. I don't know exactly what their plans are, but I imagine that they
will make it available very early in January close to the same time that we
ship the product to the OEMs.

MSJ: But isn't SCO one of your OEMs?

DUNSMUIR: We have what's called a second-source agreement with SCO. What
that actually means is that we work together to create XENIX. They have
access on a daily basis to the code, and therefore they are in a much better
position to put together a product. In fact, in the case of XENIX 386,
they're actually doing a bit of device driver work, and so forth.

One other thing needs to be said about SCO. At the present time, if you buy
XENIX directly from SCO, you can get certain added features that we don't
provide. This added value is being incorporated into XENIX 386, and we are
committed to having functionally identical products from now on. This should
silence some criticism in the press that SCO and MS XENIX versions are not
compatible. We are very concerned about providing ISVs and end-users with a
compatible platform for their XENIX applications whether they choose to buy
XENIX from SCO or one of our other OEMs.


V/386 Toolkit

MSJ: Although XENIX 386 isn't available right now, a XENIX System V/386
Toolkit is. Can you tell us something about it?

DUNSMUIR: The XENIX System V/386 Toolkit is a special upgrade package for
Microsoft XENIX System V/286. It's for independent software developers who
want to write XENIX 386 applications now.

The Toolkit includes both the program development tools needed to create
XENIX 386 applications and the execution environment needed to run them.
Virtually all of the 386 applications developed under the Toolkit will
execute without recompilation under XENIX 386.

Program development tools include a Microsoft C compiler for the 386; a new
version of the Microsoft Macro Assembler, extended to support the 386
instruction set and addressing modes; and a debugger for 386 programs.

These tools can be installed on any computer running XENIX 286. But the
execution environment portion of the Toolkit requires not only XENIX 286,
but also a system with a 386 processor.

────────────────────────────────────────────────────────────────────────────

A New Generation of Debugging Arrives with CodeView

───────────────────────────────────────────────────────────────────────────
Also see the related articles:
  A Look at DEBUG and SYMDEB
  Codeview Command Summary
───────────────────────────────────────────────────────────────────────────

Charles Petzold

Debugging is such an awful, dirty, unglamorous, time-consuming job that
nobody likes to think much about it. Perhaps for this reason, good debugging
tools have always lagged behind other advances in language technology. It
wasn't until the introduction of SYMDEB (SYMbolic DEBugger, first included
with the Macro Assembler 3.0) that Microsoft offered programmers an
alternative to the inadequate DEBUG.

The CodeView(TM) debugger included with Microsoft's C Compiler Version 4.00,
however, represents a new generation. Just as SYMDEB added to DEBUG's
features by reading in source code and displaying symbolic references to
global variables and subroutines, CodeView advances over SYMDEB by including
symbolic local variable and subroutine names, new data display and enter
types, an 8087 math coprocessor dump, and some additional debugging
techniques.

CodeView uses a full-screen windowed display and includes a mouse, menu, and
command line interface. It can do C expression evaluation and UNIX-like
regular expression evaluation. For now, CodeView is limited to Microsoft C
4.00 and cannot yet be used for debugging Microsoft Windows applications,
but it will eventually be adapted for other languages.


Prepararation

The mechanics of preparing a program for CodeView are simple. When compiling
a program, use the /Zi switch

  MSC PROGRAM /Zi;

This option directs the compiler to include in the .OBJ file the names,
addresses, and types of all global and local variables; the names,
addresses, and return values of all global and static functions; and source
code line number references.

If you don't need all this information (perhaps for a module that's already
in good shape), you can specify that only global functions and variables and
line number information be included in the .OBJ file by using the /Zd
switch

  MSC MODULE /Zd;

This is the same switch used for preparing programs for SYMDEB.

In either case, you might also want to disable optimization with the /Od
switch. This will make it easier to correlate source code and the compiled
assembly language.

When you link, use the /CO (CodeView) switch

  LINK PROGRAM /CO;

The /CO switch directs LINK to include the debugging information from each
.OBJ file in the .EXE file together with directory paths of the .OBJ files.
CodeView uses these directory paths to search for the C source code file.
You will want to help CodeView out by keeping source code and object files
in the same subdirectory.

Both CodeView and SYMDEB are easier to use if you follow certain programming
rules. For instance, since SYMDEB can handle only global functions and
variables, you might be less inclined to use local variables or functions
defined as static. With CodeView, however, you don't have to accommodate the
debugger in this way.

The rules for programming for CodeView are generally the same as those for
writing structured readable C. You should use small modular functions. You
should keep source code statements to one line each since multiple-line
source code statements do not show up well in CodeView's combined source and
assembly display. You can get a better feel for the relationship between
source and compiled code by avoiding long, complex C statements.

On the other hand, CodeView does not show source from include files and
macros. Also, since LIB strips out the debugging information, CodeView
cannot show source code for functions retrieved from libraries, even if the
modules were compiled with the /Zi switch. For this reason you will want to
wait until certain modules have been debugged before putting them in
libraries.


Letting MAKE Help Out

The symbolic information that LINK embeds into an .EXE file for use by
CodeView can make the .EXE much larger than normal. You will not want to
distribute an .EXE file with this information because anybody looking at the
program in CodeView can learn a great deal about its inner workings from the
variable and procedure names that CodeView displays.

You can use MAKE to help out with the chore of compiling a program with or
without CodeView information. At the top of the make-file for a particular
program, define several macros

  CVC  = /Zi /Od
  CVL  = /CO
  COMP = msc $* $(CVC) ;
  LINK  = link $** $(CVL) ;

where CVC and CVL are the CodeView switches for the compile and link steps,
respectively. After the definition of these macros, you can set up the make-
file like this:

  prog.obj : prog.c
         $(COMP)
  module1.obj : module1.c
         $(COMP)
  module2.obj : module2.c
         $(COMP)
  prog.exe : prog.obj\module1.obj\module2.obj
         $(LINK)

When it is time to prepare the final .EXE file without embedded debugging
information, just undefine the macros on the MAKE command line

  MAKE prog /DCVC= /DCVL=

Alternatively, you can use the EXEPACK utility to strip debugging
information from the .EXE file.


Starting It Up

In many cases you can bring up CodeView for debugging your program with the
command line

  CV PROGRAM

However, certain situations (such as using a PC compatible or a black-and-
white monitor on a color adapter) will warrant CodeView command line
switches. These switches must be placed before the name of the program that
you're debugging.

The most important command line switches relate to how CodeView uses the
display. Because CodeView is a full-screen debugger, it must share the
display with the out-put from the program you are debugging. Depending on
the hardware installed on your system, you have several options for handling
this problem.

For systems with one video adapter, CodeView will use either video
"swapping" or video "flipping" to keep the CodeView and program output
separate.

If you have only a monochrome adapter on your system, CodeView will use the
"swapping" method. CodeView allocates some memory to save the contents of
the display. It swaps screen displays between this memory and the video
adapter memory when switching back and forth between CodeView and your
program.

If you have only a Color Graphics Adapter (CGA) or Enhanced Graphics Adapter
(EGA) on your system, CodeView will, by default, use the "flipping" option.
CodeView will use different video pages for its own display and the display
of the program being debugged. Flipping between these video pages is faster
than swapping video contents in and out of memory.

You cannot use screen flipping with a monochrome adapter because it has only
4K of memory and one video page. You also cannot use flipping on a CGA or
EGA if you are debugging a program that uses multiple video pages or
graphics. In this case, you must override the default and specify screen
swapping with the /S parameter.

Both screen flipping and screen swapping have some problems. Every time you
trace a statement, CodeView switches the display to program output and back
again. You're likely to find your head aching before you find the bug. If
you are debugging a program that directly accesses output ports on the video
adapter, CodeView may not be able to restore the exact video state when it
returns to your program output display. But there is really no alternative
to this when debugging programs using a single monitor.

These problems can be alleviated with a second display adapter. If you have
two adapters on your system, you can specify the /2 option when you bring up
CodeView, and CodeView will use the second adapter. (This option is not in
the manual, but it's mentioned in the C 4.00 README.DOC file.) Unfortunately
(as also mentioned in this README file), the /2 option does not allow the
mouse to work on the debugging display.

As with SYMDEB, you can redirect CodeView input and output through a serial
port and use a separate debugging terminal. However, you lose many of the
advantages of working with CodeView in a full-screen mode.


Menus and the Mouse

Although the pull-down menu interface and mouse support in CodeView make it
easier to use than most debuggers, early encounters with CodeView can be
quite confusing. There are actually several user interfaces──a SYMDEB-like
command line, a Windows-like menu, the PC function keys, and the mouse. Each
of these different interfaces has some distinct features and also some
overlap. You can usually do things in several different ways, and it is
precisely this flexibility that makes CodeView a bit awkward at first.

On entry to CodeView, the screen is divided into two windows called Display
and Dialog. The Display window shows either your source code, or source code
combined with the compiled assembly language code.

The Dialog window at first appears to be SYMDEB running in a window.
However, you'll find that CodeView is missing a few DEBUG and SYMDEB
commands (Fill, Move, Compare) and adds other valuable functions, such as
expression evaluation, symbol examination, a file browser with a search
capability, and additional methods to set breakpoints.

Everything you can do in CodeView is done by entering commands in the Dialog
window. This allows you to use CodeView in a SYMDEB-like teletype mode and
still retain full functionality. Some commands (Dump, Enter, Assemble) must
be entered in the Dialog window and cannot be done any other way.

The menu, mouse, and function key interfaces duplicate many of the more
common commands. They do not add functionality but they do provide ease of
use.

Let's explore some of the ways you can use CodeView by examining the guts of
debugging──tracing through code.


Trace, Go, and Break

Users of DEBUG and SYMDEB are familiar with the Trace and Go commands. DEBUG
3.x and SYMDEB also include a Proceed command. CodeView implements all three
of these methods to trace code.

The Trace command uses the Intel microprocessors' single-step trap to
single-step through instructions. Just as in DEBUG and SYMDEB, you can do a
one-instruction trace in CodeView by entering T in the Dialog window. If
you're in assembly mode, you'll step through assembly language instructions.
In source code mode, you'll step through source code instructions. You can
also do a one-instruction trace by pressing the F8 function key or clicking
Trace! on the menu with the left mouse button. A multiple-instruction trace
is possible only through the Dialog window by including a value after the T.

The Program Step command (which was called the Proceed command when
introduced in DEBUG 3.0) is similar to the Trace command except that it will
not trace through subroutine calls or software interrupts. This is good for
skipping through BIOS calls or subroutines that you know work. You can
Program Step by entering P in the Dialog window, pressing F10, or clicking
Trace! on the menu with the right mouse button.

Note that Program Step works by setting a breakpoint following the call or
interrupt that it skips through. This breakpoint will not work in ROM or
with calls and interrupts to routines that adjust the return address.

The Go command just executes code until it hits a breakpoint. You can set a
breakpoint at the same time you do a Go by entering G in the Dialog window
with a breakpoint expressed as an address, line number, or symbolic label.
Alternatively, you can position the cursor in the Display window at the
break line and press F7. For a Go command with no break, press F5. With the
mouse, you can Go to a particular line by clicking the line with the right
mouse button. For a no-break Go, click Go! on the menu.

Users of SYMDEB are also familiar with setting explicit breakpoints. The
advantage of this method over specifying a breakpoint in the Go command is
that you can set more than one of them, and they are sticky──the breakpoint
will remain in effect until you disable it or clear it.

The breakpoint command in CodeView works like the one in SYMDEB. Twenty
breakpoints are available. Setting a breakpoint with the mouse is very
easy──you just click on the line with the left mouse button. You can also
use the keyboard to move the cursor to the break line and press F9.


Beyond SYMDEB

Now let's look at some of the ways that CodeView goes beyond the
functionality of SYMDEB. We have watches, watchpoints, and tracepoints. They
all appear on the same menu, they are listed in the same window, and they
have confusingly similar names. They are not the same.

A "watch" is a C expression using any global or currently available local
variable. The expression is displayed in the Watch window along with the
calculated value. For instance, if you have a routine that writes directly
to the video display memory using two variables called row and col, you
might want to keep a watch on the implied display address referenced by
these two variables. Your watch would look like this:

  2 * (row + 80 * col)

The C expression evaluator can even handle calls to your program's C
functions. This lets you test individual functions by passing experimental
values to them.

A "watchpoint" is generally a Boolean expression, again expressed using C
syntax with global and currently available local variable names. When the
watchpoint becomes true (i.e., is nonzero), CodeView will break and return
control to you.

For both watches and watchpoints, CodeView must repeatedly evaluate the
expression while single-stepping through code. For watches, it just displays
the new value. For watchpoints, it breaks if the expression evaluates to a
nonzero value.

A "tracepoint" is similar to a watchpoint in that it can cause a break in
program execution. But while a watchpoint depends on the evaluation of an
expression, a tracepoint will break when an area of memory changes. This is
much easier and faster for CodeView to do, but is not quite as flexible for
the user.

For instance, suppose you had a for loop that increments the variable i,
such as

  for (i = 0; i < 100; i++)

If you wanted to break every time that i changed value, you could set a
tracepoint that is simply the expression

  i

But if i is a registered variable, the tracepoint would not work, because i
does not refer to an area of memory.

Also, if you wanted to break every ten cycles through, you could not set a
tracepoint that looks like

  i % 10

because "i % 10" does not refer to a memory location. What you really need
is a watchpoint that looks like this:

  i % 10 == 0


Watching Tracepoints

In debugging any program it is advisable to try to localize your problem
before setting either watchpoints or tracepoints. Watchpoints take longer to
evaluate than tracepoints, but they do not need to refer to memory
locations. Here's a simple rule: When you think you need to set a watchpoint
or tracepoint, try to do it as a tracepoint first. If you can't, use a
watchpoint.

Watchpoints and tracepoints both require some time at every program step.
For real-time applications, you may need a hardware debugger.


Null Pointers

The ability to test the contents of any variable or any region of memory
continually is a very powerful debugging tool. For instance, you may be
familiar with the run-time message

  error 2001: Null pointer assignment

At the very beginning of your program's data segment is a small area of
memory containing some zeros and the Microsoft copyright notice. The clean-
up exit code calculates a checksum on this memory. If the memory has been
changed, the "Null pointer assignment" message is displayed.

In C, a null pointer is an invalid pointer. If the beginning of the data
segment has been changed, then it may very well be the result of using a
null pointer. The message you get doesn't mean you've just assigned 0 to a
pointer. It means that you've actually used that pointer to write something
into memory.

You can find the buggy code that does this by setting a tracepoint on this
memory. From the command line of the Dialog window, you can enter

  TPB 0 L 8

This means "Set a Tracepoint of type Byte, starting at address 0 for a
Length of 8." CodeView displays the current values in a Dump-like format.

Or you can do essentially the same thing as an expression from the
Tracepoint option of the Watch menu

  *((double *) 0)

The contents of this double pointer to zero are the eight bytes starting at
address 0. When this memory is changed, CodeView will break and show you
where it happened.

If you have a particular character string getting chewed up or an array
picking up some strange values or even code that gets destroyed, you can set
a tracepoint at the variable or the explicit address, and CodeView will
break when the damage happens.


The Panic Button

CodeView also supports dynamic breaking using the SysReq key on the PC-AT.
The SysReq key usually works to regain control when your program seems to
avoid the breakpoints you set for it. The Calls option on the menu (or the K
command in the Dialog window) displays a stack trace. The stack trace is
good for answering the question "Where am I and how did I get here?" It
displays a list of nested subroutines, the parameters passed to them, and
(when the command is entered in the Dialog window) the line numbers where
these routines were called.


The Caveats

Some warnings about CodeView:

First, if you've been using DEBUG or SYMDEB, you should be aware that all
the CodeView commands use a civilian form of notation called base-10 or
decimal. If you enter D 197F, you'll get an error message. You should have
typed D 6527 or D 0x197F instead. You can change the radix to hexadecimal if
you can't get accustomed to this. (Dumps, assembly language listings, and
register contents are displayed in hexadecimal regardless.)

However, be aware that watch, watchpoint, and tracepoint expressions also
use the radix. If you set the radix to 16, the numbers you type in the
expressions will also be interpreted as hexadecimal. For instance, this is
the watch I showed earlier:

  2 * (row + 80 * col)

After radix is set to 16, the 80 is interpreted as hexadecimal and would
actually be the decimal value 128. If you'll be changing the radix, write
expressions with explicit hexadecimal or decimal prefixes, such as

  2 * (row + 0x50 * col)
or
  2 * (row + 0n80 * col)

Second, CodeView 1.00 has at least one bug in its assembly and disassembly
logic regarding the DWORD data type (typically used in floating point
arithmetic). It treats DWORDs as WORDs.

Finally, don't get rid of SYMDEB. CodeView itself uses 160K of memory, and
.EXEs prepared for CodeView are larger than normal to include the symbol
information, so you may have problems with large programs. Go back to SYMDEB
for those.

SYMDEB is also easier to use for some chores, such as taking a quick look at
some disassembled ROM code, printing a disassembly, or creating short
assembly language .COM files. For exploration and experimentation, you'll
still think SYMDEB. For serious debugging, you'll definitely want to use Code

───────────────────────────────────────────────────────────────────────────
A Look at DEBUG and SYMDEB
───────────────────────────────────────────────────────────────────────────

Remember the "good" old days? you'd start off with and innocent looking
peice of C code:

mainmenu ()
        {
        int i, code, good, donw = 0 ;

        do
                {
                topline (atl) ;
                menutype ("MAIN MENU", at3) ;

                clrwin (6, 10, 24, 69, at2) ;
                clrwin (6, 0, 24, 9, at3) ;
                clrwin (6, 70, 24, 79, at3) ;

                writestr ( 7, 21, "Press:", at2) ;

But when it came time to do a little debugging in DEBUG, all you saw is
something that just doesn't look the same:

-u 24a
1434:024A 55           PUSH      BP
1434:024B 8BEC         MOV       BP,SP
1434:024D V80800       MOV       AX,0008
1434:0250 E8881C       CALL      1EDB
1434:0253 57           PUSH      DI
1434:0254 56           PUSH      SI
1434:0255 C746FE0000   MOV       WORD PTR [BP-02],0000
1434:025A FF36A241     PUSH      [41A2]
1434:025E E8B304       CALL      0714
1434:0261 83C402       ADD       SP,+02
1434:0264 FF36A641     PUSH      [41A6]
1434:0268 B86E01       MOV       AX,016E
1434:026B 50           PUSH      AX
1434:026C E8E004       CALL      074F
1434:026F 83C404       ADD       AP,+04
1434:0272 FF36A441     PUSH      [41A4]

Are we really sure it's the same program? Fortunately, SYMDEB took some of
the guesswork out of debugging (below). Today, the CodeView debugger
included with the Microsoft C Compiler 4.00 makes debugging even less
painful.

-u _mainmenu
12:             {
_TEXT:_mainmenu:
1AA4:024A 55           PUSH      BP
1AA4:024B 8BEC         MOV       BP,SP
1AA4:024D B80800       MOV       AX,0008
1AA4:0250 E8881C       CALL      _chkstk
1AA4:0253 57           PUSH      DI
1AA4:0254 56           PUSH      SI
20:            int i, code, good, done = 0 ;
1AA4:0255 C746FE0000   MOV       Word Ptr [BP-02],0000
24:                   topline (atl) ;
1AA4:025A FF36A241     PUSH      [41A2]
1AA4:025E E8B304       CALL      _topline
1AA4:0261 83C402       ADD       SP,+02

───────────────────────────────────────────────────────────────────────────
Codeview Command Summary
───────────────────────────────────────────────────────────────────────────

╓┌─────────────┌────────────────────┌────────────┌────────────┌──────────────╖
Command       Dialog Window        Menu         Mouse        Function Key

Assemble      A (address)

Breakpoint    BC list:*            Run/Clear    Left click   F9 (cursor on
Clear                              Breakpoints               break line)

Breakpoint    BD list:*
Disable

Breakpoint    BE list:*
Enable
Command       Dialog Window        Menu         Mouse        Function Key
Enable

Breakpoint    BL
List

Breakpoint    BP (address)                      Left click   F9 (cursor on
              (passcount)                       break line   break line)
              (commands)

Dump Data     D (type) (range)

Enter Data    E (type) address
              (list)

Execute       E                    Run/Execute

Go            G                    Go!          Click Go!    F3

Goto          G (break                          Right click  F7 (cursor on
              address)                          break line   break line)
Command       Dialog Window        Menu         Mouse        Function Key
              address)                          break line   break line)

Help          H                    View/Help                 F1

Stack Trace   K                    Calls

Restart       L                    Run/Restart
              (arguments)

Restart & Go                       Run/Start

Radix         N (radix)

Program step  P (count)                         Right click  F10
                                                Trace !

Quit          Q                    File/Quit

Register      R                    Options/                  F2
Display                            Registers
Command       Dialog Window        Menu         Mouse        Function Key
Display                            Registers

Register      R (register)                      Click on
Change        ((=)                              Flag
              expression))                      Mnemonic

Set Mode -    S +                  View/Source               F3
Source

Set Mode -    S -                  View/                     F3
                                   Assemble

Trace         T (count)            Trace!       Left click   F8
                                                Trace!

Tracepoint    TP? expression       Watch/
              (,format)            Tracepoint
              TP (type) range

Unassemble    U (range)
Command       Dialog Window        Menu         Mouse        Function Key
Unassemble    U (range)

View File     V (. (filename:)     File/Load
              line)
              V (expression)

Watch         W? expression        Watch/Add
              (,format)            Watch
              W(type) range

Watch List    W

Watchpoint    WP? expression       Watch/
                                   Watchpoint

Examine       X? (module!)
Symbol        (function.)
              (symbol) (*)

Examine       X*
Command       Dialog Window        Menu         Mouse        Function Key
Examine       X*
Module Names

Delete Watch  Y number             Watch/Delete
                                   Watch

8087 Dump     7

Display       ?expression          View/
                                   Evaluate

Expression    (,format)
Display       .
Location

Redraw        .@
Screen

Screen        \                    View/Output
Exchange
Command       Dialog Window        Menu         Mouse        Function Key
Exchange

Search        /(expression)        Search/Find

Search Next   /                    Search/Next
Search                             Search/
Previous                           Previous

Search Label  V (expression)       Search/Label

Shell to DOS  !                    File/Shell

Tab Size      # number


────────────────────────────────────────────────────────────────────────────

Ask Dr. Bob!

Peeks and Pokes

Dear Dr. Bob,

In my programs I often have to get at memory outside my program segment, say
to retrieve values from the BIOS data area or to write directly to the
display. In BASIC I can use PEEK and POKE. How can I do PEEKs and POKEs in
C?──"C"urious

Dear "C"urious,

Very easily. The flexibility you have using pointers in C makes this job
almost trivial. For instance, if you need to retrieve the current video mode
(located at address 0040:0049 in the BIOS data area), you can use the
statement in Figure 1. The 0x400049L is a long integer containing the
segment and offset address of the value you want to access. The (char far *)
casts this number into a far pointer to a character. The indirection
operator * retrieves the 1-byte value stored at this pointer.

You can also use a similar construction to store values, as shown in
Figure 2. Obviously, in this example, you would not do this unless your
program has manually changed the video mode and you want the BIOS data area
to agree with what you have done.

If you have to reference data outside your program frequently, you might
want to set up a couple of macros, as shown in Figure 3, where s and o are
the segment and offset parts of the far address. You would use MEMC for 1-
byte characters and MEMI for integers. The examples in Figure 3 would then
look like Figure 4.

If you need a far pointer that accesses the IBM Color Graphics Adapter, you
could define it as shown in Figure 5.


Interrupts

Dear Dr. Bob,

I've been using the intdos and int86 functions included with the Microsoft C
3.0 compiler to call DOS and BIOS routines directly. I haven't experienced
many problems. However, I recently tried to use int86 for the DOS Interrupts
0x25 and 0x26 (which read and write absolute sectors on a disk) and I can't
get it to work. For instance, this program should read the first sector of a
drive A: disk into the array called buffer:

  #include <dos.h>
  main ()
  {
  char    buffer [512];
  union   REGS regs;
  regs.h.al=0;
  regs.x.bx=(int)buffer;
  regs.x.cx=1;
  regs.x.dx=0;
  int86 (0x25, &regs, &regs);
  }

But this program crashes. What's the problem?──Puzzled

Dear Puzzled,

Interrupts 0x25 and 0x26 are a little different from the other DOS and BIOS
interrupts because they leave the original flags on the stack. The stack
must be adjusted after returning from the interrupt. The int86 function
included with C 3.0 did not make this adjustment.

The solution to your problem is as simple as an upgrade. The int86 function
in Microsoft C 4.0 specifically checks for Interrupts 0x25 and 0x26 and
adjusts the stack after the interrupt function returns control.


Arrays and Pointers

Dear Dr. Bob,

I've been programming in C for a while now, but I still get confused about
the relationship between arrays and pointers. For instance, I can define two
variables like this:

  char str1[] = "This is a string" ;
  char *str2 = "This is a string" ;

For some purposes, str1 and str2 appear to work the same. Other times,
however, it's obvious that they are not the same. What is the difference
between a character array and a pointer to a character string, and when
should I use one instead of the other.──Unsure

Dear Unsure,

Both str1 and str2 may be used as pointers that address the first element of
the string. Both

  puts (str1) ;

and

  puts (str2) ;

print the string on the display. It may help to think of str2 as a pointer
to the string and str1 as shorthand notation for &str1[0], which is a
pointer to the first element of the array.

You can see what the compiler does with these two definitions by compiling
with the /Fc switch. The compiler creates a combined source code and
assembly code listing created as a file with the .COD extension.

If both str1 and str2 are defined at the external level (outside of any
function), then str1 is compiled into the assembly language statement shown
in Figure 6, while str2 is compiled in the manner shown in Figure 7.
The pointer definition takes up 2 additional bytes of memory.

When you define and initialize str1, you are allocating 17 bytes to hold the
string and the terminating zero. When you define str2, however, you are
really only allocating 2 bytes for the pointer. The initialization directs
the compiler to store in str2 the address of the string. In theory, this
string should be treated as "read-only" (but nothing bad will happen if you
try to write into it). For str1, you've defined the whole string, so you can
read from it and write to it.

Again in theory, if you also define

  char str3[] = "This is a string" ;

and

  char *str4 = "This is a string" ;

then str1 and str3 must define two separate strings. However, it would be
legitimate for a compiler to initialize str2 and str4 as pointers to the
same string. (The Microsoft C Compiler will not do this.)

For both str1 and str2, you can reference characters in the strings using
either array notation or indirection. All of these statements will then put
the character 's' into the character variable ch:

  ch = *(str1 + 3) ;
  ch = str1[3] ;
  ch = *(str2 + 3) ;
  ch = str2[3] ;

The assignment statements using str1 are compiled into assembly language
statements that look something like this:

  mov al,[_str1 + 3]
  mov [_ch],al

However, an extra assembly language step is required for the assignments
using str2 because the address of the string must be retrieved from str2
before the string can be referenced:

  mov bx,[_str2]
  mov al,[bx + 3]
  mov [_ch],al

If str1 and str2 are defined inside a function, then str1 must be defined as
static:

  static char str1[] = "This is a string" ;

because arrays can't be initialized inside functions. However, you can
define str2 either way:

  char *str2 = "This is a string" ;

or

  static char *str2 = "This is a string" ;

In the first case, 2 bytes will be allocated on the function's stack frame
for str2, and the function will store the address of the string in that
location. In the second case, the str2 pointer variable is stored in static
memory just as if it were defined outside the function.

So far it looks like the array definition is preferable to the pointer
definition, but this is not always the case. One important distinction is
that str2 is an lvalue, but str1 is not; that is, you can assign another
pointer to str2. You can say

  str2 = str1 ;

but you cannot say

  str1 = str2 ;

If you go back and look at the assembly language code for the str1
definition, you will see that this second statement makes no sense.

Initialized strings are more efficient when defined as an array. However,
if you need to use the variable name as a pointer to other strings, then
you must use a pointer definition.


Recognizing Windows

Dear Dr. Bob,

When I try to execute a Windows application on the DOS command level, I get
the message "This program requires Microsoft Windows." How does a Windows
application determine whether it's running under Windows or running under
plain MS-DOS?──Wondering

Dear Wondering,

It doesn't. Although Windows programs have an .EXE extension, the format of
the .EXE file is different from the DOS .EXE format. This "new format
executable" is created by LINK4, the Microsoft Windows linker. Only the
Windows loader knows about the new format of the .EXE file; MS-DOS does not.
But the new format executable is constructed in such a way that it appears
to MS-DOS to be an old format executable.

The program that runs when Microsoft Windows loads the .EXE file is the
Windows application. The program that runs when MS-DOS loads the .EXE file
is a small piece of code that prints the message "This program requires
Microsoft Windows."

The new format executable is required to give Microsoft Windows more
information about the program. Windows must know which segments of the
program are code and which are data, which segments it can move around
in memory and

which segments must be fixed, whether a segment can be loaded immediately or
only when it is needed, and so forth.

The new format executable also contains support for resources (menus, icons,
cursors, bitmaps, dialog boxes, etc.) that Windows loads from the disk as
the application requires them. All calls to Windows functions from the
application appear in the new format executable as dummied-out far calls.
The Microsoft Windows loader resolves these calls based on a table that is
also within the .EXE file.

The program that runs when MS-DOS attempts to execute the Windows
application can be any .EXE file and not necessarily the standard one. You
could even use a non-Windows version of your application for that file.


Figure 1:

videomode = *(char far *)0x400049L ;


Figure 2:

*(char far *)0x400049L = videomode ;


Figure 3:

#define MEMC(s,o) (*(char far *)((long)(s)<<16|(o)))
#define MEMI(s,o) (*(int far *)((long)(s)<<16|(o)))


Figure 4:

videomode = MEMC (0x40, 0x49) ;
MEMC (0x40, 0x49) = videomode ;


Figure 5:

int far *colorscreen = 0xB8000000L ;


Figure 6:

_str1    db "This is a string", 0


Figure 7:

label    db "This is a string", 0
_str2    dw label