Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Tools

Text Screen Metrics


MAR89: STRUCTURED PROGRAMMING

Ahhh, Comdex. Recall the classic Rocky & Bullwinkle episode in which Bullwinkle is interrogated by a crew of trenchcoated FBI types who speak only in a jack Webb monotone? After a few questions, Bullwinkle begins to adopt the same monotone, and the spooks take umbrage.

"You're trying to make fun of the way we talk?" one asks.

"No," replies Bullwinkle, "but it's catching!"

I thought of Bullwinkle after trudging past the eleven-hundredth booth full of wool-suited Asian gentlemen selling power supplies and clone cases, each of whom greeted me by asking, "Are you dealer?"

After awhile I had to choke back the impulse to reply, "No, I writer."

The Invasion of the Pie-Expanders

Comdex has always been primarily a hardware show, and in recent years has become an increasingly Asian hardware show, hence my subliminal impulse to deal with our Pacific Rim partners in their own syntax. Against constant protectionist pressures from both left and right, I have to keep reminding people that the Asian manufacturers keep making the pie bigger. The more entry-level people who buy cheap Asian clones in 1989, the more experienced users will buy higher-cost American (and generally more powerful) 80486 machines in 1991. Growth in our industry requires bringing 'em in at the bottom and spreading computing skills throughout society. American companies prefer to priceskim at the top, and go belly-up when the cream is exhausted --leaving us no recourse but to keep generating cream. By doing us the favor of manufacturing our industry's loss-leaders, our Asian partners are keeping Compaq and Apple alive.

At Comdex, the hardware to gladden a programmer's heart was almost universally American --and expensive. ATT Bell Labs was showing a research monitor --not even a pre-prototype --with a monochrome resolution of 4096 by 4096 and a 16-Mbyte refresh buffer. 300 DPI video to match 300 DPI lasers. Maybe 1994.

International Meta Systems showed their Max2 30 MIPS accelerator board for Digitalk's Smalltalk/V. The 4-Mbyte $8,000 system makes the Xerox Dorado workstation I played with in 1983 look pretty sick --and doesn't lock you out of mainstream DOS and OS/2 applications. (I'll have a lot more to say about Smalltalk in future columns.)

I was astonished to see a Xerox-blessed implementation of Alan Kay's Dynabook product there, offered by Scenario Inc. of Somerville Mass. The Dynabook, of course, was the book-shaped, 100 percent flat-screen computer Xerox PARC proposed in 1976 as the future of computing. You curl up in your cushy chair and read it like a book, thumbing the screen to flip pages and touching hot-link keywords to traverse a hypertext thread. No keyboard, although Xerox's original design included one. Scenario's Dynabook has a CD-ROM reader and will be positioned as a research tool, with a giant effort to get low-cost ($40) reference works and classics on CD-ROM. An amazing thing -- and only $5,000.

My own personal Comdex favorites (perhaps because they're a realizable fantasy) are Brier Technology's 20-Mbyte and 40-Mbyte 3.5-inch 70ms diskette drives. The $30 diskettes are mechanically identical to PS/2 and Mac diskettes, and the drives are no larger. Furthermore, the drives can detect and read both 720K and 1.4-Mbyte diskettes. Two of these critters, plus a fast internal 40-Mbyte hard drive are all a programmer's workstation would need for backup, archiving, and multiple media compatibility. Brier showed at the Datavue booth --which might give you a hint as to where the drives will first appear. No price yet. Available '89.

Products like these hint at what our programmer's workstation will be in 1992. I call it the Red Hot Shoebox, and the machine itself will be a minimal swelling on the cable between the keyboard and the screen. But oh, what a swelling....

Comdex Software

Hiding behind a mountain of clone motherboards were a few important programmer tools. The best kept secret had to be GoScript, an impressive $195 PostScript clone from LaserGo Inc. Feed it a megabyte of RAM and any supported graphics-capable printer can print like a LaserWriter. With everybody else skimming the cream, GoScript could become the Turbo Pascal of the PostScript world. Sell Adobe, quick.

In the same general category are Maxem's Cause and Clarion Software's Clarion, two application generators slanted toward vertical-market developers. Those of you who make your living creating integrated dog-kennel management systems had better abandon both C and Pascal --neither is necessary in the transaction-dominated world of vertical markets, where everything's diskbound to begin with and performance is not a compelling consideration. What either product can do in a weekend could take weeks in traditional languages, with results that are solid and attractive if not blazingly fast. Cause provides an intriguing portability link between the PC and the Mac --probably the best I've seen.

The sharpest tool I encountered at Comdex was not even "at" Comdex --the company was not exhibiting and was only giving private demos to the programmer press. SoftTools of Atlanta demonstrated CASE:W, and I can only characterize it as the sword that will cut the Gordian knot of Microsoft's Windows/PM API. Basically, CASE:W is a prototyping tool that does for Windows and PM what Bricklin's Demo does for DOS text screens. Once you have interactively created (via point-and-drag) your menus and menu trees, icons, and dialog boxes, CASE:W compiles the prototype to a C source file containing all the hairy stuff of a Windows application. Windows is an object-friendly environment that dispatches event messages to an application, which must parse those events and take action. Creating the event handler is the tough part, and CASE:W does all that. The output source file is crisply arranged and well-commented. Even I, who laid C aside years ago as the sorry mess it is, could pick up and read the skeletal event handler.

CASE:W is a C tool for now, but nothing would prevent SoftTools (or some other firm) from doing the same tool for Modula-2 or Pascal. Time saved: Awesome. This is the future, guys. Bone up on CASE techniques and keep your eyes open. In the thick of a recession, it could save your skin.

The Text Mode Point Spread

Last month I explained how to use BIOS calls to determine which of the several IBM video adapters was installed in a given machine. Why is this useful? The obvious reason is to determine which of the numerous graphics modes are legal for any given board. But graphics entirely aside, there are numerous text-oriented reasons for knowing which adapter is on the bus.

First and foremost is the matter of screen size. Most people who have graduated to EGAs or VGAs now know that PC screens are not immutably limited to 25 lines. The EGA can display 25-and 43-line screens. The VGA can display 25-, 28-, and 50-line screens. Most good programming environments (including Turbo Pascal, TopSpeed Modula-2, and QuickBasic) can make use of the larger screens, and with some care, your applications can too.

Screen size a la PC is a peculiar concept in that it is not mode-dependent but font-dependent. Understand PC text fonts, and you'll get an understanding of screen size along for free.

The first-generation video adapters, the CGA and MDA, have only one font in ROM. The CGA font is an 8 x 8 font, meaning that each character in the font is created from pixels in a matrix eight characters high by eight characters wide. To leave space between horizontally adjacent characters, and to allow for lower-case descenders on characters like p and q, the character patterns are limited to a 7 x 7 pixel subset of the 8 x 8 pixel matrix.

The MDA font is an 8 x 14 font with a twist: The rightmost vertical column of pixels within the font is duplicated to its own right, making the font look like a 9 x 14 font. Since in all but the line-draw characters the rightmost column is empty, the result is that the line-draw characters are "stretched" so that they can touch one another in the horizontal direction, while allowing a little extra horizontal space between normal characters. This is done with electrical smoke and mirrors within the MDA's circuitry, and since the MDA's font can't be modified there isn't much more to be said about it.

The fun starts when you consider the EGA. The EGA's default font is also 8 x 14, but without the MDA's ninth-inning stretch. In truth, the ASCII characters in the EGA's default font are limited to a 7 x 9 pixel subset of the 8 x 14 matrix. There are three dead pixels below the font's baseline, and two dead pixels above. This allows true 2-pixel descenders to be used on lower-case characters, and also provides comfortable vertical spacing between character rows on the screen. The characters themselves, however, are not much better formed than the CGA's crude 7 x 7 patterns.

The EGA contains another font in ROM. This alternate font was included to allow the EGA to fully emulate the CGA, and is thus identical to the CGA's 8 x 8 font. This font can be loaded in place of the 8 x 14 font, and when loaded, whammo! Your screen suddenly contains 43 lines rather than 25.

How so? It's a question of allocating scan lines. In text mode, the EGA places 350-scan lines on the screen. If it allocates these scan lines to character rows at 14-scan lines per row, you can fit 25 lines on the screen. On the other hand, if you allocate the scan lines to character rows at 8-scan lines per row, you get 43 lines and change. The "change" amounts to 6-scan lines -- about all the EGA ever exhibits of "overscan," what we used to call the "text border" on the CGA. In the default mode, there is essentially no overscan. (So everybody puh-leez stop asking me how to set the text border color on the EGA!)

Changing fonts trips some additional logic within the EGA that tells the BIOS at what line to scroll and all, but in the large it's only a question of allocating a fixed number of scan lines in two different ways. The fonts are the same width, so we differentiate them by their pixel height. This is sometimes called the fonts' point size, but it has little to do with traditional print measurement by points.

PS/2 Complications

In keeping with IBM's "extend but don't replace" policy, the VGA has everything that the CGA and EGA have, and then some. In addition to both the 8- and 14-pixel fonts, the VGA has a 16-pixel font, which is its default font. The VGA puts 400 scan lines on the screen in text mode. Allocate those lines to a 16-pixel font and you get 25 lines. Allocate 400 lines to a 14-pixel font, and you get 28 lines. Finally, allocate 400 lines to an 8-pixel font, and you get 50 lines.

The MCGA, which is the VGA's developmentally-handicapped little brother, has a stubborn BIOS that understands only the 16-pixel font and will refuse to display other than a 25-line screen. There are rumored methods to club the BIOS from behind and do the work yourself, but having neither an MCGA nor much respect for it, I haven't explored further.

The secret to determining the number of lines currently on the screen (and hence the size of the current screen buffer) is to determine what font is current loaded. Once you've identified the resident adapter, that part is a snap.

Numerous Enumerations

In my last column, I defined an enumerated type encompassing all the PC display adapters.

<PRE>
AdapterType =
   (None,MDA,CGA,EGAMono,
   EGAColor,VGAMono,VGAColor,
   MCGAMono,MCGAColor);</PRE>
   
<P>

   
   

Given that there are only three legal font sizes, it makes sense to enumerate them as well.

FontSize = (Font8, Font14, Font16);

Enumerated types help your code to document itself, and being 8-bit quantities, they are small and fast compared to hanging string tags on things like display adapters. The function we'll use to detect the current font, GetFontSize, will return a value of type FontSize.

Most display adapters only support one font size, which can be reliably hard-coded as part of a CASE statement. For the EGA and VGA, font size is found through a BIOS call. Video interrupt 10H has a service that returns various bits of information about the current video state in the registers. The service is selected with 11H in AH and 30H in AL. On return, the number of pixels in the current font will be found GetFontSize is contained within the Turbo Pascal unit TextInfo in Listing One (textinfo.pas), page 142.

The number of lines on a screen is a function of the adapter type and the font size. This suggests that a two-dimensional array of integers can be defined such that indexing into the array with the adapter type and the font size will yield the number of lines on a screen for that combination of adapter type and font size. Both factors are present as enumerated types, and enumerated types may index arrays in both Pascal and Modula-2.

This is the algorithm for the procedure GetTextBufferStats, also in Listing One. Line counts are arranged in a table that comprises the beef in an array constant. With some comments to show what the table rows and columns represent, it's an example of creating code that is truly self-documenting, in that the documentation is the code. The alternative is to thread through a multiplynested IF statement that would admittedly save 54 bytes of data segment but (in addition to being slower) would be a great deal harder to read.

Override!

There's something else a little different about GetTextBufferStats. Its CheckFor-Override parameter is a procedural type. Procedural types have always existed in Modula-2 and have only recently been added to Turbo Pascal with version 5.0. What happens inside GetTextBufferStats is this: The number of characters in a line is returned by BIOS VIDEO service $F. The procedure then uses Query-AdapterType and GetFontSize to derive a screen-line count through BIOS-based methods that assume one of the IBM display adapters or an exact clone.

The nature of the PC text video architecture, however, allows stretching screen extents considerably. Assuming that a refresh buffer begins at either $B000 (monochrome) or $B800 (color), it may be mapped any convenient way, say, 132 x 43 or 80 x 40. My own display is the absolutely stunning Genius VHR 401 display, which was a desktop publishing display (728 x 1004, portrait-style) long before there was any such thing as desktop publishing. It has something the other DTP displays don't have: a large-format monochrome text mode that may be configured as 80 columns by the standard letter-paper-sized 66 lines, or by 70, 76, or 83 lines as desired. Ten minutes with that tube will make you wonder why we've settled for 25-line brain-damaged television sets for so long --but don't get me started on that particular rant. (You'll hear it in this column often enough in the future.)

The point is that there may be displays that support more than the IBM-supported 80 columns and 50 rows -- or some oddball values in between. 132-column text displays exist for spreadsheet jockeys. And lord only knows what can be done with a PCjr --a machine so bizarre that I don't even consider it IBM-compatible. The extended text video devices that I've seen (including the Genius) always "look like" some standard IBM board from a BIOS and control level to keep some measure of compatibility. Only the character-to-refresh buffer mapping differs. To support such creatures, you need to be able to override the logic in GetTextBufferStats with custom logic supplied by you or someone else. This can be done simply by executing an override procedure after executing GetTextBufferStats but allowing GetTextBufferStats to call the override procedure itself ensures that the job will be done.

The override procedure should perform its own display detection specific to the oddball target display, and if the oddball display is found, the override routine can replace the IBM-supported X and Y extents with the oddball display's X and Y extents. GetTextBufferStats then calculates the screen size using the overriding values. The override procedure NullOverride provided with the TextInfo unit does nothing at all -- it simply lets the IBM-supported values pass unchanged. I'll provide an example of a real override procedure next month, specific to the Genius VHR display.

Note that its use of procedural parameters forces TextInfo to require compilation with Turbo Pascal 5.0.

Odds 'n Ends

TextInfo exists to tell us important things about the machine's display in text mode. As I alluded to earlier, there is the matter of the starting address of the video refresh buffer. A simple rule applies here: Any video board connected to a monochrome display keeps its buffer at $B000, and any board connected to a color display keeps its buffer at $B800. The exceptions are things like (blecch!) black-and-white TV sets, or those awful $69 composite video monochrome monitors from the dawn of PC time. Both connect only to CGAs, however, so the buffer remains at $B800, monochrome notwithstanding. Once your video board has been correctly detected, the GetTextBufferOrigin function can return the buffer's address without fail.

Similarly, GetBIOSTextMode returns the current text mode with a single BIOS call. This is handy when you want to move into a specific video mode (perhaps one of the multitude of graphics modes) and still return to the text mode that was in force when your application began running.

One thing TextInfo does not address is how many pages of display buffer memory are available in the installed video board. The VGA has as many as 32, while the poor MDA has only one. Some clones have more than IBM boards, some less. The infuriating thing is that I have yet to discover a reliable means of determining how many pages are available in the installed board by inspection. You can assume that a CGA will have four pages ... and you could be wrong. The clone business is like that. Any clues? This boy would like to know.

Listing Two (texttest.pas), page 143, is a short program that exercises TextInfo. Listings Three and Four (textinfo.def and textinfo.mod), pages 143 and 144, are the TopSpeed Modula-2 implementation of TextInfo. (Both the definition and implementation modules, which Turbo Pascal combines in one unit file.) Listing Five (texttest.mod), page 146, is the test program in Modula-2.

Part of the Plan

One of the reasons I picked up this column is that my compatriot Kent Porter has stepped aside to kick off his graphics column. In pondering that, it occurred to me that while we spend a great deal of effort making our graphics screens flashy and dynamic, with exploding icons and lord knows what else, our text screens are content to look like something scraped off a 3278 mainframe terminal. My first big effort here, consequently, will be to provide you with tools to allow you to put some flash in your text displays as well. The fun's only beginning. In the coming months I'll be tossing some INLINE directives, machine code externals, and virtualized screen machines your way.

In the meantime, Mr. Byte and I are going to set the big scope out in the driveway and catch the opposition of Jupiter, who always looks better with a few belts. Drink in the night when you can, my friends --the stars are the very best antidote for a frantic life.

Products Mentioned

GoScript --LaserGo Inc. 9235 Trade Pl, Ste. A Sand Diego, CA 92126 619-530-2400 $195

Dynabook --Scenario Inc. 235 Holland St. Somerville, MA 02144 617-625-1818 $4,995

Cause --Maxem Corp. 1550 E University Ave. Mesa, AZ 85203 602-827-8181 $495

CASE:W --SofTools Inc. One Dunwoody Park, Ste. 130 Atlanta, GA 30338 404-399-6236 $1,495

Clarion Prof. Developer --Clarion Software 150 E Sample Rd. Pompano Beach, FL 33064 305-785-4555 $695

Max2 30 MIPS --Accelerator board International Meta Systems 23844 Hawthorne Blvd., Ste. 200 Torrance, CA 90505 213-375-4700 $5,695

BR3020 20-Mbyte, 3.5-inch diskette drive Brier Technology 2363 Bering Dr. San Jose, CA 95131 408-435-8463 (price not set)

_STRUCTURED PROGRAMMING COLUMN_ by Jeff Duntemann

[LISTING ONE]

<a name="00a5_000e">


{--------------------------------------------------------------}
{                        TextInfo                              }
{                                                              }
{             Text video information library                   }
{                                                              }
{                             by Jeff Duntemann                }
{                             Turbo Pascal V5.0                }
{                             Last update 11/20/88             }
{--------------------------------------------------------------}

UNIT TextInfo;

INTERFACE

USES DOS;


TYPE
  AdapterType  = (None,MDA,CGA,EGAMono,EGAColor,VGAMono,
                 VGAColor,MCGAMono,MCGAColor);

  FontSize     = (Font8,Font14,Font16);

  { The following type definition *requires* Turbo Pascal 5.0! }
  OverrideProc = PROCEDURE(VAR ForceX : Byte; VAR ForceY : Byte);


VAR
  TextBufferOrigin  : Pointer;
  TextBufferSize    : Word;
  VisibleX,VisibleY : Byte;


FUNCTION  GetBIOSTextMode : Byte;          { Returns BIOS text mode }

FUNCTION  GetFontSize : FontSize;          { Returns font height code }

FUNCTION  GetTextBufferOrigin : Pointer;   { Returns pointer to text buffer }

{ Returns visible X and Y extent plus buffer size in bytes: }

PROCEDURE GetTextBufferStats(VAR BX : Byte;
                             VAR BY : Byte;
                             VAR BuffSize : Word;
                             CheckForOverride : OverrideProc);

PROCEDURE NullOverride(VAR ForceX : Byte; VAR ForceY : Byte);

FUNCTION  QueryAdapterType : AdapterType;      { Returns installed display }

FUNCTION  FontCode(Height : Byte) : FontSize;  { Returns font height code }

FUNCTION  FontHeight(Code : FontSize) : Byte;  { Returns font height value}



IMPLEMENTATION


FUNCTION GetBIOSTextMode : Byte;

VAR
  Regs : Registers; { Type Registers is exported by the DOS unit }

BEGIN
  Regs.AH := $0F;   { BIOS VIDEO Service $F: Get Current Video Mode }
  Intr($10,Regs);
  GetBIOSTextMode := Regs.AL;  { Mode is returned in AL }
END;



FUNCTION QueryAdapterType : AdapterType;

VAR
  Regs : Registers; { Type Registers is exported by the DOS unit }
  Code : Byte;

BEGIN
  Regs.AH := $1A;  { Attempt to call VGA Identify Adapter Function }
  Regs.AL := $00;  { Must clear AL to 0 ... }
  Intr($10,Regs);
  IF Regs.AL = $1A THEN  { ...so that if $1A comes back in AL...  }
    BEGIN                { ...we know a PS/2 video BIOS is out there. }
      CASE Regs.BL OF    { Code comes back in BL }
        $00 : QueryAdapterType := None;
        $01 : QueryAdapterType := MDA;
        $02 : QueryAdapterType := CGA;
        $04 : QueryAdapterType := EGAColor;
        $05 : QueryAdapterType := EGAMono;
        $07 : QueryAdapterType := VGAMono;
        $08 : QueryAdapterType := VGAColor;
        $0A,$0C : QueryAdapterType := MCGAColor;
        $0B : QueryAdapterType := MCGAMono;
        ELSE QueryAdapterType := CGA
      END { CASE }
    END
  ELSE
  { If it's not PS/2 we have to check for the presence of an EGA BIOS: }
    BEGIN
      Regs.AH := $12;       { Select Alternate Function service }
      Regs.BX := $10;       { BL=$10 means return EGA information }
      Intr($10,Regs);       { Call BIOS VIDEO }
      IF Regs.BX <> $10 THEN { BX unchanged means EGA is NOT there...}
        BEGIN
          Regs.AH := $12;   { Once we know Alt Function exists... }
          Regs.BL := $10;   { ...we call it again to see if it's... }
          Intr($10,Regs);   { ...EGA color or EGA monochrome. }
          IF (Regs.BH = 0) THEN QueryAdapterType := EGAColor
            ELSE QueryAdapterType := EGAMono
        END
      ELSE  { Now we know we have an CGA or MDA; let's see which: }
        BEGIN
          Intr($11,Regs);   { Equipment determination service }
          Code := (Regs.AL AND $30) SHR 4;
          CASE Code of
            1 : QueryAdapterType := CGA;
            2 : QueryAdapterType := CGA;
            3 : QueryAdapterType := MDA
            ELSE QueryAdapterType := None
          END { Case }
        END
    END;
END;


{ All we're doing here is converting numeric font heights }
{ to their corresponding values of type FontSize.         }

FUNCTION FontCode(Height : Byte) : FontSize;

BEGIN
  CASE Height OF
     8 : FontCode := Font8;
    14 : FontCode := Font14;
    16 : FontCode := Font16;
  END { CASE }
END;


{ Likewise, this function converts values of type FontSize }
{ to their corresponding numeriuc values.                  }

FUNCTION FontHeight(Code : FontSize) : Byte;

BEGIN
  CASE Code OF
    Font8  : FontHeight := 8;
    Font14 : FontHeight := 14;
    Font16 : FontHeight := 16;
  END { CASE }
END;



FUNCTION GetFontSize : FontSize;

VAR
  Regs : Registers;  { Type Registers is exported by the DOS unit }

BEGIN
  CASE QueryAdapterType OF
    CGA       : GetFontSize := Font8;
    MDA       : GetFontSize := Font14;
    MCGAMono,
    MCGAColor : GetFontSize := Font16; { Wretched thing knows but 1 font! }
    EGAMono,        { These adapters may be using any of several different }
    EGAColor,       { font cell heights, so we need to query the BIOS to }
    VGAMono,        { find  out which is currently in use. }
    VGAColor  : BEGIN
                  WITH Regs DO
                    BEGIN
                      AH := $11;  { EGA/VGA Information Call }
                      AL := $30;
                      BH := 0;
                    END;
                  Intr($10,Regs); { On return, CX contains the font height }
                  GetFontSize := FontCode(Regs.CX);
                END
  END  { CASE }
END;



FUNCTION GetTextBufferOrigin : Pointer;

{ The rule is:  For boards attached to monochrome monitors, the buffer }
{ origin is $B000:0; for boards attached to color monitors (including  }
{ all composite monitors and TV's) the buffer origin is $B800:0.       }

BEGIN
  CASE QueryAdapterType OF
    CGA,MCGAColor,EGAColor,VGAColor : GetTextBufferOrigin := Ptr($B800,0);
    MDA,MCGAMono, EGAMono, VGAMono  : GetTextBufferOrigin := Ptr($B000,0);
  END  { CASE }
END;


{ This proc provides initial values for the dimensions of the visible }
{ display and (hence) the size of the visible refresh buffer.  It is  }
{ called  by the initialization section during startup *BUT* you must }
{ call it again after any mode change or font change to be sure of    }
{ having accurate values in the three variables! }

PROCEDURE GetTextBufferStats(VAR BX : Byte;        { Visible X dimension }
                             VAR BY : Byte;        { Visible Y dimension }
                             VAR BuffSize : Word;  { Refresh buffer size }
{ This requires TP5.0! }     CheckForOverride : OverrideProc);

CONST
  ScreenLinesMatrix : ARRAY[AdapterType,FontSize] OF Integer =
                   { Font8:  Font14: Font16: }
  {      None: }     ((25,     25,     25),
  {       MDA: }      (-1,     25,     -1),
  {       CGA: }      (25,     -1,     -1),
  {   EGAMono: }      (43,     25,     -1),
  {  EGAColor: }      (43,     25,     -1),
  {   VGAMono: }      (50,     28,     25),
  {  VGAColor: }      (50,     28,     25),
  {  MCGAMono: }      (-1,     -1,     25),
  { MCGAColor: }      (-1,     -1,     25));

VAR
  Regs : Registers;   { Type Registers is exported by the DOS unit }

BEGIN
  Regs.AH := $0F; { BIOS VIDEO Service $F: Get Current Video Mode }
  Intr($10,Regs);
  BX := Regs.AH;  { Number of characters in a line returned in AH }

  BY := ScreenLinesMatrix[QueryAdapterType,GetFontSize];
  IF BY > 0 THEN
    BEGIN
      CheckForOverride(BX,BY);  { See if something weird is on the bus... }
      BuffSize := (BX * 2) * BY { Calculate the buffer size in bytes }
    END
  ELSE BuffSize := 0;
END;

{ This is the default override proc, and is called anytime you're }
{ not concerned about finding a nonstandard text adapter on the   }
{ bus.  (Funny graphics cards with normal text modes don't matter }
{ to this library.)  If you want to capture any weird cards, you  }

{ must provide your own override proc that can detect the card    }
{ and return correct values for the visible X and Y dimensions.   }

PROCEDURE NullOverride(VAR ForceX : Byte; VAR ForceY : Byte);

BEGIN
  { Like I said; Null... }
END;


{ The initialization section provides some initial values for the   }
{ exported variables TextBufferOrigin, VisibleX, VisibleY, and      }
{ TextBufferSize, so that you can use the variables without further }
{ kafeuthering. }

BEGIN
  TextBufferOrigin := GetTextBufferOrigin;
  GetTextBufferStats(VisibleX,VisibleY,TextBufferSize,NullOverride);
END.





<a name="00a5_000f"><a name="00a5_000f">
<a name="00a5_0010">
[LISTING TWO]
<a name="00a5_0010">


PROGRAM TextTest;

USES TextInfo;

BEGIN
  Write('The installed adapter is ');
  CASE QueryAdapterType OF
    None : Writeln('nothing I''ve ever seen.');
    MDA  : Writeln('an MDA .');
    CGA  : Writeln('a CGA.');
    EGAMono,EGAColor : Writeln('an EGA.');
    VGAMono,VGAColor : Writeln('a VGA.');
    MCGAMono,MCGAColor : Writeln('an MCGA.');
  END; { CASE }
  Writeln('The current font height is ',FontHeight(GetFontSize),'.');
  Writeln('The current BIOS text mode is ',GetBIOSTextMode,'.');
  Writeln('The current screen is ',VisibleX,' character wide',
          ' and ',VisibleY,' characters wide;');

  Writeln('  and occupies ',TextBufferSize,' bytes in memory.');
END.





<a name="00a5_0011"><a name="00a5_0011">
<a name="00a5_0012">
[LISTING THREE]
<a name="00a5_0012">


(*--------------------------------------------------------------*)
(*                        TEXTINFO                              *)
(*                                                              *)
(*     Text video information library -- Definition module      *)
(*                                                              *)
(*                             by Jeff Duntemann                *)
(*                             TopSpeed Modula 2 V1.12          *)
(*                             Last update 12/7/88              *)
(*--------------------------------------------------------------*)

DEFINITION MODULE TextInfo;

TYPE
  AdapterType = (None,MDA,CGA,EGAMono,EGAColor,VGAMono,
                 VGAColor,MCGAMono,MCGAColor);

  FontSize    = (Font8,Font14,Font16);

  OverrideProc = PROCEDURE(VAR BYTE,VAR BYTE);

VAR
  TextBufferOrigin  : ADDRESS;   (* Address of video refresh buffer   *)
  TextBufferSize    : CARDINAL;  (* Bytes contained in refresh buffer *)
  VisibleX,VisibleY : SHORTCARD; (* Dimensions of the visible display *)


PROCEDURE GetBIOSTextMode() : SHORTCARD;

PROCEDURE GetTextBufferOrigin() : ADDRESS;

PROCEDURE GetTextBufferStats(VAR BufX : BYTE;         (* Visible X dimension *)
                             VAR BufY : BYTE;         (* Visible Y dimension *)
                             VAR BuffSize : CARDINAL; (* Refresh buffer size *)
                             CheckForOverride : OverrideProc);

PROCEDURE QueryAdapterType() : AdapterType;

PROCEDURE FontCode(Height : SHORTCARD) : FontSize;

PROCEDURE FontHeight(Code : FontSize) : SHORTCARD;

PROCEDURE GetFontSize() : FontSize;

PROCEDURE NullOverride(VAR ForceX : BYTE; VAR ForceY : BYTE);

END TextInfo.





<a name="00a5_0013"><a name="00a5_0013">
<a name="00a5_0014">
[LISTING FOUR]
<a name="00a5_0014">


(*--------------------------------------------------------------*)
(*                        TEXTINFO                              *)
(*                                                              *)
(*   Text video information library -- Implementation module    *)
(*                                                              *)
(*                             by Jeff Duntemann                *)
(*                             TopSpeed Modula 2 V1.12          *)
(*                             Last update 12/7/88              *)
(*--------------------------------------------------------------*)

IMPLEMENTATION MODULE TextInfo;

FROM SYSTEM IMPORT Registers;
FROM Lib IMPORT Intr;

VAR
  ColorBufOrg [0B800H:0] : WORD; (* First word in color refresh buffer *)
  MonoBufOrg  [0B000H:0] : WORD; (* First word in mono refresh buffer *)


PROCEDURE GetBIOSTextMode() : SHORTCARD;

VAR
  Regs : Registers;

BEGIN
  Regs.AH := 0FH;    (* VIDEO service 0FH *)
  Intr(Regs,10H);
  RETURN Regs.AL     (* AL contains current text mode on return *)
END GetBIOSTextMode;


PROCEDURE QueryAdapterType() : AdapterType;

VAR
  Regs : Registers;
  Code : SHORTCARD;


BEGIN
  Regs.AH := 1AH;  (* Attempt to call VGA Identify Adapter Function *)
  Regs.AL := 0;          (* Must clear AL to 0 ... *)
  Intr(Regs,10H);
  IF Regs.AL = 1AH THEN  (* ...so that if $1A comes back in AL...  *)
                         (* ...we know a PS/2 video BIOS is out there. *)
    CASE Regs.BL OF      (* Code comes back in BL *)
      0 : RETURN None             |
      1 : RETURN MDA;             |
      2 : RETURN CGA;             |
      4 : RETURN EGAColor;        |
      5 : RETURN EGAMono;         |
      7 : RETURN VGAMono;         |
      8 : RETURN VGAColor;        |
      0AH,0CH : RETURN MCGAColor; |
      0BH : RETURN MCGAMono;      |
      ELSE RETURN CGA
    END (* CASE *)
  ELSE
  (* If it's not PS/2 we have to check for the presence of an EGA BIOS: *)
    Regs.AH := 12H;       (* Select Alternate Function service *)
    Regs.BX := 10H;       (* BL=$10 means return EGA information *)
    Intr(Regs,10H);       (* Call BIOS VIDEO *)
    IF Regs.BX <> 10H THEN (* BX unchanged means EGA is NOT there...*)
        Regs.AH := 12H;   (* Once we know Alt Function exists... *)
        Regs.BL := 10H;   (* ...we call it again to see if it's... *)
        Intr(Regs,10H);   (* ...EGA color or EGA monochrome. *)
        IF (Regs.BH = 0) THEN RETURN EGAColor
        ELSE RETURN EGAMono
        END
    ELSE  (* Now we know we have an CGA or MDA; let's see which: *)
      Intr(Regs,11H);   (* Equipment determination service *)
      Code := SHORTCARD(BITSET(Regs.AL) * BITSET{4..5}) >> 4;
      CASE Code OF
        1 : RETURN CGA  |
        2 : RETURN CGA  |
        3 : RETURN MDA
        ELSE RETURN None
      END (* Case *)
    END
  END
END QueryAdapterType;


(* This is a simple "clean conversion" function for relating the *)
(* enumerated font size constants to SHORTCARD numeric font size *)
(* values.  *)

PROCEDURE FontCode(Height : SHORTCARD) : FontSize;

BEGIN
  CASE Height OF
    8 : RETURN Font8  |
   14 : RETURN Font14 |
   16 : RETURN Font16
   ELSE RETURN Font8
  END  (* CASE *)
END FontCode;


(* This is a simple "clean conversion" function for relating the  *)
(* SHORTCARD numeric font size values to the enumerated font size *)
(* constants *)

PROCEDURE FontHeight(Code : FontSize) : SHORTCARD;

BEGIN
  CASE Code OF
    Font8  : RETURN 8  |
    Font14 : RETURN 14 |
    Font16 : RETURN 16
  END (* CASE *)
END FontHeight;



PROCEDURE GetFontSize() : FontSize;

VAR
  Regs : Registers;

BEGIN
  CASE QueryAdapterType() OF
    CGA       : RETURN Font8  |
    MDA       : RETURN Font14 |
    MCGAMono,
    MCGAColor : RETURN Font16 |
    EGAMono,        (* These adapters may be using any of several  *)
    EGAColor,       (* different font cell heights, so we need to query *)
    VGAMono,        (* BIOS to find out which is currently in use. *)
    VGAColor  : WITH Regs DO
                  AH := 11H;  (* EGA/VGA Information Call *)
                  AL := 30H;
                  BL := 0;
                END;
                Intr(Regs,10H);
                RETURN FontCode(SHORTCARD(Regs.CX))
  END  (* CASE *)
END GetFontSize;


PROCEDURE GetTextBufferOrigin() : ADDRESS;

(* The rule is:  For boards attached to monochrome monitors, the buffer *)
(* origin is $B000:0; for boards attached to color monitors (including  *)
(* all composite monitors and TV's) the buffer origin is $B800:0.       *)

BEGIN
  CASE QueryAdapterType() OF
    CGA,MCGAColor,EGAColor,VGAColor : RETURN ADR(ColorBufOrg) |
    MDA,MCGAMono, EGAMono, VGAMono  : RETURN ADR(MonoBufOrg)
  END  (* CASE *)
END GetTextBufferOrigin;


(* This one function returns essential screen/buffer size information. *)
(* It is called by the initializing body of this module but should be  *)
(* called again after *any* mode change or font change! *)

PROCEDURE GetTextBufferStats(VAR BufX : BYTE;         (* Visible X dimension *)
                             VAR BufY : BYTE;         (* Visible Y dimension *)
                             VAR BuffSize : CARDINAL; (* Refresh buffer size *)
                             CheckForOverride : OverrideProc);

TYPE
  FontPoints  = ARRAY[Font8..Font16] OF INTEGER;
  PointsArray = ARRAY[None..MCGAColor] OF FontPoints;

VAR
  Regs : Registers;   (* Type Registers is exported by the DOS unit *)
  ScreenLinesMatrix : PointsArray;
  Adapter : AdapterType;
  Font    : FontSize;

(* TopSpeed can't do two-dimensional array aggregates, Turbo Pascal *)
(* style (arrgh) so we have to make it an array of arrays: *)

BEGIN
  ScreenLinesMatrix := PointsArray(
  (*      None: *)     FontPoints(25,     25,     25),
  (*       MDA: *)     FontPoints(-1,     25,     -1),
  (*       CGA: *)     FontPoints(25,     -1,     -1),
  (*   EGAMono: *)     FontPoints(43,     25,     -1),
  (*  EGAColor: *)     FontPoints(43,     25,     -1),
  (*   VGAMono: *)     FontPoints(50,     28,     25),
  (*  VGAColor: *)     FontPoints(50,     28,     25),
  (*  MCGAMono: *)     FontPoints(-1,     -1,     25),
  (* MCGAColor: *)     FontPoints(-1,     -1,     25));

  Regs.AH := 0FH;  (* BIOS VIDEO Service $F: Get Current Video Mode *)
  Intr(Regs,10H);
  BufX := Regs.AH; (* Number of characters in a line returned in AH *)

  BufY := SHORTCARD(ScreenLinesMatrix[QueryAdapterType(),GetFontSize()]);
  IF SHORTCARD(BufY) > 0 THEN
    CheckForOverride(BufX,BufY);  (* See if odd adapter is on the bus... *)
    (* Calculate the buffer size in bytes: *)
    BuffSize := (CARDINAL(BufX) * 2) * CARDINAL(BufY)
    ELSE BuffSize := 0
  END
END GetTextBufferStats;


(* This is the "default" override proc, called when there is no *)
(* suspicion of anything nonstandard on the bus.  Replace with  *)
(* a custom proc that looks for any nonstandard video adapter.  *)

PROCEDURE NullOverride(VAR ForceX : BYTE; VAR ForceY : BYTE);

BEGIN
  (* Like I said; Null... *)
END NullOverride;


(* The module body, like a Pascal unit initialization section, is *)
(* executed before the client program that imports this module or *)
(* any part of it. *)

BEGIN
  TextBufferOrigin := GetTextBufferOrigin();
  GetTextBufferStats(VisibleX,VisibleY,TextBufferSize,NullOverride);
END TextInfo.




<a name="00a5_0015"><a name="00a5_0015">
<a name="00a5_0016">
[LISTING FIVE]
<a name="00a5_0016">


MODULE TextTest;

FROM IO       IMPORT WrStr,WrLn,WrCard,WrShtCard;
FROM TextInfo IMPORT AdapterType,QueryAdapterType,GetFontSize,
                     FontHeight,GetBIOSTextMode,VisibleX,VisibleY,
                     TextBufferSize;

BEGIN
  WrStr("The installed adapter is ");
  CASE QueryAdapterType() OF
    None : WrStr("nothing I've ever seen.")     |
    MDA  : WrStr("an MDA.")                     |
    CGA  : WrStr("a CGA.")                      |
    EGAMono,EGAColor : WrStr("an EGA.")         |
    VGAMono,VGAColor : WrStr("a VGA.")          |
    MCGAMono,MCGAColor : WrStr("an MCGA.");
  END; (* CASE *)
  WrLn;
  WrStr('The current font height is ');
  WrShtCard(FontHeight(GetFontSize()),2);
  WrStr("."); WrLn;
  WrStr("The current BIOS text mode is ");
  WrShtCard(GetBIOSTextMode(),2);
  WrStr("."); WrLn;
  (* VisibleX and VisibleY are initialized by TextInfo module body *)
  WrStr("The current screen is ");
  WrShtCard(VisibleX,2);
  WrStr(" character wide and ");
  WrShtCard(VisibleY,2);
  WrStr(" characters high;");
  WrLn;
  WrStr("  and occupies ");
  (* TextBufferSize is initialized by TextInfo module body *)
  WrCard(TextBufferSize,6);
  WrStr(" bytes in memory."); WrLn;
END TextTest.












Copyright © 1989, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.